using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; using System.Diagnostics; using System.Web; using IProviderAssociationStore = DotNetOpenId.IAssociationStore; using ProviderMemoryStore = DotNetOpenId.AssociationMemoryStore; using DotNetOpenId.Configuration; namespace DotNetOpenId.Provider { /// /// Offers services for a web page that is acting as an OpenID identity server. /// [DebuggerDisplay("Endpoint: {Endpoint}, OpenId Request: {Query.ContainsKey(\"openid.mode\")}")] public class OpenIdProvider { internal Signatory Signatory { get; private set; } internal MessageEncoder Encoder; /// /// The incoming request's Url. /// /// /// This is used for certain security checks internally. It should not /// be used for its Query property, as it will be irrelevant on POST requests. /// Instead, use the OpenIdProvider.Query field. /// internal Uri RequestUrl; /// /// The query of the incoming request. /// internal IDictionary Query; /// /// The version of OpenId being used by the Relying Party /// sending the incoming request. /// internal Protocol Protocol { get; private set; } internal static Uri DefaultProviderEndpoint { get { return getProviderEndpointFromContext(); } } internal static Uri DefaultRequestUrl { get { return Util.GetRequestUrlFromContext(); } } internal static NameValueCollection DefaultQuery { get { return Util.GetQueryOrFormFromContextNVC(); } } /// /// Constructs an OpenId server that uses the HttpApplication dictionary as /// its association store and detects common settings. /// /// /// This method requires a current ASP.NET HttpContext. /// public OpenIdProvider() : this(Configuration.Store.CreateInstanceOfStore(HttpApplicationStore), getProviderEndpointFromContext(), Util.GetRequestUrlFromContext(), Util.GetQueryOrFormFromContext()) { } /// /// Constructs an OpenId server that uses a given query and IAssociationStore. /// /// /// The application-level store where associations with OpenId consumers will be preserved. /// /// /// The Internet-facing URL that responds to OpenID requests. /// /// The incoming request URL. /// /// The name/value pairs that came in on the /// QueryString of a GET request or in the entity of a POST request. /// For example: (Request.HttpMethod == "GET" ? Request.QueryString : Request.Form). /// public OpenIdProvider(IProviderAssociationStore store, Uri providerEndpoint, Uri requestUrl, NameValueCollection query) : this(store, providerEndpoint, requestUrl, Util.NameValueCollectionToDictionary(query)) { } OpenIdProvider(IProviderAssociationStore store, Uri providerEndpoint, Uri requestUrl, IDictionary query) { if (store == null) throw new ArgumentNullException("store"); if (providerEndpoint == null) throw new ArgumentNullException("providerEndpoint"); if (requestUrl == null) throw new ArgumentNullException("requestUrl"); if (query == null) throw new ArgumentNullException("query"); Settings = new ProviderSecuritySettings(); Endpoint = providerEndpoint; RequestUrl = requestUrl; Query = query; Signatory = new Signatory(store); Encoder = new SigningMessageEncoder(Signatory); store.ClearExpiredAssociations(); // every so often we should do this. } /// /// The provider URL that responds to OpenID requests. /// /// /// An auto-detect attempt is made if an ASP.NET HttpContext is available. /// internal Uri Endpoint { get; private set; } // TODO: make this property public WHEN its security settings are actually supported. /// /// Provides access to the adjustable security settings of this instance /// of . /// internal ProviderSecuritySettings Settings { get; private set; } bool requestProcessed; Request request; /// /// Gets the incoming OpenID request if there is one, or null if none was detected. /// /// /// Requests may be infrastructural to OpenID and allow auto-responses, or they may /// be authentication requests where the Provider site has to make decisions based /// on its own user database and policies. /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] // this property getter executes code public IRequest Request { get { if (!requestProcessed) { request = decodeRequest(); requestProcessed = true; } return request; } } /// /// Decodes an incoming web request in to a . /// /// A Request object, or null if the given query doesn't represent an OpenId request. Request decodeRequest() { if (!Provider.Request.IsOpenIdRequest(Query)) { return null; } Protocol = Protocol.Detect(Query); Request req = Provider.Request.CreateRequest(this); Logger.InfoFormat("Received OpenID {0} request.{1}{2}", req.Mode, Environment.NewLine, Util.ToString(Query)); return req; } /// /// Allows a Provider to send an identity assertion on behalf of one /// of its members in order to redirect the member to a relying party /// web site and log him/her in immediately in one uninterrupted step. /// /// /// The URL of the relying party web site. /// This will typically be the home page, but may be a longer URL if /// that Relying Party considers the scope of its realm to be more specific. /// The URL provided here must allow discovery of the Relying Party's /// XRDS document that advertises its OpenID RP endpoint. /// /// /// The Identifier you are asserting your member controls. /// /// /// The Identifier you know your user by internally. This will typically /// be the same as . /// /// /// An object describing the HTTP response to send /// the user agent to allow the redirect with assertion to happen. /// public IResponse PrepareUnsolicitedAssertion(Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier) { if (relyingParty == null) throw new ArgumentNullException("relyingParty"); if (claimedIdentifier == null) throw new ArgumentNullException("claimedIdentifier"); if (localIdentifier == null) throw new ArgumentNullException("localIdentifier"); Logger.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier); return AssertionMessage.CreateUnsolicitedAssertion(this, relyingParty, claimedIdentifier, localIdentifier); } const string associationStoreKey = "DotNetOpenId.Provider.OpenIdProvider.AssociationStore"; /// /// The standard state storage mechanism that uses ASP.NET's HttpApplication state dictionary /// to store associations. /// public static IProviderAssociationStore HttpApplicationStore { get { HttpContext context = HttpContext.Current; if (context == null) throw new InvalidOperationException(Strings.IAssociationStoreRequiredWhenNoHttpContextAvailable); var store = (IProviderAssociationStore)context.Application[associationStoreKey]; if (store == null) { context.Application.Lock(); try { if ((store = (IProviderAssociationStore)context.Application[associationStoreKey]) == null) { context.Application[associationStoreKey] = store = new ProviderMemoryStore(); } } finally { context.Application.UnLock(); } } return store; } } static Uri getProviderEndpointFromContext() { HttpContext context = HttpContext.Current; if (context == null) throw new InvalidOperationException(Strings.HttpContextRequiredForThisOverload); UriBuilder builder = new UriBuilder(Util.GetRequestUrlFromContext()); builder.Query = null; builder.Fragment = null; return builder.Uri; } /// /// Gets the relevant Configuration section for this OpenIdRelyingParty. /// /// /// This is not a static member because depending on the context within which we are /// invoked, the configuration section might be different. (location tag, for example). /// internal static ProviderSection Configuration { get { return ProviderSection.Configuration; } } } }