//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Provider.Extensions {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.Messages;
using Validation;
///
/// A set of methods designed to assist in improving interop across different
/// OpenID implementations and their extensions.
///
internal static class ExtensionsInteropHelper {
///
/// Transforms an AX attribute type URI from the axschema.org format into a given format.
///
/// The ax schema org format type URI.
/// The target format. Only one flag should be set.
/// The AX attribute type URI in the target format.
internal static string TransformAXFormatTestHook(string axSchemaOrgFormatTypeUri, AXAttributeFormats targetFormat) {
return OpenIdExtensionsInteropHelper.TransformAXFormat(axSchemaOrgFormatTypeUri, targetFormat);
}
///
/// Looks for Simple Registration and Attribute Exchange (all known formats)
/// request extensions and returns them as a Simple Registration extension,
/// and adds the new extension to the original request message if it was absent.
///
/// The authentication request.
///
/// The Simple Registration request if found,
/// or a fabricated one based on the Attribute Exchange extension if found,
/// or null if no attribute extension request is found.
internal static ClaimsRequest UnifyExtensionsAsSreg(this Provider.IHostProcessedRequest request) {
Requires.NotNull(request, "request");
var req = (Provider.HostProcessedRequest)request;
var sreg = req.GetExtension();
if (sreg != null) {
return sreg;
}
var ax = req.GetExtension();
if (ax != null) {
sreg = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.TypeUris.Standard);
sreg.Synthesized = true;
((IProtocolMessageWithExtensions)req.RequestMessage).Extensions.Add(sreg);
sreg.BirthDate = GetDemandLevelFor(ax, WellKnownAttributes.BirthDate.WholeBirthDate);
sreg.Country = GetDemandLevelFor(ax, WellKnownAttributes.Contact.HomeAddress.Country);
sreg.Email = GetDemandLevelFor(ax, WellKnownAttributes.Contact.Email);
sreg.FullName = GetDemandLevelFor(ax, WellKnownAttributes.Name.FullName);
sreg.Gender = GetDemandLevelFor(ax, WellKnownAttributes.Person.Gender);
sreg.Language = GetDemandLevelFor(ax, WellKnownAttributes.Preferences.Language);
sreg.Nickname = GetDemandLevelFor(ax, WellKnownAttributes.Name.Alias);
sreg.PostalCode = GetDemandLevelFor(ax, WellKnownAttributes.Contact.HomeAddress.PostalCode);
sreg.TimeZone = GetDemandLevelFor(ax, WellKnownAttributes.Preferences.TimeZone);
}
return sreg;
}
///
/// Converts the Simple Registration extension response to whatever format the original
/// attribute request extension came in.
///
/// The authentication request with the response extensions already added.
/// The cancellation token.
///
/// A task that completes with the asynchronous operation.
///
///
/// If the original attribute request came in as AX, the Simple Registration extension is converted
/// to an AX response and then the Simple Registration extension is removed from the response.
///
internal static async Task ConvertSregToMatchRequestAsync(this Provider.IHostProcessedRequest request, CancellationToken cancellationToken) {
var req = (Provider.HostProcessedRequest)request;
var protocolMessage = await req.GetResponseAsync(cancellationToken);
var response = protocolMessage as IProtocolMessageWithExtensions; // negative responses don't support extensions.
var sregRequest = request.GetExtension();
if (sregRequest != null && response != null) {
if (sregRequest.Synthesized) {
var axRequest = request.GetExtension();
ErrorUtilities.VerifyInternal(axRequest != null, "How do we have a synthesized Sreg request without an AX request?");
var sregResponse = response.Extensions.OfType().SingleOrDefault();
if (sregResponse == null) {
// No Sreg response to copy from.
return;
}
// Remove the sreg response since the RP didn't ask for it.
response.Extensions.Remove(sregResponse);
AXAttributeFormats format = OpenIdExtensionsInteropHelper.DetectAXFormat(axRequest.Attributes.Select(att => att.TypeUri));
if (format == AXAttributeFormats.None) {
// No recognized AX attributes were requested.
return;
}
var axResponse = response.Extensions.OfType().SingleOrDefault();
if (axResponse == null) {
axResponse = new FetchResponse();
response.Extensions.Add(axResponse);
}
AddAXAttributeValue(axResponse, WellKnownAttributes.BirthDate.WholeBirthDate, format, sregResponse.BirthDateRaw);
AddAXAttributeValue(axResponse, WellKnownAttributes.Contact.HomeAddress.Country, format, sregResponse.Country);
AddAXAttributeValue(axResponse, WellKnownAttributes.Contact.HomeAddress.PostalCode, format, sregResponse.PostalCode);
AddAXAttributeValue(axResponse, WellKnownAttributes.Contact.Email, format, sregResponse.Email);
AddAXAttributeValue(axResponse, WellKnownAttributes.Name.FullName, format, sregResponse.FullName);
AddAXAttributeValue(axResponse, WellKnownAttributes.Name.Alias, format, sregResponse.Nickname);
AddAXAttributeValue(axResponse, WellKnownAttributes.Preferences.TimeZone, format, sregResponse.TimeZone);
AddAXAttributeValue(axResponse, WellKnownAttributes.Preferences.Language, format, sregResponse.Language);
if (sregResponse.Gender.HasValue) {
AddAXAttributeValue(axResponse, WellKnownAttributes.Person.Gender, format, OpenIdExtensionsInteropHelper.GenderEncoder.Encode(sregResponse.Gender));
}
}
}
}
///
/// Adds the AX attribute value to the response if it is non-empty.
///
/// The AX Fetch response to add the attribute value to.
/// The attribute type URI in axschema.org format.
/// The target format of the actual attribute to write out.
/// The value of the attribute.
private static void AddAXAttributeValue(FetchResponse ax, string typeUri, AXAttributeFormats format, string value) {
if (!string.IsNullOrEmpty(value)) {
string targetTypeUri = OpenIdExtensionsInteropHelper.TransformAXFormat(typeUri, format);
if (!ax.Attributes.Contains(targetTypeUri)) {
ax.Attributes.Add(targetTypeUri, value);
}
}
}
///
/// Gets the demand level for an AX attribute.
///
/// The AX fetch request to search for the attribute.
/// The type URI of the attribute in axschema.org format.
/// The demand level for the attribute.
private static DemandLevel GetDemandLevelFor(FetchRequest ax, string typeUri) {
Requires.NotNull(ax, "ax");
Requires.NotNullOrEmpty(typeUri, "typeUri");
foreach (AXAttributeFormats format in OpenIdExtensionsInteropHelper.ForEachFormat(AXAttributeFormats.All)) {
string typeUriInFormat = OpenIdExtensionsInteropHelper.TransformAXFormat(typeUri, format);
if (ax.Attributes.Contains(typeUriInFormat)) {
return ax.Attributes[typeUriInFormat].IsRequired ? DemandLevel.Require : DemandLevel.Request;
}
}
return DemandLevel.NoRequest;
}
}
}