diff options
94 files changed, 2306 insertions, 187 deletions
@@ -24,7 +24,7 @@ <UsingTask AssemblyFile="$(ProjectRoot)lib\MSBuild.Community.Tasks.dll" TaskName="NUnit" /> <ItemGroup> - <SampleProjects Include="$(ProjectRoot)samples\**\*.csproj" /> + <SampleProjects Include="$(ProjectRoot)samples\**\*.csproj;$(ProjectRoot)samples\**\*.vbproj" /> <SampleSites Include="OAuthConsumer;OAuthServiceProvider;InfoCardRelyingParty" /> <ProjectTemplates Include="$(ProjectRoot)projecttemplates\**\*.csproj" /> <ILMergeInputAssemblies Include="$(OutputPath)$(ProductName).dll; @@ -113,22 +113,27 @@ <ItemGroup> <ToolProjects Include="$(ProjectRoot)Samples\OpenIdOfflineProvider\OpenIdOfflineProvider.csproj" /> + </ItemGroup> + + <MSBuild Projects="@(ToolProjects)" /> + + <ItemGroup> <OfflineProvider Include=" - $(ProjectRoot)Samples\OpenIdOfflineProvider\bin\$(TargetFrameworkVersion)\$(Configuration)\**\*.dll; - $(ILMergeOutputAssembly).*; - $(ProjectRoot)Samples\OpenIdOfflineProvider\bin\$(TargetFrameworkVersion)\$(Configuration)\OpenIdOfflineProvider.exe" - Exclude=" - $(ProjectRoot)Samples\OpenIdOfflineProvider\bin\$(TargetFrameworkVersion)\$(Configuration)\$(ProductName).*; + $(OutputPath)**\*.dll; + $(OutputPath)OpenIdOfflineProvider.exe" + Exclude=" + $(OutputPath)$(ProductName).*; + $(ILMergeOutputAssembly); "/> <OfflineProviderTargets Include=" @(OfflineProvider->'$(ToolsDirectory)%(RecursiveDir)%(FileName)%(Extension)')"/> + <OfflineProvider Include="$(ILMergeOutputAssembly)" /> + <OfflineProviderTargets Include="$(ToolsDirectory)$(ProductName).dll" /> <AllToolSources Include="@(OfflineProvider)" /> <AllToolTargets Include="@(OfflineProviderTargets)" /> </ItemGroup> - <MSBuild Projects="@(ToolProjects)" /> - <MakeDir Directories="@(ToolsDirectory)" /> <Copy SourceFiles="@(AllToolSources)" DestinationFiles="@(AllToolTargets)" SkipUnchangedFiles="true" /> diff --git a/lib/DotNetOpenAuth.BuildTasks.dll b/lib/DotNetOpenAuth.BuildTasks.dll Binary files differindex f3a61b3..fc5788a 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 091c3ca..8d6282e 100644 --- a/lib/DotNetOpenAuth.BuildTasks.pdb +++ b/lib/DotNetOpenAuth.BuildTasks.pdb diff --git a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs index 2a98ffe..29973c2 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.ApplicationBlock { using System; using System.Collections.Generic; using System.IO; + using System.Net; using System.Xml; using System.Xml.Linq; using DotNetOpenAuth.Messaging; @@ -38,6 +39,18 @@ namespace DotNetOpenAuth.ApplicationBlock { /// </summary> private static readonly MessageReceivingEndpoint GetFriendTimelineStatusEndpoint = new MessageReceivingEndpoint("http://twitter.com/statuses/friends_timeline.xml", HttpDeliveryMethods.GetRequest); + private static readonly MessageReceivingEndpoint UpdateProfileBackgroundImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_background_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest); + + private static readonly MessageReceivingEndpoint UpdateProfileImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest); + + /// <summary> + /// Initializes static members of the <see cref="TwitterConsumer"/> class. + /// </summary> + static TwitterConsumer() { + // Twitter can't handle the Expect 100 Continue HTTP header. + ServicePointManager.FindServicePoint(GetFavoritesEndpoint.Location).Expect100Continue = false; + } + public static XDocument GetUpdates(ConsumerBase twitter, string accessToken) { IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFriendTimelineStatusEndpoint, accessToken); return XDocument.Load(XmlReader.Create(response.GetResponseReader())); @@ -47,5 +60,32 @@ namespace DotNetOpenAuth.ApplicationBlock { IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFavoritesEndpoint, accessToken); return XDocument.Load(XmlReader.Create(response.GetResponseReader())); } + + public static XDocument UpdateProfileBackgroundImage(ConsumerBase twitter, string accessToken, string image, bool tile) { + var parts = new[] { + MultipartPostPart.CreateFormFilePart("image", image, "image/" + Path.GetExtension(image).Substring(1).ToLowerInvariant()), + MultipartPostPart.CreateFormPart("tile", tile.ToString().ToLowerInvariant()), + }; + HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileBackgroundImageEndpoint, accessToken, parts); + request.ServicePoint.Expect100Continue = false; + IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request); + string responseString = response.GetResponseReader().ReadToEnd(); + return XDocument.Parse(responseString); + } + + public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, string pathToImage) { + string contentType = "image/" + Path.GetExtension(pathToImage).Substring(1).ToLowerInvariant(); + return UpdateProfileImage(twitter, accessToken, File.OpenRead(pathToImage), contentType); + } + + public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, Stream image, string contentType) { + var parts = new[] { + MultipartPostPart.CreateFormFilePart("image", "twitterPhoto", contentType, image), + }; + HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileImageEndpoint, accessToken, parts); + IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request); + string responseString = response.GetResponseReader().ReadToEnd(); + return XDocument.Parse(responseString); + } } } diff --git a/samples/OAuthConsumer/Twitter.aspx b/samples/OAuthConsumer/Twitter.aspx index a659533..30ce2a0 100644 --- a/samples/OAuthConsumer/Twitter.aspx +++ b/samples/OAuthConsumer/Twitter.aspx @@ -16,9 +16,19 @@ </asp:View> <asp:View runat="server"> <h2>Updates</h2> - <p>Ok, Twitter has authorized us to download your feeds. Click 'Get updates' to download - updates to this sample. Notice how we never asked you for your Twitter username - or password. </p> + <p>Ok, Twitter has authorized us to download your feeds. Notice how we never asked + you for your Twitter username or password. </p> + <p> + Upload a new profile photo: + <asp:FileUpload ID="profilePhoto" runat="server" /> + <asp:Button ID="uploadProfilePhotoButton" runat="server" + onclick="uploadProfilePhotoButton_Click" Text="Upload photo" /> + <asp:Label ID="photoUploadedLabel" runat="server" EnableViewState="False" + Text="Done!" Visible="False"></asp:Label> + </p> + <p> + Click 'Get updates' to download updates to this sample. + </p> <asp:Button ID="downloadUpdates" runat="server" Text="Get updates" OnClick="downloadUpdates_Click" /> <asp:PlaceHolder runat="server" ID="resultsPlaceholder" /> </asp:View> diff --git a/samples/OAuthConsumer/Twitter.aspx.cs b/samples/OAuthConsumer/Twitter.aspx.cs index 9b9eced..f309396 100644 --- a/samples/OAuthConsumer/Twitter.aspx.cs +++ b/samples/OAuthConsumer/Twitter.aspx.cs @@ -75,4 +75,20 @@ public partial class Twitter : System.Web.UI.Page { tableBuilder.Append("</table>"); resultsPlaceholder.Controls.Add(new Literal { Text = tableBuilder.ToString() }); } + + protected void uploadProfilePhotoButton_Click(object sender, EventArgs e) { + if (profilePhoto.PostedFile.ContentType == null) { + photoUploadedLabel.Visible = true; + photoUploadedLabel.Text = "Select a file first."; + return; + } + + var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager); + XDocument imageResult = TwitterConsumer.UpdateProfileImage( + twitter, + this.AccessToken, + profilePhoto.PostedFile.InputStream, + profilePhoto.PostedFile.ContentType); + photoUploadedLabel.Visible = true; + } } diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Code/State.vb b/samples/OpenIdRelyingPartyWebFormsVB/Code/State.vb new file mode 100644 index 0000000..e00cb4f --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Code/State.vb @@ -0,0 +1,46 @@ +Imports DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy +Imports DotNetOpenAuth.OpenId.Extensions.SimpleRegistration +Imports DotNetOpenAuth.OpenId.Extensions.AttributeExchange + +Public Class State + Public Shared Property ProfileFields() As ClaimsResponse + Get + Return HttpContext.Current.Session("ProfileFields") + End Get + Set(ByVal value As ClaimsResponse) + HttpContext.Current.Session("ProfileFields") = value + End Set + End Property + + Public Shared Property FetchResponse() As FetchResponse + Get + Return HttpContext.Current.Session("FetchResponse") + End Get + Set(ByVal value As FetchResponse) + HttpContext.Current.Session("FetchResponse") = value + End Set + End Property + + Public Shared Property FriendlyLoginName() As String + Get + Return HttpContext.Current.Session("FriendlyLoginName") + End Get + Set(ByVal value As String) + HttpContext.Current.Session("FriendlyLoginName") = value + End Set + End Property + + Public Shared Property PapePolicies() As PolicyResponse + Get + Return HttpContext.Current.Session("PapePolicies") + End Get + Set(ByVal value As PolicyResponse) + HttpContext.Current.Session("PapePolicies") = value + End Set + End Property + + Public Shared Sub Clear() + FriendlyLoginName = Nothing + End Sub + +End Class diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Code/TracePageAppender.vb b/samples/OpenIdRelyingPartyWebFormsVB/Code/TracePageAppender.vb new file mode 100644 index 0000000..dfc2db5 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Code/TracePageAppender.vb @@ -0,0 +1,10 @@ +Imports System.IO + +Public Class TracePageAppender + Inherits log4net.Appender.AppenderSkeleton + + Protected Overrides Sub Append(ByVal loggingEvent As log4net.Core.LoggingEvent) + Dim sw As StringWriter = New StringWriter(Global_asax.LogMessages) + Layout.Format(sw, loggingEvent) + End Sub +End Class
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx b/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx new file mode 100644 index 0000000..53f6dfc --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx @@ -0,0 +1,18 @@ +<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="OpenIdRelyingPartyWebFormsVB._Default" + MasterPageFile="~/Site.Master" %> + +<%@ Register Assembly="DotNetOpenAuth" Namespace="DotNetOpenAuth" TagPrefix="openid" %> +<asp:Content runat="server" ContentPlaceHolderID="head"> + <openid:XrdsPublisher ID="XrdsPublisher1" runat="server" XrdsUrl="~/xrds.aspx" /> +</asp:Content> +<asp:Content runat="server" ContentPlaceHolderID="main"> + <h2> + Relying Party + </h2> + <p> + Visit the + <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/MembersOnly/Default.aspx" + Text="Members Only" /> + area. (This will trigger a login demo). + </p> +</asp:Content> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx.designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx.designer.vb new file mode 100644 index 0000000..ce59c43 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx.designer.vb @@ -0,0 +1,26 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.4927 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + + +Partial Public Class _Default + + '''<summary> + '''form1 control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents form1 As Global.System.Web.UI.HtmlControls.HtmlForm +End Class diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx.vb b/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx.vb new file mode 100644 index 0000000..3e023c0 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Default.aspx.vb @@ -0,0 +1,8 @@ +Partial Public Class _Default + Inherits System.Web.UI.Page + + Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load + + End Sub + +End Class
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Global.asax b/samples/OpenIdRelyingPartyWebFormsVB/Global.asax new file mode 100644 index 0000000..92716f1 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.vb" Inherits="OpenIdRelyingPartyWebFormsVB.Global_asax" Language="vb" %> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Global.asax.vb b/samples/OpenIdRelyingPartyWebFormsVB/Global.asax.vb new file mode 100644 index 0000000..60ab0cc --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Global.asax.vb @@ -0,0 +1,66 @@ +Imports System +Imports System.Collections.Specialized +Imports System.Configuration +Imports System.IO +Imports System.Text +Imports System.Web +Imports DotNetOpenAuth.ApplicationBlock +Imports DotNetOpenAuth.OAuth +Imports OpenIdRelyingPartyWebFormsVB + +Public Class Global_asax + Inherits HttpApplication + + Public Shared Logger As log4net.ILog = log4net.LogManager.GetLogger(GetType(Global_asax)) + + Friend Shared LogMessages As StringBuilder = New StringBuilder + + Public Shared Function CollectionToString(ByVal collection As NameValueCollection) As String + Dim sw As StringWriter = New StringWriter + For Each key As String In collection.Keys + sw.WriteLine("{0} = '{1}'", key, collection(key)) + Next + Return sw.ToString + End Function + + Protected Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) + log4net.Config.XmlConfigurator.Configure() + Logger.Info("Sample starting...") + End Sub + + Protected Sub Application_End(ByVal sender As Object, ByVal e As EventArgs) + Logger.Info("Sample shutting down...") + ' this would be automatic, but in partial trust scenarios it is not. + log4net.LogManager.Shutdown() + End Sub + + Protected Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs) + ' System.Diagnostics.Debugger.Launch(); + Logger.DebugFormat("Processing {0} on {1} ", Request.HttpMethod, stripQueryString(Request.Url)) + If (Request.QueryString.Count > 0) Then + Logger.DebugFormat("Querystring follows: " & vbLf & "{0}", CollectionToString(Request.QueryString)) + End If + If (Request.Form.Count > 0) Then + Logger.DebugFormat("Posted form follows: " & vbLf & "{0}", CollectionToString(Request.Form)) + End If + End Sub + + Protected Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs) + Logger.DebugFormat("User {0} authenticated.", (Not (HttpContext.Current.User) Is Nothing)) + 'TODO: Warning!!!, inline IF is not supported ? + End Sub + + Protected Sub Application_EndRequest(ByVal sender As Object, ByVal e As EventArgs) + + End Sub + + Protected Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) + Logger.ErrorFormat("An unhandled exception was raised. Details follow: {0}", HttpContext.Current.Server.GetLastError) + End Sub + + Private Shared Function stripQueryString(ByVal uri As Uri) As String + Dim builder As UriBuilder = New UriBuilder(uri) + builder.Query = Nothing + Return builder.ToString + End Function +End Class
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx b/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx new file mode 100644 index 0000000..d0e978b --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx @@ -0,0 +1,28 @@ +<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Login.aspx.vb" Inherits="OpenIdRelyingPartyWebFormsVB.Login" + ValidateRequest="false" MasterPageFile="~/Site.Master" %> + +<%@ Register Assembly="DotNetOpenAuth" Namespace="DotNetOpenAuth.OpenId.RelyingParty" TagPrefix="rp" %> +<asp:Content ID="Content1" runat="server" ContentPlaceHolderID="Main"> + <h2>Login Page </h2> + <rp:OpenIdLogin ID="OpenIdLogin1" runat="server" CssClass="openid_login" RequestCountry="Request" + RequestEmail="Require" RequestGender="Require" RequestPostalCode="Require" RequestTimeZone="Require" + RememberMeVisible="True" PolicyUrl="~/PrivacyPolicy.aspx" TabIndex="1" /> + <fieldset title="Knobs"> + <asp:CheckBox ID="requireSslCheckBox" runat="server" + Text="RequireSsl (high security) mode" + oncheckedchanged="requireSslCheckBox_CheckedChanged" /><br /> + <h4 style="margin-top: 0; margin-bottom: 0">PAPE policies</h4> + <asp:CheckBoxList runat="server" ID="papePolicies"> + <asp:ListItem Text="Request phishing resistant authentication" Value="http://schemas.openid.net/pape/policies/2007/06/phishing-resistant" /> + <asp:ListItem Text="Request multi-factor authentication" Value="http://schemas.openid.net/pape/policies/2007/06/multi-factor" /> + <asp:ListItem Text="Request physical multi-factor authentication" Value="http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical" /> + <asp:ListItem Text="Request PPID identifier" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" /> + </asp:CheckBoxList> + <p>Try the PPID identifier functionality against the OpenIDProviderMvc sample.</p> + </fieldset> + <p><a href="loginGoogleApps.aspx">Log in using Google Apps for Domains</a>. </p> + <p> + <rp:OpenIdButton runat="server" ImageUrl="~/images/yahoo.png" Text="Login with Yahoo!" ID="yahooLoginButton" + Identifier="https://me.yahoo.com/" OnLoggingIn="OpenIdLogin1_LoggingIn" OnLoggedIn="OpenIdLogin1_LoggedIn" /> + </p> +</asp:Content> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx.designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx.designer.vb new file mode 100644 index 0000000..4cf1a96 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx.designer.vb @@ -0,0 +1,53 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.4927 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + + +Partial Public Class Login + + '''<summary> + '''OpenIdLogin1 control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents OpenIdLogin1 As Global.DotNetOpenAuth.OpenId.RelyingParty.OpenIdLogin + + '''<summary> + '''requireSslCheckBox control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents requireSslCheckBox As Global.System.Web.UI.WebControls.CheckBox + + '''<summary> + '''papePolicies control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents papePolicies As Global.System.Web.UI.WebControls.CheckBoxList + + '''<summary> + '''yahooLoginButton control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents yahooLoginButton As Global.DotNetOpenAuth.OpenId.RelyingParty.OpenIdButton +End Class diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx.vb b/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx.vb new file mode 100644 index 0000000..339f62c --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Login.aspx.vb @@ -0,0 +1,52 @@ +Imports DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy +Imports DotNetOpenAuth.OpenId.RelyingParty +Imports DotNetOpenAuth.OpenId.Extensions.SimpleRegistration + +Partial Public Class Login + Inherits System.Web.UI.Page + + Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load + OpenIdLogin1.Focus() + End Sub + + Protected Sub requireSslCheckBox_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) + OpenIdLogin1.RequireSsl = requireSslCheckBox.Checked + End Sub + + Protected Sub OpenIdLogin1_LoggingIn(ByVal sender As Object, ByVal e As DotNetOpenAuth.OpenId.RelyingParty.OpenIdEventArgs) Handles OpenIdLogin1.LoggingIn + prepareRequest(e.Request) + End Sub + + ''' <summary> + ''' Fired upon login. + ''' </summary> + ''' <param name="sender">The source of the event.</param> + ''' <param name="e">The <see cref="DotNetOpenAuth.OpenId.RelyingParty.OpenIdEventArgs"/> instance containing the event data.</param> + ''' <remarks> + ''' Note, that straight after login, forms auth will redirect the user + ''' to their original page. So this page may never be rendererd. + ''' </remarks> + Protected Sub OpenIdLogin1_LoggedIn(ByVal sender As Object, ByVal e As DotNetOpenAuth.OpenId.RelyingParty.OpenIdEventArgs) Handles OpenIdLogin1.LoggedIn + State.FriendlyLoginName = e.Response.FriendlyIdentifierForDisplay + State.ProfileFields = e.Response.GetExtension(Of ClaimsResponse)() + State.PapePolicies = e.Response.GetExtension(Of PolicyResponse)() + End Sub + + Private Sub prepareRequest(ByVal request As IAuthenticationRequest) + ' Collect the PAPE policies requested by the user. + Dim policies As New List(Of String) + For Each item As ListItem In Me.papePolicies.Items + If item.Selected Then + policies.Add(item.Value) + End If + Next + ' Add the PAPE extension if any policy was requested. + If (policies.Count > 0) Then + Dim pape As New PolicyRequest + For Each policy As String In policies + pape.PreferredPolicies.Add(policy) + Next + request.AddExtension(pape) + End If + End Sub +End Class
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx new file mode 100644 index 0000000..7f1fa0e --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx @@ -0,0 +1,15 @@ +<%@ Page Language="vb" AutoEventWireup="true" CodeBehind="loginProgrammatic.aspx.vb" + Inherits="OpenIdRelyingPartyWebFormsVB.LoginProgrammatic" MasterPageFile="~/Site.Master" %> +<asp:Content ID="Content1" runat="server" ContentPlaceHolderID="Main"> + <h2>Login Page </h2> + <asp:Label ID="Label1" runat="server" Text="OpenID Login" /> + <asp:TextBox ID="openIdBox" runat="server" /> + <asp:Button ID="loginButton" runat="server" Text="Login" OnClick="loginButton_Click" /> + <asp:CustomValidator runat="server" ID="openidValidator" ErrorMessage="Invalid OpenID Identifier" + ControlToValidate="openIdBox" EnableViewState="false" OnServerValidate="openidValidator_ServerValidate" /> + <br /> + <asp:Label ID="loginFailedLabel" runat="server" EnableViewState="False" Text="Login failed" + Visible="False" /> + <asp:Label ID="loginCanceledLabel" runat="server" EnableViewState="False" Text="Login canceled" + Visible="False" /> +</asp:Content>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.designer.vb new file mode 100644 index 0000000..907fcda --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.designer.vb @@ -0,0 +1,71 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.4927 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + + +Partial Public Class LoginProgrammatic + + '''<summary> + '''Label1 control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents Label1 As Global.System.Web.UI.WebControls.Label + + '''<summary> + '''openIdBox control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents openIdBox As Global.System.Web.UI.WebControls.TextBox + + '''<summary> + '''loginButton control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents loginButton As Global.System.Web.UI.WebControls.Button + + '''<summary> + '''openidValidator control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents openidValidator As Global.System.Web.UI.WebControls.CustomValidator + + '''<summary> + '''loginFailedLabel control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents loginFailedLabel As Global.System.Web.UI.WebControls.Label + + '''<summary> + '''loginCanceledLabel control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents loginCanceledLabel As Global.System.Web.UI.WebControls.Label +End Class diff --git a/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb new file mode 100644 index 0000000..6cac182 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/LoginProgrammatic.aspx.vb @@ -0,0 +1,76 @@ +Imports System.Net +Imports System.Web.Security +Imports DotNetOpenAuth.Messaging +Imports DotNetOpenAuth.OpenId +Imports DotNetOpenAuth.OpenId.Extensions.SimpleRegistration +Imports DotNetOpenAuth.OpenId.RelyingParty + +Public Class LoginProgrammatic + Inherits System.Web.UI.Page + + Private Shared relyingParty As New OpenIdRelyingParty + + Protected Sub openidValidator_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs) + ' This catches common typos that result in an invalid OpenID Identifier. + args.IsValid = Identifier.IsValid(args.Value) + End Sub + + Protected Sub loginButton_Click(ByVal sender As Object, ByVal e As EventArgs) + If Not Me.Page.IsValid Then + Return + ' don't login if custom validation failed. + End If + Try + Dim request As IAuthenticationRequest = relyingParty.CreateRequest(Me.openIdBox.Text) + ' This is where you would add any OpenID extensions you wanted + ' to include in the authentication request. + request.AddExtension(New ClaimsRequest() With { _ + .Country = DemandLevel.Request, _ + .Email = DemandLevel.Request, _ + .Gender = DemandLevel.Require, _ + .PostalCode = DemandLevel.Require, _ + .TimeZone = DemandLevel.Require _ + }) + ' Send your visitor to their Provider for authentication. + request.RedirectToProvider() + Catch ex As ProtocolException + ' The user probably entered an Identifier that + ' was not a valid OpenID endpoint. + Me.openidValidator.Text = ex.Message + Me.openidValidator.IsValid = False + End Try + End Sub + + Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load + Me.openIdBox.Focus() + ' For debugging/testing, we allow remote clearing of all associations... + ' NOT a good idea on a production site. + If (Request.QueryString("clearAssociations") = "1") Then + Application.Remove("DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.ApplicationStore") + ' Force a redirect now to prevent the user from logging in while associations + ' are constantly being cleared. + Dim builder As UriBuilder = New UriBuilder(Request.Url) + builder.Query = Nothing + Me.Response.Redirect(builder.Uri.AbsoluteUri) + End If + Dim response As IAuthenticationResponse = relyingParty.GetResponse + If response IsNot Nothing Then + Select Case response.Status + Case AuthenticationStatus.Authenticated + ' This is where you would look for any OpenID extension responses included + ' in the authentication assertion. + Dim claimsResponse As ClaimsResponse = response.GetExtension(Of ClaimsResponse)() + State.ProfileFields = claimsResponse + ' Store off the "friendly" username to display -- NOT for username lookup + State.FriendlyLoginName = response.FriendlyIdentifierForDisplay + ' Use FormsAuthentication to tell ASP.NET that the user is now logged in, + ' with the OpenID Claimed Identifier as their username. + FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, False) + Case AuthenticationStatus.Canceled + Me.loginCanceledLabel.Visible = True + Case AuthenticationStatus.Failed + Me.loginFailedLabel.Visible = True + End Select + End If + End Sub +End Class
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/Default.aspx b/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/Default.aspx new file mode 100644 index 0000000..441ef84 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/Default.aspx @@ -0,0 +1,34 @@ +<%@ Page Language="VB" AutoEventWireup="true" MasterPageFile="~/Site.Master" %> +<%@ Import Namespace="OpenIdRelyingPartyWebFormsVB" %> +<%@ Register Src="~/MembersOnly/ProfileFieldsDisplay.ascx" TagPrefix="cc1" TagName="ProfileFieldsDisplay" %> +<asp:Content ID="Content1" runat="server" ContentPlaceHolderID="Main"> + <h2> + Members Only Area + </h2> + <p> + Congratulations, <b><asp:LoginName ID="LoginName1" runat="server" /></b>. + You have completed the OpenID login process. + </p> + +<% If (State.PapePolicies IsNot Nothing) Then%> + <p>A PAPE extension was included in the authentication with this content: </p> + <ul> + <% If (State.PapePolicies.NistAssuranceLevel.HasValue) Then%> + <li>Nist: <%=HttpUtility.HtmlEncode(State.PapePolicies.NistAssuranceLevel.Value.ToString())%></li> + <% End If + For Each policy As String In State.PapePolicies.ActualPolicies%> + <li><%=HttpUtility.HtmlEncode(policy)%></li> + <% Next%> + </ul> +<% End If %> + +<% If State.ProfileFields IsNot Nothing Then + profileFieldsDisplay.ProfileValues = State.ProfileFields%> + <p> + In addition to authenticating you, your OpenID Provider may + have told us something about you using the + Simple Registration extension: + </p> + <cc1:ProfileFieldsDisplay runat="server" ID="profileFieldsDisplay" /> +<% End If%> +</asp:Content> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/ProfileFieldsDisplay.ascx b/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/ProfileFieldsDisplay.ascx new file mode 100644 index 0000000..f6864e9 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/ProfileFieldsDisplay.ascx @@ -0,0 +1,91 @@ +<%@ Control Language="VB" AutoEventWireup="true" %> +<%@ Import Namespace="DotNetOpenAuth.OpenId.Extensions.SimpleRegistration" %> + +<script runat="server"> + + Private _profileValues As ClaimsResponse + Public Property ProfileValues() As ClaimsResponse + Get + Return _profileValues + End Get + Set(ByVal value As ClaimsResponse) + _profileValues = value + End Set + End Property + +</script> + +<table id="profileFieldsTable" runat="server"> + <tr> + <td> + Nickname + </td> + <td> + <%=ProfileValues.Nickname %> + </td> + </tr> + <tr> + <td> + Email + </td> + <td> + <%=ProfileValues.Email%> + </td> + </tr> + <tr> + <td> + FullName + </td> + <td> + <%=ProfileValues.FullName%> + </td> + </tr> + <tr> + <td> + Date of Birth + </td> + <td> + <%=ProfileValues.BirthDate.ToString()%> + </td> + </tr> + <tr> + <td> + Gender + </td> + <td> + <%=ProfileValues.Gender.ToString()%> + </td> + </tr> + <tr> + <td> + Post Code + </td> + <td> + <%=ProfileValues.PostalCode%> + </td> + </tr> + <tr> + <td> + Country + </td> + <td> + <%=ProfileValues.Country%> + </td> + </tr> + <tr> + <td> + Language + </td> + <td> + <%=ProfileValues.Language%> + </td> + </tr> + <tr> + <td> + Timezone + </td> + <td> + <%=ProfileValues.TimeZone%> + </td> + </tr> +</table> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/Web.config b/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/Web.config new file mode 100644 index 0000000..3cfad05 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/MembersOnly/Web.config @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- + Note: As an alternative to hand editing this file you can use the + web admin tool to configure settings for your application. Use + the Website->Asp.Net Configuration option in Visual Studio. + A full list of settings and comments can be found in + machine.config.comments usually located in + \Windows\Microsoft.Net\Framework\v2.x\Config +--> +<configuration> + <appSettings/> + <connectionStrings/> + <system.web> + <authorization> + <deny users="?"/> + </authorization> + </system.web> +</configuration> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/Application.Designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Application.Designer.vb new file mode 100644 index 0000000..8a621ae --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/Application.myapp b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Application.myapp new file mode 100644 index 0000000..758895d --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Application.myapp @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + <MySubMain>false</MySubMain> + <SingleInstance>false</SingleInstance> + <ShutdownMode>0</ShutdownMode> + <EnableVisualStyles>true</EnableVisualStyles> + <AuthenticationMode>0</AuthenticationMode> + <ApplicationType>1</ApplicationType> + <SaveMySettingsOnExit>true</SaveMySettingsOnExit> +</MyApplicationData> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/AssemblyInfo.vb b/samples/OpenIdRelyingPartyWebFormsVB/My Project/AssemblyInfo.vb new file mode 100644 index 0000000..813551f --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/AssemblyInfo.vb @@ -0,0 +1,34 @@ +Imports System +Imports System.Reflection +Imports System.Runtime.InteropServices + +' General Information about an assembly is controlled through the following +' set of attributes. Change these attribute values to modify the information +' associated with an assembly. + +' Review the values of the assembly attributes +<Assembly: AssemblyTitle("OpenIdRelyingPartyWebFormsVB")> +<Assembly: AssemblyDescription("")> +<Assembly: AssemblyCompany("")> +<Assembly: AssemblyProduct("OpenIdRelyingPartyWebFormsVB")> +<Assembly: AssemblyCopyright("Copyright © 2010")> +<Assembly: AssemblyTrademark("")> + +<Assembly: ComVisible(False)> + +'The following GUID is for the ID of the typelib if this project is exposed to COM +<Assembly: Guid("334e9cee-0d47-4d70-924b-b5098a3432cb")> + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' <Assembly: AssemblyVersion("1.0.*")> + +<Assembly: AssemblyVersion("1.0.0.0")> +<Assembly: AssemblyFileVersion("1.0.0.0")> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/MyExtensions/MyWebExtension.vb b/samples/OpenIdRelyingPartyWebFormsVB/My Project/MyExtensions/MyWebExtension.vb new file mode 100644 index 0000000..78c7c19 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/MyExtensions/MyWebExtension.vb @@ -0,0 +1,73 @@ +#If _MyType <> "Empty" Then + +Namespace My + ''' <summary> + ''' Module used to define the properties that are available in the My Namespace for Web projects. + ''' </summary> + ''' <remarks></remarks> + <Global.Microsoft.VisualBasic.HideModuleName()> _ + Module MyWebExtension + Private s_Computer As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.Devices.ServerComputer) + Private s_User As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.ApplicationServices.WebUser) + Private s_Log As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.Logging.AspLog) + ''' <summary> + ''' Returns information about the host computer. + ''' </summary> + <Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _ + Friend ReadOnly Property Computer() As Global.Microsoft.VisualBasic.Devices.ServerComputer + Get + Return s_Computer.GetInstance() + End Get + End Property + ''' <summary> + ''' Returns information for the current Web user. + ''' </summary> + <Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _ + Friend ReadOnly Property User() As Global.Microsoft.VisualBasic.ApplicationServices.WebUser + Get + Return s_User.GetInstance() + End Get + End Property + ''' <summary> + ''' Returns Request object. + ''' </summary> + <Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _ + <Global.System.ComponentModel.Design.HelpKeyword("My.Request")> _ + Friend ReadOnly Property Request() As Global.System.Web.HttpRequest + <Global.System.Diagnostics.DebuggerHidden()> _ + Get + Dim CurrentContext As Global.System.Web.HttpContext = Global.System.Web.HttpContext.Current + If CurrentContext IsNot Nothing Then + Return CurrentContext.Request + End If + Return Nothing + End Get + End Property + ''' <summary> + ''' Returns Response object. + ''' </summary> + <Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _ + <Global.System.ComponentModel.Design.HelpKeyword("My.Response")> _ + Friend ReadOnly Property Response() As Global.System.Web.HttpResponse + <Global.System.Diagnostics.DebuggerHidden()> _ + Get + Dim CurrentContext As Global.System.Web.HttpContext = Global.System.Web.HttpContext.Current + If CurrentContext IsNot Nothing Then + Return CurrentContext.Response + End If + Return Nothing + End Get + End Property + ''' <summary> + ''' Returns the Asp log object. + ''' </summary> + <Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _ + Friend ReadOnly Property Log() As Global.Microsoft.VisualBasic.Logging.AspLog + Get + Return s_Log.GetInstance() + End Get + End Property + End Module +End Namespace + +#End If
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/Resources.Designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Resources.Designer.vb new file mode 100644 index 0000000..b38f687 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Resources.Designer.vb @@ -0,0 +1,62 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + '<summary> + ' A strongly-typed resource class, for looking up localized strings, etc. + '</summary> + <Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0"), _ + Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _ + Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _ + Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + '<summary> + ' Returns the cached ResourceManager instance used by this class. + '</summary> + <Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("OpenIdRelyingPartyWebFormsVB.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + '<summary> + ' Overrides the current thread's CurrentUICulture property for all + ' resource lookups using this strongly typed resource class. + '</summary> + <Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set(ByVal value As Global.System.Globalization.CultureInfo) + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/Resources.resx b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Resources.resx @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/Settings.Designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Settings.Designer.vb new file mode 100644 index 0000000..82c5982 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + <Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _ + Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0"), _ + Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + <Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + <Global.Microsoft.VisualBasic.HideModuleNameAttribute(), _ + Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _ + Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _ + Friend Module MySettingsProperty + + <Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _ + Friend ReadOnly Property Settings() As Global.OpenIdRelyingPartyWebFormsVB.My.MySettings + Get + Return Global.OpenIdRelyingPartyWebFormsVB.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/samples/OpenIdRelyingPartyWebFormsVB/My Project/Settings.settings b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Settings.settings new file mode 100644 index 0000000..85b890b --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/My Project/Settings.settings @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8'?> +<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" UseMySettingsClassName="true"> + <Profiles> + <Profile Name="(Default)" /> + </Profiles> + <Settings /> +</SettingsFile> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/OpenIdRelyingPartyWebFormsVB.vbproj b/samples/OpenIdRelyingPartyWebFormsVB/OpenIdRelyingPartyWebFormsVB.vbproj new file mode 100644 index 0000000..a43008c --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/OpenIdRelyingPartyWebFormsVB.vbproj @@ -0,0 +1,224 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.30729</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{F289B925-4307-4BEC-B411-885CE70E3379}</ProjectGuid> + <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</ProjectTypeGuids> + <OutputType>Library</OutputType> + <RootNamespace>OpenIdRelyingPartyWebFormsVB</RootNamespace> + <AssemblyName>OpenIdRelyingPartyWebFormsVB</AssemblyName> + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + <MyType>Custom</MyType> + <OptionExplicit>On</OptionExplicit> + <OptionCompare>Binary</OptionCompare> + <OptionStrict>Off</OptionStrict> + <OptionInfer>On</OptionInfer> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <DefineDebug>true</DefineDebug> + <DefineTrace>true</DefineTrace> + <OutputPath>bin\</OutputPath> + <DocumentationFile>OpenIdRelyingPartyWebFormsVB.xml</DocumentationFile> + <NoWarn>41999,42016,42020,42021,42022</NoWarn> + <WarningsAsErrors>42017,42018,42019,42032,42036</WarningsAsErrors> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <DefineDebug>false</DefineDebug> + <DefineTrace>true</DefineTrace> + <Optimize>true</Optimize> + <OutputPath>bin\</OutputPath> + <DocumentationFile>OpenIdRelyingPartyWebFormsVB.xml</DocumentationFile> + <NoWarn>41999,42016,42020,42021,42022</NoWarn> + <WarningsAsErrors>42017,42018,42019,42032,42036</WarningsAsErrors> + </PropertyGroup> + <ItemGroup> + <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\..\lib\log4net.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Drawing" /> + <Reference Include="System.Core"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + <Reference Include="System.Data.DataSetExtensions"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + <Reference Include="System.Web.Extensions"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + <Reference Include="System.Xml.Linq"> + <RequiredTargetFramework>3.5</RequiredTargetFramework> + </Reference> + <Reference Include="System.Web" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Configuration" /> + <Reference Include="System.Web.Services" /> + <Reference Include="System.EnterpriseServices" /> + <Reference Include="System.Web.Mobile" /> + </ItemGroup> + <ItemGroup> + <Import Include="Microsoft.VisualBasic" /> + <Import Include="System" /> + <Import Include="System.Collections" /> + <Import Include="System.Collections.Generic" /> + <Import Include="System.Data" /> + <Import Include="System.Linq" /> + <Import Include="System.Xml.Linq" /> + <Import Include="System.Diagnostics" /> + <Import Include="System.Collections.Specialized" /> + <Import Include="System.Configuration" /> + <Import Include="System.Text" /> + <Import Include="System.Text.RegularExpressions" /> + <Import Include="System.Web" /> + <Import Include="System.Web.Caching" /> + <Import Include="System.Web.SessionState" /> + <Import Include="System.Web.Security" /> + <Import Include="System.Web.Profile" /> + <Import Include="System.Web.UI" /> + <Import Include="System.Web.UI.WebControls" /> + <Import Include="System.Web.UI.WebControls.WebParts" /> + <Import Include="System.Web.UI.HtmlControls" /> + </ItemGroup> + <ItemGroup> + <Content Include="Default.aspx" /> + <Content Include="Login.aspx" /> + <Content Include="Web.config" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Code\State.vb" /> + <Compile Include="Code\TracePageAppender.vb" /> + <Compile Include="Default.aspx.vb"> + <SubType>ASPXCodeBehind</SubType> + <DependentUpon>Default.aspx</DependentUpon> + </Compile> + <Compile Include="Default.aspx.designer.vb"> + <DependentUpon>Default.aspx</DependentUpon> + </Compile> + <Compile Include="Global.asax.vb"> + <DependentUpon>Global.asax</DependentUpon> + </Compile> + <Compile Include="Login.aspx.designer.vb"> + <DependentUpon>Login.aspx</DependentUpon> + </Compile> + <Compile Include="Login.aspx.vb"> + <DependentUpon>Login.aspx</DependentUpon> + <SubType>ASPXCodebehind</SubType> + </Compile> + <Compile Include="LoginProgrammatic.aspx.designer.vb"> + <DependentUpon>LoginProgrammatic.aspx</DependentUpon> + </Compile> + <Compile Include="LoginProgrammatic.aspx.vb"> + <DependentUpon>LoginProgrammatic.aspx</DependentUpon> + <SubType>ASPXCodebehind</SubType> + </Compile> + <Compile Include="My Project\AssemblyInfo.vb" /> + <Compile Include="My Project\Application.Designer.vb"> + <AutoGen>True</AutoGen> + <DependentUpon>Application.myapp</DependentUpon> + </Compile> + <Compile Include="My Project\MyExtensions\MyWebExtension.vb"> + <VBMyExtensionTemplateID>Microsoft.VisualBasic.Web.MyExtension</VBMyExtensionTemplateID> + <VBMyExtensionTemplateVersion>1.0.0.0</VBMyExtensionTemplateVersion> + </Compile> + <Compile Include="My Project\Resources.Designer.vb"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> + <Compile Include="My Project\Settings.Designer.vb"> + <AutoGen>True</AutoGen> + <DependentUpon>Settings.settings</DependentUpon> + <DesignTimeSharedInput>True</DesignTimeSharedInput> + </Compile> + <Compile Include="TracePage.aspx.designer.vb"> + <DependentUpon>TracePage.aspx</DependentUpon> + </Compile> + <Compile Include="TracePage.aspx.vb"> + <DependentUpon>TracePage.aspx</DependentUpon> + <SubType>ASPXCodebehind</SubType> + </Compile> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="My Project\Resources.resx"> + <Generator>VbMyResourcesResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.vb</LastGenOutput> + <CustomToolNamespace>My.Resources</CustomToolNamespace> + <SubType>Designer</SubType> + </EmbeddedResource> + </ItemGroup> + <ItemGroup> + <Content Include="Global.asax" /> + <Content Include="LoginProgrammatic.aspx" /> + <Content Include="MembersOnly\ProfileFieldsDisplay.ascx" /> + <Content Include="MembersOnly\Web.config" /> + <None Include="My Project\Application.myapp"> + <Generator>MyApplicationCodeGenerator</Generator> + <LastGenOutput>Application.Designer.vb</LastGenOutput> + </None> + <None Include="My Project\Settings.settings"> + <Generator>SettingsSingleFileGenerator</Generator> + <CustomToolNamespace>My</CustomToolNamespace> + <LastGenOutput>Settings.Designer.vb</LastGenOutput> + </None> + <Content Include="favicon.ico" /> + <Content Include="images\attention.png" /> + <Content Include="images\DotNetOpenAuth.png" /> + <Content Include="images\openid_login.gif" /> + <Content Include="images\yahoo.png" /> + <Content Include="MembersOnly\Default.aspx" /> + <Content Include="PrivacyPolicy.aspx" /> + <Content Include="Site.Master" /> + <Content Include="styles.css" /> + <Content Include="TracePage.aspx" /> + <Content Include="xrds.aspx" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\src\DotNetOpenAuth\DotNetOpenAuth.csproj"> + <Project>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</Project> + <Name>DotNetOpenAuth</Name> + </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj"> + <Project>{AA78D112-D889-414B-A7D4-467B34C7B663}</Project> + <Name>DotNetOpenAuth.ApplicationBlock</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Folder Include="App_Data\" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" /> + <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> + <ProjectExtensions> + <VisualStudio> + <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}"> + <WebProjectProperties> + <UseIIS>False</UseIIS> + <AutoAssignPort>True</AutoAssignPort> + <DevelopmentServerPort>27433</DevelopmentServerPort> + <DevelopmentServerVPath>/</DevelopmentServerVPath> + <IISUrl> + </IISUrl> + <NTLMAuthentication>False</NTLMAuthentication> + <UseCustomServer>False</UseCustomServer> + <CustomServerUrl> + </CustomServerUrl> + <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile> + </WebProjectProperties> + </FlavorProperties> + </VisualStudio> + </ProjectExtensions> +</Project>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/PrivacyPolicy.aspx b/samples/OpenIdRelyingPartyWebFormsVB/PrivacyPolicy.aspx new file mode 100644 index 0000000..7b72cd4 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/PrivacyPolicy.aspx @@ -0,0 +1,7 @@ +<%@ Page Language="VB" AutoEventWireup="true" MasterPageFile="~/Site.Master" %> +<asp:Content ID="Content1" runat="server" ContentPlaceHolderID="Main"> + <h2>Privacy Policy</h2> + <p> + Some privacy policy would go here. + </p> +</asp:Content>
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Site.Master b/samples/OpenIdRelyingPartyWebFormsVB/Site.Master new file mode 100644 index 0000000..48a8e50 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Site.Master @@ -0,0 +1,39 @@ +<%@ Master Language="VB" AutoEventWireup="true" %> +<%@ Import Namespace="OpenIdRelyingPartyWebFormsVB" %> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<script runat="server"> + Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) + friendlyUsername.Text = State.FriendlyLoginName + End Sub + + Protected Sub LoginStatus1_LoggedOut(ByVal sender As Object, ByVal e As EventArgs) + State.Clear() + End Sub +</script> + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head runat="server"> + <title>OpenID Relying Party, by DotNetOpenAuth</title> + <link href="styles.css" rel="stylesheet" type="text/css" /> + <asp:ContentPlaceHolder ID="head" runat="server" /> +</head> +<body> + <form id="form1" runat="server"> + <span style="float: right"> + <asp:Image runat="server" ID="openIdUsernameImage" ImageUrl="~/images/openid_login.gif" + EnableViewState="false" /> + <asp:Label runat="server" ID="friendlyUsername" Text="" EnableViewState="false" /> + <asp:LoginStatus ID="LoginStatus1" runat="server" OnLoggedOut="LoginStatus1_LoggedOut" /> + </span> + <div> + <a href="http://dotnetopenauth.net"> + <img runat="server" src="~/images/DotNetOpenAuth.png" title="Jump to the project web site." + alt="DotNetOpenAuth" border='0' /></a> + </div> + <div> + <asp:ContentPlaceHolder ID="Main" runat="server" /> + </div> + </form> +</body> +</html> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx b/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx new file mode 100644 index 0000000..8df914b --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx @@ -0,0 +1,16 @@ +<%@ Page Language="VB" AutoEventWireup="true" CodeBehind="TracePage.aspx.vb" Inherits="OpenIdRelyingPartyWebFormsVB.TracePage" %> + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head id="Head1" runat="server"> + <title></title> +</head> +<body> + <form id="form1" runat="server"> + <p align="right"> + <asp:Button runat="server" Text="Clear log" ID="clearLogButton" OnClick="clearLogButton_Click" /> + </p> + <pre><asp:PlaceHolder runat="server" ID="placeHolder1" /></pre> + </form> +</body> +</html> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx.designer.vb b/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx.designer.vb new file mode 100644 index 0000000..9928c68 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx.designer.vb @@ -0,0 +1,53 @@ +'------------------------------------------------------------------------------ +' <auto-generated> +' This code was generated by a tool. +' Runtime Version:2.0.50727.4927 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' </auto-generated> +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + + +Partial Public Class TracePage + + '''<summary> + '''Head1 control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents Head1 As Global.System.Web.UI.HtmlControls.HtmlHead + + '''<summary> + '''form1 control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents form1 As Global.System.Web.UI.HtmlControls.HtmlForm + + '''<summary> + '''clearLogButton control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents clearLogButton As Global.System.Web.UI.WebControls.Button + + '''<summary> + '''placeHolder1 control. + '''</summary> + '''<remarks> + '''Auto-generated field. + '''To modify move field declaration from designer file to code-behind file. + '''</remarks> + Protected WithEvents placeHolder1 As Global.System.Web.UI.WebControls.PlaceHolder +End Class diff --git a/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx.vb b/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx.vb new file mode 100644 index 0000000..9a1b8c1 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/TracePage.aspx.vb @@ -0,0 +1,13 @@ +Public Class TracePage + Inherits System.Web.UI.Page + + Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) + Me.placeHolder1.Controls.Add(New Label() With {.Text = HttpUtility.HtmlEncode(Global_asax.LogMessages.ToString())}) + End Sub + + Protected Sub clearLogButton_Click(ByVal sender As Object, ByVal e As EventArgs) + Global_asax.LogMessages.Length = 0 + ' clear the page immediately, and allow for F5 without a Postback warning. + Me.Response.Redirect(Me.Request.Url.AbsoluteUri) + End Sub +End Class
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/Web.config b/samples/OpenIdRelyingPartyWebFormsVB/Web.config new file mode 100644 index 0000000..f488900 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/Web.config @@ -0,0 +1,112 @@ +<?xml version="1.0"?> +<configuration> + <configSections> + <section name="uri" type="System.Configuration.UriSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler" requirePermission="false" /> + <section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true"/> + </configSections> + + <!-- 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. --> + <uri> + <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> + + <!-- this is an optional configuration section where aspects of dotnetopenauth can be customized --> + <dotNetOpenAuth> + <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" /> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />--> + </behaviors> + <!-- Uncomment the following to activate the sample custom store. --> + <!--<store type="OpenIdRelyingPartyWebFormsVB.CustomStore, OpenIdRelyingPartyWebFormsVB" />--> + </relyingParty> + </openid> + <messaging> + <untrustedWebRequest> + <whitelistHosts> + <!-- since this is a sample, and will often be used with localhost --> + <add name="localhost" /> + </whitelistHosts> + </untrustedWebRequest> + </messaging> + <!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. --> + <reporting enabled="true" /> + </dotNetOpenAuth> + + <appSettings> + <!-- Fill in your various consumer keys and secrets here to make the sample work. --> + <!-- You must get these values by signing up with each individual service provider. --> + <!-- Google sign-up: https://www.google.com/accounts/ManageDomains --> + <add key="googleConsumerKey" value="demo.dotnetopenauth.net"/> + <add key="googleConsumerSecret" value="5Yv1TfKm1551QrXZ9GpqepeD"/> + </appSettings> + + <system.web> + <!--<sessionState cookieless="true" />--> + <compilation debug="true"/> + <customErrors mode="RemoteOnly"/> + <authentication mode="Forms"> + <forms name="OpenIdRelyingPartyVBSession"/> <!-- named cookie prevents conflicts with other samples --> + </authentication> + <trace enabled="false" writeToDiagnosticsTrace="true" /> + <!-- Trust level discussion: + Full: everything works (this is required for Google Apps for Domains support) + High: TRACE compilation symbol must NOT be defined + Medium: doesn't work unless originUrl=".*" or WebPermission.Connect is extended, and Google Apps doesn't work. + Low: doesn't work because WebPermission.Connect is denied. + --> + <trust level="Medium" originUrl=".*"/> + </system.web> + + <!-- log4net is a 3rd party (free) logger library that DotNetOpenAuth will use if present but does not require. --> + <log4net> + <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> + <file value="RelyingParty.log" /> + <appendToFile value="true" /> + <rollingStyle value="Size" /> + <maxSizeRollBackups value="10" /> + <maximumFileSize value="100KB" /> + <staticLogFileName value="true" /> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%date (GMT%date{%z}) [%thread] %-5level %logger - %message%newline" /> + </layout> + </appender> + <appender name="TracePageAppender" type="OpenIdRelyingPartyWebFormsVB.TracePageAppender, OpenIdRelyingPartyWebFormsVB"> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%date (GMT%date{%z}) [%thread] %-5level %logger - %message%newline" /> + </layout> + </appender> + <!-- Setup the root category, add the appenders and set the default level --> + <root> + <level value="INFO" /> + <!--<appender-ref ref="RollingFileAppender" />--> + <appender-ref ref="TracePageAppender" /> + </root> + <!-- Specify the level for some specific categories --> + <logger name="DotNetOpenAuth"> + <level value="INFO" /> + </logger> + </log4net> + + <runtime> + <legacyHMACWarning enabled="0" /> + </runtime> +</configuration> diff --git a/samples/OpenIdRelyingPartyWebFormsVB/favicon.ico b/samples/OpenIdRelyingPartyWebFormsVB/favicon.ico Binary files differnew file mode 100644 index 0000000..e227dbe --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/favicon.ico diff --git a/samples/OpenIdRelyingPartyWebFormsVB/images/DotNetOpenAuth.png b/samples/OpenIdRelyingPartyWebFormsVB/images/DotNetOpenAuth.png Binary files differnew file mode 100644 index 0000000..442b986 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/images/DotNetOpenAuth.png diff --git a/samples/OpenIdRelyingPartyWebFormsVB/images/attention.png b/samples/OpenIdRelyingPartyWebFormsVB/images/attention.png Binary files differnew file mode 100644 index 0000000..8003700 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/images/attention.png diff --git a/samples/OpenIdRelyingPartyWebFormsVB/images/openid_login.gif b/samples/OpenIdRelyingPartyWebFormsVB/images/openid_login.gif Binary files differnew file mode 100644 index 0000000..cde836c --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/images/openid_login.gif diff --git a/samples/OpenIdRelyingPartyWebFormsVB/images/yahoo.png b/samples/OpenIdRelyingPartyWebFormsVB/images/yahoo.png Binary files differnew file mode 100644 index 0000000..3129217 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/images/yahoo.png diff --git a/samples/OpenIdRelyingPartyWebFormsVB/styles.css b/samples/OpenIdRelyingPartyWebFormsVB/styles.css new file mode 100644 index 0000000..2e4d3db --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/styles.css @@ -0,0 +1,10 @@ +h2 +{ + font-style: italic; +} + +body +{ + font-family: Cambria, Arial, Times New Roman; + font-size: 12pt; +}
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebFormsVB/xrds.aspx b/samples/OpenIdRelyingPartyWebFormsVB/xrds.aspx new file mode 100644 index 0000000..130ca30 --- /dev/null +++ b/samples/OpenIdRelyingPartyWebFormsVB/xrds.aspx @@ -0,0 +1,29 @@ +<%@ Page Language="VB" AutoEventWireup="true" ContentType="application/xrds+xml" %><?xml version="1.0" encoding="UTF-8"?> +<%-- +This page is a required for relying party discovery per OpenID 2.0. +It allows Providers to call back to the relying party site to confirm the +identity that it is claiming in the realm and return_to URLs. +This page should be pointed to by the 'realm' home page, which in this sample +is default.aspx. +--%> +<xrds:XRDS + xmlns:xrds="xri://$xrds" + xmlns:openid="http://openid.net/xmlns/1.0" + xmlns="xri://$xrd*($v*2.0)"> + <XRD> + <Service priority="1"> + <Type>http://specs.openid.net/auth/2.0/return_to</Type> + <%-- Every page with an OpenID login should be listed here. --%> + <URI priority="1"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/login.aspx"))%></URI> + <URI priority="2"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginProgrammatic.aspx"))%></URI> + <URI priority="3"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/ajaxlogin.aspx"))%></URI> + <URI priority="4"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/NoIdentityOpenId.aspx"))%></URI> + <URI priority="5"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginPlusOAuth.aspx"))%></URI> + <URI priority="6"><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/loginPlusOAuthSampleOP.aspx"))%></URI> + </Service> + <Service> + <Type>http://specs.openid.net/extensions/ui/icon</Type> + <URI><%=New Uri(Request.Url, Response.ApplyAppPathModifier("~/images/DotNetOpenAuth.png"))%></URI> + </Service> + </XRD> +</xrds:XRDS> diff --git a/samples/Samples.sln b/samples/Samples.sln index 2761358..0a138cc 100644 --- a/samples/Samples.sln +++ b/samples/Samples.sln @@ -106,6 +106,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoRelyingPart EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdOfflineProvider", "OpenIdOfflineProvider\OpenIdOfflineProvider.csproj", "{5C65603B-235F-47E6-B536-06385C60DE7F}" EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "OpenIdRelyingPartyWebFormsVB", "OpenIdRelyingPartyWebFormsVB\OpenIdRelyingPartyWebFormsVB.vbproj", "{F289B925-4307-4BEC-B411-885CE70E3379}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeAnalysis|Any CPU = CodeAnalysis|Any CPU @@ -191,6 +193,12 @@ Global {5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -204,6 +212,7 @@ Global {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {A4059F7E-8E6F-4FA2-A1D5-1B9B46C93F82} {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {A4059F7E-8E6F-4FA2-A1D5-1B9B46C93F82} {5C65603B-235F-47E6-B536-06385C60DE7F} = {A4059F7E-8E6F-4FA2-A1D5-1B9B46C93F82} + {F289B925-4307-4BEC-B411-885CE70E3379} = {A4059F7E-8E6F-4FA2-A1D5-1B9B46C93F82} {5100F73C-3082-4B81-95DD-F443F90B8EA7} = {812D828E-C91A-45AB-BAE9-3FC6D9560F9F} {DD52C0C8-F986-495A-AAA1-090CFE2F801F} = {812D828E-C91A-45AB-BAE9-3FC6D9560F9F} {6EC36418-DBC5-4AD1-A402-413604AA7A08} = {812D828E-C91A-45AB-BAE9-3FC6D9560F9F} diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj index daf6327..3f76f78 100644 --- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj +++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj @@ -80,7 +80,9 @@ <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" /> + <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> <Reference Include="Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>$(SystemRoot)\System32\inetsrv\Microsoft.Web.Administration.dll</HintPath> diff --git a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs index 48b5d5c..23db9a6 100644 --- a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs +++ b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; using System.Linq; using System.Text; -using Microsoft.Build.Utilities; using Microsoft.Build.Framework; -using System.IO; +using Microsoft.Build.Utilities; namespace DotNetOpenAuth.BuildTasks { public class GetBuildVersion : Task { @@ -52,18 +54,54 @@ namespace DotNetOpenAuth.BuildTasks { return string.Empty; } - string headContent = string.Empty; + string commitId = string.Empty; + + // First try asking Git for the HEAD commit id + try { + string cmdPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); + ProcessStartInfo psi = new ProcessStartInfo(cmdPath, "/c git rev-parse HEAD"); + psi.WindowStyle = ProcessWindowStyle.Hidden; + psi.RedirectStandardOutput = true; + psi.UseShellExecute = false; + Process git = Process.Start(psi); + commitId = git.StandardOutput.ReadLine(); + git.WaitForExit(); + if (git.ExitCode != 0) { + commitId = string.Empty; + } + if (commitId != null) { + commitId = commitId.Trim(); + if (commitId.Length == 40) { + return commitId; + } + } + } catch (InvalidOperationException) { + } catch (Win32Exception) { + } + + // Failing being able to use the git command to figure out the HEAD commit ID, try the filesystem directly. try { - headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim(); + string headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim(); if (headContent.StartsWith("ref:", StringComparison.Ordinal)) { string refName = headContent.Substring(5).Trim(); - headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/" + refName)).Trim(); + string refPath = Path.Combine(this.GitRepoRoot, ".git/" + refName); + if (File.Exists(refPath)) { + commitId = File.ReadAllText(refPath).Trim(); + } else { + string packedRefPath = Path.Combine(this.GitRepoRoot, ".git/packed-refs"); + string matchingLine = File.ReadAllLines(packedRefPath).FirstOrDefault(line => line.EndsWith(refName)); + if (matchingLine != null) { + commitId = matchingLine.Substring(0, matchingLine.IndexOf(' ')); + } + } + } else { + commitId = headContent; } } catch (FileNotFoundException) { } catch (DirectoryNotFoundException) { } - return headContent.Trim(); + return commitId.Trim(); } private Version ReadVersionFromFile() { diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs index ce0928e..8a8ae25 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs @@ -11,8 +11,10 @@ namespace DotNetOpenAuth.Test.Messaging using System.Collections.Specialized; using System.IO; using System.Net; + using System.Text.RegularExpressions; using System.Web; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Test.Mocks; using NUnit.Framework; [TestFixture] @@ -139,5 +141,79 @@ namespace DotNetOpenAuth.Test.Messaging Assert.AreEqual("%C2%80", MessagingUtilities.EscapeUriDataStringRfc3986("\u0080")); Assert.AreEqual("%E3%80%81", MessagingUtilities.EscapeUriDataStringRfc3986("\u3001")); } + + /// <summary> + /// Verifies the overall format of the multipart POST is correct. + /// </summary> + [TestCase] + public void PostMultipart() { + var httpHandler = new TestWebRequestHandler(); + bool callbackTriggered = false; + httpHandler.Callback = req => { + Match m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)"); + Assert.IsTrue(m.Success, "Content-Type HTTP header not set correctly."); + string boundary = m.Groups[1].Value; + string expectedEntity = "--{0}\r\nContent-Disposition: form-data; name=\"a\"\r\n\r\nb\r\n--{0}--\r\n"; + expectedEntity = string.Format(expectedEntity, boundary); + string actualEntity = httpHandler.RequestEntityAsString; + Assert.AreEqual(expectedEntity, actualEntity); + callbackTriggered = true; + Assert.AreEqual(req.ContentLength, actualEntity.Length); + IncomingWebResponse resp = new CachedDirectWebResponse(); + return resp; + }; + var request = (HttpWebRequest)WebRequest.Create("http://someserver"); + var parts = new[] { + MultipartPostPart.CreateFormPart("a", "b"), + }; + request.PostMultipart(httpHandler, parts); + Assert.IsTrue(callbackTriggered); + } + + /// <summary> + /// Verifies proper behavior of GetHttpVerb + /// </summary> + [TestCase] + public void GetHttpVerbTest() { + Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest)); + Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest)); + Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest)); + Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest)); + Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest)); + + Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); + Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); + Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); + Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); + Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); + } + + /// <summary> + /// Verifies proper behavior of GetHttpVerb on invalid input. + /// </summary> + [TestCase, ExpectedException(typeof(ArgumentException))] + public void GetHttpVerbOutOfRangeTest() { + MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.PostRequest); + } + + /// <summary> + /// Verifies proper behavior of GetHttpDeliveryMethod + /// </summary> + [TestCase] + public void GetHttpDeliveryMethodTest() { + Assert.AreEqual(HttpDeliveryMethods.GetRequest, MessagingUtilities.GetHttpDeliveryMethod("GET")); + Assert.AreEqual(HttpDeliveryMethods.PostRequest, MessagingUtilities.GetHttpDeliveryMethod("POST")); + Assert.AreEqual(HttpDeliveryMethods.HeadRequest, MessagingUtilities.GetHttpDeliveryMethod("HEAD")); + Assert.AreEqual(HttpDeliveryMethods.PutRequest, MessagingUtilities.GetHttpDeliveryMethod("PUT")); + Assert.AreEqual(HttpDeliveryMethods.DeleteRequest, MessagingUtilities.GetHttpDeliveryMethod("DELETE")); + } + + /// <summary> + /// Verifies proper behavior of GetHttpDeliveryMethod for an unexpected input + /// </summary> + [TestCase, ExpectedException(typeof(ArgumentException))] + public void GetHttpDeliveryMethodOutOfRangeTest() { + MessagingUtilities.GetHttpDeliveryMethod("UNRECOGNIZED"); + } } } diff --git a/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs deleted file mode 100644 index a293895..0000000 --- a/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="MultipartPostPartTests.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Messaging { - using System.CodeDom.Compiler; - using System.Collections.Generic; - using System.Diagnostics.Contracts; - using System.IO; - using System.Net; - using DotNetOpenAuth.Messaging; - using NUnit.Framework; - - [TestFixture] - public class MultipartPostPartTests : TestBase { - /// <summary> - /// Verifies that the Length property matches the length actually serialized. - /// </summary> - [TestCase] - public void FormDataSerializeMatchesLength() { - var part = MultipartPostPart.CreateFormPart("a", "b"); - VerifyLength(part); - } - - /// <summary> - /// Verifies that the length property matches the length actually serialized. - /// </summary> - [TestCase] - public void FileSerializeMatchesLength() { - using (TempFileCollection tfc = new TempFileCollection()) { - string file = tfc.AddExtension(".txt"); - File.WriteAllText(file, "sometext"); - var part = MultipartPostPart.CreateFormFilePart("someformname", file, "text/plain"); - VerifyLength(part); - } - } - - /// <summary> - /// Verifies MultiPartPost sends the right number of bytes. - /// </summary> - [TestCase] - public void MultiPartPostAscii() { - using (TempFileCollection tfc = new TempFileCollection()) { - string file = tfc.AddExtension("txt"); - File.WriteAllText(file, "sometext"); - this.VerifyFullPost(new List<MultipartPostPart> { - MultipartPostPart.CreateFormPart("a", "b"), - MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"), - }); - } - } - - /// <summary> - /// Verifies MultiPartPost sends the right number of bytes. - /// </summary> - [TestCase] - public void MultiPartPostMultiByteCharacters() { - using (TempFileCollection tfc = new TempFileCollection()) { - string file = tfc.AddExtension("txt"); - File.WriteAllText(file, "\x1020\x818"); - this.VerifyFullPost(new List<MultipartPostPart> { - MultipartPostPart.CreateFormPart("a", "\x987"), - MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"), - }); - } - } - - private static void VerifyLength(MultipartPostPart part) { - Contract.Requires(part != null); - - var expectedLength = part.Length; - var ms = new MemoryStream(); - var sw = new StreamWriter(ms); - part.Serialize(sw); - sw.Flush(); - var actualLength = ms.Length; - Assert.AreEqual(expectedLength, actualLength); - } - - private void VerifyFullPost(List<MultipartPostPart> parts) { - var request = (HttpWebRequest)WebRequest.Create("http://localhost"); - var handler = new Mocks.TestWebRequestHandler(); - bool posted = false; - handler.Callback = req => { - foreach (string header in req.Headers) { - TestUtilities.TestLogger.InfoFormat("{0}: {1}", header, req.Headers[header]); - } - TestUtilities.TestLogger.InfoFormat(handler.RequestEntityAsString); - Assert.AreEqual(req.ContentLength, handler.RequestEntityStream.Length); - posted = true; - return null; - }; - request.PostMultipart(handler, parts); - Assert.IsTrue(posted, "HTTP POST never sent."); - } - } -} diff --git a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs index a293895..08524b2 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs @@ -38,6 +38,15 @@ namespace DotNetOpenAuth.Test.Messaging { } /// <summary> + /// Verifies file multiparts identify themselves as files and not merely form-data. + /// </summary> + [TestCase] + public void FilePartAsFile() { + var part = MultipartPostPart.CreateFormFilePart("somename", "somefile", "plain/text", new MemoryStream()); + Assert.AreEqual("file", part.ContentDisposition); + } + + /// <summary> /// Verifies MultiPartPost sends the right number of bytes. /// </summary> [TestCase] diff --git a/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs index 342a303..1b3c2b2 100644 --- a/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs +++ b/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs @@ -11,11 +11,11 @@ namespace DotNetOpenAuth.Test.Mocks { internal class TestDirectedMessage : TestMessage, IDirectedProtocolMessage { internal TestDirectedMessage() { - this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest; + this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.HeadRequest; } internal TestDirectedMessage(MessageTransport transport) : base(transport) { - this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest; + this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.HeadRequest; } #region IDirectedProtocolMessage Members diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 5f068d1..c7b9f89 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -227,6 +227,11 @@ namespace DotNetOpenAuth.Test.ChannelElements { this.ParameterizedRequestTest(HttpDeliveryMethods.PostRequest); } + [TestCase] + public void RequestUsingHead() { + this.ParameterizedRequestTest(HttpDeliveryMethods.HeadRequest); + } + /// <summary> /// Verifies that messages asking for special HTTP status codes get them. /// </summary> @@ -323,7 +328,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { this.webRequestHandler.Callback = (req) => { Assert.IsNotNull(req); HttpRequestInfo reqInfo = ConvertToRequestInfo(req, this.webRequestHandler.RequestEntityStream); - Assert.AreEqual(scheme == HttpDeliveryMethods.PostRequest ? "POST" : "GET", reqInfo.HttpMethod); + Assert.AreEqual(MessagingUtilities.GetHttpVerb(scheme), reqInfo.HttpMethod); var incomingMessage = this.channel.ReadFromRequest(reqInfo) as TestMessage; Assert.IsNotNull(incomingMessage); Assert.AreEqual(request.Age, incomingMessage.Age); diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index d55996c..55d43e8 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -185,6 +185,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoRelyingPart EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoProvider", "..\samples\OpenIdWebRingSsoProvider\OpenIdWebRingSsoProvider.csproj", "{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}" EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "OpenIdRelyingPartyWebFormsVB", "..\samples\OpenIdRelyingPartyWebFormsVB\OpenIdRelyingPartyWebFormsVB.vbproj", "{F289B925-4307-4BEC-B411-885CE70E3379}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeAnalysis|Any CPU = CodeAnalysis|Any CPU @@ -315,6 +317,12 @@ Global {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -332,6 +340,8 @@ Global {BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} + {F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} {7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs index 5ac88da..0335af5 100644 --- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs +++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs @@ -126,6 +126,7 @@ namespace DotNetOpenAuth.Configuration { /// be present. /// </remarks> private static T CreateInstanceFromXaml(Stream xaml) { + Contract.Ensures(Contract.Result<T>() != null); return (T)XamlReader.Load(xaml); } } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index ee271f3..3bfecae 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -327,6 +327,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OAuth\ConsumerSecuritySettings.cs" /> <Compile Include="OAuth\DesktopConsumer.cs" /> <Compile Include="GlobalSuppressions.cs" /> + <Compile Include="Messaging\IMessageWithBinaryData.cs" /> <Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" /> <Compile Include="Messaging\ChannelEventArgs.cs" /> <Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" /> diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs index a3343ba..e436846 100644 --- a/src/DotNetOpenAuth/GlobalSuppressions.cs +++ b/src/DotNetOpenAuth/GlobalSuppressions.cs @@ -56,3 +56,4 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnOutgoingResponse(DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnIncomingRequest(DotNetOpenAuth.OpenId.Provider.IRequest)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.Provider.ProviderSecuritySettings)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")] diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs index b37c93a..dd34d90 100644 --- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs @@ -153,10 +153,13 @@ namespace DotNetOpenAuth.Messaging { /// <returns>The seekable Stream instance that contains a copy of what was returned in the HTTP response.</returns> private static MemoryStream CacheNetworkStreamAndClose(HttpWebResponse response, int maximumBytesToRead) { Contract.Requires<ArgumentNullException>(response != null); + Contract.Ensures(Contract.Result<MemoryStream>() != null); // Now read and cache the network stream Stream networkStream = response.GetResponseStream(); MemoryStream cachedStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : Math.Min((int)response.ContentLength, maximumBytesToRead)); + Contract.Assume(networkStream.CanRead, "HttpWebResponse.GetResponseStream() always returns a readable stream."); // CC missing + Contract.Assume(cachedStream.CanWrite, "This is a MemoryStream -- it's always writable."); // CC missing networkStream.CopyTo(cachedStream); cachedStream.Seek(0, SeekOrigin.Begin); diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index ef6486a..1bddf02 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -580,7 +580,15 @@ namespace DotNetOpenAuth.Messaging { fields = request.QueryStringBeforeRewriting.ToDictionary(); } - return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient()); + MessageReceivingEndpoint recipient; + try { + recipient = request.GetRecipient(); + } catch (ArgumentException ex) { + Logger.Messaging.WarnFormat("Unrecognized HTTP request: " + ex.ToString()); + return null; + } + + return (IDirectedProtocolMessage)this.Receive(fields, recipient); } /// <summary> @@ -819,6 +827,24 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Prepares to send a request to the Service Provider as the query string in a HEAD request. + /// </summary> + /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param> + /// <returns>The web request ready to send.</returns> + /// <remarks> + /// This method is simply a standard HTTP HEAD request with the message parts serialized to the query string. + /// This method satisfies OAuth 1.0 section 5.2, item #3. + /// </remarks> + protected virtual HttpWebRequest InitializeRequestAsHead(IDirectedProtocolMessage requestMessage) { + Contract.Requires<ArgumentNullException>(requestMessage != null); + Contract.Requires<ArgumentException>(requestMessage.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient); + + HttpWebRequest request = this.InitializeRequestAsGet(requestMessage); + request.Method = "HEAD"; + return request; + } + + /// <summary> /// Prepares to send a request to the Service Provider as the payload of a POST request. /// </summary> /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param> @@ -838,7 +864,18 @@ namespace DotNetOpenAuth.Messaging { HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient); httpRequest.CachePolicy = this.CachePolicy; httpRequest.Method = "POST"; - this.SendParametersInEntity(httpRequest, fields); + + var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; + if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { + var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData); + + // When sending multi-part, all data gets send as multi-part -- even the non-binary data. + multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); + this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields); + } else { + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart); + this.SendParametersInEntity(httpRequest, fields); + } return httpRequest; } @@ -914,6 +951,19 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Sends the given parameters in the entity stream of an HTTP request in multi-part format. + /// </summary> + /// <param name="httpRequest">The HTTP request.</param> + /// <param name="fields">The parameters to send.</param> + /// <remarks> + /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes + /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>. + /// </remarks> + protected void SendParametersInEntityAsMultipart(HttpWebRequest httpRequest, IEnumerable<MultipartPostPart> fields) { + httpRequest.PostMultipartNoGetResponse(this.WebRequestHandler, fields); + } + + /// <summary> /// Verifies the integrity and applicability of an incoming message. /// </summary> /// <param name="message">The message just received.</param> diff --git a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs index caf292a..9db5169 100644 --- a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs +++ b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs @@ -154,7 +154,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="T:System.NotSupportedException"> /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. /// </exception> - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void Add(KeyValuePair<TKey, TValue> item) { throw new NotSupportedException(); } diff --git a/src/DotNetOpenAuth/Messaging/EmptyList.cs b/src/DotNetOpenAuth/Messaging/EmptyList.cs index ba918a4..68cdabd 100644 --- a/src/DotNetOpenAuth/Messaging/EmptyList.cs +++ b/src/DotNetOpenAuth/Messaging/EmptyList.cs @@ -99,7 +99,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="T:System.NotSupportedException"> /// The <see cref="T:System.Collections.Generic.IList`1"/> is read-only. /// </exception> - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void RemoveAt(int index) { throw new ArgumentOutOfRangeException("index"); } @@ -115,7 +115,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="T:System.NotSupportedException"> /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. /// </exception> - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void Add(T item) { throw new NotSupportedException(); } diff --git a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs index 71825bc..29ec533 100644 --- a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs +++ b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs @@ -35,6 +35,8 @@ namespace DotNetOpenAuth.Messaging { /// to avoid double-caching.</para> /// </remarks> public static IEnumerable<T> CacheGeneratedResults<T>(this IEnumerable<T> sequence) { + Contract.Requires<ArgumentNullException>(sequence != null); + // Don't create a cache for types that don't need it. if (sequence is IList<T> || sequence is ICollection<T> || diff --git a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs index 17c8f7a..1443fff 100644 --- a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs +++ b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs @@ -46,8 +46,13 @@ namespace DotNetOpenAuth.Messaging { DeleteRequest = 0x10, /// <summary> + /// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3). + /// </summary> + HeadRequest = 0x20, + + /// <summary> /// The flags that control HTTP verbs. /// </summary> - HttpVerbMask = PostRequest | GetRequest | PutRequest | DeleteRequest, + HttpVerbMask = PostRequest | GetRequest | PutRequest | DeleteRequest | HeadRequest, } } diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs index af03ba9..3420de9 100644 --- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs @@ -187,6 +187,8 @@ namespace DotNetOpenAuth.Messaging { /// </remarks> IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) { Contract.Requires<ArgumentNullException>(request != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null); throw new System.NotImplementedException(); } @@ -209,6 +211,9 @@ namespace DotNetOpenAuth.Messaging { IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options) { Contract.Requires<ArgumentNullException>(request != null); Contract.Requires<NotSupportedException>(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported); + Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null); + ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name)); throw new System.NotImplementedException(); } diff --git a/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs new file mode 100644 index 0000000..f411cf5 --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------- +// <copyright file="IMessageWithBinaryData.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + + /// <summary> + /// The interface that classes must implement to be serialized/deserialized + /// as protocol or extension messages that uses POST multi-part data for binary content. + /// </summary> + [ContractClass(typeof(IMessageWithBinaryDataContract))] + public interface IMessageWithBinaryData : IDirectedProtocolMessage { + /// <summary> + /// Gets the parts of the message that carry binary data. + /// </summary> + /// <value>A list of parts. Never null.</value> + IList<MultipartPostPart> BinaryData { get; } + + /// <summary> + /// Gets a value indicating whether this message should be sent as multi-part POST. + /// </summary> + bool SendAsMultipart { get; } + } + + /// <summary> + /// The contract class for the <see cref="IMessageWithBinaryData"/> interface. + /// </summary> + [ContractClassFor(typeof(IMessageWithBinaryData))] + internal sealed class IMessageWithBinaryDataContract : IMessageWithBinaryData { + #region IMessageWithBinaryData Members + + /// <summary> + /// Gets the parts of the message that carry binary data. + /// </summary> + /// <value>A list of parts. Never null.</value> + IList<MultipartPostPart> IMessageWithBinaryData.BinaryData { + get { + Contract.Ensures(Contract.Result<IList<MultipartPostPart>>() != null); + throw new NotImplementedException(); + } + } + + /// <summary> + /// Gets a value indicating whether this message should be sent as multi-part POST. + /// </summary> + bool IMessageWithBinaryData.SendAsMultipart { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IMessage Properties + + /// <summary> + /// Gets the version of the protocol or extension this message is prepared to implement. + /// </summary> + /// <value></value> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + Version IMessage.Version { + get { + Contract.Ensures(Contract.Result<Version>() != null); + return default(Version); // dummy return + } + } + + /// <summary> + /// Gets the extra, non-standard Protocol parameters included in the message. + /// </summary> + /// <value></value> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + IDictionary<string, string> IMessage.ExtraData { + get { + Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null); + return default(IDictionary<string, string>); + } + } + + #endregion + + #region IDirectedProtocolMessage Members + + /// <summary> + /// Gets the preferred method of transport for the message. + /// </summary> + /// <remarks> + /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent: + /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript + /// to automate submission. + /// </remarks> + HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Gets the URL of the intended receiver of this message. + /// </summary> + Uri IDirectedProtocolMessage.Recipient { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IProtocolMessage Members + + /// <summary> + /// Gets the level of protection this message requires. + /// </summary> + MessageProtections IProtocolMessage.RequiredProtection { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Gets a value indicating whether this is a direct or indirect message. + /// </summary> + MessageTransport IProtocolMessage.Transport { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IMessage methods + + /// <summary> + /// Checks the message state for conformity to the protocol specification + /// and throws an exception if the message is invalid. + /// </summary> + /// <remarks> + /// <para>Some messages have required fields, or combinations of fields that must relate to each other + /// in specialized ways. After deserializing a message, this method checks the state of the + /// message to see if it conforms to the protocol.</para> + /// <para>Note that this property should <i>not</i> check signatures or perform any state checks + /// outside this scope of this particular message.</para> + /// </remarks> + /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception> + void IMessage.EnsureValidMessage() { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs b/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs index ba25c12..87ea553 100644 --- a/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs +++ b/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs @@ -6,12 +6,14 @@ namespace DotNetOpenAuth.Messaging { using System; + using System.Diagnostics.Contracts; using DotNetOpenAuth.OAuth.ChannelElements; /// <summary> /// An interface that must be implemented by message transforms/validators in order /// to be included in the channel stack. /// </summary> + [ContractClass(typeof(ITamperProtectionChannelBindingElementContract))] public interface ITamperProtectionChannelBindingElement : IChannelBindingElement { /// <summary> /// Gets or sets the delegate that will initialize the non-serialized properties necessary on a @@ -25,4 +27,102 @@ namespace DotNetOpenAuth.Messaging { /// <returns>The cloned instance.</returns> ITamperProtectionChannelBindingElement Clone(); } + + /// <summary> + /// Contract class for the <see cref="ITamperProtectionChannelBindingElement"/> interface. + /// </summary> + [ContractClassFor(typeof(ITamperProtectionChannelBindingElement))] + internal abstract class ITamperProtectionChannelBindingElementContract : ITamperProtectionChannelBindingElement { + #region ITamperProtectionChannelBindingElement Properties + + /// <summary> + /// Gets or sets the delegate that will initialize the non-serialized properties necessary on a + /// signable message so that its signature can be correctly calculated or verified. + /// </summary> + Action<ITamperResistantOAuthMessage> ITamperProtectionChannelBindingElement.SignatureCallback { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + #endregion + + #region IChannelBindingElement Members + + /// <summary> + /// Gets or sets the channel that this binding element belongs to. + /// </summary> + /// <remarks> + /// This property is set by the channel when it is first constructed. + /// </remarks> + Channel IChannelBindingElement.Channel { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + MessageProtections IChannelBindingElement.Protection { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Prepares a message for sending based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The message to prepare for sending.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) { + Contract.Requires<ArgumentNullException>(message != null); + Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null); + throw new NotImplementedException(); + } + + /// <summary> + /// Performs any transformation on an incoming message that may be necessary and/or + /// validates an incoming message based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The incoming message to process.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <exception cref="ProtocolException"> + /// Thrown when the binding element rules indicate that this message is invalid and should + /// NOT be processed. + /// </exception> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) { + Contract.Requires<ArgumentNullException>(message != null); + Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null); + throw new NotImplementedException(); + } + + #endregion + + #region ITamperProtectionChannelBindingElement Methods + + /// <summary> + /// Clones this instance. + /// </summary> + /// <returns>The cloned instance.</returns> + ITamperProtectionChannelBindingElement ITamperProtectionChannelBindingElement.Clone() { + Contract.Ensures(Contract.Result<ITamperProtectionChannelBindingElement>() != null); + throw new NotImplementedException(); + } + + #endregion + } } diff --git a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs index e26a672..e39a047 100644 --- a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs +++ b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs @@ -24,6 +24,7 @@ namespace DotNetOpenAuth.Messaging { : this(new Uri(locationUri), method) { Contract.Requires<ArgumentNullException>(locationUri != null); Contract.Requires<ArgumentOutOfRangeException>(method != HttpDeliveryMethods.None); + Contract.Requires<ArgumentOutOfRangeException>((method & HttpDeliveryMethods.HttpVerbMask) != 0, MessagingStrings.GetOrPostFlagsRequired); } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs index e5b725b..0bbac42 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs @@ -70,6 +70,15 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Looks up a localized string similar to Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false.. + /// </summary> + internal static string BinaryDataRequiresMultipart { + get { + return ResourceManager.GetString("BinaryDataRequiresMultipart", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed.. /// </summary> internal static string CurrentHttpContextRequired { diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx index 3d4e317..34385d4 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx @@ -297,4 +297,7 @@ <data name="StreamMustHaveKnownLength" xml:space="preserve"> <value>The stream must have a known length.</value> </data> + <data name="BinaryDataRequiresMultipart" xml:space="preserve"> + <value>Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index be5927f..c29ec8c 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -141,7 +141,7 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Sends an multipart HTTP POST request (useful for posting files). + /// Sends a multipart HTTP POST request (useful for posting files). /// </summary> /// <param name="request">The HTTP request.</param> /// <param name="requestHandler">The request handler.</param> @@ -152,14 +152,60 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentNullException>(requestHandler != null); Contract.Requires<ArgumentNullException>(parts != null); + PostMultipartNoGetResponse(request, requestHandler, parts); + return requestHandler.GetResponse(request); + } + + /// <summary> + /// Assembles a message comprised of the message on a given exception and all inner exceptions. + /// </summary> + /// <param name="exception">The exception.</param> + /// <returns>The assembled message.</returns> + public static string ToStringDescriptive(this Exception exception) { + // The input being null is probably bad, but since this method is called + // from a catch block, we don't really want to throw a new exception and + // hide the details of this one. + if (exception == null) { + Logger.Messaging.Error("MessagingUtilities.GetAllMessages called with null input."); + } + + StringBuilder message = new StringBuilder(); + while (exception != null) { + message.Append(exception.Message); + exception = exception.InnerException; + if (exception != null) { + message.Append(" "); + } + } + + return message.ToString(); + } + + /// <summary> + /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it. + /// </summary> + /// <param name="request">The HTTP request.</param> + /// <param name="requestHandler">The request handler.</param> + /// <param name="parts">The parts to include in the POST entity.</param> + internal static void PostMultipartNoGetResponse(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Requires<ArgumentNullException>(requestHandler != null); + Contract.Requires<ArgumentNullException>(parts != null); + Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart"); + parts = parts.CacheGeneratedResults(); string boundary = Guid.NewGuid().ToString(); + string initialPartLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "--{0}\r\n", boundary); string partLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}\r\n", boundary); string finalTrailingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}--\r\n", boundary); request.Method = "POST"; request.ContentType = "multipart/form-data; boundary=" + boundary; - request.ContentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length; + long contentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length; + if (parts.Any()) { + contentLength -= 2; // the initial part leading boundary has no leading \r\n + } + request.ContentLength = contentLength; // Setting the content-encoding to "utf-8" causes Google to reply // with a 415 UnsupportedMediaType. But adding it doesn't buy us @@ -169,8 +215,10 @@ namespace DotNetOpenAuth.Messaging { var requestStream = requestHandler.GetRequestStream(request); try { StreamWriter writer = new StreamWriter(requestStream, Channel.PostEntityEncoding); + bool firstPart = true; foreach (var part in parts) { - writer.Write(partLeadingBoundary); + writer.Write(firstPart ? initialPartLeadingBoundary : partLeadingBoundary); + firstPart = false; part.Serialize(writer); part.Dispose(); } @@ -185,33 +233,6 @@ namespace DotNetOpenAuth.Messaging { requestStream.Dispose(); } } - - return requestHandler.GetResponse(request); - } - - /// <summary> - /// Assembles a message comprised of the message on a given exception and all inner exceptions. - /// </summary> - /// <param name="exception">The exception.</param> - /// <returns>The assembled message.</returns> - public static string ToStringDescriptive(this Exception exception) { - // The input being null is probably bad, but since this method is called - // from a catch block, we don't really want to throw a new exception and - // hide the details of this one. - if (exception == null) { - Logger.Messaging.Error("MessagingUtilities.GetAllMessages called with null input."); - } - - StringBuilder message = new StringBuilder(); - while (exception != null) { - message.Append(exception.Message); - exception = exception.InnerException; - if (exception != null) { - message.Append(" "); - } - } - - return message.ToString(); } /// <summary> @@ -646,6 +667,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="request">The request to get recipient information from.</param> /// <returns>The recipient.</returns> + /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) { return new MessageReceivingEndpoint(request.UrlBeforeRewriting, GetHttpDeliveryMethod(request.HttpMethod)); } @@ -655,6 +677,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="httpVerb">The HTTP verb.</param> /// <returns>A <see cref="HttpDeliveryMethods"/> enum value that is within the <see cref="HttpDeliveryMethods.HttpVerbMask"/>.</returns> + /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> internal static HttpDeliveryMethods GetHttpDeliveryMethod(string httpVerb) { if (httpVerb == "GET") { return HttpDeliveryMethods.GetRequest; @@ -664,6 +687,8 @@ namespace DotNetOpenAuth.Messaging { return HttpDeliveryMethods.PutRequest; } else if (httpVerb == "DELETE") { return HttpDeliveryMethods.DeleteRequest; + } else if (httpVerb == "HEAD") { + return HttpDeliveryMethods.HeadRequest; } else { throw ErrorUtilities.ThrowArgumentNamed("httpVerb", MessagingStrings.UnsupportedHttpVerb, httpVerb); } @@ -675,14 +700,16 @@ namespace DotNetOpenAuth.Messaging { /// <param name="httpMethod">The HTTP method.</param> /// <returns>An HTTP verb, such as GET, POST, PUT, or DELETE.</returns> internal static string GetHttpVerb(HttpDeliveryMethods httpMethod) { - if ((httpMethod & HttpDeliveryMethods.GetRequest) != 0) { + if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.GetRequest) { return "GET"; - } else if ((httpMethod & HttpDeliveryMethods.PostRequest) != 0) { + } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PostRequest) { return "POST"; - } else if ((httpMethod & HttpDeliveryMethods.PutRequest) != 0) { + } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PutRequest) { return "PUT"; - } else if ((httpMethod & HttpDeliveryMethods.DeleteRequest) != 0) { + } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.DeleteRequest) { return "DELETE"; + } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.HeadRequest) { + return "HEAD"; } else if ((httpMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) { return "GET"; // if AuthorizationHeaderRequest is specified without an explicit HTTP verb, assume GET. } else { diff --git a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs index 7ef89a4..f9a5988 100644 --- a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs +++ b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs @@ -116,6 +116,10 @@ namespace DotNetOpenAuth.Messaging { /// <param name="contentType">Type of the content in HTTP Content-Type format.</param> /// <returns>The constructed part.</returns> public static MultipartPostPart CreateFormFilePart(string name, string filePath, string contentType) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name)); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(filePath)); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType)); + string fileName = Path.GetFileName(filePath); return CreateFormFilePart(name, fileName, contentType, File.OpenRead(filePath)); } @@ -130,11 +134,11 @@ namespace DotNetOpenAuth.Messaging { /// <returns>The constructed part.</returns> public static MultipartPostPart CreateFormFilePart(string name, string fileName, string contentType, Stream content) { Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name)); - Contract.Requires<ArgumentException>(fileName != null); - Contract.Requires<ArgumentException>(contentType != null); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName)); + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType)); Contract.Requires<ArgumentException>(content != null); - var part = new MultipartPostPart("form-data"); + var part = new MultipartPostPart("file"); part.ContentAttributes["name"] = name; part.ContentAttributes["filename"] = fileName; part.PartHeaders[HttpRequestHeader.ContentType] = contentType; diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs index 49e30cb..f6eed24 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs @@ -269,7 +269,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// Sets a named value in the message. /// </summary> /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param> - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")] public void Add(KeyValuePair<string, string> item) { this.Add(item.Key, item.Value); } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 32d2fec..08e2411 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -286,6 +286,9 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <param name="messagePartEncoder">The message part encoder type.</param> /// <returns>An instance of the desired encoder.</returns> private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) { + Contract.Requires<ArgumentNullException>(messagePartEncoder != null); + Contract.Ensures(Contract.Result<IMessagePartEncoder>() != null); + IMessagePartEncoder encoder; if (!encoders.TryGetValue(messagePartEncoder, out encoder)) { encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index 8c5980f..b0e938f 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System.Diagnostics.Contracts; using System.Globalization; using System.IO; + using System.Linq; using System.Net; using System.Text; using System.Web; @@ -87,7 +88,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="message">The message with data to encode.</param> /// <returns>A dictionary of name-value pairs with their strings encoded.</returns> - internal static IDictionary<string, string> GetUriEscapedParameters(MessageDictionary message) { + internal static IDictionary<string, string> GetUriEscapedParameters(IEnumerable<KeyValuePair<string, string>> message) { var encodedDictionary = new Dictionary<string, string>(); UriEscapeParameters(message, encodedDictionary); return encodedDictionary; @@ -155,8 +156,16 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } } + MessageReceivingEndpoint recipient; + try { + recipient = request.GetRecipient(); + } catch (ArgumentException ex) { + Logger.OAuth.WarnFormat("Unrecognized HTTP request: " + ex.ToString()); + return null; + } + // Deserialize the message using all the data we've collected. - var message = (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient()); + var message = (IDirectedProtocolMessage)this.Receive(fields, recipient); // Add receiving HTTP transport information required for signature generation. var signedMessage = message as ITamperResistantOAuthMessage; @@ -194,9 +203,13 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if ((transmissionMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) { httpRequest = this.InitializeRequestAsAuthHeader(request); } else if ((transmissionMethod & HttpDeliveryMethods.PostRequest) != 0) { + var requestMessageWithBinaryData = request as IMessageWithBinaryData; + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || !requestMessageWithBinaryData.SendAsMultipart, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader); httpRequest = this.InitializeRequestAsPost(request); } else if ((transmissionMethod & HttpDeliveryMethods.GetRequest) != 0) { httpRequest = InitializeRequestAsGet(request); + } else if ((transmissionMethod & HttpDeliveryMethods.HeadRequest) != 0) { + httpRequest = InitializeRequestAsHead(request); } else if ((transmissionMethod & HttpDeliveryMethods.PutRequest) != 0) { httpRequest = this.InitializeRequestAsPut(request); } else if ((transmissionMethod & HttpDeliveryMethods.DeleteRequest) != 0) { @@ -264,7 +277,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="source">The dictionary with names and values to encode.</param> /// <param name="destination">The dictionary to add the encoded pairs to.</param> - private static void UriEscapeParameters(IDictionary<string, string> source, IDictionary<string, string> destination) { + private static void UriEscapeParameters(IEnumerable<KeyValuePair<string, string>> source, IDictionary<string, string> destination) { Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(destination != null); @@ -341,12 +354,22 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if (hasEntity) { // WARNING: We only set up the request stream for the caller if there is // extra data. If there isn't any extra data, the caller must do this themselves. - if (requestMessage.ExtraData.Count > 0) { - SendParametersInEntity(httpRequest, requestMessage.ExtraData); + var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; + if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { + // Include the binary data in the multipart entity, and any standard text extra message data. + // The standard declared message parts are included in the authorization header. + var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData); + multiPartFields.AddRange(requestMessage.ExtraData.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); + this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields); } else { - // We'll assume the content length is zero since the caller may not have - // anything. They're responsible to change it when the add the payload if they have one. - httpRequest.ContentLength = 0; + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart); + if (requestMessage.ExtraData.Count > 0) { + this.SendParametersInEntity(httpRequest, requestMessage.ExtraData); + } else { + // We'll assume the content length is zero since the caller may not have + // anything. They're responsible to change it when the add the payload if they have one. + httpRequest.ContentLength = 0; + } } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs index 004e7d5..b45da66 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System.Collections.Specialized; using System.Diagnostics.Contracts; using System.Globalization; + using System.Linq; using System.Text; using System.Web; using DotNetOpenAuth.Messaging; @@ -157,7 +158,26 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant()); - var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary); + // For multipart POST messages, only include the message parts that are NOT + // in the POST entity (those parts that may appear in an OAuth authorization header). + var encodedDictionary = new Dictionary<string, string>(); + IEnumerable<KeyValuePair<string, string>> partsToInclude = Enumerable.Empty<KeyValuePair<string, string>>(); + var binaryMessage = message as IMessageWithBinaryData; + if (binaryMessage != null && binaryMessage.SendAsMultipart) { + HttpDeliveryMethods authHeaderInUseFlags = HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest; + ErrorUtilities.VerifyProtocol((binaryMessage.HttpMethods & authHeaderInUseFlags) == authHeaderInUseFlags, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader); + + // Include the declared keys in the signature as those will be signable. + // Cache in local variable to avoid recalculating DeclaredKeys in the delegate. + ICollection<string> declaredKeys = messageDictionary.DeclaredKeys; + partsToInclude = messageDictionary.Where(pair => declaredKeys.Contains(pair.Key)); + } else { + partsToInclude = messageDictionary; + } + + foreach (var pair in OAuthChannel.GetUriEscapedParameters(partsToInclude)) { + encodedDictionary[pair.Key] = pair.Value; + } // An incoming message will already have included the query and form parameters // in the message dictionary, but an outgoing message COULD have SOME parameters diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs index bdb0219..67c5205 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <summary> /// The various signing binding elements that may be applicable to a message in preferred use order. /// </summary> - private ITamperProtectionChannelBindingElement[] signers; + private readonly ITamperProtectionChannelBindingElement[] signers; /// <summary> /// Initializes a new instance of the <see cref="SigningBindingElementChain"/> class. @@ -93,6 +93,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </returns> public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { foreach (IChannelBindingElement signer in this.signers) { + ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null."); MessageProtections? result = signer.ProcessOutgoingMessage(message); if (result.HasValue) { return result; @@ -113,6 +114,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </returns> public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { foreach (IChannelBindingElement signer in this.signers) { + ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null."); MessageProtections? result = signer.ProcessIncomingMessage(message); if (result.HasValue) { return result; diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs index 6c0ce42..48f54d7 100644 --- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth { using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; + using System.Linq; using System.Net; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -111,6 +112,27 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Prepares an authorized request that carries an HTTP multi-part POST, allowing for binary data. + /// </summary> + /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param> + /// <param name="accessToken">The access token that permits access to the protected resource.</param> + /// <param name="binaryData">Extra parameters to include in the message. Must not be null, but may be empty.</param> + /// <returns>The initialized WebRequest object.</returns> + public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken, IEnumerable<MultipartPostPart> binaryData) { + Contract.Requires<ArgumentNullException>(endpoint != null); + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken)); + Contract.Requires<ArgumentNullException>(binaryData != null); + + AccessProtectedResourceRequest message = this.CreateAuthorizingMessage(endpoint, accessToken); + foreach (MultipartPostPart part in binaryData) { + message.BinaryData.Add(part); + } + + HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message); + return wr; + } + + /// <summary> /// Prepares an HTTP request that has OAuth authorization already attached to it. /// </summary> /// <param name="message">The OAuth authorization message to attach to the HTTP request.</param> diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs index b60fda4..f3231f0 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OAuth.Messages { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; @@ -13,7 +14,12 @@ namespace DotNetOpenAuth.OAuth.Messages { /// A message attached to a request for protected resources that provides the necessary /// credentials to be granted access to those resources. /// </summary> - public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage { + public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage, IMessageWithBinaryData { + /// <summary> + /// A store for the binary data that is carried in the message. + /// </summary> + private List<MultipartPostPart> binaryData = new List<MultipartPostPart>(); + /// <summary> /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. /// </summary> @@ -43,5 +49,24 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </remarks> [MessagePart("oauth_token", IsRequired = true)] public string AccessToken { get; set; } + + #region IMessageWithBinaryData Members + + /// <summary> + /// Gets the parts of the message that carry binary data. + /// </summary> + /// <value>A list of parts. Never null.</value> + public IList<MultipartPostPart> BinaryData { + get { return this.binaryData; } + } + + /// <summary> + /// Gets a value indicating whether this message should be sent as multi-part POST. + /// </summary> + public bool SendAsMultipart { + get { return this.HttpMethod == "POST" && this.BinaryData.Count > 0; } + } + + #endregion } } diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index bb4d5d9..fb4c9a1 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -124,6 +124,15 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Looks up a localized string similar to Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.. + /// </summary> + internal static string MultipartPostMustBeUsedWithAuthHeader { + get { + return ResourceManager.GetString("MultipartPostMustBeUsedWithAuthHeader", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.. /// </summary> internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface { diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx index bbeeda9..34b314b 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx @@ -138,6 +138,9 @@ <data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve"> <value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value> </data> + <data name="MultipartPostMustBeUsedWithAuthHeader" xml:space="preserve"> + <value>Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.</value> + </data> <data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve"> <value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value> </data> diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs index 9014762..6205f55 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth { using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; using System.Linq; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth.ChannelElements; @@ -94,6 +95,7 @@ namespace DotNetOpenAuth.OAuth { /// </summary> /// <returns>The created signing element.</returns> internal ITamperProtectionChannelBindingElement CreateTamperProtectionElement() { + Contract.Requires(this.TamperProtectionElements != null); return new SigningBindingElementChain(this.TamperProtectionElements.Select(el => (ITamperProtectionChannelBindingElement)el.Clone()).ToArray()); } } diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs index d7dca9a..580bdfd 100644 --- a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs +++ b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs @@ -23,7 +23,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors { /// to the originally requested extension and format. /// </summary> [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sreg", Justification = "Abbreviation")] - public class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior { + public sealed class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior { /// <summary> /// Initializes static members of the <see cref="AXFetchAsSregTransform"/> class. /// </summary> diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs index 4fa70a0..c516e8f 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Text; @@ -76,6 +77,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "It doesn't look too bad to me. :)")] public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { var extendableMessage = message as IProtocolMessageWithExtensions; if (extendableMessage != null) { diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs index 67efe8e..76a1c9b 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs @@ -70,7 +70,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { /// <remarks> /// The user's preferred languages as a [BCP 47] language priority list, represented as a comma-separated list of BCP 47 basic language ranges in descending priority order. For instance, the value "fr-CA,fr-FR,en-CA" represents the preference for French spoken in Canada, French spoken in France, followed by English spoken in Canada. /// </remarks> - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design")] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design.")] [MessagePart("lang", AllowEmpty = false)] public CultureInfo[] LanguagePreference { get; set; } diff --git a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs index b6ed2f7..e96f362 100644 --- a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs +++ b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; @@ -266,6 +267,7 @@ namespace DotNetOpenAuth.OpenId { /// an alternative plan. /// </remarks> /// <exception cref="ProtocolException">Thrown if the certificate chain is invalid or unverifiable.</exception> + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "By design")] private static void VerifyCertChain(List<X509Certificate2> certs) { var chain = new X509Chain(); foreach (var cert in certs) { @@ -418,6 +420,7 @@ namespace DotNetOpenAuth.OpenId { /// <summary> /// A description of a web server that hosts host-meta documents. /// </summary> + [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "By design")] public class HostMetaProxy { /// <summary> /// Initializes a new instance of the <see cref="HostMetaProxy"/> class. diff --git a/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs index f637b37..eb2bf98 100644 --- a/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs +++ b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Text; @@ -27,6 +28,7 @@ namespace DotNetOpenAuth.OpenId { /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By design")] [Pure] IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain); } diff --git a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs index 6b89e92..8e4cb9d 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs @@ -32,14 +32,14 @@ namespace DotNetOpenAuth.OpenId.Messages { #pragma warning restore 0414 /// <summary> - /// Backing store for the <see cref="Incoming"/> property. + /// Backing store for the <see cref="ExtraData"/> property. /// </summary> - private bool incoming; + private readonly Dictionary<string, string> extraData = new Dictionary<string, string>(); /// <summary> - /// Backing store for the <see cref="ExtraData"/> property. + /// Backing store for the <see cref="Incoming"/> property. /// </summary> - private Dictionary<string, string> extraData = new Dictionary<string, string>(); + private bool incoming; /// <summary> /// Initializes a new instance of the <see cref="RequestBase"/> class. diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs index 611101d..10c69a3 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs @@ -25,6 +25,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <summary> /// Offers services for a web page that is acting as an OpenID identity server. /// </summary> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "By design")] [ContractVerification(true)] public sealed class OpenIdProvider : IDisposable { /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs index 6e141ad..b6a1b76 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs @@ -38,7 +38,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.VerifyDiscoveryMatchesAssertion(relyingParty); - Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.ClaimedIdentifier, this.Provider.Uri); + Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.Response.ClaimedIdentifier, this.Provider.Uri); } #region IAuthenticationResponse Properties diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs index 8499178..f0a1574 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs @@ -106,7 +106,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param> /// <param name="nonce">A series of random characters.</param> - /// <param name="timestamp">The timestamp that together with the nonce string make it unique. + /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique. /// The timestamp may also be used by the data store to clear out old nonces.</param> /// <returns> /// True if the nonce+timestamp (combination) was not previously in the database. @@ -119,8 +119,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// is retrieved or set using the /// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property. /// </remarks> - public bool StoreNonce(string context, string nonce, DateTime timestamp) { - return this.nonceStore.StoreNonce(context, nonce, timestamp); + public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { + return this.nonceStore.StoreNonce(context, nonce, timestampUtc); } #endregion diff --git a/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs index e338542..b1a3430 100644 --- a/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs +++ b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; @@ -21,6 +22,7 @@ namespace DotNetOpenAuth.OpenId { /// <summary> /// The discovery service for XRI identifiers that uses an XRI proxy resolver for discovery. /// </summary> + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Justification = "Acronym")] public class XriDiscoveryProxyService : IIdentifierDiscoveryService { /// <summary> /// The magic URL that will provide us an XRDS document for a given XRI identifier. diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs index 6af41fa..729f603 100644 --- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs @@ -94,7 +94,9 @@ namespace DotNetOpenAuth.OpenId { public override bool Equals(object obj) { XriIdentifier other = obj as XriIdentifier; if (obj != null && other == null && Identifier.EqualityOnStrings) { // test hook to enable MockIdentifier comparison - other = Identifier.Parse(obj.ToString()) as XriIdentifier; + string objString = obj.ToString(); + ErrorUtilities.VerifyInternal(!string.IsNullOrEmpty(objString), "Identifier.ToString() returned a null or empty string."); + other = Identifier.Parse(objString) as XriIdentifier; } if (other == null) { return false; diff --git a/src/DotNetOpenAuth/Reporting.cs b/src/DotNetOpenAuth/Reporting.cs index 4e4bbf5..2235986 100644 --- a/src/DotNetOpenAuth/Reporting.cs +++ b/src/DotNetOpenAuth/Reporting.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth { using System; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; @@ -88,6 +89,8 @@ namespace DotNetOpenAuth { /// <summary> /// Initializes static members of the <see cref="Reporting"/> class. /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "We do more than field initialization here.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Reporting MUST NOT cause unhandled exceptions.")] static Reporting() { Enabled = DotNetOpenAuthSection.Configuration.Reporting.Enabled; if (Enabled) { @@ -446,6 +449,7 @@ namespace DotNetOpenAuth { /// <summary> /// Sends the stats report asynchronously, and careful to not throw any unhandled exceptions. /// </summary> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Unhandled exceptions MUST NOT be thrown from here.")] private static void SendStatsAsync() { // Do it on a background thread since it could take a while and we // don't want to slow down this request we're borrowing. @@ -635,6 +639,7 @@ namespace DotNetOpenAuth { /// </summary> public void Dispose() { this.Dispose(true); + GC.SuppressFinalize(this); } #endregion @@ -775,6 +780,7 @@ namespace DotNetOpenAuth { /// </summary> public void Dispose() { this.Dispose(true); + GC.SuppressFinalize(this); } #endregion diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs index 589a155..f1c8be3 100644 --- a/src/DotNetOpenAuth/Yadis/Yadis.cs +++ b/src/DotNetOpenAuth/Yadis/Yadis.cs @@ -138,6 +138,8 @@ namespace DotNetOpenAuth.Yadis { internal static IncomingWebResponse Request(IDirectWebRequestHandler requestHandler, Uri uri, bool requireSsl, params string[] acceptTypes) { Contract.Requires<ArgumentNullException>(requestHandler != null); Contract.Requires<ArgumentNullException>(uri != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.CachePolicy = IdentifierDiscoveryCachePolicy; diff --git a/src/version.txt b/src/version.txt index 1809198..1545d96 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -3.4.0 +3.5.0 |