summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs')
-rw-r--r--src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs b/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs
new file mode 100644
index 0000000..305976a
--- /dev/null
+++ b/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs
@@ -0,0 +1,298 @@
+//-----------------------------------------------------------------------
+// <copyright file="Identifier.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// An Identifier is either a "http" or "https" URI, or an XRI.
+ /// </summary>
+ [Serializable]
+ [ContractVerification(true)]
+ [Pure]
+ [ContractClass(typeof(IdentifierContract))]
+ public abstract class Identifier {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Identifier"/> class.
+ /// </summary>
+ /// <param name="originalString">The original string before any normalization.</param>
+ /// <param name="isDiscoverySecureEndToEnd">Whether the derived class is prepared to guarantee end-to-end discovery
+ /// and initial redirect for authentication is performed using SSL.</param>
+ [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "string", Justification = "Emphasis on string instead of the strong-typed Identifier.")]
+ protected Identifier(string originalString, bool isDiscoverySecureEndToEnd) {
+ this.OriginalString = originalString;
+ this.IsDiscoverySecureEndToEnd = isDiscoverySecureEndToEnd;
+ }
+
+ /// <summary>
+ /// Gets the original string that was normalized to create this Identifier.
+ /// </summary>
+ internal string OriginalString { get; private set; }
+
+ /// <summary>
+ /// Gets the Identifier in the form in which it should be serialized.
+ /// </summary>
+ /// <value>
+ /// For Identifiers that were originally deserialized, this is the exact same
+ /// string that was deserialized. For Identifiers instantiated in some other way, this is
+ /// the normalized form of the string used to instantiate the identifier.
+ /// </value>
+ internal virtual string SerializedString {
+ get { return this.IsDeserializedInstance ? this.OriginalString : this.ToString(); }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether <see cref="Identifier"/> instances are considered equal
+ /// based solely on their string reprsentations.
+ /// </summary>
+ /// <remarks>
+ /// This property serves as a test hook, so that MockIdentifier instances can be considered "equal"
+ /// to UriIdentifier instances.
+ /// </remarks>
+ protected internal static bool EqualityOnStrings { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this Identifier will ensure SSL is
+ /// used throughout the discovery phase and initial redirect of authentication.
+ /// </summary>
+ /// <remarks>
+ /// If this is <c>false</c>, a value of <c>true</c> may be obtained by calling
+ /// <see cref="TryRequireSsl"/>.
+ /// </remarks>
+ protected internal bool IsDiscoverySecureEndToEnd { get; private set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance was initialized from
+ /// deserializing a message.
+ /// </summary>
+ /// <remarks>
+ /// This is interesting because when an Identifier comes from the network,
+ /// we can't normalize it and then expect signatures to still verify.
+ /// But if the Identifier is initialized locally, we can and should normalize it
+ /// before serializing it.
+ /// </remarks>
+ protected bool IsDeserializedInstance { get; private set; }
+
+ /// <summary>
+ /// Converts the string representation of an Identifier to its strong type.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>The particular Identifier instance to represent the value given.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "Not all identifiers are URIs.")]
+ [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "Our named alternate is Parse.")]
+ [DebuggerStepThrough]
+ public static implicit operator Identifier(string identifier) {
+ Contract.Requires<ArgumentException>(identifier == null || identifier.Length > 0);
+ Contract.Ensures((identifier == null) == (Contract.Result<Identifier>() == null));
+
+ if (identifier == null) {
+ return null;
+ }
+ return Parse(identifier);
+ }
+
+ /// <summary>
+ /// Converts a given Uri to a strongly-typed Identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to convert.</param>
+ /// <returns>The result of the conversion.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "We have a Parse function.")]
+ [DebuggerStepThrough]
+ public static implicit operator Identifier(Uri identifier) {
+ Contract.Ensures((identifier == null) == (Contract.Result<Identifier>() == null));
+ if (identifier == null) {
+ return null;
+ }
+
+ return new UriIdentifier(identifier);
+ }
+
+ /// <summary>
+ /// Converts an Identifier to its string representation.
+ /// </summary>
+ /// <param name="identifier">The identifier to convert to a string.</param>
+ /// <returns>The result of the conversion.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "We have a Parse function.")]
+ [DebuggerStepThrough]
+ public static implicit operator string(Identifier identifier) {
+ Contract.Ensures((identifier == null) == (Contract.Result<string>() == null));
+ if (identifier == null) {
+ return null;
+ }
+ return identifier.ToString();
+ }
+
+ /// <summary>
+ /// Parses an identifier string and automatically determines
+ /// whether it is an XRI or URI.
+ /// </summary>
+ /// <param name="identifier">Either a URI or XRI identifier.</param>
+ /// <returns>An <see cref="Identifier"/> instance for the given value.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Some of these identifiers are not properly formatted to be Uris at this stage.")]
+ public static Identifier Parse(string identifier) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(identifier));
+ Contract.Ensures(Contract.Result<Identifier>() != null);
+
+ return Parse(identifier, false);
+ }
+
+ /// <summary>
+ /// Parses an identifier string and automatically determines
+ /// whether it is an XRI or URI.
+ /// </summary>
+ /// <param name="identifier">Either a URI or XRI identifier.</param>
+ /// <param name="serializeExactValue">if set to <c>true</c> this Identifier will serialize exactly as given rather than in its normalized form.</param>
+ /// <returns>
+ /// An <see cref="Identifier"/> instance for the given value.
+ /// </returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Some of these identifiers are not properly formatted to be Uris at this stage.")]
+ public static Identifier Parse(string identifier, bool serializeExactValue) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(identifier));
+ Contract.Ensures(Contract.Result<Identifier>() != null);
+
+ Identifier id;
+ if (XriIdentifier.IsValidXri(identifier)) {
+ id = new XriIdentifier(identifier);
+ } else {
+ id = new UriIdentifier(identifier);
+ }
+
+ id.IsDeserializedInstance = serializeExactValue;
+ return id;
+ }
+
+ /// <summary>
+ /// Attempts to parse a string for an OpenId Identifier.
+ /// </summary>
+ /// <param name="value">The string to be parsed.</param>
+ /// <param name="result">The parsed Identifier form.</param>
+ /// <returns>
+ /// True if the operation was successful. False if the string was not a valid OpenId Identifier.
+ /// </returns>
+ public static bool TryParse(string value, out Identifier result) {
+ if (string.IsNullOrEmpty(value)) {
+ result = null;
+ return false;
+ }
+
+ if (IsValid(value)) {
+ result = Parse(value);
+ return true;
+ } else {
+ result = null;
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Checks the validity of a given string representation of some Identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>
+ /// <c>true</c> if the specified identifier is valid; otherwise, <c>false</c>.
+ /// </returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Some of these identifiers are not properly formatted to be Uris at this stage.")]
+ public static bool IsValid(string identifier) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(identifier));
+ return XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier);
+ }
+
+ /// <summary>
+ /// Tests equality between two <see cref="Identifier"/>s.
+ /// </summary>
+ /// <param name="id1">The first Identifier.</param>
+ /// <param name="id2">The second Identifier.</param>
+ /// <returns>
+ /// <c>true</c> if the two instances should be considered equal; <c>false</c> otherwise.
+ /// </returns>
+ public static bool operator ==(Identifier id1, Identifier id2) {
+ return id1.EqualsNullSafe(id2);
+ }
+
+ /// <summary>
+ /// Tests inequality between two <see cref="Identifier"/>s.
+ /// </summary>
+ /// <param name="id1">The first Identifier.</param>
+ /// <param name="id2">The second Identifier.</param>
+ /// <returns>
+ /// <c>true</c> if the two instances should be considered unequal; <c>false</c> if they are equal.
+ /// </returns>
+ public static bool operator !=(Identifier id1, Identifier id2) {
+ return !id1.EqualsNullSafe(id2);
+ }
+
+ /// <summary>
+ /// Tests equality between two <see cref="Identifier"/>s.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ Debug.Fail("This should be overridden in every derived class.");
+ return base.Equals(obj);
+ }
+
+ /// <summary>
+ /// Gets the hash code for an <see cref="Identifier"/> for storage in a hashtable.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ Debug.Fail("This should be overridden in every derived class.");
+ return base.GetHashCode();
+ }
+
+ /// <summary>
+ /// Reparses the specified identifier in order to be assured that the concrete type that
+ /// implements the identifier is one of the well-known ones.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>Either <see cref="XriIdentifier"/> or <see cref="UriIdentifier"/>.</returns>
+ internal static Identifier Reparse(Identifier identifier) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Ensures(Contract.Result<Identifier>() != null);
+
+ return Parse(identifier, identifier.IsDeserializedInstance);
+ }
+
+ /// <summary>
+ /// Returns an <see cref="Identifier"/> that has no URI fragment.
+ /// Quietly returns the original <see cref="Identifier"/> if it is not
+ /// a <see cref="UriIdentifier"/> or no fragment exists.
+ /// </summary>
+ /// <returns>A new <see cref="Identifier"/> instance if there was a
+ /// fragment to remove, otherwise this same instance..</returns>
+ [Pure]
+ internal abstract Identifier TrimFragment();
+
+ /// <summary>
+ /// Converts a given identifier to its secure equivalent.
+ /// UriIdentifiers originally created with an implied HTTP scheme change to HTTPS.
+ /// Discovery is made to require SSL for the entire resolution process.
+ /// </summary>
+ /// <param name="secureIdentifier">
+ /// The newly created secure identifier.
+ /// If the conversion fails, <paramref name="secureIdentifier"/> retains
+ /// <i>this</i> identifiers identity, but will never discover any endpoints.
+ /// </param>
+ /// <returns>
+ /// True if the secure conversion was successful.
+ /// False if the Identifier was originally created with an explicit HTTP scheme.
+ /// </returns>
+ internal abstract bool TryRequireSsl(out Identifier secureIdentifier);
+ }
+}