diff options
63 files changed, 1052 insertions, 188 deletions
@@ -10,6 +10,7 @@ StyleCop.Cache *~ PrecompiledWeb *.suo +*.log *.cache *.user *.tmp diff --git a/EnlistmentInfo.props b/EnlistmentInfo.props index 69cacb4..0c5d62d 100644 --- a/EnlistmentInfo.props +++ b/EnlistmentInfo.props @@ -5,6 +5,6 @@ <StyleCopTreatErrorsAsWarnings Condition=" '$(StyleCopTreatErrorsAsWarnings)' == '' and '$(Configuration)' == 'Release' ">false</StyleCopTreatErrorsAsWarnings> <StyleCopTreatErrorsAsWarnings Condition=" '$(StyleCopTreatErrorsAsWarnings)' == '' ">true</StyleCopTreatErrorsAsWarnings> - <ProjectRoot>$(MSBuildThisFileDirectory)\</ProjectRoot> + <ProjectRoot>$(MSBuildThisFileDirectory)</ProjectRoot> </PropertyGroup> </Project>
\ No newline at end of file @@ -4,14 +4,17 @@ <Import Project="$(ProjectRoot)tools\Translation.targets"/> <ItemGroup> - <ProjectsInDrop Include=" + <NightlyProjects Include=" samples\tools.proj; tools\drop.proj; + nuget\nuget.proj; " /> - <ProjectsToPublish Include=" + <NightlyProjects Include=" samples\samples.proj; doc\doc.proj; - " /> + "> + <Targets>DeployableArchive</Targets> + </NightlyProjects> <ProjectsToClean Include=" $(SolutionPath); @@ -61,11 +64,11 @@ </Target> <Target Name="Nightly"> - <MSBuild Projects="@(ProjectsInDrop)" Targets="%(ProjectsInDrop.Targets)" BuildInParallel="$(BuildInParallel)" /> + <MSBuild Projects="@(NightlyProjects)" Targets="%(NightlyProjects.Targets)" BuildInParallel="$(BuildInParallel)" /> </Target> <Target Name="Publish"> - <MSBuild Projects="@(ProjectsToPublish)" Targets="Publish" BuildInParallel="$(BuildInParallel)" /> + <MSBuild Projects="@(ProjectsToPublish)" Targets="%(ProjectsToPublish.Targets)" BuildInParallel="$(BuildInParallel)" /> </Target> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.automated.targets"/> diff --git a/doc/doc.proj b/doc/doc.proj index e75565c..55b2cb8 100644 --- a/doc/doc.proj +++ b/doc/doc.proj @@ -22,26 +22,30 @@ </PropertyGroup> </Target> - <Target Name="PrepareForPublish" DependsOnTargets="Prepare;BuildProduct;Html"> + <Target Name="PrepareForPublish" DependsOnTargets="BuildProduct;Html"> + <PropertyGroup> + <DocZip>$(DropDirectoryNoSlash)-htmldoc$(ZipFormat)</DocZip> + </PropertyGroup> + <ItemGroup> <DocSources Include="$(ProjectRoot)doc\api\**\*" /> - <DocTargets Include="@(DocSources->'$(DocWebRoot)\$(BranchName)\%(RecursiveDir)%(Filename)%(Extension)')" /> </ItemGroup> </Target> - <Target Name="Publish" + <Target Name="DeployableArchive" DependsOnTargets="PrepareForPublish" Inputs="@(DocSources)" - Outputs="@(DocTargets)"> - <RemoveDir Directories="$(DocWebRoot)\$(BranchName)" /> - <MakeDir Directories="$(DocWebRoot)\$(BranchName)" /> - <Copy SourceFiles="@(DocSources)" DestinationFiles="@(DocTargets)" SkipUnchangedFiles="true" /> + Outputs="$(DocZip)"> + <Delete Files="$(DocZip)" /> + <AddFilesTo7Zip + Files="@(DocSources)" + ZipFileName="$(DocZip)" + WorkingDirectory="$(ProjectRoot)doc\api\" + ToolPath="$(Zip7ToolPath)" /> </Target> - <Target Name="Unpublish" - DependsOnTargets="DeleteSiteOnIis" - Condition=" '$(DocWebRoot)' != '' "> - </Target> + <Target Name="Publish" + DependsOnTargets="DeployableArchive" /> <Target Name="CreateSiteOnIis" DependsOnTargets="Prepare"> <Error Text="The PublishDocsWebSiteName property must be set." Condition=" '$(PublishDocsWebSiteName)' == '' "/> diff --git a/lib/DotNetOpenAuth.BuildTasks.dll b/lib/DotNetOpenAuth.BuildTasks.dll Binary files differindex 27631d5..bb667f9 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 b01adda..bcb8d51 100644 --- a/lib/DotNetOpenAuth.BuildTasks.pdb +++ b/lib/DotNetOpenAuth.BuildTasks.pdb diff --git a/lib/DotNetOpenAuth.BuildTasks.targets b/lib/DotNetOpenAuth.BuildTasks.targets index e57ec97..27ca0ed 100644 --- a/lib/DotNetOpenAuth.BuildTasks.targets +++ b/lib/DotNetOpenAuth.BuildTasks.targets @@ -28,4 +28,6 @@ <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="DowngradeProjects" /> <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="HardLinkCopy" /> <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="PrepareOhlohRelease" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="AddFilesTo7Zip" /> + <UsingTask AssemblyFile="DotNetOpenAuth.BuildTasks.dll" TaskName="NuGetPack" /> </Project> diff --git a/nuget/DotNetOpenAuth.nuspec b/nuget/DotNetOpenAuth.nuspec new file mode 100644 index 0000000..05fc5f1 --- /dev/null +++ b/nuget/DotNetOpenAuth.nuspec @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<package> + <metadata> + <id>DotNetOpenAuth</id> + <version>$version$</version> + <authors>Andrew Arnott</authors> + <owners>Andrew Arnott</owners> + <projectUrl>http://www.dotnetopenauth.net/</projectUrl> + <iconUrl>https://github.com/AArnott/dotnetopenid/raw/v3.4/doc/logo/dnoa-logo_32x32.png</iconUrl> + <licenseUrl>http://www.opensource.org/licenses/ms-pl.html</licenseUrl> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <summary>OpenID, OAuth, & InfoCard library for web sites/services and apps.</summary> + <description>A C# library that adds OpenID 2.0 Provider and Relying Party, OAuth Consumer and Service Provider, and InfoCard Selector support to your web site both programmatically and through convenient drop-in ASP.NET controls.</description> + <language>en-US</language> + <dependencies> + <!-- optional --> + <!--<dependency id="log4net" version="1.2.10" />--> + </dependencies> + </metadata> +</package>
\ No newline at end of file diff --git a/nuget/content/web.config.transform b/nuget/content/web.config.transform new file mode 100644 index 0000000..07e22d9 --- /dev/null +++ b/nuget/content/web.config.transform @@ -0,0 +1,63 @@ +<configuration> + <configSections> + <section name="uri" type="System.Configuration.UriSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true"/> + </configSections> + + <uri> + <!-- The uri section is necessary to turn on .NET 3.5 support for IDN (international domain names), + which is necessary for OpenID urls with unicode characters in the domain/host name. + It is also required to put the Uri class into RFC 3986 escaping mode, which OpenID and OAuth require. --> + <idn enabled="All"/> + <iriParsing enabled="true"/> + </uri> + + <system.net> + <defaultProxy enabled="true" /> + <settings> + <!-- This setting causes .NET to check certificate revocation lists (CRL) + before trusting HTTPS certificates. But this setting tends to not + be allowed in shared hosting environments. --> + <!--<servicePointManager checkCertificateRevocationList="true"/>--> + </settings> + </system.net> + + <runtime> + <!-- This prevents the Windows Event Log from frequently logging that HMAC1 is being used (when the other party needs it). --> + <legacyHMACWarning enabled="0" /> + + <!-- When targeting ASP.NET MVC 2, this assemblyBinding makes MVC 1 references relink + to MVC 2 so libraries such as DotNetOpenAuth that compile against MVC 1 will work with it. --> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> + <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> + + <dotNetOpenAuth> + <!-- This is an optional configuration section where aspects of dotnetopenauth can be customized. --> + <!-- For a complete set of configuration options see http://www.dotnetopenauth.net/developers/code-snippets/configuration-options/ --> + <openid> + <relyingParty> + <security requireSsl="false" /> + <behaviors> + <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible + with OPs that use Attribute Exchange (in various formats). --> + <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> + </behaviors> + </relyingParty> + </openid> + <messaging> + <untrustedWebRequest> + <whitelistHosts> + <!-- Uncomment to enable communication with localhost (should generally not activate in production!) --> + <!--<add name="localhost" />--> + </whitelistHosts> + </untrustedWebRequest> + </messaging> + <!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. --> + <reporting enabled="true" /> + </dotNetOpenAuth> +</configuration>
\ No newline at end of file diff --git a/nuget/nuget.proj b/nuget/nuget.proj new file mode 100644 index 0000000..f8b8e93 --- /dev/null +++ b/nuget/nuget.proj @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))\EnlistmentInfo.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))' != '' " /> + <Import Project="$(MSBuildProjectDirectory)\..\tools\DotNetOpenAuth.automated.props"/> + + <Target Name="Layout" DependsOnTargets="BuildUnifiedProduct;ReSignDelaySignedAssemblies"> + <PropertyGroup> + <NuGetLayoutPath>$(DropsRoot)NuGet\$(BuildVersion)\</NuGetLayoutPath> + </PropertyGroup> + + <MSBuild Projects="$(ProjectRoot)src\DotNetOpenAuth\DotNetOpenAuth.csproj" Targets="DocumentationProjectOutputGroup" BuildInParallel="$(BuildInParallel)"> + <Output TaskParameter="TargetOutputs" ItemName="NuGetSource"/> + </MSBuild> + + <!-- IMPORTANT: These must appear as separate ItemGroups or else batching screws it up. --> + <ItemGroup> + <NuGetSource Include="%(ResignedAssembliesOutputs.Identity)" Condition=" '%(FileName)%(Extension)' == 'DotNetOpenAuth.dll' "/> + <NuGetSource> + <TargetPath>$(NuGetLayoutPath)lib\%(FileName)%(Extension)</TargetPath> + </NuGetSource> + </ItemGroup> + <ItemGroup> + <NuGetContentSource Include="$(ProjectRoot)NuGet\content\**"/> + </ItemGroup> + <ItemGroup> + <NuGetSource Include="@(NuGetContentSource)"> + <TargetPath>$(NuGetLayoutPath)content\%(RecursiveDir)%(FileName)%(Extension)</TargetPath> + </NuGetSource> + + <NuSpecSource Include="DotNetOpenAuth.nuspec"> + <LayoutPath>$(NuGetLayoutPath)</LayoutPath> + <BeforeTokens>$version$</BeforeTokens> + <AfterTokens>$(BuildVersion)</AfterTokens> + </NuSpecSource> + + <NuSpecTarget Include="@(NuSpecSource->'$(NuGetLayoutPath)%(FileName)%(Extension)')" /> + </ItemGroup> + <ItemGroup> + <NuGetContentsTarget Include="%(NuGetSource.TargetPath)" /> + </ItemGroup> + + <CopyWithTokenSubstitution + SourceFiles="@(NuSpecSource)" + DestinationFiles="@(NuSpecTarget)" + /> + + <Copy + SourceFiles="@(NuGetSource)" + DestinationFiles="@(NuGetContentsTarget)" + SkipUnchangedFiles="true" /> + + <Purge Directories="$(NuGetLayoutPath)" IntendedFiles="@(NuGetContentsTarget);@(NuSpecTarget)" /> + </Target> + + <Target Name="Build" DependsOnTargets="Layout"> + <NuGetPack + NuSpec="%(NuSpecTarget.Identity)" + BaseDirectory="%(NuSpecTarget.LayoutPath)" + OutputPackageDirectory="$(DropsRoot)" + ToolPath="$(NuGetToolPath)" /> + </Target> + + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.automated.targets"/> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> +</Project>
\ No newline at end of file diff --git a/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs b/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs index 721e124..ff586dc 100644 --- a/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs +++ b/samples/OAuthServiceProvider/Code/DatabaseTokenManager.cs @@ -42,7 +42,11 @@ namespace OAuthServiceProvider.Code { } public void UpdateToken(IServiceProviderRequestToken token) { - // Nothing to do here, since we're using Linq To SQL. + // Nothing to do here, since we're using Linq To SQL, and + // We call LinqToSql's SubmitChanges method via our Global.Application_EndRequest method. + // This is a good pattern because we only save changes if the request didn't end up somehow failing. + // But if you DO want to save changes at this point, you could do it like so: + ////Global.DataContext.SubmitChanges(); } #endregion diff --git a/samples/OAuthServiceProvider/Code/OAuthToken.cs b/samples/OAuthServiceProvider/Code/OAuthToken.cs index 182a3e3..9099237 100644 --- a/samples/OAuthServiceProvider/Code/OAuthToken.cs +++ b/samples/OAuthServiceProvider/Code/OAuthToken.cs @@ -62,5 +62,34 @@ namespace OAuthServiceProvider.Code { } #endregion + + /// <summary> + /// Called by LinqToSql when the <see cref="IssueDate"/> property is about to change. + /// </summary> + /// <param name="value">The value.</param> + partial void OnIssueDateChanging(DateTime value) { + if (value.Kind == DateTimeKind.Unspecified) { + throw new ArgumentException("The DateTime.Kind cannot be Unspecified to ensure accurate timestamp checks."); + } + } + + /// <summary> + /// Called by LinqToSql when <see cref="IssueDate"/> has changed. + /// </summary> + partial void OnIssueDateChanged() { + if (this.IssueDate.Kind == DateTimeKind.Local) { + this._IssueDate = this.IssueDate.ToUniversalTime(); + } + } + + /// <summary> + /// Called by LinqToSql when a token instance is deserialized. + /// </summary> + partial void OnLoaded() { + if (this.IssueDate.Kind == DateTimeKind.Unspecified) { + // this detail gets lost in db storage, but must be reaffirmed so that expiratoin checks succeed. + this._IssueDate = DateTime.SpecifyKind(this.IssueDate, DateTimeKind.Utc); + } + } } }
\ No newline at end of file diff --git a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs index bd0fdbf..5445875 100644 --- a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs +++ b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs @@ -9,63 +9,193 @@ namespace OpenIdProviderMvc.Controllers { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Behaviors; using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using DotNetOpenAuth.OpenId.Provider; using OpenIdProviderMvc.Code; public class OpenIdController : Controller { internal static OpenIdProvider OpenIdProvider = new OpenIdProvider(); - internal static IAuthenticationRequest PendingAuthenticationRequest { - get { return ProviderEndpoint.PendingAuthenticationRequest; } - set { ProviderEndpoint.PendingAuthenticationRequest = value; } - } - [ValidateInput(false)] public ActionResult Provider() { IRequest request = OpenIdProvider.GetRequest(); if (request != null) { - var authRequest = request as IAuthenticationRequest; - if (authRequest != null) { - PendingAuthenticationRequest = authRequest; - if (authRequest.IsReturnUrlDiscoverable(OpenIdProvider) == RelyingPartyDiscoveryResult.Success && - User.Identity.IsAuthenticated && - (authRequest.IsDirectedIdentity || this.UserControlsIdentifier(authRequest))) { - return this.SendAssertion(); - } else { - return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion") }); - } - } - + // Some requests are automatically handled by DotNetOpenAuth. If this is one, go ahead and let it go. if (request.IsResponseReady) { return OpenIdProvider.PrepareResponse(request).AsActionResult(); - } else { - return RedirectToAction("LogOn", "Account"); } + + // This is apparently one that the host (the web site itself) has to respond to. + ProviderEndpoint.PendingRequest = (IHostProcessedRequest)request; + + // Try responding immediately if possible. + ActionResult response; + if (this.AutoRespondIfPossible(out response)) { + return response; + } + + // We can't respond immediately with a positive result. But if we still have to respond immediately... + if (ProviderEndpoint.PendingRequest.Immediate) { + // We can't stop to prompt the user -- we must just return a negative response. + return this.SendAssertion(); + } + + return this.RedirectToAction("AskUser"); } else { - return View(); + // No OpenID request was recognized. This may be a user that stumbled on the OP Endpoint. + return this.View(); } } + /// <summary> + /// Displays a confirmation page. + /// </summary> + /// <returns>The response for the user agent.</returns> [Authorize] + public ActionResult AskUser() { + if (ProviderEndpoint.PendingRequest == null) { + // Oops... precious little we can confirm without a pending OpenID request. + return this.RedirectToAction("Index", "Home"); + } + + // The user MAY have just logged in. Try again to respond automatically to the RP if appropriate. + ActionResult response; + if (this.AutoRespondIfPossible(out response)) { + return response; + } + + this.ViewData["Realm"] = ProviderEndpoint.PendingRequest.Realm; + + return this.View(); + } + + [HttpPost, Authorize, ValidateAntiForgeryToken] + public ActionResult AskUserResponse(bool confirmed) { + if (ProviderEndpoint.PendingAnonymousRequest != null) { + ProviderEndpoint.PendingAnonymousRequest.IsApproved = confirmed; + } else if (ProviderEndpoint.PendingAuthenticationRequest != null) { + ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = confirmed; + } else { + throw new InvalidOperationException("There's no pending authentication request!"); + } + + return this.SendAssertion(); + } + + /// <summary> + /// Sends a positive or a negative assertion, based on how the pending request is currently marked. + /// </summary> + /// <returns>An MVC redirect result.</returns> public ActionResult SendAssertion() { - IAuthenticationRequest authReq = PendingAuthenticationRequest; - PendingAuthenticationRequest = null; // clear session static so we don't do this again - if (authReq == null) { + var pendingRequest = ProviderEndpoint.PendingRequest; + var authReq = pendingRequest as IAuthenticationRequest; + var anonReq = pendingRequest as IAnonymousRequest; + ProviderEndpoint.PendingRequest = null; // clear session static so we don't do this again + if (pendingRequest == null) { throw new InvalidOperationException("There's no pending authentication request!"); } - if (authReq.IsDirectedIdentity) { - authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + // Set safe defaults if somehow the user ended up (perhaps through XSRF) here before electing to send data to the RP. + if (anonReq != null && !anonReq.IsApproved.HasValue) { + anonReq.IsApproved = false; } - if (!authReq.IsDelegatedIdentifier) { - authReq.ClaimedIdentifier = authReq.LocalIdentifier; + + if (authReq != null && !authReq.IsAuthenticated.HasValue) { + authReq.IsAuthenticated = false; } - // Respond to AX/sreg extension requests. - //// Real web sites would have code here + if (authReq != null && authReq.IsAuthenticated.Value) { + if (authReq.IsDirectedIdentity) { + authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + } + + if (!authReq.IsDelegatedIdentifier) { + authReq.ClaimedIdentifier = authReq.LocalIdentifier; + } + } + + // Respond to AX/sreg extension requests only on a positive result. + if ((authReq != null && authReq.IsAuthenticated.Value) || + (anonReq != null && anonReq.IsApproved.Value)) { + // Look for a Simple Registration request. When the AXFetchAsSregTransform behavior is turned on + // in the web.config file as it is in this sample, AX requests will come in as SReg requests. + var claimsRequest = pendingRequest.GetExtension<ClaimsRequest>(); + if (claimsRequest != null) { + var claimsResponse = claimsRequest.CreateResponse(); + + // This simple respond to a request check may be enhanced to only respond to an individual attribute + // request if the user consents to it explicitly, in which case this response extension creation can take + // place in the confirmation page action rather than here. + if (claimsRequest.Email != DemandLevel.NoRequest) { + claimsResponse.Email = User.Identity.Name + "@dotnetopenauth.net"; + } + + pendingRequest.AddResponseExtension(claimsResponse); + } + } + + return OpenIdProvider.PrepareResponse(pendingRequest).AsActionResult(); + } + + /// <summary> + /// Attempts to formulate an automatic response to the RP if the user's profile allows it. + /// </summary> + /// <param name="response">Receives the ActionResult for the caller to return, or <c>null</c> if no automatic response can be made.</param> + /// <returns>A value indicating whether an automatic response is possible.</returns> + private bool AutoRespondIfPossible(out ActionResult response) { + // If the odds are good we can respond to this one immediately (without prompting the user)... + if (ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable(OpenIdProvider) == RelyingPartyDiscoveryResult.Success + && User.Identity.IsAuthenticated + && this.HasUserAuthorizedAutoLogin(ProviderEndpoint.PendingRequest)) { + // Is this is an identity authentication request? (as opposed to an anonymous request)... + if (ProviderEndpoint.PendingAuthenticationRequest != null) { + // If this is directed identity, or if the claimed identifier being checked is controlled by the current user... + if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity + || this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest)) { + ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true; + response = this.SendAssertion(); + return true; + } + } + + // If this is an anonymous request, we can respond to that too. + if (ProviderEndpoint.PendingAnonymousRequest != null) { + ProviderEndpoint.PendingAnonymousRequest.IsApproved = true; + response = this.SendAssertion(); + return true; + } + } + + response = null; + return false; + } + + /// <summary> + /// Determines whether the currently logged in user has authorized auto login to the requesting relying party. + /// </summary> + /// <param name="request">The incoming request.</param> + /// <returns> + /// <c>true</c> if it is safe to respond affirmatively to this request and all extensions + /// without further user confirmation; otherwise, <c>false</c>. + /// </returns> + private bool HasUserAuthorizedAutoLogin(IHostProcessedRequest request) { + // TODO: host should implement this method meaningfully, consulting their user database. + // Make sure the user likes the RP + if (true/*User.UserLikesRP(request.Realm))*/) { + // And make sure the RP is only asking for information about the user that the user has granted before. + if (true/*User.HasGrantedExtensions(request)*/) { + // For now for the purposes of the sample, we'll disallow auto-logins when an sreg request is present. + if (request.GetExtension<ClaimsRequest>() != null) { + return false; + } + + return true; + } + } - authReq.IsAuthenticated = this.UserControlsIdentifier(authReq); - return OpenIdProvider.PrepareResponse(authReq).AsActionResult(); + // If we aren't sure the user likes this site and is willing to disclose the requested info, return false + // so the user has the opportunity to explicity choose whether to share his/her info. + return false; } /// <summary> diff --git a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj index 794a91e..9dc060e 100644 --- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj +++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj @@ -92,6 +92,7 @@ <Content Include="Views\Account\ChangePassword.aspx" /> <Content Include="Views\Account\ChangePasswordSuccess.aspx" /> <Content Include="Views\Account\Register.aspx" /> + <Content Include="Views\OpenId\AskUser.aspx" /> <Content Include="Views\Shared\Xrds.aspx" /> <Content Include="Views\OpenId\Provider.aspx" /> <Content Include="Views\User\Identity.aspx" /> diff --git a/samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx b/samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx new file mode 100644 index 0000000..5098325 --- /dev/null +++ b/samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx @@ -0,0 +1,38 @@ +<%@ Page Title="Do you want to log into another web site?" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" + Inherits="System.Web.Mvc.ViewPage" %> + +<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> + <h2> + Logging in somewhere? + </h2> + <p> + Are you trying to log into + <b><%= Html.Encode(ViewData["Realm"]) %></b>? + </p> + <% using (Html.BeginForm("AskUserResponse", "OpenId")) { %> + <%= Html.AntiForgeryToken() %> + <%= Html.Hidden("confirmed", "false") %> + <div style="display: none" id="responseButtonsDiv"> + <input type="submit" value="yes" onclick="document.getElementsByName('confirmed')[0].value = 'true'; return true;" /> + <input type="submit" value="no" /> + </div> + <div id="javascriptDisabled"> + <b>Javascript appears to be disabled in your browser. </b>This page requires Javascript + to be enabled to better protect your security. + </div> + <script language="javascript" type="text/javascript"> + //<![CDATA[ + // we use HTML to hide the action buttons and Javascript to show them + // to protect against click-jacking in an iframe whose javascript is disabled. + document.getElementById('responseButtonsDiv').style.display = 'block'; + document.getElementById('javascriptDisabled').style.display = 'none'; + + // Frame busting code (to protect us from being hosted in an iframe). + // This protects us from click-jacking. + if (document.location !== window.top.location) { + window.top.location = document.location; + } + //]]> + </script> + <% } %> +</asp:Content> diff --git a/samples/OpenIdProviderMvc/Web.config b/samples/OpenIdProviderMvc/Web.config index cc30638..9b1eb90 100644 --- a/samples/OpenIdProviderMvc/Web.config +++ b/samples/OpenIdProviderMvc/Web.config @@ -46,6 +46,7 @@ profile matches, the default behavior is assumed. --> <!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />--> <add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" /> + <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="RelyingPartyWebForms.CustomStore, RelyingPartyWebForms" />--> diff --git a/samples/OpenIdProviderWebForms/Code/InMemoryTokenManager.cs b/samples/OpenIdProviderWebForms/Code/InMemoryTokenManager.cs index b04f736..1cb7094 100644 --- a/samples/OpenIdProviderWebForms/Code/InMemoryTokenManager.cs +++ b/samples/OpenIdProviderWebForms/Code/InMemoryTokenManager.cs @@ -49,7 +49,7 @@ namespace OpenIdProviderWebForms.Code { } public void UpdateToken(IServiceProviderRequestToken token) { - // Nothing to do here, since there's not database in this sample. + // Nothing to do here, since there's no database in this sample. } #endregion diff --git a/samples/OpenIdProviderWebForms/Provider.ashx.cs b/samples/OpenIdProviderWebForms/Provider.ashx.cs index c8441cf..3285b57 100644 --- a/samples/OpenIdProviderWebForms/Provider.ashx.cs +++ b/samples/OpenIdProviderWebForms/Provider.ashx.cs @@ -28,8 +28,7 @@ // redirects and user prompts can appear and eventually some page can decide // to respond to the OpenID authentication request either affirmatively or // negatively. - ProviderEndpoint.PendingAnonymousRequest = request as IAnonymousRequest; - ProviderEndpoint.PendingAuthenticationRequest = request as IAuthenticationRequest; + ProviderEndpoint.PendingRequest = request as IHostProcessedRequest; // We delegate that approval process to our utility method that we share // with our other Provider sample page server.aspx. diff --git a/samples/OpenIdRelyingPartyClassicAsp/default.asp b/samples/OpenIdRelyingPartyClassicAsp/default.asp index cc2bd57..ddb8dc0 100644 --- a/samples/OpenIdRelyingPartyClassicAsp/default.asp +++ b/samples/OpenIdRelyingPartyClassicAsp/default.asp @@ -25,10 +25,14 @@ assembly is found.</li> <li>Register DotNetOpenAuth as a COM server:<br /> <span class="command">%windir%\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe - /tlb DotNetOpenAuth.dll</span></li> + /tlb DotNetOpenAuth.dll</span><br /> + Note that you may need to copy System.Web.Mvc.dll into the same directory as dotnetopenauth.dll + if it is not already in your GAC.</li> <li>Install DotNetOpenAuth into the GAC. The gacutil.exe tool may be in an SDK directory, which will be in your path if you opened a Visual Studio Command Prompt.<br /> - <span class="command">gacutil.exe /i DotNetOpenAuth.dll</span></li> + <span class="command">gacutil.exe /i DotNetOpenAuth.dll</span><br /> + Be sure to use a gacutil.exe that comes from a .NET 2.0-3.5 directory (not .NET 1.x). + </li> </ol> <p>Another thing to be aware of is that with classic ASP there is no Web.config file in which to customize DotNetOpenAuth behavior. And the COM interfaces diff --git a/samples/OpenIdRelyingPartyClassicAsp/login.asp b/samples/OpenIdRelyingPartyClassicAsp/login.asp index 18c4d4f..90112f9 100644 --- a/samples/OpenIdRelyingPartyClassicAsp/login.asp +++ b/samples/OpenIdRelyingPartyClassicAsp/login.asp @@ -13,17 +13,19 @@ <h2>Login Page</h2> <% dim realm, thisPageUrl, requestUrl, dnoi, authentication - realm = "http://" + Request.ServerVariables("HTTP_HOST") + "/classicaspdnoi/" - thisPageUrl = "http://" + Request.ServerVariables("HTTP_HOST") + Request.ServerVariables("URL") - requestUrl = "http://" + Request.ServerVariables("HTTP_HOST") + Request.ServerVariables("HTTP_URL") + realm = "http://" + Request.ServerVariables("HTTP_HOST") + "/classicaspdnoi/" ' change this to be the home page of your web site, without the filename. + requestUrl = "http://" + Request.ServerVariables("HTTP_HOST") + Request.ServerVariables("HTTP_URL") ' this is the full URL of the current incoming request. Set dnoi = server.CreateObject("DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty") On Error Resume Next + ' Since this page both starts the OpenID authentication flow and receives the response, we don't + ' yet know whether this particular request is already in the response phase. Check that now. Set authentication = dnoi.ProcessAuthentication(requestUrl, Request.Form) If Err.number <> 0 Then + ' Oops, report something that went wrong. Response.Write "<p>" + Server.HTMLEncode(Err.Description) + "</p>" End If On Error Goto 0 - if Not authentication Is Nothing then + if Not authentication Is Nothing then ' if this WAS an OpenID response coming in... If authentication.Successful Then Session("ClaimedIdentifier") = authentication.ClaimedIdentifier If Not authentication.ClaimsResponse Is Nothing Then @@ -35,9 +37,10 @@ else Response.Write "Authentication failed: " + authentication.ExceptionMessage end if - elseif Request.Form("openid_identifier") <> "" then + elseif Request.Form("openid_identifier") <> "" then ' if the user is only now starting the authentication flow... dim redirectUrl On Error Resume Next + thisPageUrl = "http://" + Request.ServerVariables("HTTP_HOST") + Request.ServerVariables("URL") ' this is the URL that will receive the response from the OpenID Provider. ' redirectUrl = dnoi.CreateRequest(Request.Form("openid_identifier"), realm, thisPageUrl) redirectUrl = dnoi.CreateRequestWithSimpleRegistration(Request.Form("openid_identifier"), realm, thisPageUrl, "nickname,email", "fullname") If Err.number <> 0 Then diff --git a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx index dc0c2bb..cec5f49 100644 --- a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx +++ b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx @@ -10,17 +10,23 @@ in an organization. </p> <p><b>Note: </b>At time of this writing, most OPs do not support this feature, although it is documented in the OpenID 2.0 spec. </p> - <asp:Label ID="Label1" runat="server" Text="OpenID Identifier" /> <asp:TextBox ID="openIdBox" - runat="server" /> - <asp:Button ID="beginButton" runat="server" Text="Begin" OnClick="beginButton_Click" /> - <asp:CustomValidator runat="server" ID="openidValidator" ErrorMessage="Invalid OpenID Identifier" - ControlToValidate="openIdBox" EnableViewState="false" Display="Dynamic" OnServerValidate="openidValidator_ServerValidate" /> - <asp:Label runat="server" EnableViewState="false" ID="resultMessage" /> + <asp:Panel runat="server" DefaultButton="beginButton"> + <asp:Label ID="Label1" runat="server" Text="OpenID Identifier" /> <asp:TextBox ID="openIdBox" + runat="server" /> + <asp:Button ID="beginButton" runat="server" Text="Begin" OnClick="beginButton_Click" /> + <asp:CustomValidator runat="server" ID="openidValidator" ErrorMessage="Invalid OpenID Identifier" + ControlToValidate="openIdBox" EnableViewState="false" Display="Dynamic" OnServerValidate="openidValidator_ServerValidate" /> + <asp:Label runat="server" EnableViewState="false" ID="resultMessage" /> + </asp:Panel> <asp:Panel runat="server" ID="ExtensionResponsesPanel" EnableViewState="false" Visible="false"> <p>We have received a reasonable response from the Provider. Below is the Simple Registration response we received, if any: </p> <table id="profileFieldsTable" runat="server"> <tr> + <td>Email </td> + <td><asp:Label runat="server" ID="emailLabel" /> </td> + </tr> + <tr> <td>Gender </td> <td><asp:Label runat="server" ID="genderLabel" /> </td> </tr> diff --git a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs index 37a5515..ca12964 100644 --- a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs +++ b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.cs @@ -19,6 +19,7 @@ // This is the "success" status we get when no authentication was requested. var sreg = response.GetExtension<ClaimsResponse>(); if (sreg != null) { + this.emailLabel.Text = sreg.Email; this.timeZoneLabel.Text = sreg.TimeZone; this.postalCodeLabel.Text = sreg.PostalCode; this.countryLabel.Text = sreg.Country; @@ -55,6 +56,7 @@ // This is where you would add any OpenID extensions you wanted // to include in the request. request.AddExtension(new ClaimsRequest { + Email = DemandLevel.Request, Country = DemandLevel.Request, Gender = DemandLevel.Require, PostalCode = DemandLevel.Require, diff --git a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs index fb959a3..348ea31 100644 --- a/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs +++ b/samples/OpenIdRelyingPartyWebForms/NoIdentityOpenId.aspx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ @@ -77,6 +76,15 @@ namespace OpenIdRelyingPartyWebForms { protected global::System.Web.UI.HtmlControls.HtmlTable profileFieldsTable; /// <summary> + /// emailLabel 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.Label emailLabel; + + /// <summary> /// genderLabel control. /// </summary> /// <remarks> diff --git a/samples/Samples.proj b/samples/Samples.proj index b1f212f..e6ac9f8 100644 --- a/samples/Samples.proj +++ b/samples/Samples.proj @@ -20,11 +20,20 @@ <ProjectsToClean Include="$(SolutionPath)"> <Targets>@(SampleSites->'%(Identity):Clean')</Targets> </ProjectsToClean> + + <_SampleWebConfigs Include="$(ProjectRoot)samples\*\web.config" /> + <PublishableWebSamples Include="@(_SampleWebConfigs->'%(RootDir)%(Directory)')" /> </ItemGroup> <Target Name="Build" DependsOnTargets="SkipVerification"> - <MSBuild Projects="@(SampleProjects)" BuildInParallel="$(BuildInParallel)" /> - <MSBuild Projects="$(SolutionPath)" Targets="@(SampleSites)" BuildInParallel="$(BuildInParallel)" /> + <ItemGroup> + <SampleProjectsToBuild Include="@(SampleProjects)" /> + <SampleProjectsToBuild Include="$(SolutionPath)"> + <Targets>%(SampleSites.Identity)</Targets> + </SampleProjectsToBuild> + </ItemGroup> + + <MSBuild Projects="@(SampleProjectsToBuild)" Targets="%(SampleProjectsToBuild.Targets)" BuildInParallel="$(BuildInParallel)" /> </Target> <Target Name="Prepare"> @@ -33,10 +42,6 @@ <PropertyGroup> <PublishSamplesWebSiteVirtualPath>/$(BranchName)</PublishSamplesWebSiteVirtualPath> </PropertyGroup> - <ItemGroup> - <_SampleWebConfigs Include="$(ProjectRoot)samples\*\web.config" /> - <PublishableWebSamples Include="@(_SampleWebConfigs->'%(RootDir)%(Directory)')" /> - </ItemGroup> <!-- Trim the trailing slash on the web sample paths so we can just get the leaf directory name. --> <Trim @@ -51,12 +56,13 @@ </ItemGroup> </Target> - <Target Name="PrepareForPublish" DependsOnTargets="Prepare;Build"> + <Target Name="PrepareForPublish" DependsOnTargets="Build"> + <PropertyGroup> + <SamplesZip>$(DropDirectoryNoSlash)-samples$(ZipFormat)</SamplesZip> + </PropertyGroup> + <ItemGroup> <SampleSources Include="$(ProjectRoot)samples\**\*" /> - <SampleSources> - <PublishedLocation>$(SampleWebRoot)\$(BranchName)\%(RecursiveDir)%(Filename)%(Extension)</PublishedLocation> - </SampleSources> </ItemGroup> <FilterItems @@ -64,26 +70,22 @@ StartsWithAny="@(PublishableWebSamples)"> <Output TaskParameter="FilteredItems" ItemName="PublishableWebSampleSources" /> </FilterItems> - - <ItemGroup> - <PublishableWebSampleTargets Include="@(PublishableWebSampleSources->'%(PublishedLocation)')" /> - </ItemGroup> </Target> - <Target Name="Publish" + <Target Name="DeployableArchive" DependsOnTargets="PrepareForPublish" Inputs="@(PublishableWebSampleSources)" - Outputs="@(PublishableWebSampleTargets)" - Condition=" '$(SampleWebRoot)' != '' "> - <RemoveDir Directories="$(SampleWebRoot)\$(BranchName)" /> - <MakeDir Directories="$(SampleWebRoot)\$(BranchName)" /> - <Copy SourceFiles="@(PublishableWebSampleSources)" DestinationFiles="@(PublishableWebSampleTargets)" SkipUnchangedFiles="true" /> + Outputs="$(SamplesZip)"> + <Delete Files="$(SamplesZip)" /> + <AddFilesTo7Zip + Files="@(PublishableWebSampleSources)" + ZipFileName="$(SamplesZip)" + WorkingDirectory="$(ProjectRoot)samples\" + ToolPath="$(Zip7ToolPath)" /> </Target> - <Target Name="Unpublish" - DependsOnTargets="DeleteSitesOnIis" - Condition=" '$(SampleWebRoot)' != '' "> - </Target> + <Target Name="Publish" + DependsOnTargets="DeployableArchive" /> <Target Name="CreateSitesOnIis" DependsOnTargets="Prepare"> <Error Text="The PublishSamplesWebSiteName property must be set." Condition=" '$(PublishSamplesWebSiteName)' == '' "/> diff --git a/samples/tools.proj b/samples/tools.proj index e7d89d9..0c413b7 100644 --- a/samples/tools.proj +++ b/samples/tools.proj @@ -15,13 +15,20 @@ </ToolProjects> </ItemGroup> - <MSBuild Projects="@(ToolProjects)" Targets="%(ToolProjects.Targets)" BuildInParallel="$(BuildInParallel)"> - <Output TaskParameter="TargetOutputs" ItemName="OfflineProvider"/> + <MSBuild Projects="@(ToolProjects)" Targets="%(ToolProjects.Targets);BuiltProjectOutputGroupDependencies" BuildInParallel="$(BuildInParallel)"> + <Output TaskParameter="TargetOutputs" ItemName="ToolProjectsOutputs"/> </MSBuild> <ItemGroup> - <!-- Remove the un-unified assembly. --> - <OfflineProvider Remove="$(OutputPath)$(SignedSubPath)$(ProductName).dll" /> + <!-- Exclude the un-unified assemblies. --> + <OfflineProvider Include="@(ToolProjectsOutputs)" + Condition=" '%(ToolProjectsOutputs.CopyLocal)' != 'false' " + Exclude=" + $(OutputPath)$(ProductName).dll; + $(OutputPath)$(SignedSubPath)$(ProductName).dll; + $(ProjectRoot)lib\Microsoft.Contracts.dll; + " /> + <!-- add the PDBs --> <OfflineProvider Include="@(OfflineProvider->'%(SymbolPath)')" /> <OfflineProviderTargets Include=" @@ -40,7 +47,7 @@ <Target Name="Build" DependsOnTargets="Layout" Returns="@(RedistributableFiles)"> <PropertyGroup> - <ToolsZip>$(ToolsDirectoryNoSlash).zip</ToolsZip> + <ToolsZip>$(ToolsDirectoryNoSlash)$(ZipFormat)</ToolsZip> </PropertyGroup> <ItemGroup> <RedistributableFiles Include="$(ToolsZip)"> @@ -49,10 +56,12 @@ </RedistributableFiles> </ItemGroup> - <Zip ZipFileName="$(ToolsZip)" - Files="@(AllToolTargets)" - WorkingDirectory="$(ToolsDirectory)" - ZipLevel="$(ZipLevel)" /> + <Delete Files="$(ToolsZip)" /> + <AddFilesTo7Zip + ZipFileName="$(ToolsZip)" + Files="@(AllToolTargets)" + WorkingDirectory="$(ToolsDirectory)" + ToolPath="$(Zip7ToolPath)" /> </Target> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.automated.targets"/> diff --git a/src/DotNetOpenAuth.BuildTasks/AddFilesTo7Zip.cs b/src/DotNetOpenAuth.BuildTasks/AddFilesTo7Zip.cs new file mode 100644 index 0000000..8bf3e16 --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/AddFilesTo7Zip.cs @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------- +// <copyright file="AddFilesTo7Zip.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; + + public class AddFilesTo7Zip : ToolTask { + /// <summary> + /// Initializes a new instance of the <see cref="AddFilesTo7Zip"/> class. + /// </summary> + public AddFilesTo7Zip() { + this.YieldDuringToolExecution = true; + } + + [Required] + public ITaskItem ZipFileName { get; set; } + + [Required] + public ITaskItem[] Files { get; set; } + + public string WorkingDirectory { get; set; } + + /// <summary> + /// Gets the name of the tool. + /// </summary> + /// <value> + /// The name of the tool. + /// </value> + protected override string ToolName { + get { return "7za.exe"; } + } + + /// <summary> + /// Generates the full path to tool. + /// </summary> + protected override string GenerateFullPathToTool() { + return this.ToolPath; + } + + protected override string GenerateCommandLineCommands() { + var args = new CommandLineBuilder(); + + args.AppendSwitch("a"); + args.AppendSwitch("--"); + + args.AppendFileNameIfNotNull(this.ZipFileName); + + return args.ToString(); + } + + /// <summary> + /// Gets the response file switch. + /// </summary> + /// <param name="responseFilePath">The response file path.</param> + protected override string GetResponseFileSwitch(string responseFilePath) { + return "@" + responseFilePath; + } + + /// <summary> + /// Gets the response file encoding. + /// </summary> + /// <value> + /// The response file encoding. + /// </value> + protected override Encoding ResponseFileEncoding { + get { return Encoding.UTF8; } + } + + /// <summary> + /// Generates the response file commands. + /// </summary> + protected override string GenerateResponseFileCommands() { + var args = new CommandLineBuilder(); + args.AppendFileNamesIfNotNull(this.Files.Select(GetWorkingDirectoryRelativePath).ToArray(), Environment.NewLine); + return args.ToString(); + } + + /// <summary> + /// Gets the working directory. + /// </summary> + protected override string GetWorkingDirectory() { + if (!String.IsNullOrEmpty(this.WorkingDirectory)) { + return this.WorkingDirectory; + } else { + return base.GetWorkingDirectory(); + } + } + + private string GetWorkingDirectoryRelativePath(ITaskItem taskItem) { + if (taskItem.ItemSpec.StartsWith(this.WorkingDirectory, StringComparison.OrdinalIgnoreCase)) { + return taskItem.ItemSpec.Substring(this.WorkingDirectory.Length); + } else { + return taskItem.ItemSpec; + } + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs index 503e168..fb42ade 100644 --- a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs +++ b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs @@ -38,7 +38,7 @@ foreach (var project in Projects) { Project doc = new Project(); - doc.Load(project.ItemSpec); + doc.Load(project.ItemSpec, ProjectLoadSettings.IgnoreMissingImports); var projectReferences = doc.EvaluatedItems.OfType<BuildItem>().Where(item => item.Name == "ProjectReference"); var matchingReferences = from reference in projectReferences diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj index 179c825..310ee9d 100644 --- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj @@ -11,7 +11,7 @@ <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>DotNetOpenAuth.BuildTasks</RootNamespace> <AssemblyName>DotNetOpenAuth.BuildTasks</AssemblyName> - <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <PublishUrl>publish\</PublishUrl> <Install>true</Install> @@ -29,6 +29,7 @@ <UseApplicationTrust>false</UseApplicationTrust> <BootstrapperEnabled>true</BootstrapperEnabled> <CodeContractsAssemblyMode>1</CodeContractsAssemblyMode> + <TargetFrameworkProfile /> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -78,14 +79,10 @@ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> + <Reference Include="Microsoft.Build" /> <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.Build.Utilities.v4.0" /> <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> @@ -101,6 +98,7 @@ </Reference> </ItemGroup> <ItemGroup> + <Compile Include="AddFilesTo7Zip.cs" /> <Compile Include="AddProjectItems.cs" /> <Compile Include="ChangeProjectReferenceToAssemblyReference.cs" /> <Compile Include="CompareFiles.cs" /> @@ -120,6 +118,7 @@ <Compile Include="CheckAdminRights.cs" /> <Compile Include="JsPack.cs" /> <Compile Include="NativeMethods.cs" /> + <Compile Include="NuGetPack.cs" /> <Compile Include="ParseMaster.cs" /> <Compile Include="PathSegment.cs" /> <Compile Include="PrepareOhlohRelease.cs" /> diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln index a144f1c..5c2ffbb 100644 --- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln @@ -9,12 +9,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\..\tools\DotNetOpenAuth.automated.targets = ..\..\tools\DotNetOpenAuth.automated.targets ..\..\lib\DotNetOpenAuth.BuildTasks.targets = ..\..\lib\DotNetOpenAuth.BuildTasks.targets ..\..\tools\DotNetOpenAuth.Common.Settings.targets = ..\..\tools\DotNetOpenAuth.Common.Settings.targets + ..\..\nuget\DotNetOpenAuth.nuspec = ..\..\nuget\DotNetOpenAuth.nuspec ..\..\tools\DotNetOpenAuth.props = ..\..\tools\DotNetOpenAuth.props ..\..\tools\DotNetOpenAuth.targets = ..\..\tools\DotNetOpenAuth.targets ..\..\tools\DotNetOpenAuth.Versioning.targets = ..\..\tools\DotNetOpenAuth.Versioning.targets ..\..\tools\drop.proj = ..\..\tools\drop.proj ..\..\EnlistmentInfo.props = ..\..\EnlistmentInfo.props ..\..\EnlistmentInfo.targets = ..\..\EnlistmentInfo.targets + ..\..\nuget\nuget.proj = ..\..\nuget\nuget.proj ..\..\tools\ohloh.proj = ..\..\tools\ohloh.proj ..\..\projecttemplates\projecttemplates.proj = ..\..\projecttemplates\projecttemplates.proj ..\..\samples\Samples.proj = ..\..\samples\Samples.proj @@ -25,6 +27,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.BuildTasks", "DotNetOpenAuth.BuildTasks.csproj", "{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{D49E2011-0E1C-4AB5-9887-BD1D42266503}" + ProjectSection(SolutionItems) = preProject + ..\..\nuget\DotNetOpenAuth.nuspec = ..\..\nuget\DotNetOpenAuth.nuspec + ..\..\nuget\nuget.proj = ..\..\nuget\nuget.proj + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{BF3868D6-3BBA-4E40-B180-213370C15494}" + ProjectSection(SolutionItems) = preProject + ..\..\nuget\content\web.config.transform = ..\..\nuget\content\web.config.transform + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,4 +52,8 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D49E2011-0E1C-4AB5-9887-BD1D42266503} = {ABBE14A3-0404-4123-9093-E598C3DD3E9B} + {BF3868D6-3BBA-4E40-B180-213370C15494} = {D49E2011-0E1C-4AB5-9887-BD1D42266503} + EndGlobalSection EndGlobal diff --git a/src/DotNetOpenAuth.BuildTasks/NuGetPack.cs b/src/DotNetOpenAuth.BuildTasks/NuGetPack.cs new file mode 100644 index 0000000..356c51f --- /dev/null +++ b/src/DotNetOpenAuth.BuildTasks/NuGetPack.cs @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------- +// <copyright file="NuGetPack.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; + + /// <summary> + /// Creates a .nupkg archive from a .nuspec file and content files. + /// </summary> + public class NuGetPack : ToolTask { + /// <summary> + /// Gets or sets the path to the .nuspec file. + /// </summary> + [Required] + public ITaskItem NuSpec { get; set; } + + /// <summary> + /// Gets or sets the base directory, the contents of which gets included in the .nupkg archive. + /// </summary> + public ITaskItem BaseDirectory { get; set; } + + /// <summary> + /// Gets or sets the path to the directory that will contain the generated .nupkg archive. + /// </summary> + public ITaskItem OutputPackageDirectory { get; set; } + + /// <summary> + /// Returns the fully qualified path to the executable file. + /// </summary> + /// <returns> + /// The fully qualified path to the executable file. + /// </returns> + protected override string GenerateFullPathToTool() { + return this.ToolPath; + } + + /// <summary> + /// Gets the name of the executable file to run. + /// </summary> + /// <returns>The name of the executable file to run.</returns> + protected override string ToolName { + get { return "NuGet.exe"; } + } + + /// <summary> + /// Runs the exectuable file with the specified task parameters. + /// </summary> + /// <returns> + /// true if the task runs successfully; otherwise, false. + /// </returns> + public override bool Execute() { + if (this.OutputPackageDirectory != null && Path.GetDirectoryName(this.OutputPackageDirectory.ItemSpec).Length > 0) { + Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPackageDirectory.ItemSpec)); + } + + string fullPackagePath = this.DeriveFullPackagePath(); + this.Log.LogMessage("Creating NuGet package '{0}'.", fullPackagePath); + + bool result = base.Execute(); + + if (result) { + this.Log.LogMessage(MessageImportance.High, "Successfully created package '{0}'.", fullPackagePath); + } + + return result; + } + + /// <summary> + /// Returns a string value containing the command line arguments to pass directly to the executable file. + /// </summary> + /// <returns> + /// A string value containing the command line arguments to pass directly to the executable file. + /// </returns> + protected override string GenerateCommandLineCommands() { + var args = new CommandLineBuilder(); + + args.AppendSwitch("pack"); + args.AppendFileNameIfNotNull(this.NuSpec); + args.AppendSwitchIfNotNull("-b ", this.BaseDirectory); + args.AppendSwitchIfNotNull("-o ", this.OutputPackageDirectory); + + return args.ToString(); + } + + /// <summary> + /// Derives the path to the generated .nupkg file. + /// </summary> + /// <returns>A relative path.</returns> + private string DeriveFullPackagePath() { + var spec = XDocument.Load(this.NuSpec.ItemSpec); + var metadata = spec.Element("package").Element("metadata"); + string id = metadata.Element("id").Value; + string version = metadata.Element("version").Value; + string baseDirectory = this.OutputPackageDirectory != null ? this.OutputPackageDirectory.ItemSpec : String.Empty; + return Path.Combine(baseDirectory, String.Format("{0}.{1}.nupkg", id, version)); + } + } +} diff --git a/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs index a0ba386..2bcc160 100644 --- a/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs +++ b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.BuildTasks { ////if (this.Assemblies.Length != 1) { //// throw new NotSupportedException("Exactly 1 assembly for signing is supported."); ////} - CommandLineBuilder args = new CommandLineBuilder(); + var args = new CommandLineBuilder(); args.AppendSwitch("-q"); if (this.KeyFile != null) { diff --git a/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs b/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs index 17647fd..d5a80a4 100644 --- a/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs +++ b/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4927 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.BuildTasks { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class TaskStrings { diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index d7f97e5..5e80f2c 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -153,7 +153,6 @@ </PropertyGroup> <ItemGroup> <Reference Include="log4net" /> - <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" /> <Reference Include="Moq" /> <Reference Include="NUnit.Framework" /> <Reference Include="System" /> diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs index 5d31d40..acb200f 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs @@ -77,6 +77,8 @@ namespace DotNetOpenAuth.Test.Messaging { OutgoingWebResponse response = this.Channel.PrepareResponse(message); Assert.AreEqual(HttpStatusCode.Redirect, response.Status); + Assert.AreEqual("text/html; charset=utf-8", response.Headers[HttpResponseHeader.ContentType]); + Assert.IsTrue(response.Body != null && response.Body.Length > 0); // a non-empty body helps get passed filters like WebSense StringAssert.StartsWith("http://provider/path", response.Headers[HttpResponseHeader.Location]); foreach (var pair in expected) { string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key); diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs index 10084b6..33970a5 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs @@ -258,7 +258,7 @@ namespace DotNetOpenAuth.Test.Messaging { } almostMatchTimer.Stop(); - const double ToleranceFactor = 0.06; + const double ToleranceFactor = 0.12; long averageTimeTicks = (totalMismatchTimer.ElapsedTicks + almostMatchTimer.ElapsedTicks) / 2; var tolerableDifference = TimeSpan.FromTicks((long)(averageTimeTicks * ToleranceFactor)); var absoluteDifference = TimeSpan.FromTicks(Math.Abs(totalMismatchTimer.ElapsedTicks - almostMatchTimer.ElapsedTicks)); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 54ed37e..479375a 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -125,7 +125,7 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { OutgoingWebResponse response = this.channel.PrepareResponse(message); Assert.AreSame(message, response.OriginalMessage); Assert.AreEqual(HttpStatusCode.OK, response.Status); - Assert.AreEqual(0, response.Headers.Count); + Assert.AreEqual(2, response.Headers.Count); NameValueCollection body = HttpUtility.ParseQueryString(response.Body); Assert.AreEqual("15", body["age"]); diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs index 0bdc36e..69bb935 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/SimpleRegistration/ClaimsResponseTests.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; + using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; using NUnit.Framework; @@ -130,6 +131,17 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { response.BirthDateRaw = "2008"; } + [TestCase] + public void ResponseAlternateTypeUriTests() { + var request = new ClaimsRequest(Constants.sreg_ns10); + request.Email = DemandLevel.Require; + + var response = new ClaimsResponse(Constants.sreg_ns10); + response.Email = "a@b.com"; + + ExtensionTestUtilities.Roundtrip(Protocol.Default, new[] { request }, new[] { response }); + } + private ClaimsResponse GetFilledData() { return new ClaimsResponse(Constants.sreg_ns) { BirthDate = new DateTime(2005, 2, 3), diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs index e069c44..0bb994c 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshotTests.cs @@ -12,16 +12,16 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; - using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; + using NUnit.Framework; - [TestClass] + [TestFixture] public class PositiveAuthenticationResponseSnapshotTests : OpenIdTestBase { /// <summary> /// Verifies that the PositiveAuthenticationResponseSnapshot is serializable, /// as required by the <see cref="OpenIdRelyingPartyAjaxControlBase"/> class. /// </summary> - [TestMethod] + [Test] public void Serializable() { var response = new Mock<IAuthenticationResponse>(MockBehavior.Strict); response.Setup(o => o.ClaimedIdentifier).Returns(VanityUri); diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd index fda2aaf..9c0ab77 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd @@ -878,6 +878,25 @@ </xs:attribute> </xs:complexType> </xs:element> + <xs:element name="webResourceUrlProvider"> + <xs:annotation> + <xs:documentation> + The type that implements the DotNetOpenAuth.IEmbeddedResourceRetrieval interface + to instantiate for obtaining URLs that fetch embedded resource streams. + Primarily useful when the System.Web.UI.Page class is not used in the ASP.NET pipeline. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements the IEmbeddedResourceRetrieval interface. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="xaml" type="xs:string" use="optional" /> + </xs:complexType> + </xs:element> </xs:choice> </xs:complexType> </xs:element> diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs index 117a542..409fca9 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs @@ -40,6 +40,11 @@ namespace DotNetOpenAuth.Configuration { private const string ReportingElementName = "reporting"; /// <summary> + /// The name of the <webResourceUrlProvider> sub-element. + /// </summary> + private const string WebResourceUrlProviderName = "webResourceUrlProvider"; + + /// <summary> /// Initializes a new instance of the <see cref="DotNetOpenAuthSection"/> class. /// </summary> internal DotNetOpenAuthSection() { @@ -116,5 +121,14 @@ namespace DotNetOpenAuth.Configuration { this[ReportingElementName] = value; } } + + /// <summary> + /// Gets or sets the type to use for obtaining URLs that fetch embedded resource streams. + /// </summary> + [ConfigurationProperty(WebResourceUrlProviderName)] + internal TypeConfigurationElement<IEmbeddedResourceRetrieval> EmbeddedResourceRetrievalProvider { + get { return (TypeConfigurationElement<IEmbeddedResourceRetrieval>)this[WebResourceUrlProviderName] ?? new TypeConfigurationElement<IEmbeddedResourceRetrieval>(); } + set { this[WebResourceUrlProviderName] = value; } + } } } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index c9900ea..15aa698 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -301,6 +301,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Configuration\HostNameOrRegexCollection.cs" /> <Compile Include="Configuration\HostNameElement.cs" /> <Compile Include="Configuration\XriResolverElement.cs" /> + <Compile Include="IEmbeddedResourceRetrieval.cs" /> <Compile Include="InfoCard\ClaimType.cs" /> <Compile Include="InfoCard\InfoCardImage.cs" /> <Compile Include="InfoCard\InfoCardStrings.Designer.cs"> diff --git a/src/DotNetOpenAuth/IEmbeddedResourceRetrieval.cs b/src/DotNetOpenAuth/IEmbeddedResourceRetrieval.cs new file mode 100644 index 0000000..b9a6fd0 --- /dev/null +++ b/src/DotNetOpenAuth/IEmbeddedResourceRetrieval.cs @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------- +// <copyright file="IEmbeddedResourceRetrieval.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth { + using System; + + /// <summary> + /// An interface that provides URLs from which embedded resources can be obtained. + /// </summary> + public interface IEmbeddedResourceRetrieval { + /// <summary> + /// Gets the URL from which the given manifest resource may be downloaded by the user agent. + /// </summary> + /// <param name="someTypeInResourceAssembly">Some type in the assembly containing the desired resource.</param> + /// <param name="manifestResourceName">Manifest name of the desired resource.</param> + /// <returns>An absolute URL.</returns> + Uri GetWebResourceUrl(Type someTypeInResourceAssembly, string manifestResourceName); + } +} diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index 3d56f02..84dbe3c 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -55,6 +55,14 @@ namespace DotNetOpenAuth.Messaging { private const int IndirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group /// <summary> + /// The HTML that should be returned to the user agent as part of a 301 Redirect. + /// </summary> + /// <value>A string that should be used as the first argument to String.Format, where the {0} should be replaced with the URL to redirect to.</value> + private const string RedirectResponseBodyFormat = @"<html><head><title>Object moved</title></head><body> +<h2>Object moved to <a href=""{0}"">here</a>.</h2> +</body></html>"; + + /// <summary> /// A list of binding elements in the order they must be applied to outgoing messages. /// </summary> [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -260,10 +268,12 @@ namespace DotNetOpenAuth.Messaging { this.ProcessOutgoingMessage(message); Logger.Channel.DebugFormat("Sending message: {0}", message.GetType().Name); + OutgoingWebResponse result; switch (message.Transport) { case MessageTransport.Direct: // This is a response to a direct message. - return this.PrepareDirectResponse(message); + result = this.PrepareDirectResponse(message); + break; case MessageTransport.Indirect: var directedMessage = message as IDirectedProtocolMessage; ErrorUtilities.VerifyArgumentNamed( @@ -275,7 +285,8 @@ namespace DotNetOpenAuth.Messaging { directedMessage.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient); - return this.PrepareIndirectResponse(directedMessage); + result = this.PrepareIndirectResponse(directedMessage); + break; default: throw ErrorUtilities.ThrowArgumentNamed( "message", @@ -283,6 +294,13 @@ namespace DotNetOpenAuth.Messaging { "Transport", message.Transport); } + + // Apply caching policy to any response. We want to disable all caching because in auth* protocols, + // caching can be utilized in identity spoofing attacks. + result.Headers[HttpResponseHeader.CacheControl] = "no-cache, no-store, max-age=0, must-revalidate"; + result.Headers[HttpResponseHeader.Pragma] = "no-cache"; + + return result; } /// <summary> @@ -733,15 +751,18 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentNullException>(fields != null); Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + // As part of this redirect, we include an HTML body in order to get passed some proxy filters + // such as WebSense. WebHeaderCollection headers = new WebHeaderCollection(); UriBuilder builder = new UriBuilder(message.Recipient); MessagingUtilities.AppendQueryArgs(builder, fields); headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri); + headers.Add(HttpResponseHeader.ContentType, "text/html; charset=utf-8"); Logger.Http.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri); OutgoingWebResponse response = new OutgoingWebResponse { Status = HttpStatusCode.Redirect, Headers = headers, - Body = null, + Body = string.Format(CultureInfo.InvariantCulture, RedirectResponseBodyFormat, builder.Uri.AbsoluteUri), OriginalMessage = message }; diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs index ca69f5f..e2638c9 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs @@ -187,6 +187,15 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Looks up a localized string similar to Failed to add extra parameter '{0}' with value '{1}'.. + /// </summary> + internal static string ExtraParameterAddFailure { + get { + return ResourceManager.GetString("ExtraParameterAddFailure", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to At least one of GET or POST flags must be present.. /// </summary> internal static string GetOrPostFlagsRequired { diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx index 102f044..30490c7 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx @@ -306,4 +306,7 @@ <data name="MessageTimestampInFuture" xml:space="preserve"> <value>This message has a timestamp of {0}, which is beyond the allowable clock skew for in the future.</value> </data> + <data name="ExtraParameterAddFailure" xml:space="preserve"> + <value>Failed to add extra parameter '{0}' with value '{1}'.</value> + </data> </root> diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 7c25f73..85ba9a9 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -774,7 +774,11 @@ namespace DotNetOpenAuth.Messaging { if (extraParameters != null) { foreach (var pair in extraParameters) { - messageDictionary.Add(pair); + try { + messageDictionary.Add(pair); + } catch (ArgumentException ex) { + throw ErrorUtilities.Wrap(ex, MessagingStrings.ExtraParameterAddFailure, pair.Key, pair.Value); + } } } } diff --git a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs index 193e445..ffde271 100644 --- a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs +++ b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs @@ -30,16 +30,14 @@ namespace DotNetOpenAuth.Mvc { /// Emits a series of stylesheet import tags to support the AJAX OpenID Selector. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <returns>HTML that should be sent directly to the browser.</returns> - public static string OpenIdSelectorStyles(this HtmlHelper html, Page page) { + public static string OpenIdSelectorStyles(this HtmlHelper html) { Contract.Requires<ArgumentNullException>(html != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Ensures(Contract.Result<string>() != null); StringWriter result = new StringWriter(); - result.WriteStylesheetLink(page, OpenId.RelyingParty.OpenIdSelector.EmbeddedStylesheetResourceName); - result.WriteStylesheetLink(page, OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName); + result.WriteStylesheetLink(OpenId.RelyingParty.OpenIdSelector.EmbeddedStylesheetResourceName); + result.WriteStylesheetLink(OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName); return result.ToString(); } @@ -47,25 +45,22 @@ namespace DotNetOpenAuth.Mvc { /// Emits a series of script import tags and some inline script to support the AJAX OpenID Selector. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <returns>HTML that should be sent directly to the browser.</returns> - public static string OpenIdSelectorScripts(this HtmlHelper html, Page page) { - return OpenIdSelectorScripts(html, page, null, null); + public static string OpenIdSelectorScripts(this HtmlHelper html) { + return OpenIdSelectorScripts(html, null, null); } /// <summary> /// Emits a series of script import tags and some inline script to support the AJAX OpenID Selector. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <param name="selectorOptions">An optional instance of an <see cref="OpenIdSelector"/> control, whose properties have been customized to express how this MVC control should be rendered.</param> /// <param name="additionalOptions">An optional set of additional script customizations.</param> /// <returns> /// HTML that should be sent directly to the browser. /// </returns> - public static string OpenIdSelectorScripts(this HtmlHelper html, Page page, OpenIdSelector selectorOptions, OpenIdAjaxOptions additionalOptions) { + public static string OpenIdSelectorScripts(this HtmlHelper html, OpenIdSelector selectorOptions, OpenIdAjaxOptions additionalOptions) { Contract.Requires<ArgumentNullException>(html != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Ensures(Contract.Result<string>() != null); if (selectorOptions == null) { @@ -92,10 +87,10 @@ window.openid_trace = {1}; // causes lots of messages"; OpenIdRelyingPartyAjaxControlBase.EmbeddedAjaxJavascriptResource, OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedScriptResourceName, }; - result.WriteScriptTags(page, scriptResources); + result.WriteScriptTags(scriptResources); if (selectorOptions.DownloadYahooUILibrary) { - result.WriteScriptTags(new[] { "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js" }); + result.WriteScriptTagsUrls(new[] { "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js" }); } var blockBuilder = new StringWriter(); @@ -163,10 +158,10 @@ window.openid_trace = {1}; // causes lots of messages"; }});"; blockBuilder.WriteLine( blockFormat, - MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenIdTextBox.EmbeddedLogoResourceName)), - MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName)), - MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)), - MessagingUtilities.GetSafeJavascriptValue(page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenIdTextBox.EmbeddedLogoResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName)), selectorOptions.Throttle, selectorOptions.Timeout.TotalMilliseconds, MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnText), @@ -183,7 +178,7 @@ window.openid_trace = {1}; // causes lots of messages"; MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationFailedToolTip)); result.WriteScriptBlock(blockBuilder.ToString()); - result.WriteScriptTags(page, OpenId.RelyingParty.OpenIdSelector.EmbeddedScriptResourceName); + result.WriteScriptTags(OpenId.RelyingParty.OpenIdSelector.EmbeddedScriptResourceName); Reporting.RecordFeatureUse("MVC " + typeof(OpenIdSelector).Name); return result.ToString(); @@ -193,20 +188,18 @@ window.openid_trace = {1}; // causes lots of messages"; /// Emits the HTML to render an OpenID Provider button as a part of the overall OpenID Selector UI. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <param name="providerIdentifier">The OP Identifier.</param> /// <param name="imageUrl">The URL of the image to display on the button.</param> /// <returns> /// HTML that should be sent directly to the browser. /// </returns> - public static string OpenIdSelectorOPButton(this HtmlHelper html, Page page, Identifier providerIdentifier, string imageUrl) { + public static string OpenIdSelectorOPButton(this HtmlHelper html, Identifier providerIdentifier, string imageUrl) { Contract.Requires<ArgumentNullException>(html != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentNullException>(providerIdentifier != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); Contract.Ensures(Contract.Result<string>() != null); - return OpenIdSelectorButton(html, page, providerIdentifier, "OPButton", imageUrl); + return OpenIdSelectorButton(html, providerIdentifier, "OPButton", imageUrl); } /// <summary> @@ -214,32 +207,28 @@ window.openid_trace = {1}; // causes lots of messages"; /// allowing the user to enter their own OpenID. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <param name="imageUrl">The URL of the image to display on the button.</param> /// <returns> /// HTML that should be sent directly to the browser. /// </returns> - public static string OpenIdSelectorOpenIdButton(this HtmlHelper html, Page page, string imageUrl) { + public static string OpenIdSelectorOpenIdButton(this HtmlHelper html, string imageUrl) { Contract.Requires<ArgumentNullException>(html != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); Contract.Ensures(Contract.Result<string>() != null); - return OpenIdSelectorButton(html, page, "OpenIDButton", "OpenIDButton", imageUrl); + return OpenIdSelectorButton(html, "OpenIDButton", "OpenIDButton", imageUrl); } /// <summary> /// Emits the HTML to render the entire OpenID Selector UI. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <param name="buttons">The buttons to include on the selector.</param> /// <returns> /// HTML that should be sent directly to the browser. /// </returns> - public static string OpenIdSelector(this HtmlHelper html, Page page, params SelectorButton[] buttons) { + public static string OpenIdSelector(this HtmlHelper html, params SelectorButton[] buttons) { Contract.Requires<ArgumentNullException>(html != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentNullException>(buttons != null); Contract.Ensures(Contract.Result<string>() != null); @@ -252,13 +241,13 @@ window.openid_trace = {1}; // causes lots of messages"; foreach (SelectorButton button in buttons) { var op = button as SelectorProviderButton; if (op != null) { - h.Write(OpenIdSelectorOPButton(html, page, op.OPIdentifier, op.Image)); + h.Write(OpenIdSelectorOPButton(html, op.OPIdentifier, op.Image)); continue; } var openid = button as SelectorOpenIdButton; if (openid != null) { - h.Write(OpenIdSelectorOpenIdButton(html, page, openid.Image)); + h.Write(OpenIdSelectorOpenIdButton(html, openid.Image)); continue; } @@ -294,16 +283,14 @@ window.openid_trace = {1}; // causes lots of messages"; /// Emits the HTML to render a button as a part of the overall OpenID Selector UI. /// </summary> /// <param name="html">The <see cref="HtmlHelper"/> on the view.</param> - /// <param name="page">The page being rendered.</param> /// <param name="id">The value to assign to the HTML id attribute.</param> /// <param name="cssClass">The value to assign to the HTML class attribute.</param> /// <param name="imageUrl">The URL of the image to draw on the button.</param> /// <returns> /// HTML that should be sent directly to the browser. /// </returns> - private static string OpenIdSelectorButton(this HtmlHelper html, Page page, string id, string cssClass, string imageUrl) { + private static string OpenIdSelectorButton(this HtmlHelper html, string id, string cssClass, string imageUrl) { Contract.Requires<ArgumentNullException>(html != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentNullException>(id != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); Contract.Ensures(Contract.Result<string>() != null); @@ -327,7 +314,7 @@ window.openid_trace = {1}; // causes lots of messages"; h.RenderBeginTag(HtmlTextWriterTag.Img); h.RenderEndTag(); - h.AddAttribute(HtmlTextWriterAttribute.Src, page.ClientScript.GetWebResourceUrl(typeof(OpenIdSelector), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)); + h.AddAttribute(HtmlTextWriterAttribute.Src, Util.GetWebResourceUrl(typeof(OpenIdSelector), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)); h.AddAttribute(HtmlTextWriterAttribute.Class, "loginSuccess"); h.AddAttribute(HtmlTextWriterAttribute.Title, "Authenticated as {0}"); h.RenderBeginTag(HtmlTextWriterTag.Img); @@ -351,7 +338,7 @@ window.openid_trace = {1}; // causes lots of messages"; /// </summary> /// <param name="writer">The writer to emit the tags to.</param> /// <param name="scriptUrls">The locations of the scripts to import.</param> - private static void WriteScriptTags(this TextWriter writer, IEnumerable<string> scriptUrls) { + private static void WriteScriptTagsUrls(this TextWriter writer, IEnumerable<string> scriptUrls) { Contract.Requires<ArgumentNullException>(writer != null); Contract.Requires<ArgumentNullException>(scriptUrls != null); @@ -364,28 +351,24 @@ window.openid_trace = {1}; // causes lots of messages"; /// Writes out script tags that import a script from resources embedded in this assembly. /// </summary> /// <param name="writer">The writer to emit the tags to.</param> - /// <param name="page">The page being rendered.</param> /// <param name="resourceName">Name of the resource.</param> - private static void WriteScriptTags(this TextWriter writer, Page page, string resourceName) { + private static void WriteScriptTags(this TextWriter writer, string resourceName) { Contract.Requires<ArgumentNullException>(writer != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(resourceName)); - WriteScriptTags(writer, page, new[] { resourceName }); + WriteScriptTags(writer, new[] { resourceName }); } /// <summary> /// Writes out script tags that import scripts from resources embedded in this assembly. /// </summary> /// <param name="writer">The writer to emit the tags to.</param> - /// <param name="page">The page being rendered.</param> /// <param name="resourceNames">The resource names.</param> - private static void WriteScriptTags(this TextWriter writer, Page page, IEnumerable<string> resourceNames) { + private static void WriteScriptTags(this TextWriter writer, IEnumerable<string> resourceNames) { Contract.Requires<ArgumentNullException>(writer != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentNullException>(resourceNames != null); - writer.WriteScriptTags(resourceNames.Select(r => page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), r))); + writer.WriteScriptTagsUrls(resourceNames.Select(r => Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), r))); } /// <summary> @@ -407,14 +390,12 @@ window.openid_trace = {1}; // causes lots of messages"; /// Writes a given CSS link. /// </summary> /// <param name="writer">The writer to emit the tags to.</param> - /// <param name="page">The page being rendered.</param> /// <param name="resourceName">Name of the resource containing the CSS content.</param> - private static void WriteStylesheetLink(this TextWriter writer, Page page, string resourceName) { + private static void WriteStylesheetLink(this TextWriter writer, string resourceName) { Contract.Requires<ArgumentNullException>(writer != null); - Contract.Requires<ArgumentNullException>(page != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(resourceName)); - WriteStylesheetLink(writer, page.ClientScript.GetWebResourceUrl(typeof(OpenIdRelyingPartyAjaxControlBase), resourceName)); + WriteStylesheetLinkUrl(writer, Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyAjaxControlBase), resourceName)); } /// <summary> @@ -422,7 +403,7 @@ window.openid_trace = {1}; // causes lots of messages"; /// </summary> /// <param name="writer">The writer to emit the tags to.</param> /// <param name="stylesheet">The stylesheet to link in.</param> - private static void WriteStylesheetLink(this TextWriter writer, string stylesheet) { + private static void WriteStylesheetLinkUrl(this TextWriter writer, string stylesheet) { Contract.Requires<ArgumentNullException>(writer != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(stylesheet)); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs index f775492..cec8042 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs @@ -32,14 +32,6 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { }; /// <summary> - /// Additional type URIs that this extension is sometimes known by remote parties. - /// </summary> - private static readonly string[] additionalTypeUris = new string[] { - Constants.sreg_ns10, - Constants.sreg_ns11other, - }; - - /// <summary> /// The type URI that this particular (deserialized) extension was read in using, /// allowing a response to alter be crafted using the same type URI. /// </summary> @@ -49,7 +41,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { /// Initializes a new instance of the <see cref="ClaimsRequest"/> class. /// </summary> public ClaimsRequest() - : base(new Version(1, 0), Constants.sreg_ns, additionalTypeUris) { + : base(new Version(1, 0), Constants.sreg_ns, Constants.AdditionalTypeUris) { } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs index 7db6d45..d4df028 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs @@ -27,7 +27,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { /// The factory method that may be used in deserialization of this message. /// </summary> internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { - if (typeUri == Constants.sreg_ns && !isProviderRole) { + if ((typeUri == Constants.sreg_ns || Array.IndexOf(Constants.AdditionalTypeUris, typeUri) >= 0) && !isProviderRole) { return new ClaimsResponse(typeUri); } @@ -69,7 +69,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { /// This value should be the same one the relying party used to send the extension request. /// </param> internal ClaimsResponse(string typeUriToUse) - : base(new Version(1, 0), typeUriToUse, EmptyList<string>.Instance) { + : base(new Version(1, 0), typeUriToUse, Constants.AdditionalTypeUris) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUriToUse)); } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs index 544ba77..9e00137 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs @@ -34,5 +34,13 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { internal const string Male = "M"; internal const string Female = "F"; } + + /// <summary> + /// Additional type URIs that this extension is sometimes known by remote parties. + /// </summary> + internal static readonly string[] AdditionalTypeUris = new string[] { + Constants.sreg_ns10, + Constants.sreg_ns11other, + }; } } diff --git a/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs b/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs index 6319c02..c0354ac 100644 --- a/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs +++ b/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs @@ -92,6 +92,13 @@ namespace DotNetOpenAuth.OpenId.Interop { } /// <summary> + /// Gets the provider endpoint that sent the assertion. + /// </summary> + public string ProviderEndpoint { + get { return this.response.Provider != null ? this.response.Provider.Uri.AbsoluteUri : null; } + } + + /// <summary> /// Gets a value indicating whether the authentication attempt succeeded. /// </summary> public bool Successful { diff --git a/src/DotNetOpenAuth/Reporting.cs b/src/DotNetOpenAuth/Reporting.cs index 612845f..2f93416 100644 --- a/src/DotNetOpenAuth/Reporting.cs +++ b/src/DotNetOpenAuth/Reporting.cs @@ -32,6 +32,11 @@ namespace DotNetOpenAuth { /// </summary> public static class Reporting { /// <summary> + /// A UTF8 encoder that doesn't emit the preamble. Used for mid-stream writers. + /// </summary> + private static readonly Encoding Utf8NoPreamble = new UTF8Encoding(false); + + /// <summary> /// A value indicating whether reporting is desirable or not. Must be logical-AND'd with !<see cref="broken"/>. /// </summary> private static bool enabled; @@ -665,7 +670,7 @@ namespace DotNetOpenAuth { this.memorySet.Add(this.reader.ReadLine()); } - this.writer = new StreamWriter(this.fileStream, Encoding.UTF8); + this.writer = new StreamWriter(this.fileStream, Utf8NoPreamble); this.lastFlushed = DateTime.Now; } @@ -818,7 +823,7 @@ namespace DotNetOpenAuth { } } - this.writer = new StreamWriter(this.fileStream, Encoding.UTF8); + this.writer = new StreamWriter(this.fileStream, Utf8NoPreamble); this.lastFlushed = DateTime.Now; } diff --git a/src/DotNetOpenAuth/Strings.Designer.cs b/src/DotNetOpenAuth/Strings.Designer.cs index 70b9fb2..1461f83 100644 --- a/src/DotNetOpenAuth/Strings.Designer.cs +++ b/src/DotNetOpenAuth/Strings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30104.0 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -79,6 +79,15 @@ namespace DotNetOpenAuth { } /// <summary> + /// Looks up a localized string similar to The current IHttpHandler is not one of types: {0}. An embedded resource URL provider must be set in your .config file.. + /// </summary> + internal static string EmbeddedResourceUrlProviderRequired { + get { + return ResourceManager.GetString("EmbeddedResourceUrlProviderRequired", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}.. /// </summary> internal static string StoreRequiredWhenNoHttpContextAvailable { diff --git a/src/DotNetOpenAuth/Strings.resx b/src/DotNetOpenAuth/Strings.resx index a7f080d..4b78664 100644 --- a/src/DotNetOpenAuth/Strings.resx +++ b/src/DotNetOpenAuth/Strings.resx @@ -126,4 +126,7 @@ <data name="ConfigurationXamlReferenceRequiresHttpContext" xml:space="preserve"> <value>The configuration XAML reference to {0} requires a current HttpContext to resolve.</value> </data> -</root> + <data name="EmbeddedResourceUrlProviderRequired" xml:space="preserve"> + <value>The current IHttpHandler is not one of types: {0}. An embedded resource URL provider must be set in your .config file.</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs index 8a18ef8..0317c4d 100644 --- a/src/DotNetOpenAuth/Util.cs +++ b/src/DotNetOpenAuth/Util.cs @@ -11,6 +11,10 @@ namespace DotNetOpenAuth { using System.Net; using System.Reflection; using System.Text; + using System.Web; + using System.Web.UI; + + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; /// <summary> @@ -24,6 +28,11 @@ namespace DotNetOpenAuth { internal const string DefaultNamespace = "DotNetOpenAuth"; /// <summary> + /// The web.config file-specified provider of web resource URLs. + /// </summary> + private static IEmbeddedResourceRetrieval embeddedResourceRetrieval = DotNetOpenAuthSection.Configuration.EmbeddedResourceRetrievalProvider.CreateInstance(null, false); + + /// <summary> /// Gets a human-readable description of the library name and version, including /// whether the build is an official or private one. /// </summary> @@ -154,6 +163,32 @@ namespace DotNetOpenAuth { } /// <summary> + /// Gets the web resource URL from a Page or <see cref="IEmbeddedResourceRetrieval"/> object. + /// </summary> + /// <param name="someTypeInResourceAssembly">Some type in resource assembly.</param> + /// <param name="manifestResourceName">Name of the manifest resource.</param> + /// <returns>An absolute URL</returns> + internal static string GetWebResourceUrl(Type someTypeInResourceAssembly, string manifestResourceName) { + Page page; + IEmbeddedResourceRetrieval retrieval; + + if (embeddedResourceRetrieval != null) { + Uri url = embeddedResourceRetrieval.GetWebResourceUrl(someTypeInResourceAssembly, manifestResourceName); + return url != null ? url.AbsoluteUri : null; + } else if ((page = HttpContext.Current.CurrentHandler as Page) != null) { + return page.ClientScript.GetWebResourceUrl(someTypeInResourceAssembly, manifestResourceName); + } else if ((retrieval = HttpContext.Current.CurrentHandler as IEmbeddedResourceRetrieval) != null) { + return retrieval.GetWebResourceUrl(someTypeInResourceAssembly, manifestResourceName).AbsoluteUri; + } else { + throw new InvalidOperationException( + string.Format( + CultureInfo.CurrentCulture, + Strings.EmbeddedResourceUrlProviderRequired, + string.Join(", ", new string[] { typeof(Page).FullName, typeof(IEmbeddedResourceRetrieval).FullName }))); + } + } + + /// <summary> /// Manages an individual deferred ToString call. /// </summary> /// <typeparam name="T">The type of object to be serialized as a string.</typeparam> diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs index 8b8c20f..357dd8d 100644 --- a/src/DotNetOpenAuth/Yadis/Yadis.cs +++ b/src/DotNetOpenAuth/Yadis/Yadis.cs @@ -151,7 +151,24 @@ namespace DotNetOpenAuth.Yadis { options |= DirectWebRequestOptions.RequireSsl; } - return requestHandler.GetResponse(request, options); + try { + return requestHandler.GetResponse(request, options); + } catch (ProtocolException ex) { + var webException = ex.InnerException as WebException; + if (webException != null) { + var response = webException.Response as HttpWebResponse; + if (response != null && response.IsFromCache) { + // We don't want to report error responses from the cache, since the server may have fixed + // whatever was causing the problem. So try again with cache disabled. + Logger.Messaging.Error("An HTTP error response was obtained from the cache. Retrying with cache disabled.", ex); + var nonCachingRequest = request.Clone(); + nonCachingRequest.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Reload); + return requestHandler.GetResponse(nonCachingRequest, options); + } + } + + throw; + } } /// <summary> diff --git a/tools/7-Zip.x86/7-zip.chm b/tools/7-Zip.x86/7-zip.chm Binary files differnew file mode 100644 index 0000000..08e4df2 --- /dev/null +++ b/tools/7-Zip.x86/7-zip.chm diff --git a/tools/7-Zip.x86/7za.exe b/tools/7-Zip.x86/7za.exe Binary files differnew file mode 100644 index 0000000..7f6bf86 --- /dev/null +++ b/tools/7-Zip.x86/7za.exe diff --git a/tools/7-Zip.x86/license.txt b/tools/7-Zip.x86/license.txt new file mode 100644 index 0000000..530ff36 --- /dev/null +++ b/tools/7-Zip.x86/license.txt @@ -0,0 +1,29 @@ + 7-Zip Command line version + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + License for use and distribution + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 7-Zip Copyright (C) 1999-2010 Igor Pavlov. + + 7za.exe is distributed under the GNU LGPL license + + Notes: + You can use 7-Zip on any computer, including a computer in a commercial + organization. You don't need to register or pay for 7-Zip. + + + GNU LGPL information + -------------------- + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You can receive a copy of the GNU Lesser General Public License from + http://www.gnu.org/ diff --git a/tools/7-Zip.x86/readme.txt b/tools/7-Zip.x86/readme.txt new file mode 100644 index 0000000..9ae4222 --- /dev/null +++ b/tools/7-Zip.x86/readme.txt @@ -0,0 +1,41 @@ +7-Zip Command line version 9.20 +------------------------------- + +7-Zip is a file archiver with high compression ratio. +7za.exe is a standalone command line version of 7-Zip. + +7-Zip Copyright (C) 1999-2010 Igor Pavlov. + +Features of 7za.exe: + - High compression ratio in new 7z format + - Supported formats: + - Packing / unpacking: 7z, xz, ZIP, GZIP, BZIP2 and TAR + - Unpacking only: Z, lzma + - Highest compression ratio for ZIP and GZIP formats. + - Fast compression and decompression + - Strong AES-256 encryption in 7z and ZIP formats. + +7za.exe is a free software distributed under the GNU LGPL. +Read license.txt for more information. + +Source code of 7za.exe and 7-Zip can be found at +http://www.7-zip.org/ + +7za.exe can work in Windows 95/98/ME/NT/2000/2003/2008/XP/Vista/7. + +There is also port of 7za.exe for POSIX systems like Unix (Linux, Solaris, OpenBSD, +FreeBSD, Cygwin, AIX, ...), MacOS X and BeOS: + +http://p7zip.sourceforge.net/ + + + This distributive packet contains the following files: + + 7za.exe - 7-Zip standalone command line version. + readme.txt - This file. + license.txt - License information. + 7-zip.chm - User's Manual in HTML Help format. + + +--- +End of document diff --git a/tools/DotNetOpenAuth.props b/tools/DotNetOpenAuth.props index b282db5..3e826bc 100644 --- a/tools/DotNetOpenAuth.props +++ b/tools/DotNetOpenAuth.props @@ -11,6 +11,9 @@ <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">obj\$(TargetFrameworkVersion)\</BaseIntermediateOutputPath> <ToolsDir>$(ProjectRoot)tools\</ToolsDir> <ZipLevel>6</ZipLevel> + <Zip7ToolPath>$(ToolsDir)7-Zip.x86\</Zip7ToolPath> + <NuGetToolPath>$(ToolsDir)NuGet\</NuGetToolPath> + <ZipFormat Condition=" '$(ZipFormat)' == '' ">.7z</ZipFormat> <ClrVersion Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">4</ClrVersion> <ClrVersion Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">2</ClrVersion> diff --git a/tools/NuGet/NuGet.exe b/tools/NuGet/NuGet.exe Binary files differnew file mode 100644 index 0000000..907d24d --- /dev/null +++ b/tools/NuGet/NuGet.exe diff --git a/tools/drop.proj b/tools/drop.proj index 4ceb6bd..fa088cb 100644 --- a/tools/drop.proj +++ b/tools/drop.proj @@ -150,14 +150,19 @@ <Target Name="Build" DependsOnTargets="Layout" Returns="@(RedistributableFiles)"> <PropertyGroup> - <DropZip>$(DropDirectoryNoSlash).zip</DropZip> + <DropZip>$(DropDirectoryNoSlash)$(ZipFormat)</DropZip> </PropertyGroup> <ItemGroup> <RedistributableFiles Include="$(DropZip)"> <Package>DotNetOpenAuth</Package> </RedistributableFiles> </ItemGroup> - <Zip Files="@(AllDropTargets)" ZipFileName="$(DropZip)" WorkingDirectory="$(DropsRoot)" ZipLevel="$(ZipLevel)" /> + <Delete Files="$(DropZip)" /> + <AddFilesTo7Zip + Files="@(AllDropTargets)" + ZipFileName="$(DropZip)" + WorkingDirectory="$(DropsRoot)" + ToolPath="$(Zip7ToolPath)" /> </Target> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.automated.targets"/> |