1 namespace DotNetOpenAuth
.OpenId
.RelyingParty
{
3 using System
.Collections
.Generic
;
4 using System
.Diagnostics
;
5 using System
.Globalization
;
8 using DotNetOpenAuth
.Messaging
;
11 /// Represents information discovered about a user-supplied Identifier.
13 [DebuggerDisplay("ClaimedIdentifier: {ClaimedIdentifier}, ProviderEndpoint: {ProviderEndpoint}, OpenId: {Protocol.Version}")]
14 internal class ServiceEndpoint
: IXrdsProviderEndpoint
{
15 private string friendlyIdentifierForDisplay
;
16 private Protocol protocol
;
17 private int? uriPriority
;
18 private int? servicePriority
;
20 private ServiceEndpoint(Identifier claimedIdentifier
, Identifier userSuppliedIdentifier
, Uri providerEndpoint
, Identifier providerLocalIdentifier
, string[] providerSupportedServiceTypeUris
, int? servicePriority
, int? uriPriority
) {
21 ErrorUtilities
.VerifyArgumentNotNull(claimedIdentifier
, "claimedIdentifier");
22 ErrorUtilities
.VerifyArgumentNotNull(providerEndpoint
, "providerEndpoint");
23 ErrorUtilities
.VerifyArgumentNotNull(providerSupportedServiceTypeUris
, "providerSupportedServiceTypeUris");
24 this.ClaimedIdentifier
= claimedIdentifier
;
25 this.UserSuppliedIdentifier
= userSuppliedIdentifier
;
26 this.ProviderEndpoint
= providerEndpoint
;
27 this.ProviderLocalIdentifier
= providerLocalIdentifier
?? claimedIdentifier
;
28 this.ProviderSupportedServiceTypeUris
= providerSupportedServiceTypeUris
;
29 this.servicePriority
= servicePriority
;
30 this.uriPriority
= uriPriority
;
34 /// Used for deserializing <see cref="ServiceEndpoint"/> from authentication responses.
36 private ServiceEndpoint(Identifier claimedIdentifier
, Identifier userSuppliedIdentifier
, Uri providerEndpoint
, Identifier providerLocalIdentifier
, Protocol protocol
) {
37 this.ClaimedIdentifier
= claimedIdentifier
;
38 this.UserSuppliedIdentifier
= userSuppliedIdentifier
;
39 this.ProviderEndpoint
= providerEndpoint
;
40 this.ProviderLocalIdentifier
= providerLocalIdentifier
?? claimedIdentifier
;
41 this.protocol
= protocol
;
44 Uri IProviderEndpoint
.Uri { get { return this.ProviderEndpoint; }
}
47 /// Gets the URL which accepts OpenID Authentication protocol messages.
50 /// Obtained by performing discovery on the User-Supplied Identifier.
51 /// This value MUST be an absolute HTTP or HTTPS URL.
53 public Uri ProviderEndpoint { get; private set; }
56 /// Returns true if the <see cref="ProviderEndpoint"/> is using an encrypted channel.
60 /// An Identifier for an OpenID Provider.
62 public Identifier ProviderIdentifier { get; private set; }
65 /// Gets the Identifier that was presented by the end user to the Relying Party,
66 /// or selected by the user at the OpenID Provider.
67 /// During the initiation phase of the protocol, an end user may enter
68 /// either their own Identifier or an OP Identifier. If an OP Identifier
69 /// is used, the OP may then assist the end user in selecting an Identifier
70 /// to share with the Relying Party.
72 public Identifier UserSuppliedIdentifier { get; private set; }
75 /// Gets the Identifier that the end user claims to own.
77 public Identifier ClaimedIdentifier { get; private set; }
80 /// Gets an alternate Identifier for an end user that is local to a
81 /// particular OP and thus not necessarily under the end user's
84 public Identifier ProviderLocalIdentifier { get; private set; }
87 /// Gets the value for the <see cref="IAuthenticationResponse.FriendlyIdentifierForDisplay"/> property.
89 public string FriendlyIdentifierForDisplay
{
91 if (this.friendlyIdentifierForDisplay
== null) {
92 XriIdentifier xri
= this.ClaimedIdentifier
as XriIdentifier
;
93 UriIdentifier uri
= this.ClaimedIdentifier
as UriIdentifier
;
95 if (UserSuppliedIdentifier
== null || String
.Equals(UserSuppliedIdentifier
, ClaimedIdentifier
, StringComparison
.OrdinalIgnoreCase
)) {
96 this.friendlyIdentifierForDisplay
= this.ClaimedIdentifier
;
98 this.friendlyIdentifierForDisplay
= this.UserSuppliedIdentifier
;
100 } else if (uri
!= null) {
101 if (uri
!= this.Protocol
.ClaimedIdentifierForOPIdentifier
) {
102 string displayUri
= uri
.Uri
.Authority
+ uri
.Uri
.PathAndQuery
;
103 displayUri
= displayUri
.TrimEnd('/');
104 // Multi-byte unicode characters get encoded by the Uri class for transit.
105 // Since this is for display purposes, we want to reverse this and display a readable
106 // representation of these foreign characters.
107 friendlyIdentifierForDisplay
= Uri
.UnescapeDataString(displayUri
);
110 Debug
.Fail("Doh! We never should have reached here.");
111 this.friendlyIdentifierForDisplay
= this.ClaimedIdentifier
;
114 return this.friendlyIdentifierForDisplay
;
119 /// Gets the list of services available at this OP Endpoint for the
120 /// claimed Identifier. May be null.
122 public string[] ProviderSupportedServiceTypeUris { get; private set; }
125 /// Gets the OpenID protocol used by the Provider.
127 public Protocol Protocol
{
129 if (this.protocol
== null) {
130 this.protocol
= Protocol
.Detect(ProviderSupportedServiceTypeUris
);
132 if (this.protocol
!= null) {
133 return this.protocol
;
135 throw new InvalidOperationException("Unable to determine the version of OpenID the Provider supports.");
139 #region IXrdsProviderEndpoint Members
142 /// Gets the priority associated with this service that may have been given
143 /// in the XRDS document.
145 int? IXrdsProviderEndpoint
.ServicePriority
{
146 get { return this.servicePriority; }
150 /// Gets the priority associated with the service endpoint URL.
152 int? IXrdsProviderEndpoint
.UriPriority
{
153 get { return this.uriPriority; }
158 internal bool IsSecure
{
159 get { return string.Equals(this.ProviderEndpoint.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); }
162 public static bool operator ==(ServiceEndpoint se1
, ServiceEndpoint se2
) {
163 return se1
.EqualsNullSafe(se2
);
166 public static bool operator !=(ServiceEndpoint se1
, ServiceEndpoint se2
) {
167 return !(se1
== se2
);
170 public bool IsTypeUriPresent(string typeUri
) {
171 return IsExtensionSupported(typeUri
);
174 public bool IsExtensionSupported(string extensionUri
) {
175 if (this.ProviderSupportedServiceTypeUris
== null) {
176 throw new InvalidOperationException("Cannot lookup extension support on a rehydrated ServiceEndpoint.");
178 return Array
.IndexOf(this.ProviderSupportedServiceTypeUris
, extensionUri
) >= 0;
181 ////public bool IsExtensionSupported(IExtension extension) {
182 //// if (extension == null) throw new ArgumentNullException("extension");
184 //// // Consider the primary case.
185 //// if (IsExtensionSupported(extension.TypeUri)) {
188 //// // Consider the secondary cases.
189 //// if (extension.AdditionalSupportedTypeUris != null) {
190 //// foreach (string extensionTypeUri in extension.AdditionalSupportedTypeUris) {
191 //// if (IsExtensionSupported(extensionTypeUri)) {
199 ////public bool IsExtensionSupported<T>() where T : Extensions.IExtension, new() {
200 //// T extension = new T();
201 //// return IsExtensionSupported(extension);
204 ////public bool IsExtensionSupported(Type extensionType) {
205 //// if (extensionType == null) throw new ArgumentNullException("extensionType");
206 //// if (!typeof(Extensions.IExtension).IsAssignableFrom(extensionType))
207 //// throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
208 //// Strings.TypeMustImplementX, typeof(Extensions.IExtension).FullName),
209 //// "extensionType");
210 //// var extension = (Extensions.IExtension)Activator.CreateInstance(extensionType);
211 //// return IsExtensionSupported(extension);
214 Version IProviderEndpoint
.Version { get { return Protocol.Version; }
}
216 public override bool Equals(object obj
) {
217 ServiceEndpoint other
= obj
as ServiceEndpoint
;
221 // We specifically do not check our ProviderSupportedServiceTypeUris array
222 // or the priority field
223 // as that is not persisted in our tokens, and it is not part of the
224 // important assertion validation that is part of the spec.
226 this.ClaimedIdentifier
== other
.ClaimedIdentifier
&&
227 this.ProviderEndpoint
== other
.ProviderEndpoint
&&
228 this.ProviderLocalIdentifier
== other
.ProviderLocalIdentifier
&&
229 this.Protocol
== other
.Protocol
;
232 public override int GetHashCode() {
233 return ClaimedIdentifier
.GetHashCode();
236 public override string ToString() {
237 StringBuilder builder
= new StringBuilder();
238 builder
.AppendLine("ClaimedIdentifier: " + ClaimedIdentifier
);
239 builder
.AppendLine("ProviderLocalIdentifier: " + ProviderLocalIdentifier
);
240 builder
.AppendLine("ProviderEndpoint: " + ProviderEndpoint
.AbsoluteUri
);
241 builder
.AppendLine("OpenID version: " + Protocol
.Version
);
242 builder
.AppendLine("Service Type URIs:");
243 if (ProviderSupportedServiceTypeUris
!= null) {
244 foreach (string serviceTypeUri
in ProviderSupportedServiceTypeUris
) {
245 builder
.Append("\t");
246 // TODO: uncomment when we support extensions
247 ////var matchingExtension = Util.FirstOrDefault(ExtensionManager.RequestExtensions, ext => ext.Key.TypeUri == serviceTypeUri);
248 ////if (matchingExtension.Key != null) {
249 //// builder.AppendLine(string.Format(CultureInfo.CurrentCulture, "{0} ({1})", serviceTypeUri, matchingExtension.Value));
251 //// builder.AppendLine(serviceTypeUri);
255 builder
.AppendLine("\t(unavailable)");
257 builder
.Length
-= Environment
.NewLine
.Length
; // trim last newline
258 return builder
.ToString();
262 /// Reads previously discovered information about an endpoint
263 /// from a solicited authentication assertion for validation.
266 /// A <see cref="ServiceEndpoint"/> object that has everything
267 /// except the <see cref="ProviderSupportedServiceTypeUris"/>
270 internal static ServiceEndpoint
Deserialize(TextReader reader
) {
271 var claimedIdentifier
= Identifier
.Parse(reader
.ReadLine());
272 var providerLocalIdentifier
= Identifier
.Parse(reader
.ReadLine());
273 string userSuppliedIdentifier
= reader
.ReadLine();
274 if (userSuppliedIdentifier
.Length
== 0) {
275 userSuppliedIdentifier
= null;
277 var providerEndpoint
= new Uri(reader
.ReadLine());
278 var protocol
= Protocol
.FindBestVersion(p
=> p
.Version
, new[] { new Version(reader.ReadLine()) }
);
279 return new ServiceEndpoint(claimedIdentifier
, userSuppliedIdentifier
, providerEndpoint
, providerLocalIdentifier
, protocol
);
282 internal static ServiceEndpoint
CreateForProviderIdentifier(
283 Identifier providerIdentifier
, Uri providerEndpoint
,
284 string[] providerSupportedServiceTypeUris
, int? servicePriority
, int? uriPriority
) {
285 Protocol protocol
= Protocol
.Detect(providerSupportedServiceTypeUris
);
287 return new ServiceEndpoint(protocol
.ClaimedIdentifierForOPIdentifier
, providerIdentifier
,
288 providerEndpoint
, protocol
.ClaimedIdentifierForOPIdentifier
,
289 providerSupportedServiceTypeUris
, servicePriority
, uriPriority
);
292 internal static ServiceEndpoint
CreateForClaimedIdentifier(Identifier claimedIdentifier
, Identifier providerLocalIdentifier
, Uri providerEndpoint
, string[] providerSupportedServiceTypeUris
, int? servicePriority
, int? uriPriority
) {
293 return CreateForClaimedIdentifier(claimedIdentifier
, null, providerLocalIdentifier
, providerEndpoint
, providerSupportedServiceTypeUris
, servicePriority
, uriPriority
);
296 internal static ServiceEndpoint
CreateForClaimedIdentifier(Identifier claimedIdentifier
, Identifier userSuppliedIdentifier
, Identifier providerLocalIdentifier
, Uri providerEndpoint
, string[] providerSupportedServiceTypeUris
, int? servicePriority
, int? uriPriority
) {
297 return new ServiceEndpoint(claimedIdentifier
, userSuppliedIdentifier
, providerEndpoint
, providerLocalIdentifier
, providerSupportedServiceTypeUris
, servicePriority
, uriPriority
);
301 /// Saves the discovered information about this endpoint
302 /// for later comparison to validate assertions.
304 internal void Serialize(TextWriter writer
) {
305 writer
.WriteLine(this.ClaimedIdentifier
);
306 writer
.WriteLine(this.ProviderLocalIdentifier
);
307 writer
.WriteLine(this.UserSuppliedIdentifier
);
308 writer
.WriteLine(this.ProviderEndpoint
);
309 writer
.WriteLine(this.Protocol
.Version
);
310 // No reason to serialize priority. We only needed priority to decide whether to use this endpoint.