summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2012-04-21 20:11:34 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2012-04-21 20:11:34 -0700
commit1b6d8c2a40a019b43b252102353170380872da45 (patch)
treefb6440ce98e45fe96eee5fb8d41c67187b70d577 /src
parent08827a078f370a0e976102e792a16095dd501b8c (diff)
downloadDotNetOpenAuth-1b6d8c2a40a019b43b252102353170380872da45.zip
DotNetOpenAuth-1b6d8c2a40a019b43b252102353170380872da45.tar.gz
DotNetOpenAuth-1b6d8c2a40a019b43b252102353170380872da45.tar.bz2
Replaces ResourceServer.VerifyAccess with a better pattern for error handling.
Fixes #122
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj1
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs8
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs78
-rw-r--r--src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs90
4 files changed, 124 insertions, 53 deletions
diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
index 65dee44..c0aef30 100644
--- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
+++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
@@ -55,6 +55,7 @@
<Compile Include="Messaging\MultipartPostPart.cs" />
<Compile Include="Messaging\NetworkDirectWebResponse.cs" />
<Compile Include="Messaging\OutgoingWebResponseActionResult.cs" />
+ <Compile Include="Messaging\ProtocolFaultResponseException.cs" />
<Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
<Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" />
<Compile Include="Messaging\Reflection\IMessagePartOriginalEncoder.cs" />
diff --git a/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs b/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs
index e26d15e..982e1c0 100644
--- a/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs
@@ -42,10 +42,10 @@ namespace DotNetOpenAuth.Messaging {
/// such that it can be sent as a protocol message response to a remote caller.
/// </summary>
/// <param name="message">The human-readable exception message.</param>
- /// <param name="faultedMessage">The message that was the cause of the exception. Must not be null.</param>
- protected internal ProtocolException(string message, IProtocolMessage faultedMessage)
- : base(message) {
- Requires.NotNull(faultedMessage, "faultedMessage");
+ /// <param name="faultedMessage">The message that was the cause of the exception. May be null.</param>
+ /// <param name="innerException">The inner exception to include.</param>
+ protected internal ProtocolException(string message, IProtocolMessage faultedMessage, Exception innerException = null)
+ : base(message, innerException) {
this.FaultedMessage = faultedMessage;
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs b/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs
new file mode 100644
index 0000000..515414b
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs
@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------
+// <copyright file="ProtocolFaultResponseException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// An exception to represent errors in the local or remote implementation of the protocol
+ /// that includes the response message that should be returned to the HTTP client to comply
+ /// with the protocol specification.
+ /// </summary>
+ public class ProtocolFaultResponseException : ProtocolException {
+ /// <summary>
+ /// The channel that produced the error response message, to be used in constructing the actual HTTP response.
+ /// </summary>
+ private readonly Channel channel;
+
+ /// <summary>
+ /// A cached value for the <see cref="ErrorResponse"/> property.
+ /// </summary>
+ private OutgoingWebResponse response;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolFaultResponseException"/> class
+ /// such that it can be sent as a protocol message response to a remote caller.
+ /// </summary>
+ /// <param name="channel">The channel to use when encoding the response message.</param>
+ /// <param name="errorResponse">The message to send back to the HTTP client.</param>
+ /// <param name="faultedMessage">The message that was the cause of the exception. May be null.</param>
+ /// <param name="innerException">The inner exception.</param>
+ /// <param name="message">The message for the exception.</param>
+ protected internal ProtocolFaultResponseException(Channel channel, IDirectResponseProtocolMessage errorResponse, IProtocolMessage faultedMessage = null, Exception innerException = null, string message = null)
+ : base(message ?? (innerException != null ? innerException.Message : null), faultedMessage, innerException) {
+ Requires.NotNull(channel, "channel");
+ Requires.NotNull(errorResponse, "errorResponse");
+ this.channel = channel;
+ this.ErrorResponseMessage = errorResponse;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolFaultResponseException"/> class.
+ /// </summary>
+ /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
+ /// that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The System.Runtime.Serialization.StreamingContext
+ /// that contains contextual information about the source or destination.</param>
+ protected ProtocolFaultResponseException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets the protocol message to send back to the client to report the error.
+ /// </summary>
+ public IDirectResponseProtocolMessage ErrorResponseMessage { get; private set; }
+
+ /// <summary>
+ /// Gets the HTTP response to forward to the client to report the error.
+ /// </summary>
+ public OutgoingWebResponse ErrorResponse {
+ get {
+ if (this.response == null) {
+ this.response = this.channel.PrepareResponse(this.ErrorResponseMessage);
+ }
+
+ return this.response;
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs
index 9540d10..ba332fe 100644
--- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs
+++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs
@@ -65,27 +65,20 @@ namespace DotNetOpenAuth.OAuth2 {
/// <summary>
/// Discovers what access the client should have considering the access token in the current request.
/// </summary>
- /// <param name="accessToken">Receives the access token describing the authorization the client has.</param>
- /// <returns>An error to return to the client if access is not authorized; <c>null</c> if access is granted.</returns>
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Try pattern")]
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")]
- public OutgoingWebResponse VerifyAccess(out AccessToken accessToken) {
- return this.VerifyAccess(this.Channel.GetRequestFromContext(), out accessToken);
- }
-
- /// <summary>
- /// Discovers what access the client should have considering the access token in the current request.
- /// </summary>
/// <param name="httpRequestInfo">The HTTP request info.</param>
- /// <param name="accessToken">Receives the access token describing the authorization the client has.</param>
/// <returns>
- /// An error to return to the client if access is not authorized; <c>null</c> if access is granted.
+ /// The access token describing the authorization the client has. Never <c>null</c>.
/// </returns>
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")]
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Try pattern")]
- public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out AccessToken accessToken) {
- Requires.NotNull(httpRequestInfo, "httpRequestInfo");
+ /// <exception cref="ProtocolFaultResponseException">
+ /// Thrown when the client is not authorized. This exception should be caught and the
+ /// <see cref="ProtocolFaultResponseException.ErrorResponse"/> message should be returned to the client.
+ /// </exception>
+ public virtual AccessToken GetAccessToken(HttpRequestBase httpRequestInfo = null) {
+ if (httpRequestInfo == null) {
+ httpRequestInfo = this.Channel.GetRequestFromContext();
+ }
+ AccessToken accessToken;
AccessProtectedResourceRequest request = null;
try {
if (this.Channel.TryReadFromRequest<AccessProtectedResourceRequest>(httpRequestInfo, out request)) {
@@ -96,18 +89,15 @@ namespace DotNetOpenAuth.OAuth2 {
ErrorUtilities.ThrowProtocol(ResourceServerStrings.InvalidAccessToken);
}
- return null;
+ return accessToken;
} else {
- var response = new UnauthorizedResponse(new ProtocolException(ResourceServerStrings.MissingAccessToken));
-
- accessToken = null;
- return this.Channel.PrepareResponse(response);
+ var ex = new ProtocolException(ResourceServerStrings.MissingAccessToken);
+ var response = new UnauthorizedResponse(ex);
+ throw new ProtocolFaultResponseException(this.Channel, response, innerException: ex);
}
} catch (ProtocolException ex) {
var response = request != null ? new UnauthorizedResponse(request, ex) : new UnauthorizedResponse(ex);
-
- accessToken = null;
- return this.Channel.PrepareResponse(response);
+ throw new ProtocolFaultResponseException(this.Channel, response, innerException: ex);
}
}
@@ -115,30 +105,29 @@ namespace DotNetOpenAuth.OAuth2 {
/// Discovers what access the client should have considering the access token in the current request.
/// </summary>
/// <param name="httpRequestInfo">The HTTP request info.</param>
- /// <param name="principal">The principal that contains the user and roles that the access token is authorized for.</param>
/// <returns>
- /// An error to return to the client if access is not authorized; <c>null</c> if access is granted.
+ /// The principal that contains the user and roles that the access token is authorized for. Never <c>null</c>.
/// </returns>
+ /// <exception cref="ProtocolFaultResponseException">
+ /// Thrown when the client is not authorized. This exception should be caught and the
+ /// <see cref="ProtocolFaultResponseException.ErrorResponse"/> message should be returned to the client.
+ /// </exception>
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")]
- public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out IPrincipal principal) {
- AccessToken accessToken;
- var result = this.VerifyAccess(httpRequestInfo, out accessToken);
- if (result == null) {
- // Mitigates attacks on this approach of differentiating clients from resource owners
- // by checking that a username doesn't look suspiciously engineered to appear like the other type.
- ErrorUtilities.VerifyProtocol(accessToken.User == null || string.IsNullOrEmpty(this.ClientPrincipalPrefix) || !accessToken.User.StartsWith(this.ClientPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ResourceOwnerNameLooksLikeClientIdentifier);
- ErrorUtilities.VerifyProtocol(accessToken.ClientIdentifier == null || string.IsNullOrEmpty(this.ResourceOwnerPrincipalPrefix) || !accessToken.ClientIdentifier.StartsWith(this.ResourceOwnerPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ClientIdentifierLooksLikeResourceOwnerName);
-
- string principalUserName = !string.IsNullOrEmpty(accessToken.User)
- ? this.ResourceOwnerPrincipalPrefix + accessToken.User
- : this.ClientPrincipalPrefix + accessToken.ClientIdentifier;
- string[] principalScope = accessToken.Scope != null ? accessToken.Scope.ToArray() : new string[0];
- principal = new OAuthPrincipal(principalUserName, principalScope);
- } else {
- principal = null;
- }
+ public virtual IPrincipal GetPrincipal(HttpRequestBase httpRequestInfo = null) {
+ AccessToken accessToken = this.GetAccessToken(httpRequestInfo);
+
+ // Mitigates attacks on this approach of differentiating clients from resource owners
+ // by checking that a username doesn't look suspiciously engineered to appear like the other type.
+ ErrorUtilities.VerifyProtocol(accessToken.User == null || string.IsNullOrEmpty(this.ClientPrincipalPrefix) || !accessToken.User.StartsWith(this.ClientPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ResourceOwnerNameLooksLikeClientIdentifier);
+ ErrorUtilities.VerifyProtocol(accessToken.ClientIdentifier == null || string.IsNullOrEmpty(this.ResourceOwnerPrincipalPrefix) || !accessToken.ClientIdentifier.StartsWith(this.ResourceOwnerPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ClientIdentifierLooksLikeResourceOwnerName);
+
+ string principalUserName = !string.IsNullOrEmpty(accessToken.User)
+ ? this.ResourceOwnerPrincipalPrefix + accessToken.User
+ : this.ClientPrincipalPrefix + accessToken.ClientIdentifier;
+ string[] principalScope = accessToken.Scope != null ? accessToken.Scope.ToArray() : new string[0];
+ var principal = new OAuthPrincipal(principalUserName, principalScope);
- return result;
+ return principal;
}
/// <summary>
@@ -146,17 +135,20 @@ namespace DotNetOpenAuth.OAuth2 {
/// </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="principal">The principal that contains the user and roles that the access token is authorized for.</param>
/// <returns>
- /// An error to return to the client if access is not authorized; <c>null</c> if access is granted.
+ /// The principal that contains the user and roles that the access token is authorized for. Never <c>null</c>.
/// </returns>
+ /// <exception cref="ProtocolFaultResponseException">
+ /// Thrown when the client is not authorized. This exception should be caught and the
+ /// <see cref="ProtocolFaultResponseException.ErrorResponse"/> message should be returned to the client.
+ /// </exception>
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")]
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Try pattern")]
- public virtual OutgoingWebResponse VerifyAccess(HttpRequestMessageProperty request, Uri requestUri, out IPrincipal principal) {
+ public virtual IPrincipal GetPrincipal(HttpRequestMessageProperty request, Uri requestUri) {
Requires.NotNull(request, "request");
Requires.NotNull(requestUri, "requestUri");
- return this.VerifyAccess(new HttpRequestInfo(request, requestUri), out principal);
+ return this.GetPrincipal(new HttpRequestInfo(request, requestUri));
}
}
}