//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
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.Messaging.Reflection;
///
/// An Identifier is either a "http" or "https" URI, or an XRI.
///
[Serializable]
[ContractVerification(true)]
[Pure]
[ContractClass(typeof(IdentifierContract))]
public abstract class Identifier {
///
/// Initializes static members of the class.
///
static Identifier() {
Func safeIdentifier = str => {
Contract.Assume(str != null);
ErrorUtilities.VerifyFormat(str.Length > 0, MessagingStrings.NonEmptyStringExpected);
return Identifier.Parse(str, true);
};
MessagePart.Map(id => id.SerializedString, id => id.OriginalString, safeIdentifier);
}
///
/// Initializes a new instance of the class.
///
/// The original string before any normalization.
/// Whether the derived class is prepared to guarantee end-to-end discovery
/// and initial redirect for authentication is performed using SSL.
[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;
}
///
/// Gets the original string that was normalized to create this Identifier.
///
internal string OriginalString { get; private set; }
///
/// Gets the Identifier in the form in which it should be serialized.
///
///
/// 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.
///
internal virtual string SerializedString {
get { return this.IsDeserializedInstance ? this.OriginalString : this.ToString(); }
}
///
/// Gets or sets a value indicating whether instances are considered equal
/// based solely on their string reprsentations.
///
///
/// This property serves as a test hook, so that MockIdentifier instances can be considered "equal"
/// to UriIdentifier instances.
///
protected internal static bool EqualityOnStrings { get; set; }
///
/// Gets a value indicating whether this Identifier will ensure SSL is
/// used throughout the discovery phase and initial redirect of authentication.
///
///
/// If this is false, a value of true may be obtained by calling
/// .
///
protected internal bool IsDiscoverySecureEndToEnd { get; private set; }
///
/// Gets a value indicating whether this instance was initialized from
/// deserializing a message.
///
///
/// 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.
///
protected bool IsDeserializedInstance { get; private set; }
///
/// Converts the string representation of an Identifier to its strong type.
///
/// The identifier.
/// The particular Identifier instance to represent the value given.
[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) {
Requires.True(identifier == null || identifier.Length > 0, "identifier");
Contract.Ensures((identifier == null) == (Contract.Result() == null));
if (identifier == null) {
return null;
}
return Parse(identifier);
}
///
/// Converts a given Uri to a strongly-typed Identifier.
///
/// The identifier to convert.
/// The result of the conversion.
[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() == null));
if (identifier == null) {
return null;
}
return new UriIdentifier(identifier);
}
///
/// Converts an Identifier to its string representation.
///
/// The identifier to convert to a string.
/// The result of the conversion.
[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() == null));
if (identifier == null) {
return null;
}
return identifier.ToString();
}
///
/// Parses an identifier string and automatically determines
/// whether it is an XRI or URI.
///
/// Either a URI or XRI identifier.
/// An instance for the given value.
[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) {
Requires.NotNullOrEmpty(identifier, "identifier");
Contract.Ensures(Contract.Result() != null);
return Parse(identifier, false);
}
///
/// Parses an identifier string and automatically determines
/// whether it is an XRI or URI.
///
/// Either a URI or XRI identifier.
/// if set to true this Identifier will serialize exactly as given rather than in its normalized form.
///
/// An instance for the given value.
///
[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) {
Requires.NotNullOrEmpty(identifier, "identifier");
Contract.Ensures(Contract.Result() != null);
Identifier id;
if (XriIdentifier.IsValidXri(identifier)) {
id = new XriIdentifier(identifier);
} else {
id = new UriIdentifier(identifier);
}
id.IsDeserializedInstance = serializeExactValue;
return id;
}
///
/// Attempts to parse a string for an OpenId Identifier.
///
/// The string to be parsed.
/// The parsed Identifier form.
///
/// True if the operation was successful. False if the string was not a valid OpenId Identifier.
///
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;
}
}
///
/// Checks the validity of a given string representation of some Identifier.
///
/// The identifier.
///
/// true if the specified identifier is valid; otherwise, false.
///
[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) {
Requires.NotNullOrEmpty(identifier, "identifier");
return XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier);
}
///
/// Tests equality between two s.
///
/// The first Identifier.
/// The second Identifier.
///
/// true if the two instances should be considered equal; false otherwise.
///
public static bool operator ==(Identifier id1, Identifier id2) {
return id1.EqualsNullSafe(id2);
}
///
/// Tests inequality between two s.
///
/// The first Identifier.
/// The second Identifier.
///
/// true if the two instances should be considered unequal; false if they are equal.
///
public static bool operator !=(Identifier id1, Identifier id2) {
return !id1.EqualsNullSafe(id2);
}
///
/// Tests equality between two s.
///
/// The to compare with the current .
///
/// true if the specified is equal to the current ; otherwise, false.
///
///
/// The parameter is null.
///
public override bool Equals(object obj) {
Debug.Fail("This should be overridden in every derived class.");
return base.Equals(obj);
}
///
/// Gets the hash code for an for storage in a hashtable.
///
///
/// A hash code for the current .
///
public override int GetHashCode() {
Debug.Fail("This should be overridden in every derived class.");
return base.GetHashCode();
}
///
/// Reparses the specified identifier in order to be assured that the concrete type that
/// implements the identifier is one of the well-known ones.
///
/// The identifier.
/// Either or .
internal static Identifier Reparse(Identifier identifier) {
Requires.NotNull(identifier, "identifier");
Contract.Ensures(Contract.Result() != null);
return Parse(identifier, identifier.IsDeserializedInstance);
}
///
/// Returns an that has no URI fragment.
/// Quietly returns the original if it is not
/// a or no fragment exists.
///
/// A new instance if there was a
/// fragment to remove, otherwise this same instance..
[Pure]
internal abstract Identifier TrimFragment();
///
/// 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.
///
///
/// The newly created secure identifier.
/// If the conversion fails, retains
/// this identifiers identity, but will never discover any endpoints.
///
///
/// True if the secure conversion was successful.
/// False if the Identifier was originally created with an explicit HTTP scheme.
///
internal abstract bool TryRequireSsl(out Identifier secureIdentifier);
}
}