using System; using System.Collections.Generic; using System.Collections.Specialized; using DotNetOpenId.Provider; using System.Runtime.Serialization; using System.Security.Permissions; using System.Diagnostics; namespace DotNetOpenId { /// /// A message did not conform to the OpenID protocol, or /// some other processing error occurred. /// [Serializable] public class OpenIdException : Exception, IEncodable { internal IDictionary Query; /// /// An Identifier (claimed or local provider) that was being processed when /// the exception was thrown. /// public Identifier Identifier { get; private set; } internal Protocol Protocol = Protocol.Default; Protocol IEncodable.Protocol { get { return this.Protocol; } } internal IDictionary ExtraArgsToReturn; internal OpenIdException(string message, Identifier identifier, IDictionary query, Exception innerException) : base(message, innerException) { this.Query = query; Identifier = identifier; if (query != null) Protocol = Protocol.Detect(query); if (query != null) { Logger.ErrorFormat("OpenIdException: {0}{1}{2}", message, Environment.NewLine, Util.ToString(query)); } else { Logger.ErrorFormat("OpenIdException: {0}", message); } } internal OpenIdException(string message, Identifier identifier, IDictionary query) : this(message, identifier, query, null) { } internal OpenIdException(string message, Identifier identifier, Exception innerException) : this(message, identifier, null, innerException) { } internal OpenIdException(string message, Identifier identifier) : this(message, identifier, null, null) { } internal OpenIdException(string message, IDictionary query) : this(message, null, query, null) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] internal OpenIdException(string message, Exception innerException) : this(message, null, null, innerException) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] internal OpenIdException(string message) : this(message, null, null, null) { } /// /// Instantiates an based on deserialized data. /// /// /// protected OpenIdException(SerializationInfo info, StreamingContext context) : base(info, context) { Query = (IDictionary)info.GetValue("query", typeof(IDictionary)); Identifier = (Identifier)info.GetValue("Identifier", typeof(Identifier)); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] internal OpenIdException() { } /// /// Serializes the exception details for binary transmission. /// [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("query", Query, typeof(IDictionary)); info.AddValue("Identifier", Identifier, typeof(Identifier)); } internal bool HasReturnTo { get { return Query == null ? false : Query.ContainsKey(Protocol.openid.return_to); } } #region IEncodable Members /// /// Gets whether this exception was generated on an OP as the result of processing a message /// that came directly from the RP. /// /// /// This is useful because it allows us to determine what kind of error reporting we'll send /// in the HTTP response. /// private bool IsDirectMessage { get { Debug.Assert(Query != null, "An OpenId exception should always be provided with the query if it is to be encoded for transmittal to the RP."); if (Query != null) { string mode = Util.GetOptionalArg(Query, Protocol.openid.mode); if (mode != null) { return mode == Protocol.Args.Mode.associate || mode == Protocol.Args.Mode.check_authentication; } } // Unable to figure it out, so we'll default to indirect message. return false; } } EncodingType IEncodable.EncodingType { get { if (IsDirectMessage) return EncodingType.DirectResponse; if (HasReturnTo) return EncodingType.IndirectMessage; Debug.Fail("Somehow we cannot tell whether this is a direct message or indirect message. Did we construct an exception without a Query parameter?"); return EncodingType.None; } } /// /// Fields that should be encoded for processing when this exception /// is thrown by a Provider and the details should be passed to the /// relying party. /// public IDictionary EncodedFields { get { var q = new Dictionary(); if (IsDirectMessage) { q.Add(Protocol.openidnp.mode, Protocol.Args.Mode.error); q.Add(Protocol.openidnp.error, Message); } else { q.Add(Protocol.openid.mode, Protocol.Args.Mode.error); q.Add(Protocol.openid.error, Message); } if (ExtraArgsToReturn != null) { foreach (var pair in ExtraArgsToReturn) { q.Add(pair.Key, pair.Value); } } return q; } } /// /// The URL that the exception details should be forwarded to. /// This is used when a Provider throws an exception that a relying /// party may find helpful in diagnosing the failure. /// public Uri RedirectUrl { get { if (Query == null) return null; return new Uri(Util.GetRequiredArg(Query, Protocol.openid.return_to)); } } #endregion } }