diff options
Diffstat (limited to 'src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs')
-rw-r--r-- | src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs | 216 |
1 files changed, 125 insertions, 91 deletions
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs index ecfd191..b1deb74 100644 --- a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs @@ -9,16 +9,19 @@ namespace DotNetOpenAuth.OAuth { using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Diagnostics.Contracts; using System.Globalization; + using System.Net.Http; using System.Security.Principal; using System.ServiceModel.Channels; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; + using Validation; /// <summary> /// A web application that allows access via OAuth. @@ -34,7 +37,7 @@ namespace DotNetOpenAuth.OAuth { public class ServiceProvider : IDisposable { /// <summary> /// The name of the key to use in the HttpApplication cache to store the - /// instance of <see cref="NonceMemoryStore"/> to use. + /// instance of <see cref="MemoryNonceStore"/> to use. /// </summary> private const string ApplicationStoreKey = "DotNetOpenAuth.OAuth.ServiceProvider.HttpApplicationStore"; @@ -53,7 +56,7 @@ namespace DotNetOpenAuth.OAuth { /// </summary> /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> - public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager) + public ServiceProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager) : this(serviceDescription, tokenManager, new OAuthServiceProviderMessageFactory(tokenManager)) { } @@ -63,8 +66,8 @@ namespace DotNetOpenAuth.OAuth { /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> /// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param> - public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) - : this(serviceDescription, tokenManager, OAuthElement.Configuration.ServiceProvider.ApplicationStore.CreateInstance(HttpApplicationStore), messageTypeProvider) { + public ServiceProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) + : this(serviceDescription, tokenManager, OAuthElement.Configuration.ServiceProvider.ApplicationStore.CreateInstance(GetHttpApplicationStore(), null), messageTypeProvider) { Requires.NotNull(serviceDescription, "serviceDescription"); Requires.NotNull(tokenManager, "tokenManager"); Requires.NotNull(messageTypeProvider, "messageTypeProvider"); @@ -76,7 +79,7 @@ namespace DotNetOpenAuth.OAuth { /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> /// <param name="nonceStore">The nonce store.</param> - public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore) + public ServiceProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore) : this(serviceDescription, tokenManager, nonceStore, new OAuthServiceProviderMessageFactory(tokenManager)) { } @@ -87,7 +90,7 @@ namespace DotNetOpenAuth.OAuth { /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> /// <param name="nonceStore">The nonce store.</param> /// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param> - public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore, OAuthServiceProviderMessageFactory messageTypeProvider) { + public ServiceProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore, OAuthServiceProviderMessageFactory messageTypeProvider) { Requires.NotNull(serviceDescription, "serviceDescription"); Requires.NotNull(tokenManager, "tokenManager"); Requires.NotNull(nonceStore, "nonceStore"); @@ -103,36 +106,9 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Gets the standard state storage mechanism that uses ASP.NET's - /// HttpApplication state dictionary to store associations and nonces. - /// </summary> - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static INonceStore HttpApplicationStore { - get { - Contract.Ensures(Contract.Result<INonceStore>() != null); - - HttpContext context = HttpContext.Current; - ErrorUtilities.VerifyOperation(context != null, Strings.StoreRequiredWhenNoHttpContextAvailable, typeof(INonceStore).Name); - var store = (INonceStore)context.Application[ApplicationStoreKey]; - if (store == null) { - context.Application.Lock(); - try { - if ((store = (INonceStore)context.Application[ApplicationStoreKey]) == null) { - context.Application[ApplicationStoreKey] = store = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge); - } - } finally { - context.Application.UnLock(); - } - } - - return store; - } - } - - /// <summary> /// Gets the description of this Service Provider. /// </summary> - public ServiceProviderDescription ServiceDescription { get; private set; } + public ServiceProviderHostDescription ServiceDescription { get; private set; } /// <summary> /// Gets or sets the generator responsible for generating new tokens and secrets. @@ -173,6 +149,33 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Gets the standard state storage mechanism that uses ASP.NET's + /// HttpApplication state dictionary to store associations and nonces. + /// </summary> + /// <param name="context">The HTTP context. If <c>null</c>, this method must be called while <see cref="HttpContext.Current"/> is non-null.</param> + /// <returns>The nonce store.</returns> + public static INonceStore GetHttpApplicationStore(HttpContextBase context = null) { + if (context == null) { + ErrorUtilities.VerifyOperation(HttpContext.Current != null, Strings.StoreRequiredWhenNoHttpContextAvailable, typeof(INonceStore).Name); + context = new HttpContextWrapper(HttpContext.Current); + } + + var store = (INonceStore)context.Application[ApplicationStoreKey]; + if (store == null) { + context.Application.Lock(); + try { + if ((store = (INonceStore)context.Application[ApplicationStoreKey]) == null) { + context.Application[ApplicationStoreKey] = store = new MemoryNonceStore(StandardExpirationBindingElement.MaximumMessageAge); + } + } finally { + context.Application.UnLock(); + } + } + + return store; + } + + /// <summary> /// Creates a cryptographically strong random verification code. /// </summary> /// <param name="format">The desired format of the verification code.</param> @@ -182,7 +185,7 @@ namespace DotNetOpenAuth.OAuth { /// length of the final string.</param> /// <returns>The verification code.</returns> public static string CreateVerificationCode(VerificationCodeFormat format, int length) { - Requires.InRange(length >= 0, "length"); + Requires.Range(length >= 0, "length"); switch (format) { case VerificationCodeFormat.IncludedInCallback: @@ -203,46 +206,65 @@ namespace DotNetOpenAuth.OAuth { /// <summary> /// Reads any incoming OAuth message. /// </summary> - /// <returns>The deserialized message.</returns> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The deserialized message. + /// </returns> /// <remarks> /// Requires HttpContext.Current. /// </remarks> - public IDirectedProtocolMessage ReadRequest() { - return this.Channel.ReadFromRequest(); + public Task<IDirectedProtocolMessage> ReadRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + return this.ReadRequestAsync((request ?? this.Channel.GetRequestFromContext()).AsHttpRequestMessage(), cancellationToken); } /// <summary> /// Reads any incoming OAuth message. /// </summary> - /// <param name="request">The HTTP request to read the message from.</param> - /// <returns>The deserialized message.</returns> - public IDirectedProtocolMessage ReadRequest(HttpRequestBase request) { - return this.Channel.ReadFromRequest(request); + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The deserialized message. + /// </returns> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public Task<IDirectedProtocolMessage> ReadRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.ReadFromRequestAsync(request, cancellationToken); } /// <summary> - /// Gets the incoming request for an unauthorized token, if any. + /// Reads a request for an unauthorized token from the incoming HTTP request. /// </summary> - /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - /// <remarks> - /// Requires HttpContext.Current. - /// </remarks> - public UnauthorizedTokenRequest ReadTokenRequest() { - return this.ReadTokenRequest(this.Channel.GetRequestFromContext()); + public Task<UnauthorizedTokenRequest> ReadTokenRequestAsync( + HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + return this.ReadTokenRequestAsync((request ?? this.channel.GetRequestFromContext()).AsHttpRequestMessage(), cancellationToken); } /// <summary> /// Reads a request for an unauthorized token from the incoming HTTP request. /// </summary> /// <param name="request">The HTTP request to read from.</param> - /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public UnauthorizedTokenRequest ReadTokenRequest(HttpRequestBase request) { - UnauthorizedTokenRequest message; - if (this.Channel.TryReadFromRequest(request, out message)) { + public async Task<UnauthorizedTokenRequest> ReadTokenRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + + var message = await this.Channel.TryReadFromRequestAsync<UnauthorizedTokenRequest>(request, cancellationToken); + if (message != null) { ErrorUtilities.VerifyProtocol(message.Version >= Protocol.Lookup(this.SecuritySettings.MinimumRequiredOAuthVersion).Version, OAuthStrings.MinimumConsumerVersionRequirementNotMet, this.SecuritySettings.MinimumRequiredOAuthVersion, message.Version); } + return message; } @@ -263,16 +285,18 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Gets the incoming request for the Service Provider to authorize a Consumer's - /// access to some protected resources. + /// Reads in a Consumer's request for the Service Provider to obtain permission from + /// the user to authorize the Consumer's access of some protected resource(s). /// </summary> - /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - /// <remarks> - /// Requires HttpContext.Current. - /// </remarks> - public UserAuthorizationRequest ReadAuthorizationRequest() { - return this.ReadAuthorizationRequest(this.Channel.GetRequestFromContext()); + public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.ReadAuthorizationRequestAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -280,12 +304,14 @@ namespace DotNetOpenAuth.OAuth { /// the user to authorize the Consumer's access of some protected resource(s). /// </summary> /// <param name="request">The HTTP request to read from.</param> - /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public UserAuthorizationRequest ReadAuthorizationRequest(HttpRequestBase request) { - UserAuthorizationRequest message; - this.Channel.TryReadFromRequest(request, out message); - return message; + public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(request, cancellationToken); } /// <summary> @@ -351,27 +377,31 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Gets the incoming request to exchange an authorized token for an access token. + /// Reads in a Consumer's request to exchange an authorized request token for an access token. /// </summary> - /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - /// <remarks> - /// Requires HttpContext.Current. - /// </remarks> - public AuthorizedTokenRequest ReadAccessTokenRequest() { - return this.ReadAccessTokenRequest(this.Channel.GetRequestFromContext()); + public Task<AuthorizedTokenRequest> ReadAccessTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.Channel.GetRequestFromContext(); + return this.ReadAccessTokenRequestAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> /// Reads in a Consumer's request to exchange an authorized request token for an access token. /// </summary> /// <param name="request">The HTTP request to read from.</param> - /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public AuthorizedTokenRequest ReadAccessTokenRequest(HttpRequestBase request) { - AuthorizedTokenRequest message; - this.Channel.TryReadFromRequest(request, out message); - return message; + public Task<AuthorizedTokenRequest> ReadAccessTokenRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(request, cancellationToken); } /// <summary> @@ -398,6 +428,9 @@ namespace DotNetOpenAuth.OAuth { /// <summary> /// Gets the authorization (access token) for accessing some protected resource. /// </summary> + /// <param name="request">HTTP details from an incoming WCF message.</param> + /// <param name="requestUri">The URI of the WCF service endpoint.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> /// <remarks> /// This method verifies that the access token and token secret are valid. @@ -405,15 +438,15 @@ namespace DotNetOpenAuth.OAuth { /// to access the resources being requested. /// </remarks> /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> - public AccessProtectedResourceRequest ReadProtectedResourceAuthorization() { - return this.ReadProtectedResourceAuthorization(this.Channel.GetRequestFromContext()); + public Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestMessageProperty request, Uri requestUri, CancellationToken cancellationToken = default(CancellationToken)) { + return this.ReadProtectedResourceAuthorizationAsync(new HttpRequestInfo(request, requestUri), cancellationToken); } /// <summary> /// Gets the authorization (access token) for accessing some protected resource. /// </summary> - /// <param name="request">HTTP details from an incoming WCF message.</param> - /// <param name="requestUri">The URI of the WCF service endpoint.</param> + /// <param name="request">The incoming HTTP request.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> /// <remarks> /// This method verifies that the access token and token secret are valid. @@ -421,14 +454,16 @@ namespace DotNetOpenAuth.OAuth { /// to access the resources being requested. /// </remarks> /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> - public AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestMessageProperty request, Uri requestUri) { - return this.ReadProtectedResourceAuthorization(new HttpRequestInfo(request, requestUri)); + public Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.ReadProtectedResourceAuthorizationAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> /// Gets the authorization (access token) for accessing some protected resource. /// </summary> /// <param name="request">The incoming HTTP request.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> /// <remarks> /// This method verifies that the access token and token secret are valid. @@ -436,11 +471,10 @@ namespace DotNetOpenAuth.OAuth { /// to access the resources being requested. /// </remarks> /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> - public AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestBase request) { + public async Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(request, "request"); - - AccessProtectedResourceRequest accessMessage; - if (this.Channel.TryReadFromRequest<AccessProtectedResourceRequest>(request, out accessMessage)) { + var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(request, cancellationToken); + if (accessMessage != null) { if (this.TokenManager.GetTokenType(accessMessage.AccessToken) != TokenType.AccessToken) { throw new ProtocolException( string.Format( @@ -458,11 +492,11 @@ namespace DotNetOpenAuth.OAuth { /// </summary> /// <param name="request">The request.</param> /// <returns>The <see cref="IPrincipal"/> instance that can be used for access control of resources.</returns> - public OAuthPrincipal CreatePrincipal(AccessProtectedResourceRequest request) { + public IPrincipal CreatePrincipal(AccessProtectedResourceRequest request) { Requires.NotNull(request, "request"); IServiceProviderAccessToken accessToken = this.TokenManager.GetAccessToken(request.AccessToken); - return new OAuth1Principal(accessToken); + return OAuthPrincipal.CreatePrincipal(accessToken.Username, accessToken.Roles); } #region IDisposable Members |