diff options
Diffstat (limited to 'src/DotNetOpenAuth.OpenId.Provider.UI')
5 files changed, 674 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj b/src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj new file mode 100644 index 0000000..150afd3 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/DotNetOpenAuth.OpenId.Provider.UI.csproj @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))\EnlistmentInfo.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))' != '' " /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + </PropertyGroup> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" /> + <PropertyGroup> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{9D0F8866-2131-4C2A-BC0E-16FEA5B50828}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>DotNetOpenAuth</RootNamespace> + <AssemblyName>DotNetOpenAuth.OpenId.Provider.UI</AssemblyName> + </PropertyGroup> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.Product.props" /> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + </PropertyGroup> + <ItemGroup> + <Compile Include="OpenId\Provider\IdentityEndpoint.cs" /> + <Compile Include="OpenId\Provider\IdentityEndpointNormalizationEventArgs.cs" /> + <Compile Include="OpenId\Provider\ProviderEndpoint.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\DotNetOpenAuth.Messaging\DotNetOpenAuth.Messaging.csproj"> + <Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project> + <Name>DotNetOpenAuth.Messaging</Name> + </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.OpenId.Provider\DotNetOpenAuth.OpenId.Provider.csproj"> + <Project>{F8284738-3B5D-4733-A511-38C23F4A763F}</Project> + <Name>DotNetOpenAuth.OpenId.Provider</Name> + </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.OpenId\DotNetOpenAuth.OpenId.csproj"> + <Project>{3896A32A-E876-4C23-B9B8-78E17D134CD3}</Project> + <Name>DotNetOpenAuth.OpenId</Name> + </ProjectReference> + <ProjectReference Include="..\Org.Mentalis.Security.Cryptography\Org.Mentalis.Security.Cryptography.csproj"> + <Project>{26DC877F-5987-48DD-9DDB-E62F2DE0E150}</Project> + <Name>Org.Mentalis.Security.Cryptography</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <ItemGroup /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> +</Project>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs new file mode 100644 index 0000000..3a18b70 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpoint.cs @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------- +// <copyright file="IdentityEndpoint.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Provider { + using System; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Drawing.Design; + using System.Web.UI; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// An ASP.NET control that manages the OpenID identity advertising tags + /// of a user's Identity Page that allow a relying party web site to discover + /// how to authenticate a user. + /// </summary> + [DefaultProperty("ServerUrl")] + [ToolboxData("<{0}:IdentityEndpoint runat=\"server\" ProviderEndpointUrl=\"\" />")] + public class IdentityEndpoint : XrdsPublisher { + #region Property viewstate keys + + /// <summary> + /// The viewstate key to use for storing the value of the <see cref="AutoNormalizeRequest"/> property. + /// </summary> + private const string AutoNormalizeRequestViewStateKey = "AutoNormalizeRequest"; + + /// <summary> + /// The viewstate key to use for storing the value of the <see cref="ProviderLocalIdentifier"/> property. + /// </summary> + private const string ProviderLocalIdentifierViewStateKey = "ProviderLocalIdentifier"; + + /// <summary> + /// The viewstate key to use for storing the value of the <see cref="ProviderVersion"/> property. + /// </summary> + private const string ProviderVersionViewStateKey = "ProviderVersion"; + + /// <summary> + /// The viewstate key to use for storing the value of the <see cref="ProviderEndpointUrl"/> property. + /// </summary> + private const string ProviderEndpointUrlViewStateKey = "ProviderEndpointUrl"; + + #endregion + + /// <summary> + /// The default value for the <see cref="ProviderVersion"/> property. + /// </summary> + private const ProtocolVersion ProviderVersionDefault = ProtocolVersion.V20; + + /// <summary> + /// Initializes a new instance of the <see cref="IdentityEndpoint"/> class. + /// </summary> + public IdentityEndpoint() { + } + + /// <summary> + /// Fired at each page request so the host web site can return the normalized + /// version of the request URI. + /// </summary> + public event EventHandler<IdentityEndpointNormalizationEventArgs> NormalizeUri; + + #region Properties + + /// <summary> + /// Gets or sets the OpenID version supported by the provider. + /// If multiple versions are supported, this should be set to the latest + /// version that this library and the Provider both support. + /// </summary> + [Category("Behavior")] + [DefaultValue(ProviderVersionDefault)] + [Description("The OpenID version supported by the provider.")] + public ProtocolVersion ProviderVersion { + get { + return this.ViewState[ProviderVersionViewStateKey] == null ? + ProviderVersionDefault : (ProtocolVersion)this.ViewState[ProviderVersionViewStateKey]; + } + + set { + this.ViewState[ProviderVersionViewStateKey] = value; + } + } + + /// <summary> + /// Gets or sets the Provider URL that processes OpenID requests. + /// </summary> + [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Forms designer property grid only supports primitive types.")] + [Bindable(true), Category("Behavior")] + [Description("The Provider URL that processes OpenID requests.")] + [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))] + public string ProviderEndpointUrl { + get { + return (string)ViewState[ProviderEndpointUrlViewStateKey]; + } + + set { + UriUtil.ValidateResolvableUrl(Page, DesignMode, value); + ViewState[ProviderEndpointUrlViewStateKey] = value; + } + } + + /// <summary> + /// Gets or sets the Identifier that is controlled by the Provider. + /// </summary> + [Bindable(true)] + [Category("Behavior")] + [Description("The user Identifier that is controlled by the Provider.")] + public string ProviderLocalIdentifier { + get { + return (string)ViewState[ProviderLocalIdentifierViewStateKey]; + } + + set { + UriUtil.ValidateResolvableUrl(Page, DesignMode, value); + ViewState[ProviderLocalIdentifierViewStateKey] = value; + } + } + + /// <summary> + /// Gets or sets a value indicating whether every incoming request + /// will be checked for normalized form and redirected if it is not. + /// </summary> + /// <remarks> + /// <para>If set to true (and it should be), you should also handle the <see cref="NormalizeUri"/> + /// event and apply your own policy for normalizing the URI.</para> + /// If multiple <see cref="IdentityEndpoint"/> controls are on a single page (to support + /// multiple versions of OpenID for example) then only one of them should have this + /// property set to true. + /// </remarks> + [Bindable(true)] + [Category("Behavior")] + [Description("Whether every incoming request will be checked for normalized form and redirected if it is not. If set to true, consider handling the NormalizeUri event.")] + public bool AutoNormalizeRequest { + get { return (bool)(ViewState[AutoNormalizeRequestViewStateKey] ?? false); } + set { ViewState[AutoNormalizeRequestViewStateKey] = value; } + } + #endregion + + /// <summary> + /// Gets the protocol to use for advertising OpenID on the identity page. + /// </summary> + internal Protocol Protocol { + get { return Protocol.Lookup(this.ProviderVersion); } + } + + /// <summary> + /// Checks the incoming request and invokes a browser redirect if the URL has not been normalized. + /// </summary> + /// <seealso cref="IdentityEndpointNormalizationEventArgs.NormalizedIdentifier"/> + protected virtual void OnNormalize() { + UriIdentifier userSuppliedIdentifier = MessagingUtilities.GetRequestUrlFromContext(); + var normalizationArgs = new IdentityEndpointNormalizationEventArgs(userSuppliedIdentifier); + + var normalizeUri = this.NormalizeUri; + if (normalizeUri != null) { + normalizeUri(this, normalizationArgs); + } else { + // Do some best-guess normalization. + normalizationArgs.NormalizedIdentifier = BestGuessNormalization(normalizationArgs.UserSuppliedIdentifier); + } + + // If we have a normalized form, we should use it. + // We compare path and query with case sensitivity and host name without case sensitivity deliberately, + // and the fragment will be asserted or cleared by the OP during authentication. + if (normalizationArgs.NormalizedIdentifier != null && + (!String.Equals(normalizationArgs.NormalizedIdentifier.Host, normalizationArgs.UserSuppliedIdentifier.Host, StringComparison.OrdinalIgnoreCase) || + !String.Equals(normalizationArgs.NormalizedIdentifier.PathAndQuery, normalizationArgs.UserSuppliedIdentifier.PathAndQuery, StringComparison.Ordinal))) { + Page.Response.Redirect(normalizationArgs.NormalizedIdentifier.AbsoluteUri); + } + } + + /// <summary> + /// Checks the incoming request and invokes a browser redirect if the URL has not been normalized. + /// </summary> + /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param> + protected override void OnLoad(EventArgs e) { + // Perform URL normalization BEFORE calling base.OnLoad, to keep + // our base XrdsPublisher from over-eagerly responding with an XRDS + // document before we've redirected. + if (this.AutoNormalizeRequest && !this.Page.IsPostBack) { + this.OnNormalize(); + } + + base.OnLoad(e); + } + + /// <summary> + /// Renders OpenID identity tags. + /// </summary> + /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> + [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")] + protected override void Render(HtmlTextWriter writer) { + Uri requestUrlBeforeRewrites = MessagingUtilities.GetRequestUrlFromContext(); + base.Render(writer); + if (!string.IsNullOrEmpty(this.ProviderEndpointUrl)) { + writer.WriteBeginTag("link"); + writer.WriteAttribute("rel", this.Protocol.HtmlDiscoveryProviderKey); + writer.WriteAttribute("href", new Uri(requestUrlBeforeRewrites, this.Page.ResolveUrl(this.ProviderEndpointUrl)).AbsoluteUri); + writer.Write(">"); + writer.WriteEndTag("link"); + writer.WriteLine(); + } + if (!string.IsNullOrEmpty(this.ProviderLocalIdentifier)) { + writer.WriteBeginTag("link"); + writer.WriteAttribute("rel", Protocol.HtmlDiscoveryLocalIdKey); + writer.WriteAttribute("href", new Uri(requestUrlBeforeRewrites, this.Page.ResolveUrl(this.ProviderLocalIdentifier)).AbsoluteUri); + writer.Write(">"); + writer.WriteEndTag("link"); + writer.WriteLine(); + } + } + + /// <summary> + /// Normalizes the URL by making the path and query lowercase, and trimming trailing slashes. + /// </summary> + /// <param name="uri">The URI to normalize.</param> + /// <returns>The normalized URI.</returns> + [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "FxCop is probably right, but we've been lowercasing host names for normalization elsewhere in the project for a long time now.")] + private static Uri BestGuessNormalization(Uri uri) { + UriBuilder uriBuilder = new UriBuilder(uri); + uriBuilder.Path = uriBuilder.Path.ToLowerInvariant(); + + // Ensure no trailing slash unless it is the only element of the path. + if (uriBuilder.Path != "/") { + uriBuilder.Path = uriBuilder.Path.TrimEnd('/'); + } + + // We trim the ? from the start of the query when we reset it because + // the UriBuilder.Query setter automatically prepends one, and we don't + // want to double them up. + uriBuilder.Query = uriBuilder.Query.TrimStart('?').ToLowerInvariant(); + return uriBuilder.Uri; + } + } +} diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs new file mode 100644 index 0000000..d190792 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/IdentityEndpointNormalizationEventArgs.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------- +// <copyright file="IdentityEndpointNormalizationEventArgs.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Provider { + using System; + + /// <summary> + /// The event arguments passed to the <see cref="IdentityEndpoint.NormalizeUri"/> event handler. + /// </summary> + public class IdentityEndpointNormalizationEventArgs : EventArgs { + /// <summary> + /// Initializes a new instance of the <see cref="IdentityEndpointNormalizationEventArgs"/> class. + /// </summary> + /// <param name="userSuppliedIdentifier">The user supplied identifier.</param> + internal IdentityEndpointNormalizationEventArgs(UriIdentifier userSuppliedIdentifier) { + this.UserSuppliedIdentifier = userSuppliedIdentifier; + } + + /// <summary> + /// Gets or sets the portion of the incoming page request URI that is relevant to normalization. + /// </summary> + /// <remarks> + /// This identifier should be used to look up the user whose identity page is being queried. + /// It MAY be set in case some clever web server URL rewriting is taking place that ASP.NET + /// does not know about but your site does. If this is the case this property should be set + /// to whatever the original request URL was. + /// </remarks> + public Uri UserSuppliedIdentifier { get; set; } + + /// <summary> + /// Gets or sets the normalized form of the user's identifier, according to the host site's policy. + /// </summary> + /// <remarks> + /// <para>This should be set to some constant value for an individual user. + /// For example, if <see cref="UserSuppliedIdentifier"/> indicates that identity page + /// for "BOB" is being called up, then the following things should be considered:</para> + /// <list> + /// <item>Normalize the capitalization of the URL: for example, change http://provider/BOB to + /// http://provider/bob.</item> + /// <item>Switch to HTTPS is it is offered: change http://provider/bob to https://provider/bob.</item> + /// <item>Strip off the query string if it is not part of the canonical identity: + /// https://provider/bob?timeofday=now becomes https://provider/bob</item> + /// <item>Ensure that any trailing slash is either present or absent consistently. For example, + /// change https://provider/bob/ to https://provider/bob.</item> + /// </list> + /// <para>When this property is set, the <see cref="IdentityEndpoint"/> control compares it to + /// the request that actually came in, and redirects the browser to use the normalized identifier + /// if necessary.</para> + /// <para>Using the normalized identifier in the request is <i>very</i> important as it + /// helps the user maintain a consistent identity across sites and across site visits to an individual site. + /// For example, without normalizing the URL, Bob might sign into a relying party site as + /// http://provider/bob one day and https://provider/bob the next day, and the relying party + /// site <i>should</i> interpret Bob as two different people because the URLs are different. + /// By normalizing the URL at the Provider's identity page for Bob, whichever URL Bob types in + /// from day-to-day gets redirected to a normalized form, so Bob is seen as the same person + /// all the time, which is of course what Bob wants. + /// </para> + /// </remarks> + public Uri NormalizedIdentifier { get; set; } + } +} diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs new file mode 100644 index 0000000..90dedc2 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs @@ -0,0 +1,267 @@ +//----------------------------------------------------------------------- +// <copyright file="ProviderEndpoint.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Provider { + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics.Contracts; + using System.Text; + using System.Web; + using System.Web.UI; + using System.Web.UI.WebControls; + using DotNetOpenAuth.Configuration; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.Messages; + + /// <summary> + /// An OpenID Provider control that automatically responds to certain + /// automated OpenID messages, and routes authentication requests to + /// custom code via an event handler. + /// </summary> + [DefaultEvent("AuthenticationChallenge")] + [ToolboxData("<{0}:ProviderEndpoint runat='server' />")] + public class ProviderEndpoint : Control { + /// <summary> + /// The key used to store the pending authentication request in the ASP.NET session. + /// </summary> + private const string PendingRequestKey = "pendingRequest"; + + /// <summary> + /// The default value for the <see cref="Enabled"/> property. + /// </summary> + private const bool EnabledDefault = true; + + /// <summary> + /// The view state key in which to store the value of the <see cref="Enabled"/> property. + /// </summary> + private const string EnabledViewStateKey = "Enabled"; + + /// <summary> + /// Backing field for the <see cref="Provider"/> property. + /// </summary> + private static OpenIdProvider provider; + + /// <summary> + /// The lock that must be obtained when initializing the provider field. + /// </summary> + private static object providerInitializerLock = new object(); + + /// <summary> + /// Fired when an incoming OpenID request is an authentication challenge + /// that must be responded to by the Provider web site according to its + /// own user database and policies. + /// </summary> + public event EventHandler<AuthenticationChallengeEventArgs> AuthenticationChallenge; + + /// <summary> + /// Fired when an incoming OpenID message carries extension requests + /// but is not regarding any OpenID identifier. + /// </summary> + public event EventHandler<AnonymousRequestEventArgs> AnonymousRequest; + + /// <summary> + /// Gets or sets the <see cref="OpenIdProvider"/> instance to use for all instances of this control. + /// </summary> + /// <value>The default value is an <see cref="OpenIdProvider"/> instance initialized according to the web.config file.</value> + public static OpenIdProvider Provider { + get { + Contract.Ensures(Contract.Result<OpenIdProvider>() != null); + if (provider == null) { + lock (providerInitializerLock) { + if (provider == null) { + provider = CreateProvider(); + } + } + } + + return provider; + } + + set { + Contract.Requires<ArgumentNullException>(value != null); + provider = value; + } + } + + /// <summary> + /// Gets or sets an incoming OpenID authentication request that has not yet been responded to. + /// </summary> + /// <remarks> + /// This request is stored in the ASP.NET Session state, so it will survive across + /// redirects, postbacks, and transfers. This allows you to authenticate the user + /// yourself, and confirm his/her desire to authenticate to the relying party site + /// before responding to the relying party's authentication request. + /// </remarks> + public static IAuthenticationRequest PendingAuthenticationRequest { + get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + Contract.Ensures(Contract.Result<IAuthenticationRequest>() == null || PendingRequest != null); + return HttpContext.Current.Session[PendingRequestKey] as IAuthenticationRequest; + } + + set { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + HttpContext.Current.Session[PendingRequestKey] = value; + } + } + + /// <summary> + /// Gets or sets an incoming OpenID anonymous request that has not yet been responded to. + /// </summary> + /// <remarks> + /// This request is stored in the ASP.NET Session state, so it will survive across + /// redirects, postbacks, and transfers. This allows you to authenticate the user + /// yourself, and confirm his/her desire to provide data to the relying party site + /// before responding to the relying party's request. + /// </remarks> + public static IAnonymousRequest PendingAnonymousRequest { + get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + Contract.Ensures(Contract.Result<IAnonymousRequest>() == null || PendingRequest != null); + return HttpContext.Current.Session[PendingRequestKey] as IAnonymousRequest; + } + + set { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + HttpContext.Current.Session[PendingRequestKey] = value; + } + } + + /// <summary> + /// Gets or sets an incoming OpenID request that has not yet been responded to. + /// </summary> + /// <remarks> + /// This request is stored in the ASP.NET Session state, so it will survive across + /// redirects, postbacks, and transfers. This allows you to authenticate the user + /// yourself, and confirm his/her desire to provide data to the relying party site + /// before responding to the relying party's request. + /// </remarks> + public static IHostProcessedRequest PendingRequest { + get { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + return HttpContext.Current.Session[PendingRequestKey] as IHostProcessedRequest; + } + + set { + Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + Contract.Requires<InvalidOperationException>(HttpContext.Current.Session != null, MessagingStrings.SessionRequired); + HttpContext.Current.Session[PendingRequestKey] = value; + } + } + + /// <summary> + /// Gets or sets a value indicating whether or not this control should + /// be listening for and responding to incoming OpenID requests. + /// </summary> + [Category("Behavior"), DefaultValue(EnabledDefault)] + public bool Enabled { + get { + return ViewState[EnabledViewStateKey] == null ? + EnabledDefault : (bool)ViewState[EnabledViewStateKey]; + } + + set { + ViewState[EnabledViewStateKey] = value; + } + } + + /// <summary> + /// Sends the response for the <see cref="PendingAuthenticationRequest"/> and clears the property. + /// </summary> + public static void SendResponse() { + var pendingRequest = PendingRequest; + PendingRequest = null; + Provider.Respond(pendingRequest); + } + + /// <summary> + /// Checks for incoming OpenID requests, responds to ones it can + /// respond to without policy checks, and fires events for custom + /// handling of the ones it cannot decide on automatically. + /// </summary> + /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param> + protected override void OnLoad(EventArgs e) { + base.OnLoad(e); + + // There is the unusual scenario that this control is hosted by + // an ASP.NET web page that has other UI on it to that the user + // might see, including controls that cause a postback to occur. + // We definitely want to ignore postbacks, since any openid messages + // they contain will be old. + if (this.Enabled && !this.Page.IsPostBack) { + // Use the explicitly given state store on this control if there is one. + // Then try the configuration file specified one. Finally, use the default + // in-memory one that's built into OpenIdProvider. + // determine what incoming message was received + IRequest request = Provider.GetRequest(); + if (request != null) { + PendingRequest = null; + + // process the incoming message appropriately and send the response + IAuthenticationRequest idrequest; + IAnonymousRequest anonRequest; + if ((idrequest = request as IAuthenticationRequest) != null) { + PendingAuthenticationRequest = idrequest; + this.OnAuthenticationChallenge(idrequest); + } else if ((anonRequest = request as IAnonymousRequest) != null) { + PendingAnonymousRequest = anonRequest; + if (!this.OnAnonymousRequest(anonRequest)) { + // This is a feature not supported by the OP, so + // go ahead and set disapproved so we can send a response. + Logger.OpenId.Warn("An incoming anonymous OpenID request message was detected, but the ProviderEndpoint.AnonymousRequest event is not handled, so returning cancellation message to relying party."); + anonRequest.IsApproved = false; + } + } + if (request.IsResponseReady) { + Provider.Respond(request); + PendingAuthenticationRequest = null; + } + } + } + } + + /// <summary> + /// Fires the <see cref="AuthenticationChallenge"/> event. + /// </summary> + /// <param name="request">The request to include in the event args.</param> + protected virtual void OnAuthenticationChallenge(IAuthenticationRequest request) { + var authenticationChallenge = this.AuthenticationChallenge; + if (authenticationChallenge != null) { + authenticationChallenge(this, new AuthenticationChallengeEventArgs(request)); + } + } + + /// <summary> + /// Fires the <see cref="AnonymousRequest"/> event. + /// </summary> + /// <param name="request">The request to include in the event args.</param> + /// <returns><c>true</c> if there were any anonymous request handlers.</returns> + protected virtual bool OnAnonymousRequest(IAnonymousRequest request) { + var anonymousRequest = this.AnonymousRequest; + if (anonymousRequest != null) { + anonymousRequest(this, new AnonymousRequestEventArgs(request)); + return true; + } else { + return false; + } + } + + /// <summary> + /// Creates the default OpenIdProvider to use. + /// </summary> + /// <returns>The new instance of OpenIdProvider.</returns> + private static OpenIdProvider CreateProvider() { + Contract.Ensures(Contract.Result<OpenIdProvider>() != null); + return new OpenIdProvider(OpenIdElement.Configuration.Provider.ApplicationStore.CreateInstance(OpenIdProvider.HttpApplicationStore)); + } + } +} diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e5cab0f --- /dev/null +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/Properties/AssemblyInfo.cs @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------- +// <copyright file="AssemblyInfo.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +// We DON'T put an AssemblyVersionAttribute in here because it is generated in the build. + +using System; +using System.Diagnostics.Contracts; +using System.Net; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using System.Web.UI; + +[assembly: TagPrefix("DotNetOpenAuth.OpenId.Provider", "op")] + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DotNetOpenAuth OpenID")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DotNetOpenAuth")] +[assembly: AssemblyCopyright("Copyright © 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en-US")] +[assembly: CLSCompliant(true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7d73990c-47c0-4256-9f20-a893add9e289")] + +[assembly: ContractVerification(true)] + +#if StrongNameSigned +// See comment at top of this file. We need this so that strong-naming doesn't +// keep this assembly from being useful to shared host (medium trust) web sites. +[assembly: AllowPartiallyTrustedCallers] + +[assembly: InternalsVisibleTo("DotNetOpenAuth.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +#else +[assembly: InternalsVisibleTo("DotNetOpenAuth.Test")] +#endif |