summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2010-01-18 20:10:18 -0800
committerAndrew Arnott <andrewarnott@gmail.com>2010-01-18 20:10:18 -0800
commit47ba4e9c49be9479b7c3d30613cab982ecda042d (patch)
tree07a2a747a70e13d80aedd41294a0a3e94f60b192 /src
parent1f79fcb88004d86372b7392f51b914af826f1b9d (diff)
parentcdb850c6cf90381e6db75365272b7d65ef5fe359 (diff)
downloadDotNetOpenAuth-47ba4e9c49be9479b7c3d30613cab982ecda042d.zip
DotNetOpenAuth-47ba4e9c49be9479b7c3d30613cab982ecda042d.tar.gz
DotNetOpenAuth-47ba4e9c49be9479b7c3d30613cab982ecda042d.tar.bz2
Merge branch 'master' into simpleauth
Conflicts: src/DotNetOpenAuth.vsmdi src/DotNetOpenAuth/DotNetOpenAuth.csproj
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs62
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs47
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs52
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs30
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CompareFiles.cs112
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs102
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs95
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs66
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs63
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj129
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln30
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs486
-rw-r--r--src/DotNetOpenAuth.BuildTasks/FilterItems.cs24
-rw-r--r--src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs71
-rw-r--r--src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs52
-rw-r--r--src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs121
-rw-r--r--src/DotNetOpenAuth.BuildTasks/JsPack.cs75
-rw-r--r--src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs103
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ParseMaster.cs250
-rw-r--r--src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs36
-rw-r--r--src/DotNetOpenAuth.BuildTasks/Publicize.cs97
-rw-r--r--src/DotNetOpenAuth.BuildTasks/Purge.cs88
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs56
-rw-r--r--src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs33
-rw-r--r--src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs45
-rw-r--r--src/DotNetOpenAuth.BuildTasks/SnToolTask.cs37
-rw-r--r--src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs117
-rw-r--r--src/DotNetOpenAuth.BuildTasks/TaskStrings.resx138
-rw-r--r--src/DotNetOpenAuth.BuildTasks/Trim.cs79
-rw-r--r--src/DotNetOpenAuth.Test/App.config6
-rw-r--r--src/DotNetOpenAuth.Test/AssemblyTesting.cs7
-rw-r--r--src/DotNetOpenAuth.Test/CoordinatorBase.cs6
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj67
-rw-r--r--src/DotNetOpenAuth.Test/LocalizationTests.cs28
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs1
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs1
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs12
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs56
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs76
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs108
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs36
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs6
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs1
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs7
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs7
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs5
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs4
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs29
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs21
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs47
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/MockRealm.cs6
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs4
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestMessageFactory.cs3
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestWebRequestHandler.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs14
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs6
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs8
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs12
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs5
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs5
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml13
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs286
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs394
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs9
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs21
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs7
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs31
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs20
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs10
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs50
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs45
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs199
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs49
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs34
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs195
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs244
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs378
-rw-r--r--src/DotNetOpenAuth.Test/Properties/AssemblyInfo.cs2
-rw-r--r--src/DotNetOpenAuth.Test/TestBase.cs13
-rw-r--r--src/DotNetOpenAuth.sln79
-rw-r--r--src/DotNetOpenAuth.vsmdi1022
-rw-r--r--src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs4
-rw-r--r--src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverterContract.cs30
-rw-r--r--src/DotNetOpenAuth/ComponentModel/UriConverter.cs8
-rw-r--r--src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs3
-rw-r--r--src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd635
-rw-r--r--src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs14
-rw-r--r--src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs2
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs15
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdElement.cs17
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs17
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs34
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs19
-rw-r--r--src/DotNetOpenAuth/Configuration/ReportingElement.cs139
-rw-r--r--src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs8
-rw-r--r--src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs13
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj131
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.icobin0 -> 1150 bytes
-rw-r--r--src/DotNetOpenAuth/GlobalSuppressions.cs13
-rw-r--r--src/DotNetOpenAuth/InfoCard/ClaimType.cs4
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardImage.cs2
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs166
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardStrings.sr.resx135
-rw-r--r--src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs3
-rw-r--r--src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs11
-rw-r--r--src/DotNetOpenAuth/InfoCard/SupportingScript.js40
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/Token.cs42
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs6
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs19
-rw-r--r--src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs7
-rw-r--r--src/DotNetOpenAuth/Logger.cs18
-rw-r--r--src/DotNetOpenAuth/Loggers/ILog.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs9
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs7
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs6
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs12
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs239
-rw-r--r--src/DotNetOpenAuth/Messaging/ChannelContract.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/ChannelEventArgs.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/EmptyDictionary.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/EmptyList.cs5
-rw-r--r--src/DotNetOpenAuth/Messaging/EnumerableCache.cs13
-rw-r--r--src/DotNetOpenAuth/Messaging/ErrorUtilities.cs76
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs20
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs148
-rw-r--r--src/DotNetOpenAuth/Messaging/IChannelBindingElement.cs8
-rw-r--r--src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs122
-rw-r--r--src/DotNetOpenAuth/Messaging/IMessageFactory.cs45
-rw-r--r--src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs152
-rw-r--r--src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs100
-rw-r--r--src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs13
-rw-r--r--src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs54
-rw-r--r--src/DotNetOpenAuth/Messaging/KeyedCollectionDelegate.cs5
-rw-r--r--src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs13
-rw-r--r--src/DotNetOpenAuth/Messaging/MessageSerializer.cs30
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs29
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.resx11
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.sr.resx294
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs199
-rw-r--r--src/DotNetOpenAuth/Messaging/MultipartPostPart.cs206
-rw-r--r--src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs6
-rw-r--r--src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs24
-rw-r--r--src/DotNetOpenAuth/Messaging/OutgoingWebResponseActionResult.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/ProtocolException.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs43
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs37
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs22
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs34
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs68
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs8
-rw-r--r--src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs12
-rw-r--r--src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs44
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs1
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs206
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/ITokenManager.cs121
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs74
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs7
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthHttpMethodBindingElement.cs12
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs3
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs9
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/PlaintextSigningBindingElement.cs7
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs7
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs39
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs3
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs22
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs21
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs3
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs57
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs27
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs11
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs5
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs29
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.resx3
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.sr.resx162
-rw-r--r--src/DotNetOpenAuth/OAuth/Protocol.cs8
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProvider.cs115
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/WebConsumer.cs14
-rw-r--r--src/DotNetOpenAuth/OpenId/Association.cs73
-rw-r--r--src/DotNetOpenAuth/OpenId/AssociationContract.cs65
-rw-r--r--src/DotNetOpenAuth/OpenId/Associations.cs26
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs54
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx18
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.sr.resx123
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs285
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs32
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs26
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs18
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs19
-rw-r--r--src/DotNetOpenAuth/OpenId/DiffieHellman/DiffieHellmanManaged.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/DiffieHellmanUtilities.cs21
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs24
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs90
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs38
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PapeUtilities.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs86
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs50
-rw-r--r--src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs504
-rw-r--r--src/DotNetOpenAuth/OpenId/IAssociationStore.cs99
-rw-r--r--src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs61
-rw-r--r--src/DotNetOpenAuth/OpenId/Identifier.cs33
-rw-r--r--src/DotNetOpenAuth/OpenId/IdentifierContract.cs15
-rw-r--r--src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs (renamed from src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs)394
-rw-r--r--src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Interop/ClaimsResponseShim.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs11
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs33
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponseContract.cs29
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs108
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs19
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs38
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.resx16
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.sr.resx340
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs14
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs103
-rw-r--r--src/DotNetOpenAuth/OpenId/Protocol.cs36
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs20
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs28
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs18
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs261
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs39
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs70
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IRequest.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs154
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs15
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs46
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/Request.cs18
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs106
-rw-r--r--src/DotNetOpenAuth/OpenId/Realm.cs63
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs33
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs200
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/Controls.cd112
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs52
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs54
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs111
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs329
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs83
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs59
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/ISetupRequiredAuthenticationResponse.cs31
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs38
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs41
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs1071
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.css49
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js875
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs16
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdEventArgs.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs320
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs20
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cd1
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs162
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs563
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js793
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs679
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js34
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs394
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.css109
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js192
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs894
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs85
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs40
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshot.cs (renamed from src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs)65
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PrivateSecretManager.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButton.cs46
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButtonContract.cs46
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/SelectorInfoCardButton.cs102
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs70
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs98
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs51
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs14
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/SecuritySettings.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs138
-rw-r--r--src/DotNetOpenAuth/OpenId/UriIdentifier.cs109
-rw-r--r--src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs108
-rw-r--r--src/DotNetOpenAuth/OpenId/XriIdentifier.cs98
-rw-r--r--src/DotNetOpenAuth/Properties/AssemblyInfo.cs6
-rw-r--r--src/DotNetOpenAuth/Reporting.cs847
-rw-r--r--src/DotNetOpenAuth/Strings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth/Strings.resx5
-rw-r--r--src/DotNetOpenAuth/Strings.sr.resx126
-rw-r--r--src/DotNetOpenAuth/UriUtil.cs11
-rw-r--r--src/DotNetOpenAuth/Util.cs13
-rw-r--r--src/DotNetOpenAuth/Xrds/TypeElement.cs4
-rw-r--r--src/DotNetOpenAuth/Xrds/XrdElement.cs9
-rw-r--r--src/DotNetOpenAuth/Xrds/XrdsDocument.cs12
-rw-r--r--src/DotNetOpenAuth/Xrds/XrdsNode.cs15
-rw-r--r--src/DotNetOpenAuth/Xrds/XrdsStrings.sr.resx132
-rw-r--r--src/DotNetOpenAuth/XrdsPublisher.cs7
-rw-r--r--src/DotNetOpenAuth/Yadis/HtmlParser.cs4
-rw-r--r--src/DotNetOpenAuth/Yadis/Yadis.cs10
-rw-r--r--src/official-build-key.pubbin0 -> 160 bytes
-rw-r--r--src/version.txt2
340 files changed, 18598 insertions, 6096 deletions
diff --git a/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs b/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs
new file mode 100644
index 0000000..30fa284
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------
+// <copyright file="AddProjectItems.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.BuildEngine;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+ using System.Collections;
+
+ public class AddProjectItems : Task {
+ /// <summary>
+ /// Gets or sets the projects to add items to.
+ /// </summary>
+ /// <value>The projects.</value>
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+
+ /// <summary>
+ /// Gets or sets the items to add to each project.
+ /// </summary>
+ /// <value>The items.</value>
+ /// <remarks>
+ /// Use the metadata "ItemType" on each item to specify the item type to use for the new
+ /// project item. If the metadata is absent, "None" is used as the item type.
+ /// </remarks>
+ [Required]
+ public ITaskItem[] Items { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ foreach (var projectTaskItem in this.Projects) {
+ var project = new Project();
+ project.Load(projectTaskItem.ItemSpec);
+
+ foreach (var projectItem in this.Items) {
+ string itemType = projectItem.GetMetadata("ItemType");
+ if (string.IsNullOrEmpty(itemType)) {
+ itemType = "None";
+ }
+ BuildItem newItem = project.AddNewItem(itemType, projectItem.ItemSpec, false);
+ var customMetadata = projectItem.CloneCustomMetadata();
+ foreach (DictionaryEntry entry in customMetadata) {
+ newItem.SetMetadata((string)entry.Key, (string)entry.Value);
+ }
+ }
+
+ project.Save(projectTaskItem.ItemSpec);
+ }
+
+ return !this.Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs
new file mode 100644
index 0000000..3ad5eb0
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Xml;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.BuildEngine;
+
+namespace DotNetOpenAuth.BuildTasks {
+ /// <summary>
+ /// Replaces Reference items HintPaths in a set of projects.
+ /// </summary>
+ public class ChangeAssemblyReference : Task {
+ /// <summary>
+ /// The projects to alter.
+ /// </summary>
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+ /// <summary>
+ /// The project reference to remove.
+ /// </summary>
+ [Required]
+ public string OldReference { get; set; }
+ /// <summary>
+ /// The assembly reference to add.
+ /// </summary>
+ [Required]
+ public string NewReference { get; set; }
+
+ const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
+ public override bool Execute() {
+ foreach (var project in Projects) {
+ Project doc = new Project();
+ doc.Load(project.ItemSpec);
+
+ var reference = doc.GetEvaluatedItemsByName("Reference").OfType<BuildItem>().
+ Where(item => string.Equals(item.GetMetadata("HintPath"), OldReference, StringComparison.OrdinalIgnoreCase)).Single();
+ reference.SetMetadata("HintPath", NewReference);
+
+ doc.Save(project.ItemSpec);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs
new file mode 100644
index 0000000..f940a72
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Xml;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.BuildEngine;
+
+namespace DotNetOpenAuth.BuildTasks {
+ /// <summary>
+ /// Replaces ProjectReference items in a set of projects with Reference items.
+ /// </summary>
+ public class ChangeProjectReferenceToAssemblyReference : Task {
+ /// <summary>
+ /// The projects to alter.
+ /// </summary>
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+ /// <summary>
+ /// The project reference to remove.
+ /// </summary>
+ [Required]
+ public string ProjectReference { get; set; }
+ /// <summary>
+ /// The assembly reference to add.
+ /// </summary>
+ [Required]
+ public string Reference { get; set; }
+
+ const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
+ public override bool Execute() {
+ foreach (var project in Projects) {
+ Log.LogMessage(MessageImportance.Normal, "Changing P2P references to assembly references in \"{0}\".", project.ItemSpec);
+
+ Project doc = new Project();
+ doc.Load(project.ItemSpec);
+
+ var projectReference = doc.EvaluatedItems.OfType<BuildItem>().Where(
+ item => item.Name == "ProjectReference" && item.Include == ProjectReference).Single();
+ doc.RemoveItem(projectReference);
+
+ var newReference = doc.AddNewItem("Reference", Path.GetFileNameWithoutExtension(Reference), true);
+ newReference.SetMetadata("HintPath", Reference);
+
+ doc.Save(project.ItemSpec);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs b/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs
new file mode 100644
index 0000000..9cfd35d
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="CheckAdminRights.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System.Security.Principal;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ public class CheckAdminRights : Task {
+ /// <summary>
+ /// Gets or sets a value indicating whether this process has elevated permissions.
+ /// </summary>
+ [Output]
+ public bool IsElevated { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ WindowsIdentity id = WindowsIdentity.GetCurrent();
+ WindowsPrincipal p = new WindowsPrincipal(id);
+ this.IsElevated = p.IsInRole(WindowsBuiltInRole.Administrator);
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs b/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs
new file mode 100644
index 0000000..51fcee4
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using System.IO;
+
+namespace DotNetOpenAuth.BuildTasks {
+ public class CompareFiles : Task {
+ /// <summary>
+ /// One set of items to compare.
+ /// </summary>
+ [Required]
+ public ITaskItem[] OriginalItems { get; set; }
+
+ /// <summary>
+ /// The other set of items to compare.
+ /// </summary>
+ [Required]
+ public ITaskItem[] NewItems { get; set; }
+
+ /// <summary>
+ /// Gets whether the items lists contain items that are identical going down the list.
+ /// </summary>
+ [Output]
+ public bool AreSame { get; private set; }
+
+ /// <summary>
+ /// Same as <see cref="AreSame"/>, but opposite.
+ /// </summary>
+ [Output]
+ public bool AreChanged { get { return !AreSame; } }
+
+ public override bool Execute() {
+ AreSame = AreFilesIdentical();
+ return true;
+ }
+
+ private bool AreFilesIdentical() {
+ if (OriginalItems.Length != NewItems.Length) {
+ return false;
+ }
+
+ for (int i = 0; i < OriginalItems.Length; i++) {
+ if (!IsContentOfFilesTheSame(OriginalItems[i].ItemSpec, NewItems[i].ItemSpec)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private bool IsContentOfFilesTheSame(string file1, string file2) {
+ // If exactly one file is missing, that's different.
+ if (File.Exists(file1) ^ File.Exists(file2)) return false;
+ // If both are missing, that's the same.
+ if (!File.Exists(file1)) return true;
+ // If both are present, we need to do a content comparison.
+ using (FileStream fileStream1 = File.OpenRead(file1)) {
+ using (FileStream fileStream2 = File.OpenRead(file2)) {
+ if (fileStream1.Length != fileStream2.Length) return false;
+ byte[] buffer1 = new byte[4096];
+ byte[] buffer2 = new byte[buffer1.Length];
+ int bytesRead;
+ do {
+ bytesRead = fileStream1.Read(buffer1, 0, buffer1.Length);
+ if (fileStream2.Read(buffer2, 0, buffer2.Length) != bytesRead) {
+ // We should never get here since we compared file lengths, but
+ // this is a sanity check.
+ return false;
+ }
+ for (int i = 0; i < bytesRead; i++) {
+ if (buffer1[i] != buffer2[i]) {
+ return false;
+ }
+ }
+ } while (bytesRead == buffer1.Length);
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Tests whether a file is up to date with respect to another,
+ /// based on existence, last write time and file size.
+ /// </summary>
+ /// <param name="sourcePath">The source path.</param>
+ /// <param name="destPath">The dest path.</param>
+ /// <returns><c>true</c> if the files are the same; <c>false</c> if the files are different</returns>
+ internal static bool FastFileEqualityCheck(string sourcePath, string destPath) {
+ FileInfo sourceInfo = new FileInfo(sourcePath);
+ FileInfo destInfo = new FileInfo(destPath);
+
+ if (sourceInfo.Exists ^ destInfo.Exists) {
+ // Either the source file or the destination file is missing.
+ return false;
+ }
+
+ if (!sourceInfo.Exists) {
+ // Neither file exists.
+ return true;
+ }
+
+ // We'll say the files are the same if their modification date and length are the same.
+ return
+ sourceInfo.LastWriteTimeUtc == destInfo.LastWriteTimeUtc &&
+ sourceInfo.Length == destInfo.Length;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs b/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs
new file mode 100644
index 0000000..e17d8f2
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CopyWithTokenSubstitution.cs
@@ -0,0 +1,102 @@
+//-----------------------------------------------------------------------
+// <copyright file="CopyWithTokenSubstitution.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ /// <summary>
+ /// Copies files and performs a search and replace for given tokens in their contents during the process.
+ /// </summary>
+ public class CopyWithTokenSubstitution : Task {
+ /// <summary>
+ /// Gets or sets the files to copy.
+ /// </summary>
+ /// <value>The files to copy.</value>
+ [Required]
+ public ITaskItem[] SourceFiles { get; set; }
+
+ /// <summary>
+ /// Gets or sets a list of files to copy the source files to.
+ /// </summary>
+ /// <value>The list of files to copy the source files to.</value>
+ [Required]
+ public ITaskItem[] DestinationFiles { get; set; }
+
+ /// <summary>
+ /// Gets or sets the destination files actually copied to.
+ /// </summary>
+ /// <remarks>
+ /// In the case of error partway through, or files not copied due to already being up to date,
+ /// this can be a subset of the <see cref="DestinationFiles"/> array.
+ /// </remarks>
+ [Output]
+ public ITaskItem[] CopiedFiles { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ /// <returns><c>true</c> if the operation was successful.</returns>
+ public override bool Execute() {
+ if (this.SourceFiles.Length != this.DestinationFiles.Length) {
+ Log.LogError("{0} inputs and {1} outputs given.", this.SourceFiles.Length, this.DestinationFiles.Length);
+ return false;
+ }
+
+ var copiedFiles = new List<ITaskItem>(this.DestinationFiles.Length);
+
+ for (int i = 0; i < this.SourceFiles.Length; i++) {
+ string sourcePath = this.SourceFiles[i].ItemSpec;
+ string destPath = this.DestinationFiles[i].ItemSpec;
+ bool skipUnchangedFiles = bool.Parse(this.SourceFiles[i].GetMetadata("SkipUnchangedFiles"));
+
+ // We deliberably consider newer destination files to be up-to-date rather than
+ // requiring equality because this task modifies the destination file while copying.
+ if (skipUnchangedFiles && File.GetLastWriteTimeUtc(sourcePath) < File.GetLastWriteTimeUtc(destPath)) {
+ Log.LogMessage(MessageImportance.Low, "Skipping \"{0}\" -> \"{1}\" because the destination is up to date.", sourcePath, destPath);
+ continue;
+ }
+
+ Log.LogMessage(MessageImportance.Normal, "Transforming \"{0}\" -> \"{1}\"", sourcePath, destPath);
+
+ string[] beforeTokens = this.SourceFiles[i].GetMetadata("BeforeTokens").Split(';');
+ string[] afterTokens = this.SourceFiles[i].GetMetadata("AfterTokens").Split(';');
+ if (beforeTokens.Length != afterTokens.Length) {
+ Log.LogError("Unequal number of before and after tokens. Before: \"{0}\". After \"{1}\".", beforeTokens, afterTokens);
+ return false;
+ }
+
+ using (StreamReader sr = File.OpenText(sourcePath)) {
+ if (!Directory.Exists(Path.GetDirectoryName(destPath))) {
+ Directory.CreateDirectory(Path.GetDirectoryName(destPath));
+ }
+ using (StreamWriter sw = File.CreateText(destPath)) {
+ StringBuilder line = new StringBuilder();
+ while (!sr.EndOfStream) {
+ line.Length = 0;
+ line.Append(sr.ReadLine());
+ for (int j = 0; j < beforeTokens.Length; j++) {
+ line.Replace(beforeTokens[j], afterTokens[j]);
+ }
+
+ sw.WriteLine(line);
+ }
+ }
+ }
+
+ copiedFiles.Add(this.DestinationFiles[i]);
+ }
+
+ this.CopiedFiles = copiedFiles.ToArray();
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs b/src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs
new file mode 100644
index 0000000..4980898
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CreateWebApplication.cs
@@ -0,0 +1,95 @@
+//-----------------------------------------------------------------------
+// <copyright file="CreateWebApplication.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Linq;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+ using Microsoft.Web.Administration;
+
+ /// <summary>
+ /// Creates or updates web applications within an existing web site in IIS.
+ /// </summary>
+ public class CreateWebApplication : Task {
+ /// <summary>
+ /// Gets or sets the name of the application pool that should host the web application.
+ /// </summary>
+ /// <value>The name of an existing application pool.</value>
+ public string ApplicationPoolName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of the web site under which to create the web application.
+ /// </summary>
+ /// <value>The name of the existing web site.</value>
+ [Required]
+ public string WebSiteName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the virtual paths within the web site that will access these applications.
+ /// </summary>
+ /// <value>The virtual path, which must start with '/'.</value>
+ [Required]
+ public ITaskItem[] VirtualPaths { get; set; }
+
+ /// <summary>
+ /// Gets or sets the full file system paths to the web applications.
+ /// </summary>
+ /// <value>The physical path.</value>
+ [Required]
+ public ITaskItem[] PhysicalPaths { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ /// <returns>A value indicating whether the task completed successfully.</returns>
+ public override bool Execute() {
+ var serverManager = new ServerManager();
+
+ if (this.PhysicalPaths.Length != this.VirtualPaths.Length) {
+ Log.LogError(TaskStrings.MismatchingArrayLengths, "PhysicalPath", "VirtualPath");
+ return false;
+ }
+
+ if (this.VirtualPaths.Length == 0) {
+ // Nothing to do.
+ return true;
+ }
+
+ // Find the root web site that this web application will be created under.
+ var site = serverManager.Sites.FirstOrDefault(s => string.Equals(s.Name, this.WebSiteName, StringComparison.OrdinalIgnoreCase));
+ if (site == null) {
+ Log.LogError(TaskStrings.NoMatchingWebSiteFound, this.WebSiteName);
+ return false;
+ }
+
+ Log.LogMessage(MessageImportance.Normal, "Creating web applications under web site: {0}", site.Name);
+
+ for (int i = 0; i < this.PhysicalPaths.Length; i++) {
+ string physicalPath = this.PhysicalPaths[i].ItemSpec;
+ string virtualPath = this.VirtualPaths[i].ItemSpec;
+
+ Log.LogMessage(MessageImportance.Normal, "\t{0} -> {1}", virtualPath, physicalPath);
+
+ var app = site.Applications.FirstOrDefault(a => string.Equals(a.Path, virtualPath, StringComparison.OrdinalIgnoreCase));
+ if (app == null) {
+ app = site.Applications.Add(virtualPath, physicalPath);
+ } else {
+ // Ensure physical path is set correctly.
+ var appRoot = app.VirtualDirectories.First(vd => vd.Path == "/");
+ appRoot.PhysicalPath = physicalPath;
+ }
+
+ if (!string.IsNullOrEmpty(this.ApplicationPoolName)) {
+ app.ApplicationPoolName = this.ApplicationPoolName;
+ }
+ }
+
+ serverManager.CommitChanges();
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs b/src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs
new file mode 100644
index 0000000..930a8c4
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/DeleteWebApplication.cs
@@ -0,0 +1,66 @@
+//-----------------------------------------------------------------------
+// <copyright file="DeleteWebApplication.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Linq;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+ using Microsoft.Web.Administration;
+
+ /// <summary>
+ /// Deletes a web application from IIS.
+ /// </summary>
+ public class DeleteWebApplication : Task {
+ /// <summary>
+ /// Gets or sets the name of the web site under which to create the web application.
+ /// </summary>
+ /// <value>The name of the existing web site.</value>
+ [Required]
+ public string WebSiteName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the virtual paths within the web site that will access these applications.
+ /// </summary>
+ /// <value>The virtual path, which must start with '/'.</value>
+ [Required]
+ public ITaskItem[] VirtualPaths { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ /// <returns>A value indicating whether the task completed successfully.</returns>
+ public override bool Execute() {
+ var serverManager = new ServerManager();
+
+ // Find the root web site that this web application will be created under.
+ var site = serverManager.Sites.FirstOrDefault(s => string.Equals(s.Name, this.WebSiteName, StringComparison.OrdinalIgnoreCase));
+ if (site == null) {
+ Log.LogMessage(MessageImportance.Low, TaskStrings.NoMatchingWebSiteFound, this.WebSiteName);
+ return true;
+ }
+
+ if (this.VirtualPaths.Length == 0) {
+ // Nothing to do.
+ return true;
+ }
+
+ foreach (ITaskItem path in this.VirtualPaths) {
+ var app = site.Applications.FirstOrDefault(a => string.Equals(a.Path, path.ItemSpec, StringComparison.OrdinalIgnoreCase));
+ if (app != null) {
+ site.Applications.Remove(app);
+ Log.LogMessage(MessageImportance.Normal, TaskStrings.DeletedWebApplication, app.Path);
+ } else {
+ Log.LogMessage(MessageImportance.Low, TaskStrings.WebApplicationNotFoundSoNotDeleted, path.ItemSpec);
+ }
+ }
+
+ serverManager.CommitChanges();
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs b/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs
new file mode 100644
index 0000000..0162c16
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/DiscoverProjectTemplates.cs
@@ -0,0 +1,63 @@
+//-----------------------------------------------------------------------
+// <copyright file="DiscoverProjectTemplates.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using System.Xml.Linq;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ public class DiscoverProjectTemplates : Task {
+ public ITaskItem[] TopLevelTemplates { get; set; }
+
+ [Output]
+ public ITaskItem[] ProjectTemplates { get; set; }
+
+ [Output]
+ public ITaskItem[] ProjectTemplateContents { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ List<ITaskItem> projectTemplates = new List<ITaskItem>();
+ List<ITaskItem> projectTemplateContents = new List<ITaskItem>();
+ foreach (ITaskItem topLevelTemplate in this.TopLevelTemplates) {
+ var vsTemplate = XElement.Load(topLevelTemplate.ItemSpec);
+ var templateContent = vsTemplate.Element(XName.Get("TemplateContent", MergeProjectWithVSTemplate.VSTemplateNamespace));
+ var projectCollection = templateContent.Element(XName.Get("ProjectCollection", MergeProjectWithVSTemplate.VSTemplateNamespace));
+ var links = projectCollection.Elements(XName.Get("ProjectTemplateLink", MergeProjectWithVSTemplate.VSTemplateNamespace));
+ var subTemplates = links.Select(
+ link => (ITaskItem)new TaskItem(
+ link.Value,
+ new Dictionary<string, string> {
+ { "TopLevelTemplate", topLevelTemplate.ItemSpec },
+ { "TopLevelTemplateFileName", Path.GetFileNameWithoutExtension(topLevelTemplate.ItemSpec) },
+ }));
+ projectTemplates.AddRange(subTemplates);
+
+ foreach (var link in links.Select(link => link.Value)) {
+ string[] files = Directory.GetFiles(Path.Combine(Path.GetDirectoryName(topLevelTemplate.ItemSpec), Path.GetDirectoryName(link)), "*.*", SearchOption.AllDirectories);
+ projectTemplateContents.AddRange(files.Select(file => (ITaskItem)new TaskItem(
+ file,
+ new Dictionary<string, string> {
+ { "TopLevelTemplate", topLevelTemplate.ItemSpec },
+ { "TopLevelTemplateFileName", Path.GetFileNameWithoutExtension(topLevelTemplate.ItemSpec) },
+ })));
+ }
+ }
+
+ this.ProjectTemplates = projectTemplates.ToArray();
+ this.ProjectTemplateContents = projectTemplateContents.ToArray();
+
+ return !this.Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
new file mode 100644
index 0000000..b8cff51
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DotNetOpenAuth.BuildTasks</RootNamespace>
+ <AssemblyName>DotNetOpenAuth.BuildTasks</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\..\lib\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>False</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
+ <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
+ <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
+ <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
+ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
+ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
+ <CodeContractsCustomRewriterAssembly>
+ </CodeContractsCustomRewriterAssembly>
+ <CodeContractsCustomRewriterClass>
+ </CodeContractsCustomRewriterClass>
+ <CodeContractsLibPaths>
+ </CodeContractsLibPaths>
+ <CodeContractsPlatformPath>
+ </CodeContractsPlatformPath>
+ <CodeContractsExtraAnalysisOptions>
+ </CodeContractsExtraAnalysisOptions>
+ <CodeContractsBaseLineFile>
+ </CodeContractsBaseLineFile>
+ <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\lib\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.Build.Engine" />
+ <Reference Include="Microsoft.Build.Framework" />
+ <Reference Include="Microsoft.Build.Utilities.v3.5">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>$(SystemRoot)\System32\inetsrv\Microsoft.Web.Administration.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AddProjectItems.cs" />
+ <Compile Include="ChangeProjectReferenceToAssemblyReference.cs" />
+ <Compile Include="CompareFiles.cs" />
+ <Compile Include="ChangeAssemblyReference.cs" />
+ <Compile Include="CopyWithTokenSubstitution.cs" />
+ <Compile Include="CreateWebApplication.cs" />
+ <Compile Include="DeleteWebApplication.cs" />
+ <Compile Include="DiscoverProjectTemplates.cs" />
+ <Compile Include="ECMAScriptPacker.cs" />
+ <Compile Include="FilterItems.cs" />
+ <Compile Include="FixupReferenceHintPaths.cs" />
+ <Compile Include="FixupShippingToolSamples.cs" />
+ <Compile Include="MergeProjectWithVSTemplate.cs" />
+ <Compile Include="GetBuildVersion.cs" />
+ <Compile Include="CheckAdminRights.cs" />
+ <Compile Include="JsPack.cs" />
+ <Compile Include="ParseMaster.cs" />
+ <Compile Include="Publicize.cs" />
+ <Compile Include="Purge.cs" />
+ <Compile Include="ReSignDelaySignedAssemblies.cs" />
+ <Compile Include="SetEnvironmentVariable.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SignatureVerification.cs" />
+ <Compile Include="SnToolTask.cs" />
+ <Compile Include="TaskStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>TaskStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="Trim.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="TaskStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>TaskStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln
new file mode 100644
index 0000000..0d0900b
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.sln
@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.BuildTasks", "DotNetOpenAuth.BuildTasks.csproj", "{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ABBE14A3-0404-4123-9093-E598C3DD3E9B}"
+ ProjectSection(SolutionItems) = preProject
+ ..\..\build.proj = ..\..\build.proj
+ ..\..\lib\DotNetOpenAuth.BuildTasks.targets = ..\..\lib\DotNetOpenAuth.BuildTasks.targets
+ ..\..\tools\DotNetOpenAuth.Common.Settings.targets = ..\..\tools\DotNetOpenAuth.Common.Settings.targets
+ ..\..\tools\DotNetOpenAuth.props = ..\..\tools\DotNetOpenAuth.props
+ ..\..\tools\DotNetOpenAuth.targets = ..\..\tools\DotNetOpenAuth.targets
+ ..\..\tools\DotNetOpenAuth.Versioning.targets = ..\..\tools\DotNetOpenAuth.Versioning.targets
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs b/src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs
new file mode 100644
index 0000000..d63d5b4
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ECMAScriptPacker.cs
@@ -0,0 +1,486 @@
+using System;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Web;
+using System.IO;
+
+/*
+ packer, version 2.0 (beta) (2005/02/01)
+ Copyright 2004-2005, Dean Edwards
+ Web: http://dean.edwards.name/
+
+ This software is licensed under the CC-GNU LGPL
+ Web: http://creativecommons.org/licenses/LGPL/2.1/
+
+ Ported to C# by Jesse Hansen, twindagger2k@msn.com
+ modified slightly by Andrew Arnott
+*/
+
+// http://dean.edwards.name/packer/
+
+namespace Dean.Edwards
+{
+ /// <summary>
+ /// Packs a javascript file into a smaller area, removing unnecessary characters from the output.
+ /// </summary>
+ internal class ECMAScriptPacker
+ {
+ /// <summary>
+ /// The encoding level to use. See http://dean.edwards.name/packer/usage/ for more info.
+ /// </summary>
+ public enum PackerEncoding { None = 0, Numeric = 10, Mid = 36, Normal = 62, HighAscii = 95 };
+
+ private PackerEncoding encoding = PackerEncoding.Normal;
+ private bool fastDecode = true;
+ private bool specialChars = false;
+ private bool enabled = true;
+
+ string IGNORE = "$1";
+
+ /// <summary>
+ /// The encoding level for this instance
+ /// </summary>
+ public PackerEncoding Encoding
+ {
+ get { return encoding; }
+ set { encoding = value; }
+ }
+
+ /// <summary>
+ /// Adds a subroutine to the output to speed up decoding
+ /// </summary>
+ public bool FastDecode
+ {
+ get { return fastDecode; }
+ set { fastDecode = value; }
+ }
+
+ /// <summary>
+ /// Replaces special characters
+ /// </summary>
+ public bool SpecialChars
+ {
+ get { return specialChars; }
+ set { specialChars = value; }
+ }
+
+ /// <summary>
+ /// Packer enabled
+ /// </summary>
+ public bool Enabled
+ {
+ get { return enabled; }
+ set { enabled = value; }
+ }
+
+ public ECMAScriptPacker()
+ {
+ Encoding = PackerEncoding.Normal;
+ FastDecode = true;
+ SpecialChars = false;
+ }
+
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ /// <param name="encoding">The encoding level for this instance</param>
+ /// <param name="fastDecode">Adds a subroutine to the output to speed up decoding</param>
+ /// <param name="specialChars">Replaces special characters</param>
+ public ECMAScriptPacker(PackerEncoding encoding, bool fastDecode, bool specialChars)
+ {
+ Encoding = encoding;
+ FastDecode = fastDecode;
+ SpecialChars = specialChars;
+ }
+
+ /// <summary>
+ /// Packs the script
+ /// </summary>
+ /// <param name="script">the script to pack</param>
+ /// <returns>the packed script</returns>
+ public string Pack(string script)
+ {
+ if (enabled)
+ {
+ script += "\n";
+ script = basicCompression(script);
+ if (SpecialChars)
+ script = encodeSpecialChars(script);
+ if (Encoding != PackerEncoding.None)
+ script = encodeKeywords(script);
+ }
+ return script;
+ }
+
+ //zero encoding - just removal of whitespace and comments
+ private string basicCompression(string script)
+ {
+ ParseMaster parser = new ParseMaster();
+ // make safe
+ parser.EscapeChar = '\\';
+ // protect strings
+ parser.Add("'[^'\\n\\r]*'", IGNORE);
+ parser.Add("\"[^\"\\n\\r]*\"", IGNORE);
+ // remove comments
+ parser.Add("\\/\\/[^\\n\\r]*[\\n\\r]");
+ parser.Add("\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/");
+ // protect regular expressions
+ parser.Add("\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)", "$2");
+ parser.Add("[^\\w\\$\\/'\"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?", IGNORE);
+ // remove: ;;; doSomething();
+ if (specialChars)
+ parser.Add(";;[^\\n\\r]+[\\n\\r]");
+ // remove redundant semi-colons
+ parser.Add(";+\\s*([};])", "$2");
+ // remove white-space
+ parser.Add("(\\b|\\$)\\s+(\\b|\\$)", "$2 $3");
+ parser.Add("([+\\-])\\s+([+\\-])", "$2 $3");
+ parser.Add("\\s+");
+ // done
+ return parser.Exec(script);
+ }
+
+ WordList encodingLookup;
+ private string encodeSpecialChars(string script)
+ {
+ ParseMaster parser = new ParseMaster();
+ // replace: $name -> n, $$name -> na
+ parser.Add("((\\$+)([a-zA-Z\\$_]+))(\\d*)",
+ new ParseMaster.MatchGroupEvaluator(encodeLocalVars));
+
+ // replace: _name -> _0, double-underscore (__name) is ignored
+ Regex regex = new Regex("\\b_[A-Za-z\\d]\\w*");
+
+ // build the word list
+ encodingLookup = analyze(script, regex, new EncodeMethod(encodePrivate));
+
+ parser.Add("\\b_[A-Za-z\\d]\\w*", new ParseMaster.MatchGroupEvaluator(encodeWithLookup));
+
+ script = parser.Exec(script);
+ return script;
+ }
+
+ private string encodeKeywords(string script)
+ {
+ // escape high-ascii values already in the script (i.e. in strings)
+ if (Encoding == PackerEncoding.HighAscii) script = escape95(script);
+ // create the parser
+ ParseMaster parser = new ParseMaster();
+ EncodeMethod encode = getEncoder(Encoding);
+
+ // for high-ascii, don't encode single character low-ascii
+ Regex regex = new Regex(
+ (Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+"
+ );
+ // build the word list
+ encodingLookup = analyze(script, regex, encode);
+
+ // encode
+ parser.Add((Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+",
+ new ParseMaster.MatchGroupEvaluator(encodeWithLookup));
+
+ // if encoded, wrap the script in a decoding function
+ return (script == string.Empty) ? "" : bootStrap(parser.Exec(script), encodingLookup);
+ }
+
+ private string bootStrap(string packed, WordList keywords)
+ {
+ // packed: the packed script
+ packed = "'" + escape(packed) + "'";
+
+ // ascii: base for encoding
+ int ascii = Math.Min(keywords.Sorted.Count, (int) Encoding);
+ if (ascii == 0)
+ ascii = 1;
+
+ // count: number of words contained in the script
+ int count = keywords.Sorted.Count;
+
+ // keywords: list of words contained in the script
+ foreach (object key in keywords.Protected.Keys)
+ {
+ keywords.Sorted[(int) key] = "";
+ }
+ // convert from a string to an array
+ StringBuilder sbKeywords = new StringBuilder("'");
+ foreach (string word in keywords.Sorted)
+ sbKeywords.Append(word + "|");
+ sbKeywords.Remove(sbKeywords.Length-1, 1);
+ string keywordsout = sbKeywords.ToString() + "'.split('|')";
+
+ string encode;
+ string inline = "c";
+
+ switch (Encoding)
+ {
+ case PackerEncoding.Mid:
+ encode = "function(c){return c.toString(36)}";
+ inline += ".toString(a)";
+ break;
+ case PackerEncoding.Normal:
+ encode = "function(c){return(c<a?\"\":e(parseInt(c/a)))+" +
+ "((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}";
+ inline += ".toString(a)";
+ break;
+ case PackerEncoding.HighAscii:
+ encode = "function(c){return(c<a?\"\":e(c/a))+" +
+ "String.fromCharCode(c%a+161)}";
+ inline += ".toString(a)";
+ break;
+ default:
+ encode = "function(c){return c}";
+ break;
+ }
+
+ // decode: code snippet to speed up decoding
+ string decode = "";
+ if (fastDecode)
+ {
+ decode = "if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1;}";
+ if (Encoding == PackerEncoding.HighAscii)
+ decode = decode.Replace("\\\\w", "[\\xa1-\\xff]");
+ else if (Encoding == PackerEncoding.Numeric)
+ decode = decode.Replace("e(c)", inline);
+ if (count == 0)
+ decode = decode.Replace("c=1", "c=0");
+ }
+
+ // boot function
+ string unpack = "function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p;}";
+ Regex r;
+ if (fastDecode)
+ {
+ //insert the decoder
+ r = new Regex("\\{");
+ unpack = r.Replace(unpack, "{" + decode + ";", 1);
+ }
+
+ if (Encoding == PackerEncoding.HighAscii)
+ {
+ // get rid of the word-boundries for regexp matches
+ r = new Regex("'\\\\\\\\b'\\s*\\+|\\+\\s*'\\\\\\\\b'");
+ unpack = r.Replace(unpack, "");
+ }
+ if (Encoding == PackerEncoding.HighAscii || ascii > (int) PackerEncoding.Normal || fastDecode)
+ {
+ // insert the encode function
+ r = new Regex("\\{");
+ unpack = r.Replace(unpack, "{e=" + encode + ";", 1);
+ }
+ else
+ {
+ r = new Regex("e\\(c\\)");
+ unpack = r.Replace(unpack, inline);
+ }
+ // no need to pack the boot function since i've already done it
+ string _params = "" + packed + "," + ascii + "," + count + "," + keywordsout;
+ if (fastDecode)
+ {
+ //insert placeholders for the decoder
+ _params += ",0,{}";
+ }
+ // the whole thing
+ return "eval(" + unpack + "(" + _params + "))\n";
+ }
+
+ private string escape(string input)
+ {
+ Regex r = new Regex("([\\\\'])");
+ return r.Replace(input, "\\$1");
+ }
+
+ private EncodeMethod getEncoder(PackerEncoding encoding)
+ {
+ switch (encoding)
+ {
+ case PackerEncoding.Mid:
+ return new EncodeMethod(encode36);
+ case PackerEncoding.Normal:
+ return new EncodeMethod(encode62);
+ case PackerEncoding.HighAscii:
+ return new EncodeMethod(encode95);
+ default:
+ return new EncodeMethod(encode10);
+ }
+ }
+
+ private string encode10(int code)
+ {
+ return code.ToString();
+ }
+
+ //lookups seemed like the easiest way to do this since
+ // I don't know of an equivalent to .toString(36)
+ private static string lookup36 = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+ private string encode36(int code)
+ {
+ string encoded = "";
+ int i = 0;
+ do
+ {
+ int digit = (code / (int) Math.Pow(36, i)) % 36;
+ encoded = lookup36[digit] + encoded;
+ code -= digit * (int) Math.Pow(36, i++);
+ } while (code > 0);
+ return encoded;
+ }
+
+ private static string lookup62 = lookup36 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ private string encode62(int code)
+ {
+ string encoded = "";
+ int i = 0;
+ do
+ {
+ int digit = (code / (int) Math.Pow(62, i)) % 62;
+ encoded = lookup62[digit] + encoded;
+ code -= digit * (int) Math.Pow(62, i++);
+ } while (code > 0);
+ return encoded;
+ }
+
+ private static string lookup95 = "";
+
+ private string encode95(int code)
+ {
+ string encoded = "";
+ int i = 0;
+ do
+ {
+ int digit = (code / (int) Math.Pow(95, i)) % 95;
+ encoded = lookup95[digit] + encoded;
+ code -= digit * (int) Math.Pow(95, i++);
+ } while (code > 0);
+ return encoded;
+ }
+
+ private string escape95(string input)
+ {
+ Regex r = new Regex("[\xa1-\xff]");
+ return r.Replace(input, new MatchEvaluator(escape95Eval));
+ }
+
+ private string escape95Eval(Match match)
+ {
+ return "\\x" + ((int) match.Value[0]).ToString("x"); //return hexadecimal value
+ }
+
+ private string encodeLocalVars(Match match, int offset)
+ {
+ int length = match.Groups[offset + 2].Length;
+ int start = length - Math.Max(length - match.Groups[offset + 3].Length, 0);
+ return match.Groups[offset + 1].Value.Substring(start, length) +
+ match.Groups[offset + 4].Value;
+ }
+
+ private string encodeWithLookup(Match match, int offset)
+ {
+ return (string) encodingLookup.Encoded[match.Groups[offset].Value];
+ }
+
+ private delegate string EncodeMethod(int code);
+
+ private string encodePrivate(int code)
+ {
+ return "_" + code;
+ }
+
+ private WordList analyze(string input, Regex regex, EncodeMethod encodeMethod)
+ {
+ // analyse
+ // retreive all words in the script
+ MatchCollection all = regex.Matches(input);
+ WordList rtrn;
+ rtrn.Sorted = new StringCollection(); // list of words sorted by frequency
+ rtrn.Protected = new HybridDictionary(); // dictionary of word->encoding
+ rtrn.Encoded = new HybridDictionary(); // instances of "protected" words
+ if (all.Count > 0)
+ {
+ StringCollection unsorted = new StringCollection(); // same list, not sorted
+ HybridDictionary Protected = new HybridDictionary(); // "protected" words (dictionary of word->"word")
+ HybridDictionary values = new HybridDictionary(); // dictionary of charCode->encoding (eg. 256->ff)
+ HybridDictionary count = new HybridDictionary(); // word->count
+ int i = all.Count, j = 0;
+ string word;
+ // count the occurrences - used for sorting later
+ do
+ {
+ word = "$" + all[--i].Value;
+ if (count[word] == null)
+ {
+ count[word] = 0;
+ unsorted.Add(word);
+ // make a dictionary of all of the protected words in this script
+ // these are words that might be mistaken for encoding
+ Protected["$" + (values[j] = encodeMethod(j))] = j++;
+ }
+ // increment the word counter
+ count[word] = (int) count[word] + 1;
+ } while (i > 0);
+ /* prepare to sort the word list, first we must protect
+ words that are also used as codes. we assign them a code
+ equivalent to the word itself.
+ e.g. if "do" falls within our encoding range
+ then we store keywords["do"] = "do";
+ this avoids problems when decoding */
+ i = unsorted.Count;
+ string[] sortedarr = new string[unsorted.Count];
+ do
+ {
+ word = unsorted[--i];
+ if (Protected[word] != null)
+ {
+ sortedarr[(int) Protected[word]] = word.Substring(1);
+ rtrn.Protected[(int) Protected[word]] = true;
+ count[word] = 0;
+ }
+ } while (i > 0);
+ string[] unsortedarr = new string[unsorted.Count];
+ unsorted.CopyTo(unsortedarr, 0);
+ // sort the words by frequency
+ Array.Sort(unsortedarr, (IComparer) new CountComparer(count));
+ j = 0;
+ /*because there are "protected" words in the list
+ we must add the sorted words around them */
+ do
+ {
+ if (sortedarr[i] == null)
+ sortedarr[i] = unsortedarr[j++].Substring(1);
+ rtrn.Encoded[sortedarr[i]] = values[i];
+ } while (++i < unsortedarr.Length);
+ rtrn.Sorted.AddRange(sortedarr);
+ }
+ return rtrn;
+ }
+
+ private struct WordList
+ {
+ public StringCollection Sorted;
+ public HybridDictionary Encoded;
+ public HybridDictionary Protected;
+ }
+
+ private class CountComparer : IComparer
+ {
+ HybridDictionary count;
+
+ public CountComparer(HybridDictionary count)
+ {
+ this.count = count;
+ }
+
+ #region IComparer Members
+
+ public int Compare(object x, object y)
+ {
+ return (int) count[y] - (int) count[x];
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/FilterItems.cs b/src/DotNetOpenAuth.BuildTasks/FilterItems.cs
new file mode 100644
index 0000000..97631c6
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/FilterItems.cs
@@ -0,0 +1,24 @@
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.Utilities;
+ using Microsoft.Build.Framework;
+
+ public class FilterItems : Task {
+ [Required]
+ public ITaskItem[] InputItems { get; set; }
+
+ [Required]
+ public ITaskItem[] StartsWithAny { get; set; }
+
+ [Output]
+ public ITaskItem[] FilteredItems { get; set; }
+
+ public override bool Execute() {
+ FilteredItems = InputItems.Where(item => StartsWithAny.Any(filter => item.ItemSpec.StartsWith(filter.ItemSpec, StringComparison.OrdinalIgnoreCase))).ToArray();
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs b/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs
new file mode 100644
index 0000000..13a4b8f
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/FixupReferenceHintPaths.cs
@@ -0,0 +1,71 @@
+//-----------------------------------------------------------------------
+// <copyright file="FixupReferenceHintPaths.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Text;
+ using Microsoft.Build.BuildEngine;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ public class FixupReferenceHintPaths : Task {
+ /// <summary>
+ /// Gets or sets the projects to fixup references for.
+ /// </summary>
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+
+ /// <summary>
+ /// Gets or sets the set of full paths to assemblies that may be found in any of the <see cref="Projects"/>.
+ /// </summary>
+ [Required]
+ public ITaskItem[] References { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ if (this.References.Length == 0 || this.Projects.Length == 0) {
+ this.Log.LogMessage(MessageImportance.Low, "Skipping reference hintpath fixup because no projects or no references were supplied.");
+ return !this.Log.HasLoggedErrors;
+ }
+
+ // Figure out what the assembly names are of the references that are available.
+ AssemblyName[] availableReferences = new AssemblyName[this.References.Length];
+ for (int i = 0; i < this.References.Length; i++) {
+ availableReferences[i] = AssemblyName.GetAssemblyName(this.References[i].ItemSpec);
+ }
+
+ foreach (var projectTaskItem in this.Projects) {
+ var project = new Project();
+ Uri projectUri = new Uri(projectTaskItem.GetMetadata("FullPath"));
+ project.Load(projectTaskItem.ItemSpec);
+
+ foreach (BuildItem referenceItem in project.GetEvaluatedItemsByName("Reference")) {
+ var referenceAssemblyName = new AssemblyName(referenceItem.Include);
+ var matchingReference = availableReferences.FirstOrDefault(r => string.Equals(r.Name, referenceAssemblyName.Name, StringComparison.OrdinalIgnoreCase));
+ if (matchingReference != null) {
+ var originalSuppliedReferenceItem = this.References[Array.IndexOf(availableReferences, matchingReference)];
+ string hintPath = originalSuppliedReferenceItem.GetMetadata("HintPath");
+ if (string.IsNullOrEmpty(hintPath)) {
+ hintPath = projectUri.MakeRelativeUri(new Uri(matchingReference.CodeBase)).OriginalString.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+ }
+ this.Log.LogMessage("Fixing up HintPath to \"{0}\" in project \"{1}\".", referenceAssemblyName.Name, projectTaskItem.ItemSpec);
+ referenceItem.SetMetadata("HintPath", hintPath);
+ }
+ }
+
+ project.Save(projectTaskItem.ItemSpec);
+ }
+
+ return !this.Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs b/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs
new file mode 100644
index 0000000..a6088c9
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/FixupShippingToolSamples.cs
@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------
+// <copyright file="FixupShippingToolSamples.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.BuildEngine;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ /// <summary>
+ /// Removes imports that only apply when a shipping tool sample builds as part of
+ /// the entire project, but not when it's part of a source code sample.
+ /// </summary>
+ public class FixupShippingToolSamples : Task {
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+
+ public string[] RemoveImportsStartingWith { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ /// <returns></returns>
+ public override bool Execute() {
+ foreach (ITaskItem projectTaskItem in this.Projects) {
+ this.Log.LogMessage("Fixing up the {0} sample for shipping as source code.", Path.GetFileNameWithoutExtension(projectTaskItem.ItemSpec));
+
+ var project = new Project();
+ Uri projectUri = new Uri(projectTaskItem.GetMetadata("FullPath"));
+ project.Load(projectTaskItem.ItemSpec, ProjectLoadSettings.IgnoreMissingImports);
+
+ if (this.RemoveImportsStartingWith != null && this.RemoveImportsStartingWith.Length > 0) {
+ project.Imports.Cast<Import>()
+ .Where(import => this.RemoveImportsStartingWith.Any(start => import.ProjectPath.StartsWith(start, StringComparison.OrdinalIgnoreCase)))
+ .ToList()
+ .ForEach(import => project.Imports.RemoveImport(import));
+ }
+
+ project.Save(projectTaskItem.ItemSpec);
+ }
+
+ return !this.Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
new file mode 100644
index 0000000..23db9a6
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace DotNetOpenAuth.BuildTasks {
+ public class GetBuildVersion : Task {
+
+ /// <summary>
+ /// Gets the version string to use in the compiled assemblies.
+ /// </summary>
+ [Output]
+ public string Version { get; private set; }
+
+ /// <summary>
+ /// Gets the Git revision control commit id for HEAD (the current source code version).
+ /// </summary>
+ [Output]
+ public string GitCommitId { get; private set; }
+
+ /// <summary>
+ /// The file that contains the version base (Major.Minor.Build) to use.
+ /// </summary>
+ [Required]
+ public string VersionFile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the parent directory of the .git directory.
+ /// </summary>
+ public string GitRepoRoot { get; set; }
+
+ public override bool Execute() {
+ try {
+ Version typedVersion = ReadVersionFromFile();
+ typedVersion = new Version(typedVersion.Major, typedVersion.Minor, typedVersion.Build, CalculateJDate(DateTime.Now));
+ Version = typedVersion.ToString();
+
+ this.GitCommitId = GetGitHeadCommitId();
+ } catch (ArgumentOutOfRangeException ex) {
+ Log.LogErrorFromException(ex);
+ return false;
+ }
+
+ return true;
+ }
+
+ private string GetGitHeadCommitId() {
+ if (string.IsNullOrEmpty(this.GitRepoRoot)) {
+ return string.Empty;
+ }
+
+ string commitId = string.Empty;
+
+ // First try asking Git for the HEAD commit id
+ try {
+ string cmdPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
+ ProcessStartInfo psi = new ProcessStartInfo(cmdPath, "/c git rev-parse HEAD");
+ psi.WindowStyle = ProcessWindowStyle.Hidden;
+ psi.RedirectStandardOutput = true;
+ psi.UseShellExecute = false;
+ Process git = Process.Start(psi);
+ commitId = git.StandardOutput.ReadLine();
+ git.WaitForExit();
+ if (git.ExitCode != 0) {
+ commitId = string.Empty;
+ }
+ if (commitId != null) {
+ commitId = commitId.Trim();
+ if (commitId.Length == 40) {
+ return commitId;
+ }
+ }
+ } catch (InvalidOperationException) {
+ } catch (Win32Exception) {
+ }
+
+ // Failing being able to use the git command to figure out the HEAD commit ID, try the filesystem directly.
+ try {
+ string headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim();
+ if (headContent.StartsWith("ref:", StringComparison.Ordinal)) {
+ string refName = headContent.Substring(5).Trim();
+ string refPath = Path.Combine(this.GitRepoRoot, ".git/" + refName);
+ if (File.Exists(refPath)) {
+ commitId = File.ReadAllText(refPath).Trim();
+ } else {
+ string packedRefPath = Path.Combine(this.GitRepoRoot, ".git/packed-refs");
+ string matchingLine = File.ReadAllLines(packedRefPath).FirstOrDefault(line => line.EndsWith(refName));
+ if (matchingLine != null) {
+ commitId = matchingLine.Substring(0, matchingLine.IndexOf(' '));
+ }
+ }
+ } else {
+ commitId = headContent;
+ }
+ } catch (FileNotFoundException) {
+ } catch (DirectoryNotFoundException) {
+ }
+
+ return commitId.Trim();
+ }
+
+ private Version ReadVersionFromFile() {
+ string[] lines = File.ReadAllLines(VersionFile);
+ string versionLine = lines[0];
+ return new Version(versionLine);
+ }
+
+ private int CalculateJDate(DateTime date) {
+ int yearLastDigit = date.Year - 2000; // can actually be two digits in or after 2010
+ DateTime firstOfYear = new DateTime(date.Year, 1, 1);
+ int dayOfYear = (date - firstOfYear).Days + 1;
+ int jdate = yearLastDigit * 1000 + dayOfYear;
+ return jdate;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/JsPack.cs b/src/DotNetOpenAuth.BuildTasks/JsPack.cs
new file mode 100644
index 0000000..fa8c7f0
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/JsPack.cs
@@ -0,0 +1,75 @@
+//-----------------------------------------------------------------------
+// <copyright file="JsPack.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ /// <summary>
+ /// Compresses .js files.
+ /// </summary>
+ public class JsPack : Task {
+ /// <summary>
+ /// The Javascript packer to use.
+ /// </summary>
+ private Dean.Edwards.ECMAScriptPacker packer = new Dean.Edwards.ECMAScriptPacker();
+
+ /// <summary>
+ /// Gets or sets the set of javascript files to compress.
+ /// </summary>
+ /// <value>The inputs.</value>
+ [Required]
+ public ITaskItem[] Inputs { get; set; }
+
+ /// <summary>
+ /// Gets or sets the paths where the packed javascript files should be saved.
+ /// </summary>
+ /// <value>The outputs.</value>
+ [Required]
+ public ITaskItem[] Outputs { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ /// <returns>A value indicating whether the packing was successful.</returns>
+ public override bool Execute() {
+ if (this.Inputs.Length != this.Outputs.Length) {
+ Log.LogError("{0} inputs and {1} outputs given.", this.Inputs.Length, this.Outputs.Length);
+ return false;
+ }
+
+ for (int i = 0; i < this.Inputs.Length; i++) {
+ if (!File.Exists(this.Outputs[i].ItemSpec) || File.GetLastWriteTime(this.Outputs[i].ItemSpec) < File.GetLastWriteTime(this.Inputs[i].ItemSpec)) {
+ Log.LogMessage(MessageImportance.Normal, TaskStrings.PackingJsFile, this.Inputs[i].ItemSpec, this.Outputs[i].ItemSpec);
+ string input = File.ReadAllText(this.Inputs[i].ItemSpec);
+ string output = this.packer.Pack(input);
+ if (!Directory.Exists(Path.GetDirectoryName(this.Outputs[i].ItemSpec))) {
+ Directory.CreateDirectory(Path.GetDirectoryName(this.Outputs[i].ItemSpec));
+ }
+
+ // Minification removes all comments, including copyright notices
+ // that must remain. So if there's metadata on this item with
+ // a copyright notice on it, stick it on the front of the file.
+ string copyright = this.Inputs[i].GetMetadata("Copyright");
+ if (!string.IsNullOrEmpty(copyright)) {
+ output = "/*" + copyright + "*/" + output;
+ }
+
+ File.WriteAllText(this.Outputs[i].ItemSpec, output, Encoding.UTF8);
+ } else {
+ Log.LogMessage(MessageImportance.Low, TaskStrings.SkipPackingJsFile, this.Inputs[i].ItemSpec);
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
new file mode 100644
index 0000000..1a8a17d
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
@@ -0,0 +1,103 @@
+//-----------------------------------------------------------------------
+// <copyright file="MergeProjectWithVSTemplate.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+ using System.Xml.Linq;
+ using System.IO;
+ using Microsoft.Build.BuildEngine;
+ using System.Diagnostics.Contracts;
+
+ public class MergeProjectWithVSTemplate : Task {
+ internal const string VSTemplateNamespace = "http://schemas.microsoft.com/developer/vstemplate/2005";
+
+ [Required]
+ public string[] ProjectItemTypes { get; set; }
+
+ [Required]
+ public string[] ReplaceParametersExtensions { get; set; }
+
+ [Required]
+ public ITaskItem[] Templates { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ foreach(ITaskItem sourceTemplateTaskItem in this.Templates) {
+ var template = XElement.Load(sourceTemplateTaskItem.ItemSpec);
+ var templateContentElement = template.Element(XName.Get("TemplateContent", VSTemplateNamespace));
+ var projectElement = templateContentElement.Element(XName.Get("Project", VSTemplateNamespace));
+ if (projectElement == null) {
+ Log.LogMessage("Skipping merge of \"{0}\" with a project because no project was referenced from the template.", sourceTemplateTaskItem.ItemSpec);
+ continue;
+ }
+
+ var projectPath = Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.ItemSpec), projectElement.Attribute("File").Value);
+ Log.LogMessage("Merging project \"{0}\" with \"{1}\".", projectPath, sourceTemplateTaskItem.ItemSpec);
+ var sourceProject = new Project();
+ sourceProject.Load(projectPath);
+
+ // Collect the project items from the project that are appropriate
+ // to include in the .vstemplate file.
+ var itemsByFolder = from item in sourceProject.EvaluatedItems.Cast<BuildItem>()
+ where this.ProjectItemTypes.Contains(item.Name)
+ orderby item.Include
+ group item by Path.GetDirectoryName(item.Include);
+ foreach (var folder in itemsByFolder) {
+ XElement parentNode = FindOrCreateParent(folder.Key, projectElement);
+
+ foreach (var item in folder) {
+ bool replaceParameters = this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.Include));
+ var itemName = XName.Get("ProjectItem", VSTemplateNamespace);
+ var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.Include), StringComparison.OrdinalIgnoreCase));
+ if (projectItem == null) {
+ projectItem = new XElement(itemName, Path.GetFileName(item.Include));
+ parentNode.Add(projectItem);
+ }
+ if (replaceParameters) {
+ projectItem.SetAttributeValue("ReplaceParameters", "true");
+ }
+ }
+ }
+
+ template.Save(sourceTemplateTaskItem.ItemSpec);
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private static XElement FindOrCreateParent(string directoryName, XElement projectElement) {
+ Contract.Requires<ArgumentNullException>(projectElement != null);
+
+ if (string.IsNullOrEmpty(directoryName)) {
+ return projectElement;
+ }
+
+ string[] segments = directoryName.Split(Path.DirectorySeparatorChar);
+ XElement parent = projectElement;
+ for (int i = 0; i < segments.Length; i++) {
+ var candidateName = XName.Get("Folder", VSTemplateNamespace);
+ var candidate = parent.Elements(XName.Get("Folder", VSTemplateNamespace)).FirstOrDefault(n => n.Attribute("Name").Value == segments[i]);
+ if (candidate == null) {
+ candidate = new XElement(
+ candidateName,
+ new XAttribute("Name", segments[i]));
+ parent.Add(candidate);
+ }
+
+ parent = candidate;
+ }
+
+ return parent;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/ParseMaster.cs b/src/DotNetOpenAuth.BuildTasks/ParseMaster.cs
new file mode 100644
index 0000000..7edba29
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ParseMaster.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Collections;
+using System.Collections.Specialized;
+
+/*
+ ParseMaster, version 1.0 (pre-release) (2005/02/01) x4
+ Copyright 2005, Dean Edwards
+ Web: http://dean.edwards.name/
+
+ This software is licensed under the CC-GNU LGPL
+ Web: http://creativecommons.org/licenses/LGPL/2.1/
+
+ Ported to C# by Jesse Hansen, twindagger2k@msn.com
+*/
+
+namespace Dean.Edwards
+{
+ /// <summary>
+ /// a multi-pattern parser
+ /// </summary>
+ internal class ParseMaster
+ {
+ // used to determine nesting levels
+ Regex GROUPS = new Regex("\\("),
+ SUB_REPLACE = new Regex("\\$"),
+ INDEXED = new Regex("^\\$\\d+$"),
+ ESCAPE = new Regex("\\\\."),
+ QUOTE = new Regex("'"),
+ DELETED = new Regex("\\x01[^\\x01]*\\x01");
+
+ /// <summary>
+ /// Delegate to call when a regular expression is found.
+ /// Use match.Groups[offset + &lt;group number&gt;].Value to get
+ /// the correct subexpression
+ /// </summary>
+ public delegate string MatchGroupEvaluator(Match match, int offset);
+
+ private string DELETE(Match match, int offset)
+ {
+ return "\x01" + match.Groups[offset].Value + "\x01";
+ }
+
+ private bool ignoreCase = false;
+ private char escapeChar = '\0';
+
+ /// <summary>
+ /// Ignore Case?
+ /// </summary>
+ public bool IgnoreCase
+ {
+ get { return ignoreCase; }
+ set { ignoreCase = value; }
+ }
+
+ /// <summary>
+ /// Escape Character to use
+ /// </summary>
+ public char EscapeChar
+ {
+ get { return escapeChar; }
+ set { escapeChar = value; }
+ }
+
+ /// <summary>
+ /// Add an expression to be deleted
+ /// </summary>
+ /// <param name="expression">Regular Expression String</param>
+ public void Add(string expression)
+ {
+ Add(expression, string.Empty);
+ }
+
+ /// <summary>
+ /// Add an expression to be replaced with the replacement string
+ /// </summary>
+ /// <param name="expression">Regular Expression String</param>
+ /// <param name="replacement">Replacement String. Use $1, $2, etc. for groups</param>
+ public void Add(string expression, string replacement)
+ {
+ if (replacement == string.Empty)
+ add(expression, new MatchGroupEvaluator(DELETE));
+
+ add(expression, replacement);
+ }
+
+ /// <summary>
+ /// Add an expression to be replaced using a callback function
+ /// </summary>
+ /// <param name="expression">Regular expression string</param>
+ /// <param name="replacement">Callback function</param>
+ public void Add(string expression, MatchGroupEvaluator replacement)
+ {
+ add(expression, replacement);
+ }
+
+ /// <summary>
+ /// Executes the parser
+ /// </summary>
+ /// <param name="input">input string</param>
+ /// <returns>parsed string</returns>
+ public string Exec(string input)
+ {
+ return DELETED.Replace(unescape(getPatterns().Replace(escape(input), new MatchEvaluator(replacement))), string.Empty);
+ //long way for debugging
+ /*input = escape(input);
+ Regex patterns = getPatterns();
+ input = patterns.Replace(input, new MatchEvaluator(replacement));
+ input = DELETED.Replace(input, string.Empty);
+ return input;*/
+ }
+
+ ArrayList patterns = new ArrayList();
+ private void add(string expression, object replacement)
+ {
+ Pattern pattern = new Pattern();
+ pattern.expression = expression;
+ pattern.replacement = replacement;
+ //count the number of sub-expressions
+ // - add 1 because each group is itself a sub-expression
+ pattern.length = GROUPS.Matches(internalEscape(expression)).Count + 1;
+
+ //does the pattern deal with sup-expressions?
+ if (replacement is string && SUB_REPLACE.IsMatch((string) replacement))
+ {
+ string sreplacement = (string) replacement;
+ // a simple lookup (e.g. $2)
+ if (INDEXED.IsMatch(sreplacement))
+ {
+ pattern.replacement = int.Parse(sreplacement.Substring(1)) - 1;
+ }
+ }
+
+ patterns.Add(pattern);
+ }
+
+ /// <summary>
+ /// builds the patterns into a single regular expression
+ /// </summary>
+ /// <returns></returns>
+ private Regex getPatterns()
+ {
+ StringBuilder rtrn = new StringBuilder(string.Empty);
+ foreach (object pattern in patterns)
+ {
+ rtrn.Append(((Pattern) pattern).ToString() + "|");
+ }
+ rtrn.Remove(rtrn.Length - 1, 1);
+ return new Regex(rtrn.ToString(), ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None );
+ }
+
+ /// <summary>
+ /// Global replacement function. Called once for each match found
+ /// </summary>
+ /// <param name="match">Match found</param>
+ private string replacement(Match match)
+ {
+ int i = 1, j = 0;
+ Pattern pattern;
+ //loop through the patterns
+ while (!((pattern = (Pattern) patterns[j++]) == null))
+ {
+ //do we have a result?
+ if (match.Groups[i].Value != string.Empty)
+ {
+ object replacement = pattern.replacement;
+ if (replacement is MatchGroupEvaluator)
+ {
+ return ((MatchGroupEvaluator) replacement)(match, i);
+ }
+ else if (replacement is int)
+ {
+ return match.Groups[(int) replacement + i].Value;
+ }
+ else
+ {
+ //string, send to interpreter
+ return replacementString(match, i, (string) replacement, pattern.length);
+ }
+ }
+ else //skip over references to sub-expressions
+ i += pattern.length;
+ }
+ return match.Value; //should never be hit, but you never know
+ }
+
+ /// <summary>
+ /// Replacement function for complicated lookups (e.g. Hello $3 $2)
+ /// </summary>
+ private string replacementString(Match match, int offset, string replacement, int length)
+ {
+ while (length > 0)
+ {
+ replacement = replacement.Replace("$" + length--, match.Groups[offset + length].Value);
+ }
+ return replacement;
+ }
+
+ private StringCollection escaped = new StringCollection();
+
+ //encode escaped characters
+ private string escape(string str)
+ {
+ if (escapeChar == '\0')
+ return str;
+ Regex escaping = new Regex("\\\\(.)");
+ return escaping.Replace(str, new MatchEvaluator(escapeMatch));
+ }
+
+ private string escapeMatch(Match match)
+ {
+ escaped.Add(match.Groups[1].Value);
+ return "\\";
+ }
+
+ //decode escaped characters
+ private int unescapeIndex = 0;
+ private string unescape(string str)
+ {
+ if (escapeChar == '\0')
+ return str;
+ Regex unescaping = new Regex("\\" + escapeChar);
+ return unescaping.Replace(str, new MatchEvaluator(unescapeMatch));
+ }
+
+ private string unescapeMatch(Match match)
+ {
+ return "\\" + escaped[unescapeIndex++];
+ }
+
+ private string internalEscape(string str)
+ {
+ return ESCAPE.Replace(str, "");
+ }
+
+ //subclass for each pattern
+ private class Pattern
+ {
+ public string expression;
+ public object replacement;
+ public int length;
+
+ public override string ToString()
+ {
+ return "(" + expression + ")";
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6fdcc21
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CustomMsBuildTasks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CustomMsBuildTasks")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("952e3aaa-5dc6-4b71-8c9c-6b485263be19")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/DotNetOpenAuth.BuildTasks/Publicize.cs b/src/DotNetOpenAuth.BuildTasks/Publicize.cs
new file mode 100644
index 0000000..f1781a7
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/Publicize.cs
@@ -0,0 +1,97 @@
+//-----------------------------------------------------------------------
+// <copyright file="Publicize.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.BuildEngine;
+ using Microsoft.Build.Utilities;
+ using Microsoft.Build.Framework;
+
+ public class Publicize : ToolTask {
+ [Required]
+ public string MSBuildExtensionsPath { get; set; }
+
+ [Required]
+ public ITaskItem Assembly { get; set; }
+
+ public bool DelaySign { get; set; }
+
+ public string KeyFile { get; set; }
+
+ public bool SkipUnchangedFiles { get; set; }
+
+ [Output]
+ public ITaskItem AccessorAssembly { get; set; }
+
+ /// <summary>
+ /// Generates the full path to tool.
+ /// </summary>
+ /// <returns>An absolute path.</returns>
+ protected override string GenerateFullPathToTool() {
+ string toolPath = Path.Combine(this.MSBuildExtensionsPath, @"Microsoft\VisualStudio\v9.0\TeamTest\Publicize.exe");
+ return toolPath;
+ }
+
+ /// <summary>
+ /// Gets the name of the tool.
+ /// </summary>
+ /// <value>The name of the tool.</value>
+ protected override string ToolName {
+ get { return "Publicize.exe"; }
+ }
+
+ /// <summary>
+ /// Validates the parameters.
+ /// </summary>
+ protected override bool ValidateParameters() {
+ if (!base.ValidateParameters()) {
+ return false;
+ }
+
+ if (this.DelaySign && string.IsNullOrEmpty(this.KeyFile)) {
+ this.Log.LogError("DelaySign=true, but no KeyFile given.");
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Generates the command line commands.
+ /// </summary>
+ protected override string GenerateCommandLineCommands() {
+ CommandLineBuilder builder = new CommandLineBuilder();
+
+ if (this.DelaySign) {
+ builder.AppendSwitch("/delaysign");
+ }
+
+ builder.AppendSwitchIfNotNull("/keyfile:", this.KeyFile);
+
+ builder.AppendFileNameIfNotNull(this.Assembly);
+
+ return builder.ToString();
+ }
+
+ public override bool Execute() {
+ this.AccessorAssembly = new TaskItem(this.Assembly);
+ this.AccessorAssembly.ItemSpec = Path.Combine(
+ Path.GetDirectoryName(this.AccessorAssembly.ItemSpec),
+ Path.GetFileNameWithoutExtension(this.AccessorAssembly.ItemSpec) + "_Accessor") + Path.GetExtension(this.AccessorAssembly.ItemSpec);
+
+ if (this.SkipUnchangedFiles && File.GetLastWriteTimeUtc(this.Assembly.ItemSpec) < File.GetLastWriteTimeUtc(this.AccessorAssembly.ItemSpec)) {
+ Log.LogMessage(MessageImportance.Low, "Skipping public accessor generation for {0} because {1} is up to date.", this.Assembly.ItemSpec, this.AccessorAssembly.ItemSpec);
+ return true;
+ }
+
+ return base.Execute();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/Purge.cs b/src/DotNetOpenAuth.BuildTasks/Purge.cs
new file mode 100644
index 0000000..e19e485
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/Purge.cs
@@ -0,0 +1,88 @@
+//-----------------------------------------------------------------------
+// <copyright file="Purge.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Build.Utilities;
+ using Microsoft.Build.Framework;
+ using System.IO;
+ using System.Text.RegularExpressions;
+
+ /// <summary>
+ /// Purges directory trees of all directories and files that are not on a whitelist.
+ /// </summary>
+ /// <remarks>
+ /// This task performs a function similar to robocopy's /MIR switch, except that
+ /// this task does not require that an entire directory tree be used as the source
+ /// in order to purge old files from the destination.
+ /// </remarks>
+ public class Purge : Task {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Purge"/> class.
+ /// </summary>
+ public Purge() {
+ this.PurgeEmptyDirectories = true;
+ }
+
+ /// <summary>
+ /// Gets or sets the root directories to purge.
+ /// </summary>
+ /// <value>The directories.</value>
+ [Required]
+ public string[] Directories { get; set; }
+
+ /// <summary>
+ /// Gets or sets the files that should be NOT be purged.
+ /// </summary>
+ public ITaskItem[] IntendedFiles { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether empty directories will be deleted.
+ /// </summary>
+ /// <value>
+ /// The default value is <c>true</c>.
+ /// </value>
+ public bool PurgeEmptyDirectories { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ HashSet<string> intendedFiles = new HashSet<string>(this.IntendedFiles.Select(file => file.GetMetadata("FullPath")), StringComparer.OrdinalIgnoreCase);
+
+ foreach (string directory in this.Directories.Select(dir => NormalizePath(dir)).Where(dir => Directory.Exists(dir))) {
+ foreach (string existingFile in Directory.GetFiles(directory, "*", SearchOption.AllDirectories)) {
+ if (!intendedFiles.Contains(existingFile)) {
+ this.Log.LogWarning("Purging file \"{0}\".", existingFile);
+ File.Delete(existingFile);
+ }
+ }
+
+ if (this.PurgeEmptyDirectories) {
+ foreach (string subdirectory in Directory.GetDirectories(directory, "*", SearchOption.AllDirectories)) {
+ // We have to check for the existance of the directory because it MAY be
+ // a descendent of a directory we already deleted in this loop.
+ if (Directory.Exists(subdirectory)) {
+ if (Directory.GetDirectories(subdirectory).Length == 0 && Directory.GetFiles(subdirectory).Length == 0) {
+ this.Log.LogWarning("Purging empty directory \"{0}\".", subdirectory);
+ Directory.Delete(subdirectory);
+ }
+ }
+ }
+ }
+ }
+
+ return !this.Log.HasLoggedErrors;
+ }
+
+ private static string NormalizePath(string path) {
+ return Regex.Replace(path, @"\\+", @"\");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs
new file mode 100644
index 0000000..a0ba386
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs
@@ -0,0 +1,56 @@
+//-----------------------------------------------------------------------
+// <copyright file="ReSignDelaySignedAssemblies.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ public class ReSignDelaySignedAssemblies : SnToolTask {
+ /// <summary>
+ /// Gets or sets the key file to use for signing.
+ /// </summary>
+ public ITaskItem KeyFile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the key container.
+ /// </summary>
+ public ITaskItem KeyContainer { get; set; }
+
+ /// <summary>
+ /// Gets or sets the assemblies to re-sign.
+ /// </summary>
+ public ITaskItem[] Assemblies { get; set; }
+
+ /// <summary>
+ /// Generates the command line commands.
+ /// </summary>
+ protected override string GenerateCommandLineCommands() {
+ ////if (this.Assemblies.Length != 1) {
+ //// throw new NotSupportedException("Exactly 1 assembly for signing is supported.");
+ ////}
+ CommandLineBuilder args = new CommandLineBuilder();
+ args.AppendSwitch("-q");
+
+ if (this.KeyFile != null) {
+ args.AppendSwitch("-R");
+ } else if (this.KeyContainer != null) {
+ args.AppendSwitch("-Rc");
+ } else {
+ throw new InvalidOperationException("Either KeyFile or KeyContainer must be set.");
+ }
+
+ args.AppendFileNameIfNotNull(this.Assemblies[0]);
+ if (this.KeyFile != null) {
+ args.AppendFileNameIfNotNull(this.KeyFile);
+ } else {
+ args.AppendFileNameIfNotNull(this.KeyContainer);
+ }
+
+ return args.ToString();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs b/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs
new file mode 100644
index 0000000..2de5976
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+
+namespace DotNetOpenAuth.BuildTasks {
+ public class SetEnvironmentVariable : Task {
+ public SetEnvironmentVariable() {
+ Scope = EnvironmentVariableTarget.Process;
+ }
+
+ /// <summary>
+ /// The name of the environment variable to set or clear.
+ /// </summary>
+ [Required]
+ public string Name { get; set; }
+ /// <summary>
+ /// The value of the environment variable, or the empty string to clear it.
+ /// </summary>
+ [Required]
+ public string Value { get; set; }
+ /// <summary>
+ /// The target environment for the variable. Machine, User, or Process.
+ /// </summary>
+ public EnvironmentVariableTarget Scope { get; set; }
+
+ public override bool Execute() {
+ Environment.SetEnvironmentVariable(Name, Value, Scope);
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs b/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs
new file mode 100644
index 0000000..2e69926
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="SignatureVerification.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using Microsoft.Build.Utilities;
+
+ public class SignatureVerification : SnToolTask {
+ /// <summary>
+ /// Gets or sets a value indicating whether to register the given assembly and public key token
+ /// for skip verification or clear any pre-existing skip verification entry.
+ /// </summary>
+ public bool SkipVerification { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of the assembly.
+ /// </summary>
+ /// <value>The name of the assembly.</value>
+ public string AssemblyName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the public key token.
+ /// </summary>
+ /// <value>The public key token.</value>
+ public string PublicKeyToken { get; set; }
+
+ /// <summary>
+ /// Generates the command line commands.
+ /// </summary>
+ protected override string GenerateCommandLineCommands() {
+ CommandLineBuilder builder = new CommandLineBuilder();
+ builder.AppendSwitch("-q");
+ if (this.SkipVerification) {
+ builder.AppendSwitch("-Vr");
+ } else {
+ builder.AppendSwitch("-Vu");
+ }
+
+ builder.AppendFileNameIfNotNull(this.AssemblyName + "," + this.PublicKeyToken);
+ return builder.ToString();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs b/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs
new file mode 100644
index 0000000..29896fe
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs
@@ -0,0 +1,37 @@
+//-----------------------------------------------------------------------
+// <copyright file="SnToolTask.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.IO;
+ using Microsoft.Build.Utilities;
+
+ public abstract class SnToolTask : ToolTask {
+ /// <summary>
+ /// Gets the name of the tool.
+ /// </summary>
+ /// <value>The name of the tool.</value>
+ protected override string ToolName {
+ get { return "sn.exe"; }
+ }
+
+ /// <summary>
+ /// Generates the full path to tool.
+ /// </summary>
+ protected override string GenerateFullPathToTool() {
+ string[] versions = new[] { "v6.0A", "v6.1", "v7.0a" };
+ string fullPath = null;
+ foreach (string version in versions) {
+ fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Microsoft SDKs\Windows\" + version + @"\bin\" + this.ToolName);
+ if (File.Exists(fullPath)) {
+ return fullPath;
+ }
+ }
+
+ throw new FileNotFoundException("Unable to find sn.exe tool.", fullPath);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs b/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs
new file mode 100644
index 0000000..17647fd
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/TaskStrings.Designer.cs
@@ -0,0 +1,117 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4927
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class TaskStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal TaskStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.BuildTasks.TaskStrings", typeof(TaskStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Web application &apos;{0}&apos; deleted..
+ /// </summary>
+ internal static string DeletedWebApplication {
+ get {
+ return ResourceManager.GetString("DeletedWebApplication", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The {0} and {1} properties must be set to arrays of equal length..
+ /// </summary>
+ internal static string MismatchingArrayLengths {
+ get {
+ return ResourceManager.GetString("MismatchingArrayLengths", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No web site with the name &apos;{0}&apos; found..
+ /// </summary>
+ internal static string NoMatchingWebSiteFound {
+ get {
+ return ResourceManager.GetString("NoMatchingWebSiteFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Packing javascript resource &quot;{0}&quot; into &quot;{1}&quot;..
+ /// </summary>
+ internal static string PackingJsFile {
+ get {
+ return ResourceManager.GetString("PackingJsFile", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Skip packing &quot;{0}&quot; because its packed version is up to date..
+ /// </summary>
+ internal static string SkipPackingJsFile {
+ get {
+ return ResourceManager.GetString("SkipPackingJsFile", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Web application &apos;{0}&apos; was not found, so it was not deleted..
+ /// </summary>
+ internal static string WebApplicationNotFoundSoNotDeleted {
+ get {
+ return ResourceManager.GetString("WebApplicationNotFoundSoNotDeleted", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/TaskStrings.resx b/src/DotNetOpenAuth.BuildTasks/TaskStrings.resx
new file mode 100644
index 0000000..50e1592
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/TaskStrings.resx
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="DeletedWebApplication" xml:space="preserve">
+ <value>Web application '{0}' deleted.</value>
+ </data>
+ <data name="MismatchingArrayLengths" xml:space="preserve">
+ <value>The {0} and {1} properties must be set to arrays of equal length.</value>
+ </data>
+ <data name="NoMatchingWebSiteFound" xml:space="preserve">
+ <value>No web site with the name '{0}' found.</value>
+ </data>
+ <data name="PackingJsFile" xml:space="preserve">
+ <value>Packing javascript resource "{0}" into "{1}".</value>
+ </data>
+ <data name="SkipPackingJsFile" xml:space="preserve">
+ <value>Skip packing "{0}" because its packed version is up to date.</value>
+ </data>
+ <data name="WebApplicationNotFoundSoNotDeleted" xml:space="preserve">
+ <value>Web application '{0}' was not found, so it was not deleted.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.BuildTasks/Trim.cs b/src/DotNetOpenAuth.BuildTasks/Trim.cs
new file mode 100644
index 0000000..972b87d
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/Trim.cs
@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------
+// <copyright file="Trim.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ /// <summary>
+ /// Trims item identities or metadata.
+ /// </summary>
+ public class Trim : Task {
+ /// <summary>
+ /// Gets or sets the name of the metadata to trim. Leave empty or null to operate on itemspec.
+ /// </summary>
+ /// <value>The name of the metadata.</value>
+ public string MetadataName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the characters that should be trimmed off if found at the start of items' ItemSpecs.
+ /// </summary>
+ public string StartCharacters { get; set; }
+
+ /// <summary>
+ /// Gets or sets the characters that should be trimmed off if found at the end of items' ItemSpecs.
+ /// </summary>
+ public string EndCharacters { get; set; }
+
+ /// <summary>
+ /// Gets or sets the substring that should be trimmed along with everything that appears after it.
+ /// </summary>
+ public string AllAfter { get; set; }
+
+ /// <summary>
+ /// Gets or sets the items with ItemSpec's to be trimmed.
+ /// </summary>
+ [Required]
+ public ITaskItem[] Inputs { get; set; }
+
+ /// <summary>
+ /// Gets or sets the items with trimmed ItemSpec strings.
+ /// </summary>
+ [Output]
+ public ITaskItem[] Outputs { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ /// <returns>A value indicating whether the task completed successfully.</returns>
+ public override bool Execute() {
+ this.Outputs = new ITaskItem[this.Inputs.Length];
+ for (int i = 0; i < this.Inputs.Length; i++) {
+ this.Outputs[i] = new TaskItem(this.Inputs[i]);
+ string value = string.IsNullOrEmpty(this.MetadataName) ? this.Outputs[i].ItemSpec : this.Outputs[i].GetMetadata(this.MetadataName);
+ if (!string.IsNullOrEmpty(this.StartCharacters)) {
+ value = value.TrimStart(this.StartCharacters.ToCharArray());
+ }
+ if (!string.IsNullOrEmpty(this.EndCharacters)) {
+ value = value.TrimEnd(this.EndCharacters.ToCharArray());
+ }
+ if (!string.IsNullOrEmpty(this.AllAfter)) {
+ int index = value.IndexOf(this.AllAfter);
+ if (index >= 0) {
+ value = value.Substring(0, index);
+ }
+ }
+ if (string.IsNullOrEmpty(this.MetadataName)) {
+ this.Outputs[i].ItemSpec = value;
+ } else {
+ this.Outputs[i].SetMetadata(this.MetadataName, value);
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/App.config b/src/DotNetOpenAuth.Test/App.config
index 359e25f..b3da723 100644
--- a/src/DotNetOpenAuth.Test/App.config
+++ b/src/DotNetOpenAuth.Test/App.config
@@ -49,5 +49,11 @@
</security>
</provider>
</openid>
+ <!-- We definitely do NOT want to report on events that happen while running tests. -->
+ <reporting enabled="false" />
</dotNetOpenAuth>
+
+ <system.diagnostics>
+ <assert assertuienabled="false"/>
+ </system.diagnostics>
</configuration> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Test/AssemblyTesting.cs b/src/DotNetOpenAuth.Test/AssemblyTesting.cs
index d0868d2..7659a82 100644
--- a/src/DotNetOpenAuth.Test/AssemblyTesting.cs
+++ b/src/DotNetOpenAuth.Test/AssemblyTesting.cs
@@ -14,10 +14,9 @@ namespace DotNetOpenAuth.Test {
public static void AssemblyInitialize(TestContext tc) {
// Make contract failures become test failures.
Contract.ContractFailed += (sender, e) => {
- if (e.FailureKind == ContractFailureKind.Precondition) {
- // Currently we ignore these so that the regular ErrorUtilities can kick in.
- e.SetHandled();
- } else {
+ // For now, we have tests that verify that preconditions throw exceptions.
+ // So we don't want to fail a test just because a precondition check failed.
+ if (e.FailureKind != ContractFailureKind.Precondition) {
e.SetHandled();
Assert.Fail(e.FailureKind.ToString() + ": " + e.Message);
}
diff --git a/src/DotNetOpenAuth.Test/CoordinatorBase.cs b/src/DotNetOpenAuth.Test/CoordinatorBase.cs
index d1bf27c..df331f3 100644
--- a/src/DotNetOpenAuth.Test/CoordinatorBase.cs
+++ b/src/DotNetOpenAuth.Test/CoordinatorBase.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Test {
using System;
+ using System.Diagnostics.Contracts;
using System.Threading;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.RelyingParty;
@@ -17,8 +18,8 @@ namespace DotNetOpenAuth.Test {
private Action<T2> party2Action;
protected CoordinatorBase(Action<T1> party1Action, Action<T2> party2Action) {
- ErrorUtilities.VerifyArgumentNotNull(party1Action, "party1Action");
- ErrorUtilities.VerifyArgumentNotNull(party2Action, "party2Action");
+ Contract.Requires<ArgumentNullException>(party1Action != null);
+ Contract.Requires<ArgumentNullException>(party2Action != null);
this.party1Action = party1Action;
this.party2Action = party2Action;
@@ -38,6 +39,7 @@ namespace DotNetOpenAuth.Test {
// terminate the other thread and inform the test host that the test failed.
Action<Action> safeWrapper = (action) => {
try {
+ TestBase.SetMockHttpContext();
action();
} catch (Exception ex) {
// We may be the second thread in an ThreadAbortException, so check the "flag"
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index bd317af..48a43db 100644
--- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
+++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
@@ -1,7 +1,11 @@
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
+ <ProjectRoot Condition="'$(ProjectRoot)' == ''">$(MSBuildProjectDirectory)\..\..\</ProjectRoot>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ </PropertyGroup>
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" />
+ <PropertyGroup>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}</ProjectGuid>
@@ -17,16 +21,15 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
- <OutputPath>..\..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsCustomRewriterAssembly>
</CodeContractsCustomRewriterAssembly>
<CodeContractsCustomRewriterClass>
</CodeContractsCustomRewriterClass>
- <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsRuntimeCheckingLevel>None</CodeContractsRuntimeCheckingLevel>
<CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
<CodeContractsBuildReferenceAssembly>False</CodeContractsBuildReferenceAssembly>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
@@ -44,22 +47,43 @@
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
<CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
- <OutputPath>..\..\bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Sign)' == 'true' ">
- <SignAssembly>true</SignAssembly>
- <AssemblyOriginatorKeyFile>..\official-build-key.pfx</AssemblyOriginatorKeyFile>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
+ <CodeContractsCustomRewriterAssembly>
+ </CodeContractsCustomRewriterAssembly>
+ <CodeContractsCustomRewriterClass>
+ </CodeContractsCustomRewriterClass>
+ <CodeContractsRuntimeCheckingLevel>None</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
+ <CodeContractsBuildReferenceAssembly>False</CodeContractsBuildReferenceAssembly>
+ <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
+ <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsLibPaths>
+ </CodeContractsLibPaths>
+ <CodeContractsPlatformPath>
+ </CodeContractsPlatformPath>
+ <CodeContractsExtraAnalysisOptions>
+ </CodeContractsExtraAnalysisOptions>
+ <CodeContractsBaseLineFile>
+ </CodeContractsBaseLineFile>
+ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
+ <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
+ <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
- <OutputPath>..\..\bin\CodeAnalysis\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -71,7 +95,7 @@
</CodeContractsCustomRewriterAssembly>
<CodeContractsCustomRewriterClass>
</CodeContractsCustomRewriterClass>
- <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsRuntimeCheckingLevel>None</CodeContractsRuntimeCheckingLevel>
<CodeContractsRunCodeAnalysis>True</CodeContractsRunCodeAnalysis>
<CodeContractsBuildReferenceAssembly>False</CodeContractsBuildReferenceAssembly>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
@@ -87,6 +111,10 @@
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>True</CodeContractsShowSquigglies>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
@@ -131,10 +159,13 @@
<Compile Include="Hosting\HostingTests.cs" />
<Compile Include="Hosting\HttpHost.cs" />
<Compile Include="Hosting\TestingWorkerRequest.cs" />
+ <Compile Include="LocalizationTests.cs" />
<Compile Include="Messaging\CollectionAssert.cs" />
<Compile Include="Messaging\EnumerableCacheTests.cs" />
<Compile Include="Messaging\ErrorUtilitiesTests.cs" />
<Compile Include="Messaging\MessageSerializerTests.cs" />
+ <Compile Include="Messaging\MultipartPostPartTests.cs" />
+ <Compile Include="Messaging\OutgoingWebResponseTests.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionTests.cs" />
<Compile Include="Messaging\Reflection\MessageDictionaryTests.cs" />
<Compile Include="Messaging\MessagingTestBase.cs" />
@@ -152,6 +183,7 @@
<Compile Include="Mocks\InMemoryTokenManager.cs" />
<Compile Include="Mocks\MockHttpRequest.cs" />
<Compile Include="Mocks\MockIdentifier.cs" />
+ <Compile Include="Mocks\MockIdentifierDiscoveryService.cs" />
<Compile Include="Mocks\MockOpenIdExtension.cs" />
<Compile Include="Mocks\MockRealm.cs" />
<Compile Include="Mocks\MockTransformationBindingElement.cs" />
@@ -186,6 +218,8 @@
<Compile Include="Messaging\Bindings\StandardReplayProtectionBindingElementTests.cs" />
<Compile Include="OpenId\ChannelElements\SigningBindingElementTests.cs" />
<Compile Include="OpenId\DiffieHellmanTests.cs" />
+ <Compile Include="OpenId\DiscoveryServices\UriDiscoveryServiceTests.cs" />
+ <Compile Include="OpenId\DiscoveryServices\XriDiscoveryProxyServiceTests.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\FetchRequestTests.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\FetchResponseTests.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeExchangeRoundtripTests.cs" />
@@ -222,6 +256,7 @@
<Compile Include="OpenId\OpenIdCoordinator.cs" />
<Compile Include="OpenId\AssociationHandshakeTests.cs" />
<Compile Include="OpenId\OpenIdTestBase.cs" />
+ <Compile Include="OpenId\OpenIdUtilitiesTests.cs" />
<Compile Include="OpenId\Provider\PerformanceTests.cs" />
<Compile Include="OpenId\ProviderEndpointDescriptionTests.cs" />
<Compile Include="OpenId\Provider\AnonymousRequestTests.cs" />
@@ -232,11 +267,12 @@
<Compile Include="OpenId\RelyingParty\AuthenticationRequestTests.cs" />
<Compile Include="OpenId\RelyingParty\FailedAuthenticationResponseTests.cs" />
<Compile Include="OpenId\RelyingParty\NegativeAuthenticationResponseTests.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdTextBoxTests.cs" />
<Compile Include="OpenId\RelyingParty\PositiveAnonymousResponseTests.cs" />
<Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponseTests.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyTests.cs" />
<Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettingsTests.cs" />
- <Compile Include="OpenId\RelyingParty\ServiceEndpointTests.cs" />
+ <Compile Include="OpenId\RelyingParty\IdentifierDiscoveryResultTests.cs" />
<Compile Include="OpenId\UriIdentifierTests.cs" />
<Compile Include="OpenId\XriIdentifierTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -291,8 +327,11 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
+ <EmbeddedResource Include="OpenId\Discovery\xrdsdiscovery\xrds20dual.xml" />
+ </ItemGroup>
+ <ItemGroup>
<Folder Include="OpenId\UI\" />
</ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
- <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
-</Project> \ No newline at end of file
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" />
+</Project>
diff --git a/src/DotNetOpenAuth.Test/LocalizationTests.cs b/src/DotNetOpenAuth.Test/LocalizationTests.cs
new file mode 100644
index 0000000..50e9a34
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/LocalizationTests.cs
@@ -0,0 +1,28 @@
+//-----------------------------------------------------------------------
+// <copyright file="LocalizationTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test {
+ using System;
+ using System.Globalization;
+ using System.Threading;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ /// <summary>
+ /// Tests various localized resources work as expected.
+ /// </summary>
+ [TestClass]
+ public class LocalizationTests {
+ /// <summary>
+ /// Tests that Serbian localized strings are correctly installed.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(InvalidOperationException), "Ovaj metod zahteva tekući HttpContext. Kao alternativa, koristite preklopljeni metod koji dozvoljava da se prosledi informacija bez HttpContext-a.")]
+ public void Serbian() {
+ Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("sr");
+ ErrorUtilities.VerifyHttpContext();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs
index cbaded1..3cc792b 100644
--- a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs
@@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings {
[TestMethod, ExpectedException(typeof(ExpiredMessageException))]
public void VerifyBadTimestampIsRejected() {
this.Channel = CreateChannel(MessageProtections.Expiration);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow - StandardExpirationBindingElement.DefaultMaximumMessageAge - TimeSpan.FromSeconds(1), false);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow - StandardExpirationBindingElement.MaximumMessageAge - TimeSpan.FromSeconds(1), false);
}
}
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs
index 26ce01c..14651bc 100644
--- a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardReplayProtectionBindingElementTests.cs
@@ -31,6 +31,7 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings {
this.protocol = Protocol.Default;
this.nonceStore = new NonceMemoryStore(TimeSpan.FromHours(3));
this.nonceElement = new StandardReplayProtectionBindingElement(this.nonceStore);
+ this.nonceElement.Channel = new Mocks.TestChannel();
this.message = new TestReplayProtectedMessage();
this.message.UtcCreationDate = DateTime.UtcNow;
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
index 669abbc..7846411 100644
--- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
@@ -201,6 +201,7 @@ namespace DotNetOpenAuth.Test.Messaging {
[TestMethod, ExpectedException(typeof(InvalidOperationException))]
public void ReadFromRequestNoContext() {
+ HttpContext.Current = null;
TestBadChannel badChannel = new TestBadChannel(false);
badChannel.ReadFromRequest();
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs b/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs
index f9e569a..db136f5 100644
--- a/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/CollectionAssert.cs
@@ -5,16 +5,18 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Test.Messaging {
+ using System;
using System.Collections;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
internal class CollectionAssert<T> {
internal static void AreEquivalent(ICollection<T> expected, ICollection<T> actual) {
- ErrorUtilities.VerifyArgumentNotNull(expected, "expected");
- ErrorUtilities.VerifyArgumentNotNull(actual, "actual");
+ Contract.Requires<ArgumentNullException>(expected != null);
+ Contract.Requires<ArgumentNullException>(actual != null);
ICollection expectedNonGeneric = new List<T>(expected);
ICollection actualNonGeneric = new List<T>(actual);
@@ -22,8 +24,8 @@ namespace DotNetOpenAuth.Test.Messaging {
}
internal static void AreEquivalentByEquality(ICollection<T> expected, ICollection<T> actual) {
- ErrorUtilities.VerifyArgumentNotNull(expected, "expected");
- ErrorUtilities.VerifyArgumentNotNull(actual, "actual");
+ Contract.Requires<ArgumentNullException>(expected != null);
+ Contract.Requires<ArgumentNullException>(actual != null);
Assert.AreEqual(expected.Count, actual.Count);
foreach (T value in expected) {
@@ -32,7 +34,7 @@ namespace DotNetOpenAuth.Test.Messaging {
}
internal static void Contains(IEnumerable<T> sequence, T element) {
- ErrorUtilities.VerifyArgumentNotNull(sequence, "sequence");
+ Contract.Requires<ArgumentNullException>(sequence != null);
if (!sequence.Contains(element)) {
Assert.Fail("Sequence did not include expected element '{0}'.", element);
diff --git a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs
index 05ac306..fd77746 100644
--- a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs
@@ -5,6 +5,8 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Test.Messaging {
+ using System;
+ using System.Collections.Specialized;
using System.Web;
using DotNetOpenAuth.Messaging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -92,5 +94,59 @@ namespace DotNetOpenAuth.Test.Messaging {
HttpRequestInfo info = new HttpRequestInfo();
Assert.IsNull(info.QueryString["hi"]);
}
+
+ /// <summary>
+ /// Verifies SSL forwarders are correctly handled when they supply X_FORWARDED_PROTO and HOST
+ /// </summary>
+ [TestMethod]
+ public void GetPublicFacingUrlSSLForwarder1() {
+ HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b");
+ var serverVariables = new NameValueCollection();
+ serverVariables["HTTP_X_FORWARDED_PROTO"] = "https";
+ serverVariables["HTTP_HOST"] = "somehost";
+ Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables);
+ Uri expected = new Uri("https://somehost/a.aspx?a=b");
+ Assert.AreEqual(expected, actual);
+ }
+
+ /// <summary>
+ /// Verifies SSL forwarders are correctly handled when they supply X_FORWARDED_PROTO and HOST:port
+ /// </summary>
+ [TestMethod]
+ public void GetPublicFacingUrlSSLForwarder2() {
+ HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b");
+ var serverVariables = new NameValueCollection();
+ serverVariables["HTTP_X_FORWARDED_PROTO"] = "https";
+ serverVariables["HTTP_HOST"] = "somehost:999";
+ Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables);
+ Uri expected = new Uri("https://somehost:999/a.aspx?a=b");
+ Assert.AreEqual(expected, actual);
+ }
+
+ /// <summary>
+ /// Verifies SSL forwarders are correctly handled when they supply just HOST
+ /// </summary>
+ [TestMethod]
+ public void GetPublicFacingUrlSSLForwarder3() {
+ HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b");
+ var serverVariables = new NameValueCollection();
+ serverVariables["HTTP_HOST"] = "somehost";
+ Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables);
+ Uri expected = new Uri("http://somehost/a.aspx?a=b");
+ Assert.AreEqual(expected, actual);
+ }
+
+ /// <summary>
+ /// Verifies SSL forwarders are correctly handled when they supply just HOST:port
+ /// </summary>
+ [TestMethod]
+ public void GetPublicFacingUrlSSLForwarder4() {
+ HttpRequest req = new HttpRequest("a.aspx", "http://someinternalhost/a.aspx?a=b", "a=b");
+ var serverVariables = new NameValueCollection();
+ serverVariables["HTTP_HOST"] = "somehost:79";
+ Uri actual = HttpRequestInfo.GetPublicFacingUrl(req, serverVariables);
+ Uri expected = new Uri("http://somehost:79/a.aspx?a=b");
+ Assert.AreEqual(expected, actual);
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
index 26ce4cd..21c589f 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
@@ -11,8 +11,10 @@ namespace DotNetOpenAuth.Test.Messaging
using System.Collections.Specialized;
using System.IO;
using System.Net;
+ using System.Text.RegularExpressions;
using System.Web;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Test.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -139,5 +141,79 @@ namespace DotNetOpenAuth.Test.Messaging
Assert.AreEqual("%C2%80", MessagingUtilities.EscapeUriDataStringRfc3986("\u0080"));
Assert.AreEqual("%E3%80%81", MessagingUtilities.EscapeUriDataStringRfc3986("\u3001"));
}
+
+ /// <summary>
+ /// Verifies the overall format of the multipart POST is correct.
+ /// </summary>
+ [TestMethod]
+ public void PostMultipart() {
+ var httpHandler = new TestWebRequestHandler();
+ bool callbackTriggered = false;
+ httpHandler.Callback = req => {
+ Match m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)");
+ Assert.IsTrue(m.Success, "Content-Type HTTP header not set correctly.");
+ string boundary = m.Groups[1].Value;
+ string expectedEntity = "--{0}\r\nContent-Disposition: form-data; name=\"a\"\r\n\r\nb\r\n--{0}--\r\n";
+ expectedEntity = string.Format(expectedEntity, boundary);
+ string actualEntity = httpHandler.RequestEntityAsString;
+ Assert.AreEqual(expectedEntity, actualEntity);
+ callbackTriggered = true;
+ Assert.AreEqual(req.ContentLength, actualEntity.Length);
+ IncomingWebResponse resp = new CachedDirectWebResponse();
+ return resp;
+ };
+ var request = (HttpWebRequest)WebRequest.Create("http://someserver");
+ var parts = new[] {
+ MultipartPostPart.CreateFormPart("a", "b"),
+ };
+ request.PostMultipart(httpHandler, parts);
+ Assert.IsTrue(callbackTriggered);
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpVerb
+ /// </summary>
+ [TestMethod]
+ public void GetHttpVerbTest() {
+ Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest));
+ Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest));
+ Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest));
+ Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest));
+ Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest));
+
+ Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpVerb on invalid input.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void GetHttpVerbOutOfRangeTest() {
+ MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.PostRequest);
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpDeliveryMethod
+ /// </summary>
+ [TestMethod]
+ public void GetHttpDeliveryMethodTest() {
+ Assert.AreEqual(HttpDeliveryMethods.GetRequest, MessagingUtilities.GetHttpDeliveryMethod("GET"));
+ Assert.AreEqual(HttpDeliveryMethods.PostRequest, MessagingUtilities.GetHttpDeliveryMethod("POST"));
+ Assert.AreEqual(HttpDeliveryMethods.HeadRequest, MessagingUtilities.GetHttpDeliveryMethod("HEAD"));
+ Assert.AreEqual(HttpDeliveryMethods.PutRequest, MessagingUtilities.GetHttpDeliveryMethod("PUT"));
+ Assert.AreEqual(HttpDeliveryMethods.DeleteRequest, MessagingUtilities.GetHttpDeliveryMethod("DELETE"));
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpDeliveryMethod for an unexpected input
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void GetHttpDeliveryMethodOutOfRangeTest() {
+ MessagingUtilities.GetHttpDeliveryMethod("UNRECOGNIZED");
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
new file mode 100644
index 0000000..57614ba
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
@@ -0,0 +1,108 @@
+//-----------------------------------------------------------------------
+// <copyright file="MultipartPostPartTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System.CodeDom.Compiler;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Net;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class MultipartPostPartTests : TestBase {
+ /// <summary>
+ /// Verifies that the Length property matches the length actually serialized.
+ /// </summary>
+ [TestMethod]
+ public void FormDataSerializeMatchesLength() {
+ var part = MultipartPostPart.CreateFormPart("a", "b");
+ VerifyLength(part);
+ }
+
+ /// <summary>
+ /// Verifies that the length property matches the length actually serialized.
+ /// </summary>
+ [TestMethod]
+ public void FileSerializeMatchesLength() {
+ using (TempFileCollection tfc = new TempFileCollection()) {
+ string file = tfc.AddExtension(".txt");
+ File.WriteAllText(file, "sometext");
+ var part = MultipartPostPart.CreateFormFilePart("someformname", file, "text/plain");
+ VerifyLength(part);
+ }
+ }
+
+ /// <summary>
+ /// Verifies file multiparts identify themselves as files and not merely form-data.
+ /// </summary>
+ [TestMethod]
+ public void FilePartAsFile() {
+ var part = MultipartPostPart.CreateFormFilePart("somename", "somefile", "plain/text", new MemoryStream());
+ Assert.AreEqual("file", part.ContentDisposition);
+ }
+
+ /// <summary>
+ /// Verifies MultiPartPost sends the right number of bytes.
+ /// </summary>
+ [TestMethod]
+ public void MultiPartPostAscii() {
+ using (TempFileCollection tfc = new TempFileCollection()) {
+ string file = tfc.AddExtension("txt");
+ File.WriteAllText(file, "sometext");
+ this.VerifyFullPost(new List<MultipartPostPart> {
+ MultipartPostPart.CreateFormPart("a", "b"),
+ MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"),
+ });
+ }
+ }
+
+ /// <summary>
+ /// Verifies MultiPartPost sends the right number of bytes.
+ /// </summary>
+ [TestMethod]
+ public void MultiPartPostMultiByteCharacters() {
+ using (TempFileCollection tfc = new TempFileCollection()) {
+ string file = tfc.AddExtension("txt");
+ File.WriteAllText(file, "\x1020\x818");
+ this.VerifyFullPost(new List<MultipartPostPart> {
+ MultipartPostPart.CreateFormPart("a", "\x987"),
+ MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"),
+ });
+ }
+ }
+
+ private static void VerifyLength(MultipartPostPart part) {
+ Contract.Requires(part != null);
+
+ var expectedLength = part.Length;
+ var ms = new MemoryStream();
+ var sw = new StreamWriter(ms);
+ part.Serialize(sw);
+ sw.Flush();
+ var actualLength = ms.Length;
+ Assert.AreEqual(expectedLength, actualLength);
+ }
+
+ private void VerifyFullPost(List<MultipartPostPart> parts) {
+ var request = (HttpWebRequest)WebRequest.Create("http://localhost");
+ var handler = new Mocks.TestWebRequestHandler();
+ bool posted = false;
+ handler.Callback = req => {
+ foreach (string header in req.Headers) {
+ TestContext.WriteLine("{0}: {1}", header, req.Headers[header]);
+ }
+ TestContext.WriteLine(handler.RequestEntityAsString);
+ Assert.AreEqual(req.ContentLength, handler.RequestEntityStream.Length);
+ posted = true;
+ return null;
+ };
+ request.PostMultipart(handler, parts);
+ Assert.IsTrue(posted, "HTTP POST never sent.");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs b/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs
new file mode 100644
index 0000000..35f9259
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Messaging/OutgoingWebResponseTests.cs
@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------
+// <copyright file="OutgoingWebResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OutgoingWebResponseTests {
+ /// <summary>
+ /// Verifies that setting the Body property correctly converts to a byte stream.
+ /// </summary>
+ [TestMethod]
+ public void SetBodyToByteStream() {
+ var response = new OutgoingWebResponse();
+ string stringValue = "abc";
+ response.Body = stringValue;
+ Assert.AreEqual(stringValue.Length, response.ResponseStream.Length);
+
+ // Verify that the actual bytes are correct.
+ Encoding encoding = new UTF8Encoding(false); // avoid emitting a byte-order mark
+ var expectedBuffer = encoding.GetBytes(stringValue);
+ var actualBuffer = new byte[stringValue.Length];
+ Assert.AreEqual(stringValue.Length, response.ResponseStream.Read(actualBuffer, 0, stringValue.Length));
+ CollectionAssert.AreEqual(expectedBuffer, actualBuffer);
+
+ // Verify that the header was set correctly.
+ Assert.AreEqual(encoding.HeaderName, response.Headers[HttpResponseHeader.ContentEncoding]);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs
index 24171e1..b9e7436 100644
--- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessageDictionaryTests.cs
@@ -300,7 +300,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection {
[TestMethod]
public void CopyTo() {
ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message);
- IDictionary<string, string> targetAsDictionary = ((IDictionary<string, string>)target);
+ IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target;
KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[target.Count + 1];
int arrayIndex = 1;
target.CopyTo(array, arrayIndex);
@@ -317,7 +317,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection {
[TestMethod]
public void ContainsKeyValuePair() {
ICollection<KeyValuePair<string, string>> target = this.MessageDescriptions.GetAccessor(this.message);
- IDictionary<string, string> targetAsDictionary = ((IDictionary<string, string>)target);
+ IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target;
Assert.IsFalse(target.Contains(new KeyValuePair<string, string>("age", "1")));
Assert.IsTrue(target.Contains(new KeyValuePair<string, string>("age", "0")));
@@ -333,7 +333,7 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection {
[TestMethod]
public void ClearValues() {
MessageDictionary target = this.MessageDescriptions.GetAccessor(this.message);
- IDictionary<string, string> targetAsDictionary = ((IDictionary<string, string>)target);
+ IDictionary<string, string> targetAsDictionary = (IDictionary<string, string>)target;
this.message.Name = "Andrew";
this.message.Age = 15;
targetAsDictionary["extra"] = "value";
diff --git a/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs b/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs
index a0e7c3f..89d165a 100644
--- a/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs
@@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Test.Messaging {
public class ResponseTests : TestBase {
[TestMethod, ExpectedException(typeof(InvalidOperationException))]
public void SendWithoutAspNetContext() {
+ HttpContext.Current = null;
new OutgoingWebResponse().Send();
}
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
index 3bbe6e3..16386de 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading;
@@ -84,7 +85,7 @@ namespace DotNetOpenAuth.Test.Mocks {
/// <param name="outgoingMessageFilter">The outgoing message filter. May be null.</param>
internal CoordinatingChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter)
: base(GetMessageFactory(wrappedChannel), wrappedChannel.BindingElements.ToArray()) {
- ErrorUtilities.VerifyArgumentNotNull(wrappedChannel, "wrappedChannel");
+ Contract.Requires<ArgumentNullException>(wrappedChannel != null);
this.wrappedChannel = wrappedChannel;
this.incomingMessageFilter = incomingMessageFilter;
@@ -220,7 +221,7 @@ namespace DotNetOpenAuth.Test.Mocks {
/// channel since their message factory is not used.
/// </remarks>
protected virtual T CloneSerializedParts<T>(T message) where T : class, IProtocolMessage {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
IProtocolMessage clonedMessage;
var messageAccessor = this.MessageDescriptions.GetAccessor(message);
@@ -251,7 +252,7 @@ namespace DotNetOpenAuth.Test.Mocks {
}
private static IMessageFactory GetMessageFactory(Channel channel) {
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
Channel_Accessor accessor = Channel_Accessor.AttachShadow(channel);
return accessor.MessageFactory;
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs
index 10b0261..e862ca6 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
+ using System.Diagnostics.Contracts;
using System.Threading;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
@@ -30,7 +31,7 @@ namespace DotNetOpenAuth.Test.Mocks {
internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, IConsumerTokenManager tokenManager)
: base(
signingBindingElement,
- new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge),
+ new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge),
tokenManager) {
}
@@ -44,7 +45,7 @@ namespace DotNetOpenAuth.Test.Mocks {
internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, IServiceProviderTokenManager tokenManager)
: base(
signingBindingElement,
- new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge),
+ new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge),
tokenManager) {
}
@@ -134,7 +135,7 @@ namespace DotNetOpenAuth.Test.Mocks {
}
private T CloneSerializedParts<T>(T message, HttpRequestInfo requestInfo) where T : class, IProtocolMessage {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
IProtocolMessage clonedMessage;
var messageAccessor = this.MessageDescriptions.GetAccessor(message);
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs
index 07f9bc9..62ea871 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -20,8 +21,8 @@ namespace DotNetOpenAuth.Test.Mocks {
/// <param name="message">The direct response message to send to the remote channel. This message will be cloned.</param>
/// <param name="receivingChannel">The receiving channel.</param>
internal CoordinatingOutgoingWebResponse(IProtocolMessage message, CoordinatingChannel receivingChannel) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyArgumentNotNull(receivingChannel, "receivingChannel");
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<ArgumentNullException>(receivingChannel != null);
this.receivingChannel = receivingChannel;
this.OriginalMessage = message;
diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
index 48547b7..35672d7 100644
--- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
@@ -106,6 +106,10 @@ namespace DotNetOpenAuth.Test.Mocks {
return this.tokens[token];
}
+ public void UpdateToken(IServiceProviderRequestToken token) {
+ // Nothing to do here, since we're using Linq To SQL.
+ }
+
#endregion
/// <summary>
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs b/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs
index 66f258d..c18ea33 100644
--- a/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/MockHttpRequest.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Net;
@@ -22,7 +23,7 @@ namespace DotNetOpenAuth.Test.Mocks {
private readonly Dictionary<Uri, IncomingWebResponse> registeredMockResponses = new Dictionary<Uri, IncomingWebResponse>();
private MockHttpRequest(IDirectWebRequestHandler mockHandler) {
- ErrorUtilities.VerifyArgumentNotNull(mockHandler, "mockHandler");
+ Contract.Requires<ArgumentNullException>(mockHandler != null);
this.MockWebRequestHandler = mockHandler;
}
@@ -41,7 +42,7 @@ namespace DotNetOpenAuth.Test.Mocks {
}
internal void RegisterMockResponse(IncomingWebResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
if (this.registeredMockResponses.ContainsKey(response.RequestUri)) {
Logger.Http.WarnFormat("Mock HTTP response already registered for {0}.", response.RequestUri);
} else {
@@ -58,9 +59,9 @@ namespace DotNetOpenAuth.Test.Mocks {
}
internal void RegisterMockResponse(Uri requestUri, Uri responseUri, string contentType, WebHeaderCollection headers, string responseBody) {
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
- ErrorUtilities.VerifyArgumentNotNull(responseUri, "responseUri");
- ErrorUtilities.VerifyNonZeroLength(contentType, "contentType");
+ Contract.Requires<ArgumentNullException>(requestUri != null);
+ Contract.Requires<ArgumentNullException>(responseUri != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(contentType));
// Set up the redirect if appropriate
if (requestUri != responseUri) {
@@ -82,8 +83,8 @@ namespace DotNetOpenAuth.Test.Mocks {
}
}
- internal void RegisterMockXrdsResponse(ServiceEndpoint endpoint) {
- ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
+ internal void RegisterMockXrdsResponse(IdentifierDiscoveryResult endpoint) {
+ Contract.Requires<ArgumentNullException>(endpoint != null);
string identityUri;
if (endpoint.ClaimedIdentifier == endpoint.Protocol.ClaimedIdentifierForOPIdentifier) {
@@ -91,11 +92,11 @@ namespace DotNetOpenAuth.Test.Mocks {
} else {
identityUri = endpoint.UserSuppliedIdentifier ?? endpoint.ClaimedIdentifier;
}
- this.RegisterMockXrdsResponse(new Uri(identityUri), new ServiceEndpoint[] { endpoint });
+ this.RegisterMockXrdsResponse(new Uri(identityUri), new IdentifierDiscoveryResult[] { endpoint });
}
- internal void RegisterMockXrdsResponse(Uri respondingUri, IEnumerable<ServiceEndpoint> endpoints) {
- ErrorUtilities.VerifyArgumentNotNull(endpoints, "endpoints");
+ internal void RegisterMockXrdsResponse(Uri respondingUri, IEnumerable<IdentifierDiscoveryResult> endpoints) {
+ Contract.Requires<ArgumentNullException>(endpoints != null);
StringBuilder xrds = new StringBuilder();
xrds.AppendLine(@"<xrds:XRDS xmlns:xrds='xri://$xrds' xmlns:openid='http://openid.net/xmlns/1.0' xmlns='xri://$xrd*($v*2.0)'>
@@ -129,12 +130,12 @@ namespace DotNetOpenAuth.Test.Mocks {
this.RegisterMockResponse(respondingUri, ContentTypes.Xrds, xrds.ToString());
}
- internal void RegisterMockXrdsResponse(UriIdentifier directedIdentityAssignedIdentifier, ServiceEndpoint providerEndpoint) {
- ServiceEndpoint identityEndpoint = ServiceEndpoint.CreateForClaimedIdentifier(
+ internal void RegisterMockXrdsResponse(UriIdentifier directedIdentityAssignedIdentifier, IdentifierDiscoveryResult providerEndpoint) {
+ IdentifierDiscoveryResult identityEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
directedIdentityAssignedIdentifier,
directedIdentityAssignedIdentifier,
- providerEndpoint.ProviderEndpoint,
- providerEndpoint.ProviderDescription,
+ providerEndpoint.ProviderLocalIdentifier,
+ new ProviderEndpointDescription(providerEndpoint.ProviderEndpoint, providerEndpoint.Capabilities),
10,
10);
this.RegisterMockXrdsResponse(identityEndpoint);
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs b/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs
index 669e4d3..9f032b8 100644
--- a/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/MockIdentifier.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
@@ -17,17 +18,17 @@ namespace DotNetOpenAuth.Test.Mocks {
/// having a dependency on a hosted web site to actually perform discovery on.
/// </summary>
internal class MockIdentifier : Identifier {
- private IEnumerable<ServiceEndpoint> endpoints;
+ private IEnumerable<IdentifierDiscoveryResult> endpoints;
private MockHttpRequest mockHttpRequest;
private Identifier wrappedIdentifier;
- public MockIdentifier(Identifier wrappedIdentifier, MockHttpRequest mockHttpRequest, IEnumerable<ServiceEndpoint> endpoints)
- : base(false) {
- ErrorUtilities.VerifyArgumentNotNull(wrappedIdentifier, "wrappedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(mockHttpRequest, "mockHttpRequest");
- ErrorUtilities.VerifyArgumentNotNull(endpoints, "endpoints");
+ public MockIdentifier(Identifier wrappedIdentifier, MockHttpRequest mockHttpRequest, IEnumerable<IdentifierDiscoveryResult> endpoints)
+ : base(wrappedIdentifier.OriginalString, false) {
+ Contract.Requires<ArgumentNullException>(wrappedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(mockHttpRequest != null);
+ Contract.Requires<ArgumentNullException>(endpoints != null);
this.wrappedIdentifier = wrappedIdentifier;
this.endpoints = endpoints;
@@ -38,6 +39,10 @@ namespace DotNetOpenAuth.Test.Mocks {
mockHttpRequest.RegisterMockXrdsResponse(new Uri(wrappedIdentifier.ToString()), endpoints);
}
+ internal IEnumerable<IdentifierDiscoveryResult> DiscoveryEndpoints {
+ get { return this.endpoints; }
+ }
+
public override string ToString() {
return this.wrappedIdentifier.ToString();
}
@@ -50,10 +55,6 @@ namespace DotNetOpenAuth.Test.Mocks {
return this.wrappedIdentifier.GetHashCode();
}
- internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) {
- return this.endpoints;
- }
-
internal override Identifier TrimFragment() {
return this;
}
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs b/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs
new file mode 100644
index 0000000..d74258d
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs
@@ -0,0 +1,47 @@
+//-----------------------------------------------------------------------
+// <copyright file="MockIdentifierDiscoveryService.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ internal class MockIdentifierDiscoveryService : IIdentifierDiscoveryService {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MockIdentifierDiscoveryService"/> class.
+ /// </summary>
+ public MockIdentifierDiscoveryService() {
+ }
+
+ #region IIdentifierDiscoveryService Members
+
+ /// <summary>
+ /// Performs discovery on the specified identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
+ /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
+ /// <returns>
+ /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
+ /// </returns>
+ public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) {
+ var mockIdentifier = identifier as MockIdentifier;
+ if (mockIdentifier == null) {
+ abortDiscoveryChain = false;
+ return Enumerable.Empty<IdentifierDiscoveryResult>();
+ }
+
+ abortDiscoveryChain = true;
+ return mockIdentifier.DiscoveryEndpoints;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs b/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs
index 4e29bba..dd17735 100644
--- a/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs
@@ -5,7 +5,9 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Test.Mocks {
+ using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
@@ -19,7 +21,7 @@ namespace DotNetOpenAuth.Test.Mocks {
/// <param name="relyingPartyDescriptions">The relying party descriptions.</param>
internal MockRealm(Realm wrappedRealm, params RelyingPartyEndpointDescription[] relyingPartyDescriptions)
: base(wrappedRealm) {
- ErrorUtilities.VerifyArgumentNotNull(relyingPartyDescriptions, "relyingPartyDescriptions");
+ Contract.Requires<ArgumentNullException>(relyingPartyDescriptions != null);
this.relyingPartyDescriptions = relyingPartyDescriptions;
}
@@ -35,7 +37,7 @@ namespace DotNetOpenAuth.Test.Mocks {
/// <returns>
/// The details of the endpoints if found, otherwise null.
/// </returns>
- internal override IEnumerable<RelyingPartyEndpointDescription> Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
+ internal override IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
return this.relyingPartyDescriptions;
}
}
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs
index 342a303..1b3c2b2 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs
@@ -11,11 +11,11 @@ namespace DotNetOpenAuth.Test.Mocks {
internal class TestDirectedMessage : TestMessage, IDirectedProtocolMessage {
internal TestDirectedMessage() {
- this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest;
+ this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.HeadRequest;
}
internal TestDirectedMessage(MessageTransport transport) : base(transport) {
- this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest;
+ this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.HeadRequest;
}
#region IDirectedProtocolMessage Members
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestMessageFactory.cs b/src/DotNetOpenAuth.Test/Mocks/TestMessageFactory.cs
index ffb117c..7b13175 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestMessageFactory.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestMessageFactory.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -32,8 +33,6 @@ namespace DotNetOpenAuth.Test.Mocks {
#region IMessageFactory Members
public IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
if (fields.ContainsKey("age")) {
if (this.signedMessages) {
if (this.expiringMessages) {
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestWebRequestHandler.cs b/src/DotNetOpenAuth.Test/Mocks/TestWebRequestHandler.cs
index b74c0f0..03dbd6b 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestWebRequestHandler.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestWebRequestHandler.cs
@@ -56,7 +56,7 @@ namespace DotNetOpenAuth.Test.Mocks {
#region IWebRequestHandler Members
public bool CanSupport(DirectWebRequestOptions options) {
- return options == DirectWebRequestOptions.None;
+ return true;
}
/// <summary>
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
index 856f164..b721fb7 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using System.Text;
@@ -34,13 +35,13 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.webRequestHandler = new TestWebRequestHandler();
this.signingElement = new RsaSha1SigningBindingElement(new InMemoryTokenManager());
- this.nonceStore = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
+ this.nonceStore = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge);
this.channel = new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory());
this.accessor = OAuthChannel_Accessor.AttachShadow(this.channel);
this.channel.WebRequestHandler = this.webRequestHandler;
}
- [TestMethod, ExpectedException(typeof(ArgumentException))]
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void CtorNullSigner() {
new OAuthChannel(null, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory());
}
@@ -226,6 +227,11 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.ParameterizedRequestTest(HttpDeliveryMethods.PostRequest);
}
+ [TestMethod]
+ public void RequestUsingHead() {
+ this.ParameterizedRequestTest(HttpDeliveryMethods.HeadRequest);
+ }
+
/// <summary>
/// Verifies that messages asking for special HTTP status codes get them.
/// </summary>
@@ -243,7 +249,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
}
private static string CreateAuthorizationHeader(IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+ Contract.Requires<ArgumentNullException>(fields != null);
StringBuilder authorization = new StringBuilder();
authorization.Append("OAuth ");
@@ -322,7 +328,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.webRequestHandler.Callback = (req) => {
Assert.IsNotNull(req);
HttpRequestInfo reqInfo = ConvertToRequestInfo(req, this.webRequestHandler.RequestEntityStream);
- Assert.AreEqual(scheme == HttpDeliveryMethods.PostRequest ? "POST" : "GET", reqInfo.HttpMethod);
+ Assert.AreEqual(MessagingUtilities.GetHttpVerb(scheme), reqInfo.HttpMethod);
var incomingMessage = this.channel.ReadFromRequest(reqInfo) as TestMessage;
Assert.IsNotNull(incomingMessage);
Assert.AreEqual(request.Age, incomingMessage.Age);
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs
index 627db8f..01d51a3 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
+ using DotNetOpenAuth.Test.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -16,6 +17,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
[TestMethod]
public void HttpsSignatureGeneration() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
+ target.Channel = new TestChannel();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest);
ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
@@ -29,6 +31,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
public void HttpsSignatureVerification() {
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest);
ITamperProtectionChannelBindingElement target = new PlaintextSigningBindingElement();
+ target.Channel = new TestChannel();
ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
message.TokenSecret = "ts";
@@ -40,6 +43,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
[TestMethod]
public void HttpsSignatureVerificationNotApplicable() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
+ target.Channel = new TestChannel();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest);
ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
@@ -52,6 +56,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
[TestMethod]
public void HttpSignatureGeneration() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
+ target.Channel = new TestChannel();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest);
ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
@@ -66,6 +71,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
[TestMethod]
public void HttpSignatureVerification() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
+ target.Channel = new TestChannel();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest);
ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
diff --git a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs
index e04edeb..5967a03 100644
--- a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs
@@ -6,7 +6,9 @@
namespace DotNetOpenAuth.Test {
using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.Test.Mocks;
@@ -25,8 +27,8 @@ namespace DotNetOpenAuth.Test {
/// <param name="serviceProviderAction">The code path of the Service Provider.</param>
internal OAuthCoordinator(ConsumerDescription consumerDescription, ServiceProviderDescription serviceDescription, Action<WebConsumer> consumerAction, Action<ServiceProvider> serviceProviderAction)
: base(consumerAction, serviceProviderAction) {
- ErrorUtilities.VerifyArgumentNotNull(consumerDescription, "consumerDescription");
- ErrorUtilities.VerifyArgumentNotNull(serviceDescription, "serviceDescription");
+ Contract.Requires<ArgumentNullException>(consumerDescription != null);
+ Contract.Requires<ArgumentNullException>(serviceDescription != null);
this.consumerDescription = consumerDescription;
this.serviceDescription = serviceDescription;
@@ -57,7 +59,7 @@ namespace DotNetOpenAuth.Test {
WebConsumer consumer = new WebConsumer(this.serviceDescription, consumerTokenManager) {
OAuthChannel = consumerChannel,
};
- ServiceProvider serviceProvider = new ServiceProvider(this.serviceDescription, serviceTokenManager) {
+ ServiceProvider serviceProvider = new ServiceProvider(this.serviceDescription, serviceTokenManager, new NonceMemoryStore()) {
OAuthChannel = serviceProviderChannel,
};
diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
index af3b1b1..a9d2f56 100644
--- a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
@@ -318,9 +318,9 @@ namespace DotNetOpenAuth.Test.OpenId {
private void ParameterizedAssociationTest(
ProviderEndpointDescription opDescription,
string expectedAssociationType) {
- Protocol protocol = Protocol.Lookup(opDescription.ProtocolVersion);
+ Protocol protocol = Protocol.Lookup(Protocol.Lookup(opDescription.Version).ProtocolVersion);
bool expectSuccess = expectedAssociationType != null;
- bool expectDiffieHellman = !opDescription.Endpoint.IsTransportSecure();
+ bool expectDiffieHellman = !opDescription.Uri.IsTransportSecure();
Association rpAssociation = null, opAssociation;
AssociateSuccessfulResponse associateSuccessfulResponse = null;
AssociateUnsuccessfulResponse associateUnsuccessfulResponse = null;
@@ -337,7 +337,7 @@ namespace DotNetOpenAuth.Test.OpenId {
op.SendResponse(req);
});
coordinator.IncomingMessageFilter = message => {
- Assert.AreSame(opDescription.ProtocolVersion, message.Version, "The message was recognized as version {0} but was expected to be {1}.", message.Version, opDescription.ProtocolVersion);
+ Assert.AreSame(opDescription.Version, message.Version, "The message was recognized as version {0} but was expected to be {1}.", message.Version, Protocol.Lookup(opDescription.Version).ProtocolVersion);
var associateSuccess = message as AssociateSuccessfulResponse;
var associateFailed = message as AssociateUnsuccessfulResponse;
if (associateSuccess != null) {
@@ -348,7 +348,7 @@ namespace DotNetOpenAuth.Test.OpenId {
}
};
coordinator.OutgoingMessageFilter = message => {
- Assert.AreSame(opDescription.ProtocolVersion, message.Version, "The message was for version {0} but was expected to be for {1}.", message.Version, opDescription.ProtocolVersion);
+ Assert.AreEqual(opDescription.Version, message.Version, "The message was for version {0} but was expected to be for {1}.", message.Version, opDescription.Version);
};
coordinator.Run();
@@ -356,7 +356,7 @@ namespace DotNetOpenAuth.Test.OpenId {
if (expectSuccess) {
Assert.IsNotNull(rpAssociation);
- Assert.AreSame(rpAssociation, associationManagerAccessor.associationStore.GetAssociation(opDescription.Endpoint, rpAssociation.Handle));
+ Assert.AreSame(rpAssociation, associationManagerAccessor.associationStore.GetAssociation(opDescription.Uri, rpAssociation.Handle));
opAssociation = coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, rpAssociation.Handle);
Assert.IsNotNull(opAssociation, "The Provider should have stored the association.");
@@ -375,7 +375,7 @@ namespace DotNetOpenAuth.Test.OpenId {
var unencryptedResponse = (AssociateUnencryptedResponse)associateSuccessfulResponse;
}
} else {
- Assert.IsNull(associationManagerAccessor.associationStore.GetAssociation(opDescription.Endpoint, new RelyingPartySecuritySettings()));
+ Assert.IsNull(associationManagerAccessor.associationStore.GetAssociation(opDescription.Uri, new RelyingPartySecuritySettings()));
Assert.IsNull(coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, new ProviderSecuritySettings()));
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs
index 4ebdf74..7de2a8b 100644
--- a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Test.OpenId {
using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OpenId;
@@ -135,8 +136,8 @@ namespace DotNetOpenAuth.Test.OpenId {
}
private void ParameterizedAuthenticationTest(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) {
- ErrorUtilities.VerifyArgument(!statelessRP || !sharedAssociation, "The RP cannot be stateless while sharing an association with the OP.");
- ErrorUtilities.VerifyArgument(positive || !tamper, "Cannot tamper with a negative response.");
+ Contract.Requires<ArgumentException>(!statelessRP || !sharedAssociation, "The RP cannot be stateless while sharing an association with the OP.");
+ Contract.Requires<ArgumentException>(positive || !tamper, "Cannot tamper with a negative response.");
ProviderSecuritySettings securitySettings = new ProviderSecuritySettings();
Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, securitySettings) : null;
var coordinator = new OpenIdCoordinator(
diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
index 5af1caf..29797dc 100644
--- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text.RegularExpressions;
using DotNetOpenAuth.Messaging;
@@ -167,7 +168,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
}
private static void RegisterMockExtension(Channel channel) {
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
ExtensionTestUtilities.RegisterExtension(channel, MockOpenIdExtension.Factory);
}
@@ -178,7 +179,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
/// <param name="protocol">The protocol to construct the message with.</param>
/// <returns>The message ready to send from OP to RP.</returns>
private IndirectSignedResponse CreateResponseWithExtensions(Protocol protocol) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
+ Contract.Requires<ArgumentNullException>(protocol != null);
IndirectSignedResponse response = new IndirectSignedResponse(protocol.Version, RPUri);
response.ProviderEndpoint = OPUri;
diff --git a/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs
index fbbea71..426e19a 100644
--- a/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/DiffieHellmanTests.cs
@@ -13,7 +13,7 @@ namespace DotNetOpenAuth.Test.OpenId {
using Org.Mentalis.Security.Cryptography;
[TestClass]
- public class DiffieHellmanTests {
+ public class DiffieHellmanTests : OpenIdTestBase {
[TestMethod]
public void Test() {
string s1 = Test1();
@@ -28,7 +28,9 @@ namespace DotNetOpenAuth.Test.OpenId {
try {
string line;
+ int lineNumber = 0;
while ((line = reader.ReadLine()) != null) {
+ TestContext.WriteLine("\tLine {0}", ++lineNumber);
string[] parts = line.Trim().Split(' ');
byte[] x = Convert.FromBase64String(parts[0]);
DiffieHellmanManaged dh = new DiffieHellmanManaged(AssociateDiffieHellmanRequest.DefaultMod, AssociateDiffieHellmanRequest.DefaultGen, x);
diff --git a/src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml b/src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml
new file mode 100644
index 0000000..9e6a66b
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/Discovery/xrdsdiscovery/xrds20dual.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS
+ xmlns:xrds="xri://$xrds"
+ xmlns:openid="http://openid.net/xmlns/1.0"
+ xmlns="xri://$xrd*($v*2.0)">
+ <XRD>
+ <Service priority="10">
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
+ <URI>http://a/b</URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
diff --git a/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs
new file mode 100644
index 0000000..f71d82f
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs
@@ -0,0 +1,286 @@
+//-----------------------------------------------------------------------
+// <copyright file="UriDiscoveryServiceTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.DiscoveryServices {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class UriDiscoveryServiceTests : OpenIdTestBase {
+ [TestMethod]
+ public void DiscoveryWithRedirects() {
+ Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, false);
+
+ // Add a couple of chained redirect pages that lead to the claimedId.
+ Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
+ Uri insecureMidpointUri = new Uri("http://localhost/insecureStop");
+ this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
+ this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));
+
+ // don't require secure SSL discovery for this test.
+ Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false);
+ Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count());
+ }
+
+ [TestMethod]
+ public void DiscoverRequireSslWithSecureRedirects() {
+ Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true);
+
+ // Add a couple of chained redirect pages that lead to the claimedId.
+ // All redirects should be secure.
+ Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
+ Uri secureMidpointUri = new Uri("https://localhost/secureStop");
+ this.MockResponder.RegisterMockRedirect(userSuppliedUri, secureMidpointUri);
+ this.MockResponder.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString()));
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
+ Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count());
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void DiscoverRequireSslWithInsecureRedirect() {
+ Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true);
+
+ // Add a couple of chained redirect pages that lead to the claimedId.
+ // Include an insecure HTTP jump in those redirects to verify that
+ // the ultimate endpoint is never found as a result of high security profile.
+ Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
+ Uri insecureMidpointUri = new Uri("http://localhost/insecureStop");
+ this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
+ this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
+ this.Discover(userSuppliedIdentifier);
+ }
+
+ [TestMethod]
+ public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() {
+ var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
+ Uri secureClaimedUri = new Uri("https://localhost/secureId");
+
+ string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource);
+ this.MockResponder.RegisterMockResponse(secureClaimedUri, "text/html", html);
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true);
+ Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count());
+ }
+
+ [TestMethod]
+ public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() {
+ var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
+
+ string html = "<html><head></head><body></body></html>";
+ WebHeaderCollection headers = new WebHeaderCollection {
+ { "X-XRDS-Location", insecureXrdsSource }
+ };
+ this.MockResponder.RegisterMockResponse(VanityUriSsl, VanityUriSsl, "text/html", headers, html);
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true);
+ Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count());
+ }
+
+ [TestMethod]
+ public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() {
+ var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
+ string html = string.Format(
+ @"
+ <html><head>
+ <meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored -->
+ <link rel='openid2.provider' href='{1}' />
+ <link rel='openid2.local_id' href='{2}' />
+ </head><body></body></html>",
+ HttpUtility.HtmlEncode(insecureXrdsSource),
+ HttpUtility.HtmlEncode(OPUriSsl.AbsoluteUri),
+ HttpUtility.HtmlEncode(OPLocalIdentifiersSsl[1].AbsoluteUri));
+ this.MockResponder.RegisterMockResponse(VanityUriSsl, "text/html", html);
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true);
+
+ // We verify that the XRDS was ignored and the LINK tags were used
+ // because the XRDS OP-LocalIdentifier uses different local identifiers.
+ Assert.AreEqual(OPLocalIdentifiersSsl[1], this.Discover(userSuppliedIdentifier).Single().ProviderLocalIdentifier);
+ }
+
+ [TestMethod]
+ public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() {
+ var insecureEndpoint = GetServiceEndpoint(0, ProtocolVersion.V20, 10, false);
+ var secureEndpoint = GetServiceEndpoint(1, ProtocolVersion.V20, 20, true);
+ UriIdentifier secureClaimedId = new UriIdentifier(VanityUriSsl, true);
+ this.MockResponder.RegisterMockXrdsResponse(secureClaimedId, new IdentifierDiscoveryResult[] { insecureEndpoint, secureEndpoint });
+ Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, this.Discover(secureClaimedId).Single().ProviderLocalIdentifier);
+ }
+
+ [TestMethod]
+ public void XrdsDirectDiscovery_10() {
+ this.FailDiscoverXrds("xrds-irrelevant");
+ this.DiscoverXrds("xrds10", ProtocolVersion.V10, null, "http://a/b");
+ this.DiscoverXrds("xrds11", ProtocolVersion.V11, null, "http://a/b");
+ this.DiscoverXrds("xrds1020", ProtocolVersion.V10, null, "http://a/b");
+ }
+
+ [TestMethod]
+ public void XrdsDirectDiscovery_20() {
+ this.DiscoverXrds("xrds20", ProtocolVersion.V20, null, "http://a/b");
+ this.DiscoverXrds("xrds2010a", ProtocolVersion.V20, null, "http://a/b");
+ this.DiscoverXrds("xrds2010b", ProtocolVersion.V20, null, "http://a/b");
+ }
+
+ [TestMethod]
+ public void HtmlDiscover_11() {
+ this.DiscoverHtml("html10prov", ProtocolVersion.V11, null, "http://a/b");
+ this.DiscoverHtml("html10both", ProtocolVersion.V11, "http://c/d", "http://a/b");
+ this.FailDiscoverHtml("html10del");
+
+ // Verify that HTML discovery generates the 1.x endpoints when appropriate
+ this.DiscoverHtml("html2010", ProtocolVersion.V11, "http://g/h", "http://e/f");
+ this.DiscoverHtml("html1020", ProtocolVersion.V11, "http://g/h", "http://e/f");
+ this.DiscoverHtml("html2010combinedA", ProtocolVersion.V11, "http://c/d", "http://a/b");
+ this.DiscoverHtml("html2010combinedB", ProtocolVersion.V11, "http://c/d", "http://a/b");
+ this.DiscoverHtml("html2010combinedC", ProtocolVersion.V11, "http://c/d", "http://a/b");
+ }
+
+ [TestMethod]
+ public void HtmlDiscover_20() {
+ this.DiscoverHtml("html20prov", ProtocolVersion.V20, null, "http://a/b");
+ this.DiscoverHtml("html20both", ProtocolVersion.V20, "http://c/d", "http://a/b");
+ this.FailDiscoverHtml("html20del");
+ this.DiscoverHtml("html2010", ProtocolVersion.V20, "http://c/d", "http://a/b");
+ this.DiscoverHtml("html1020", ProtocolVersion.V20, "http://c/d", "http://a/b");
+ this.DiscoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d", "http://a/b");
+ this.DiscoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d", "http://a/b");
+ this.DiscoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d", "http://a/b");
+ this.FailDiscoverHtml("html20relative");
+ }
+
+ [TestMethod]
+ public void XrdsDiscoveryFromHead() {
+ this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
+ this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null, "http://a/b");
+ }
+
+ [TestMethod]
+ public void XrdsDiscoveryFromHttpHeader() {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ headers.Add("X-XRDS-Location", new Uri("http://localhost/xrds1020.xml").AbsoluteUri);
+ this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
+ this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, "http://a/b", headers);
+ }
+
+ /// <summary>
+ /// Verifies that a dual identifier yields only one service endpoint by default.
+ /// </summary>
+ [TestMethod]
+ public void DualIdentifierOffByDefault() {
+ this.MockResponder.RegisterMockResponse(VanityUri, "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds20dual.xml"));
+ var results = this.Discover(VanityUri).ToList();
+ Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == r.Protocol.ClaimedIdentifierForOPIdentifier), "OP Identifier missing from discovery results.");
+ Assert.AreEqual(1, results.Count, "Unexpected additional services discovered.");
+ }
+
+ /// <summary>
+ /// Verifies that a dual identifier yields two service endpoints when that feature is turned on.
+ /// </summary>
+ [TestMethod]
+ public void DualIdentifier() {
+ this.MockResponder.RegisterMockResponse(VanityUri, "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds20dual.xml"));
+ var rp = this.CreateRelyingParty(true);
+ rp.Channel.WebRequestHandler = this.RequestHandler;
+ rp.SecuritySettings.AllowDualPurposeIdentifiers = true;
+ var results = rp.Discover(VanityUri).ToList();
+ Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == r.Protocol.ClaimedIdentifierForOPIdentifier), "OP Identifier missing from discovery results.");
+ Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == VanityUri), "Claimed identifier missing from discovery results.");
+ Assert.AreEqual(2, results.Count, "Unexpected additional services discovered.");
+ }
+
+ private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect) {
+ this.Discover(url, version, expectedLocalId, providerEndpoint, expectSreg, useRedirect, null);
+ }
+
+ private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect, WebHeaderCollection headers) {
+ Protocol protocol = Protocol.Lookup(version);
+ Uri baseUrl = new Uri("http://localhost/");
+ UriIdentifier claimedId = new Uri(baseUrl, url);
+ UriIdentifier userSuppliedIdentifier = new Uri(baseUrl, "Discovery/htmldiscovery/redirect.aspx?target=" + url);
+ if (expectedLocalId == null) {
+ expectedLocalId = claimedId;
+ }
+ Identifier idToDiscover = useRedirect ? userSuppliedIdentifier : claimedId;
+
+ string contentType;
+ if (url.EndsWith("html")) {
+ contentType = "text/html";
+ } else if (url.EndsWith("xml")) {
+ contentType = "application/xrds+xml";
+ } else {
+ throw new InvalidOperationException();
+ }
+ this.MockResponder.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType, headers ?? new WebHeaderCollection(), LoadEmbeddedFile(url));
+
+ IdentifierDiscoveryResult expected = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
+ claimedId,
+ expectedLocalId,
+ new ProviderEndpointDescription(new Uri(providerEndpoint), new string[] { protocol.ClaimedIdentifierServiceTypeURI }), // services aren't checked by Equals
+ null,
+ null);
+
+ IdentifierDiscoveryResult se = this.Discover(idToDiscover).FirstOrDefault(ep => ep.Equals(expected));
+ Assert.IsNotNull(se, url + " failed to be discovered.");
+
+ // Do extra checking of service type URIs, which aren't included in
+ // the ServiceEndpoint.Equals method.
+ Assert.AreEqual(expectSreg ? 2 : 1, se.Capabilities.Count);
+ Assert.IsTrue(se.Capabilities.Contains(protocol.ClaimedIdentifierServiceTypeURI));
+ Assert.AreEqual(expectSreg, se.IsExtensionSupported<ClaimsRequest>());
+ }
+
+ private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) {
+ this.DiscoverXrds(page, version, expectedLocalId, providerEndpoint, null);
+ }
+
+ private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, WebHeaderCollection headers) {
+ if (!page.Contains(".")) {
+ page += ".xml";
+ }
+ this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, false, headers);
+ this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, true, headers);
+ }
+
+ private void DiscoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool useRedirect) {
+ this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, providerEndpoint, false, useRedirect);
+ }
+
+ private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) {
+ string page = scenario + ".html";
+ this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, false);
+ this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, true);
+ }
+
+ private void FailDiscover(string url) {
+ UriIdentifier userSuppliedId = new Uri(new Uri("http://localhost"), url);
+
+ this.MockResponder.RegisterMockResponse(new Uri(userSuppliedId), userSuppliedId, "text/html", LoadEmbeddedFile(url));
+
+ Assert.AreEqual(0, this.Discover(userSuppliedId).Count()); // ... but that no endpoint info is discoverable
+ }
+
+ private void FailDiscoverHtml(string scenario) {
+ this.FailDiscover("/Discovery/htmldiscovery/" + scenario + ".html");
+ }
+
+ private void FailDiscoverXrds(string scenario) {
+ this.FailDiscover("/Discovery/xrdsdiscovery/" + scenario + ".xml");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs
new file mode 100644
index 0000000..9247bb6
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/XriDiscoveryProxyServiceTests.cs
@@ -0,0 +1,394 @@
+//-----------------------------------------------------------------------
+// <copyright file="XriDiscoveryProxyServiceTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.DiscoveryServices {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class XriDiscoveryProxyServiceTests : OpenIdTestBase {
+ [TestMethod]
+ public void Discover() {
+ string xrds = @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*Arnott</Query>
+ <Status ceid='off' cid='verified' code='100'/>
+ <Expires>2008-07-14T02:03:24.000Z</Expires>
+ <ProviderID>xri://=</ProviderID>
+ <LocalID>!9b72.7dd1.50a9.5ccd</LocalID>
+ <CanonicalID>=!9B72.7DD1.50A9.5CCD</CanonicalID>
+
+ <Service priority='10'>
+ <ProviderID>xri://!!1008</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='default' select='false'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null' select='false'/>
+ <URI append='qxri' priority='1'>http://1id.com/contact/</URI>
+
+ </Service>
+ <Service priority='10'>
+ <ProviderID>xri://!!1008</ProviderID>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Type match='null' select='false'/>
+ <URI append='qxri' priority='1'>http://1id.com/</URI>
+ </Service>
+
+ <Service priority='10'>
+ <ProviderID>xri://!!1008</ProviderID>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <URI append='none' priority='10'>http://1id.com/sso</URI>
+ </Service>
+</XRD>";
+ Dictionary<string, string> mocks = new Dictionary<string, string> {
+ { "https://xri.net/=Arnott?_xrd_r=application/xrd%2Bxml;sep=false", xrds },
+ { "https://xri.net/=!9B72.7DD1.50A9.5CCD?_xrd_r=application/xrd%2Bxml;sep=false", xrds },
+ };
+ this.MockResponder.RegisterMockXrdsResponses(mocks);
+
+ string expectedCanonicalId = "=!9B72.7DD1.50A9.5CCD";
+ IdentifierDiscoveryResult se = this.VerifyCanonicalId("=Arnott", expectedCanonicalId);
+ Assert.AreEqual(Protocol.V10, Protocol.Lookup(se.Version));
+ Assert.AreEqual("http://1id.com/sso", se.ProviderEndpoint.ToString());
+ Assert.AreEqual(se.ClaimedIdentifier, se.ProviderLocalIdentifier);
+ Assert.AreEqual("=Arnott", se.FriendlyIdentifierForDisplay);
+ }
+
+ [TestMethod]
+ public void DiscoverCommunityInameCanonicalIDs() {
+ string llliResponse = @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*llli</Query>
+ <Status ceid='off' cid='verified' code='100'/>
+ <Expires>2008-07-14T02:21:06.000Z</Expires>
+ <ProviderID>xri://@</ProviderID>
+ <LocalID>!72cd.a072.157e.a9c6</LocalID>
+ <CanonicalID>@!72CD.A072.157E.A9C6</CanonicalID>
+ <Service priority='10'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <URI append='none' priority='1'>https://login.llli.org/server/</URI>
+ </Service>
+ <Service priority='1'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type match='null' select='false'/>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Path match='default'/>
+ <Path>(+index)</Path>
+ <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
+ </Service>
+ <Service priority='10'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://$res*auth*($v*2.0)</Type>
+ <MediaType>application/xrds+xml;trust=none</MediaType>
+ <URI priority='10'>http://resolve.ezibroker.net/resolve/@llli/</URI>
+ </Service>
+ <Service priority='10'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null'/>
+ <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
+ </Service>
+</XRD>
+";
+ string llliAreaResponse = @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*area</Query>
+ <Status cid='verified' code='100'>SUCCESS</Status>
+ <ServerStatus code='100'>SUCCESS</ServerStatus>
+ <Expires>2008-07-15T01:21:07.000Z</Expires>
+ <ProviderID>xri://!!1003</ProviderID>
+ <LocalID>0000.0000.3B9A.CA0C</LocalID>
+ <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C</CanonicalID>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <URI append='none' priority='1'>https://login.llli.org/server/</URI>
+ </Service>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null'/>
+ <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
+ </Service>
+ <Service priority='1'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Type match='null' select='false'/>
+ <Path>(+index)</Path>
+ <Path match='default'/>
+ <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
+ </Service>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://$res*auth*($v*2.0)</Type>
+ <MediaType>application/xrds+xml;trust=none</MediaType>
+ <URI>http://resolve.ezibroker.net/resolve/@llli*area/</URI>
+ </Service>
+</XRD>";
+ string llliAreaCanadaUnattachedResponse = @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*canada.unattached</Query>
+ <Status cid='verified' code='100'>SUCCESS</Status>
+ <ServerStatus code='100'>SUCCESS</ServerStatus>
+ <Expires>2008-07-15T01:21:08.000Z</Expires>
+ <ProviderID>xri://!!1003</ProviderID>
+ <LocalID>0000.0000.3B9A.CA41</LocalID>
+ <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41</CanonicalID>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <URI append='none' priority='1'>https://login.llli.org/server/</URI>
+ </Service>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null'/>
+ <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
+ </Service>
+ <Service priority='1'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Type match='null' select='false'/>
+ <Path>(+index)</Path>
+ <Path match='default'/>
+ <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
+ </Service>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://$res*auth*($v*2.0)</Type>
+ <MediaType>application/xrds+xml;trust=none</MediaType>
+ <URI>http://resolve.ezibroker.net/resolve/@llli*area*canada.unattached/</URI>
+ </Service>
+</XRD>";
+ string llliAreaCanadaUnattachedAdaResponse = @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*ada</Query>
+ <Status cid='verified' code='100'>SUCCESS</Status>
+ <ServerStatus code='100'>SUCCESS</ServerStatus>
+ <Expires>2008-07-15T01:21:10.000Z</Expires>
+ <ProviderID>xri://!!1003</ProviderID>
+ <LocalID>0000.0000.3B9A.CA01</LocalID>
+ <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01</CanonicalID>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <URI append='none' priority='1'>https://login.llli.org/server/</URI>
+ </Service>
+ <Service>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null'/>
+ <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
+ </Service>
+ <Service priority='1'>
+ <ProviderID>xri://!!1003!103</ProviderID>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Type match='null' select='false'/>
+ <Path>(+index)</Path>
+ <Path match='default'/>
+ <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
+ </Service>
+</XRD>";
+ string webResponse = @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*Web</Query>
+ <Status ceid='off' cid='verified' code='100'/>
+ <Expires>2008-07-14T02:21:12.000Z</Expires>
+ <ProviderID>xri://=</ProviderID>
+ <LocalID>!91f2.8153.f600.ae24</LocalID>
+ <CanonicalID>=!91F2.8153.F600.AE24</CanonicalID>
+ <Service priority='10'>
+ <Type select='true'>xri://+i-service*(+locator)*($v*1.0)</Type>
+ <Path select='true'>(+locator)</Path>
+ <MediaType match='default' select='false'/>
+ <URI append='qxri'>http://locator.fullxri.com/locator/</URI>
+ </Service>
+ <Service priority='10'>
+ <ProviderID>xri://=web</ProviderID>
+ <Type select='true'>xri://$res*auth*($v*2.0)</Type>
+ <Type select='true'>xri://$res*auth*($v*2.0)</Type>
+ <MediaType select='true'>application/xrds+xml</MediaType>
+ <URI append='qxri' priority='1'>https://resolve.freexri.com/ns/=web/</URI>
+ <URI append='qxri' priority='2'>http://resolve.freexri.com/ns/=web/</URI>
+ </Service>
+ <Service priority='10'>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <Type select='true'>http://specs.openid.net/auth/2.0/signon</Type>
+ <Path select='true'>(+login)</Path>
+ <Path match='default' select='false'/>
+ <MediaType match='default' select='false'/>
+ <URI append='none' priority='2'>http://authn.fullxri.com/authentication/</URI>
+ <URI append='none' priority='1'>https://authn.fullxri.com/authentication/</URI>
+ </Service>
+ <Service priority='10'>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null' select='false'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null' select='false'/>
+ <MediaType match='default' select='false'/>
+ <URI append='qxri'>http://contact.fullxri.com/contact/</URI>
+ </Service>
+ <KeyInfo xmlns='http://www.w3.org/2000/09/xmldsig#'>
+ <X509Data>
+ <X509Certificate>
+MIIExzCCA6+gAwIBAgIJAM+MlFr0Sth6MA0GCSqGSIb3DQEBBQUAMIGdMR8wHQYD
+VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE
+CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs
+YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs
+YWluLmVkdTAeFw0wNjA4MTcxOTU5NTNaFw0xMTA4MTYxOTU5NTNaMIGdMR8wHQYD
+VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE
+CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs
+YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs
+YWluLmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL6uFqas4dK6
+A2wTZL0viRQNJrPyFnFBDSZGib/2ijhgzed/vvmZIBM9sFpwahcuR5hvyKUe37/c
+/RSZXoNDi/eiNOx4qb0l9UB6bd8qvc4V1PnLE7L+ZYcmwrvTKm4x8qXMgEv1wca2
+FPsreHNPdLiTUZ8v0tDTWi3Mgi7y47VTzJaTkcfmO1nL6xAtln5sLdH0PbMM3LAp
+T1d3nwI3VdbhqqZ+6+OKEuC8gk5iH4lfrbr6C9bYS6vzIKrotHpZ3N2aIC3NMjJD
+PMw/mfCuADfRNlHXgZW+0zyUkwGTMDea8qgsoAMWJGdeTIw8I1I3RhnbgLzdsNQl
+b/1ZXx1uJRUCAwEAAaOCAQYwggECMB0GA1UdDgQWBBQe+xSjYTrlfraJARjMxscb
+j36jvDCB0gYDVR0jBIHKMIHHgBQe+xSjYTrlfraJARjMxscbj36jvKGBo6SBoDCB
+nTEfMB0GA1UEAxMWU3VwZXJ2aWxsYWluOiBUaGUgUm9vdDELMAkGA1UEBhMCVVMx
+ETAPBgNVBAgTCE5ldyBZb3JrMQ8wDQYDVQQHEwZHb3RoYW0xIDAeBgNVBAoTF1N1
+cGVydmlsbGFpbiBVbml2ZXJzaXR5MScwJQYJKoZIhvcNAQkBFhhwZW5ndWluQHN1
+cGVydmlsbGFpbi5lZHWCCQDPjJRa9ErYejAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBBQUAA4IBAQC4SPBDGYAxfbXd8N5OvG0drM7a5hjXfcCZpiILlPSRpxp79yh7
+I5vVWxBxUfolwbei7PTBVy7CE27SUbSICeqWjcDCfjNjiZk6mLS80rm/TdLrHSyM
++Ujlw9MGcBGaLI+sdziDUMtTQDpeAyQTaGVbh1mx5874Hlo1VXqGYNo0RwR+iLfs
+x48VuO6GbWVyxtktkE2ypz1KLWiyI056YynydRvuBCBHeRqGUixPlH9CrmeSCP2S
+sfbiKnMOGXjIYbvbsTAMdW2iqg6IWa/fgxhvZoAXChM9bkhisJQc0qD0J5TJQwgr
+uEyb50RJ7DWmXctSC0b3eymZ2lSXxAWNOsNy
+ </X509Certificate>
+ </X509Data>
+ </KeyInfo>
+</XRD>";
+ this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> {
+ { "https://xri.net/@llli?_xrd_r=application/xrd%2Bxml;sep=false", llliResponse },
+ { "https://xri.net/@llli*area?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaResponse },
+ { "https://xri.net/@llli*area*canada.unattached?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedResponse },
+ { "https://xri.net/@llli*area*canada.unattached*ada?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedAdaResponse },
+ { "https://xri.net/=Web?_xrd_r=application/xrd%2Bxml;sep=false", webResponse },
+ });
+ this.VerifyCanonicalId("@llli", "@!72CD.A072.157E.A9C6");
+ this.VerifyCanonicalId("@llli*area", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C");
+ this.VerifyCanonicalId("@llli*area*canada.unattached", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41");
+ this.VerifyCanonicalId("@llli*area*canada.unattached*ada", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01");
+ this.VerifyCanonicalId("=Web", "=!91F2.8153.F600.AE24");
+ }
+
+ [TestMethod]
+ public void DiscoveryCommunityInameDelegateWithoutCanonicalID() {
+ this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> {
+ { "https://xri.net/=Web*andrew.arnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*andrew.arnott</Query>
+ <Status cid='absent' code='100'>Success</Status>
+ <ServerStatus code='100'>Success</ServerStatus>
+ <Expires>2008-07-14T03:30:59.722Z</Expires>
+ <ProviderID>=!91F2.8153.F600.AE24</ProviderID>
+ <Service>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <Path select='true'>(+login)</Path>
+ <Path match='default'/>
+ <MediaType match='default'/>
+ <URI append='none' priority='2'>http://www.myopenid.com/server</URI>
+ <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate>
+ </Service>
+ <Service>
+ <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null'/>
+ <MediaType match='default'/>
+ <URI append='qxri'>http://contact.freexri.com/contact/</URI>
+ </Service>
+ <Service>
+ <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Path select='true'>(+index)</Path>
+ <Path match='default'/>
+ <MediaType match='default'/>
+ <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI>
+ </Service>
+ <Service>
+ <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <Path select='true'>(+login)</Path>
+ <Path match='default'/>
+ <MediaType match='default'/>
+ <URI append='none' priority='2'>http://authn.freexri.com/authentication/</URI>
+ <URI append='none' priority='1'>https://authn.freexri.com/authentication/</URI>
+ </Service>
+ <ServedBy>OpenXRI</ServedBy>
+</XRD>" },
+ { "https://xri.net/@id*andrewarnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?>
+<XRD xmlns='xri://$xrd*($v*2.0)'>
+ <Query>*andrewarnott</Query>
+ <Status cid='absent' code='100'>Success</Status>
+ <ServerStatus code='100'>Success</ServerStatus>
+ <Expires>2008-07-14T03:31:00.466Z</Expires>
+ <ProviderID>@!B1E8.C27B.E41C.25C3</ProviderID>
+ <Service>
+ <Type select='true'>http://openid.net/signon/1.0</Type>
+ <Path select='true'>(+login)</Path>
+ <Path match='default'/>
+ <MediaType match='default'/>
+ <URI append='none' priority='2'>http://www.myopenid.com/server</URI>
+ <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate>
+ </Service>
+ <Service>
+ <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
+ <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
+ <Type match='null'/>
+ <Path select='true'>(+contact)</Path>
+ <Path match='null'/>
+ <MediaType match='default'/>
+ <URI append='qxri'>http://contact.freexri.com/contact/</URI>
+ </Service>
+ <Service>
+ <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
+ <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
+ <Path select='true'>(+index)</Path>
+ <Path match='default'/>
+ <MediaType match='default'/>
+ <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI>
+ </Service>
+ <ServedBy>OpenXRI</ServedBy>
+</XRD>" },
+ });
+ // Consistent with spec section 7.3.2.3, we do not permit
+ // delegation on XRI discovery when there is no CanonicalID present.
+ this.VerifyCanonicalId("=Web*andrew.arnott", null);
+ this.VerifyCanonicalId("@id*andrewarnott", null);
+ }
+
+ private IdentifierDiscoveryResult VerifyCanonicalId(Identifier iname, string expectedClaimedIdentifier) {
+ var se = this.Discover(iname).FirstOrDefault();
+ if (expectedClaimedIdentifier != null) {
+ Assert.IsNotNull(se);
+ Assert.AreEqual(expectedClaimedIdentifier, se.ClaimedIdentifier.ToString(), "i-name {0} discovery resulted in unexpected CanonicalId", iname);
+ Assert.IsTrue(se.Capabilities.Count > 0);
+ } else {
+ Assert.IsNull(se);
+ }
+ return se;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs
index 1051092..fa05e94 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeExchangeRoundtripTests.cs
@@ -34,8 +34,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
var request = new StoreRequest();
var newAttribute = new AttributeValues(
IncrementingAttribute,
- "val" + (incrementingAttributeValue++).ToString(),
- "val" + (incrementingAttributeValue++).ToString());
+ "val" + (this.incrementingAttributeValue++).ToString(),
+ "val" + (this.incrementingAttributeValue++).ToString());
request.Attributes.Add(newAttribute);
var successResponse = new StoreResponse();
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs
index 228bda3..48b5727 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/AttributeExchange/AttributeRequestTests.cs
@@ -25,7 +25,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
new AttributeRequest(string.Empty);
}
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
public void CtorNullTypeUri() {
new AttributeRequest(null);
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs
index 47c8ec4..334fc93 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs
@@ -5,7 +5,9 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Test.OpenId.Extensions {
+ using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
@@ -71,7 +73,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
}
internal static void RegisterExtension(Channel channel, StandardOpenIdExtensionFactory.CreateDelegate extensionFactory) {
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
var factory = (OpenIdExtensionFactoryAggregator)channel.BindingElements.OfType<ExtensionsBindingElement>().Single().ExtensionFactory;
factory.Factories.OfType<StandardOpenIdExtensionFactory>().Single().RegisterExtension(extensionFactory);
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs
index ba5e335..b913f96 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Test.OpenId {
+ using System.Collections.ObjectModel;
using System.Linq;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions;
@@ -118,11 +119,9 @@ namespace DotNetOpenAuth.Test.OpenId {
/// </summary>
/// <param name="typeUri">The type URI.</param>
private void InjectAdvertisedTypeUri(string typeUri) {
- var serviceEndpoint = ServiceEndpoint_Accessor.AttachShadow(((ServiceEndpoint)this.authReq.Provider));
- serviceEndpoint.ProviderDescription = ProviderEndpointDescription_Accessor.AttachShadow(
- new ProviderEndpointDescription(
- serviceEndpoint.ProviderDescription.Endpoint,
- serviceEndpoint.ProviderDescription.Capabilities.Concat(new[] { typeUri })));
+ var serviceEndpoint = (IdentifierDiscoveryResult)this.authReq.Provider;
+ serviceEndpoint.SetCapabilitiesForTestHook(
+ new ReadOnlyCollection<string>(serviceEndpoint.Capabilities.Concat(new[] { typeUri }).ToList()));
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
index f69fc8b..7a60a32 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
@@ -16,15 +16,30 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.UI {
public void Defaults() {
UIRequest request = new UIRequest();
Assert.AreEqual("popup", request.Mode);
- Assert.AreEqual(CultureInfo.CurrentUICulture, request.LanguagePreference);
+ Assert.AreEqual(1, request.LanguagePreference.Length);
+ Assert.AreEqual(CultureInfo.CurrentUICulture, request.LanguagePreference[0]);
}
[TestMethod]
- public void LanguagePreferenceEncoding() {
+ public void LanguagePreferenceEncodingDecoding() {
var request = new UIRequest();
- request.LanguagePreference = new CultureInfo("en-US");
MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(request);
+
+ request.LanguagePreference = new[] { new CultureInfo("en-US") };
Assert.AreEqual("en-US", dictionary["lang"]);
+
+ request.LanguagePreference = new[] { new CultureInfo("en-US"), new CultureInfo("es-ES") };
+ Assert.AreEqual("en-US,es-ES", dictionary["lang"]);
+
+ // Now test decoding
+ dictionary["lang"] = "en-US";
+ Assert.AreEqual(1, request.LanguagePreference.Length);
+ Assert.AreEqual(new CultureInfo("en-US"), request.LanguagePreference[0]);
+
+ dictionary["lang"] = "en-US,es-ES";
+ Assert.AreEqual(2, request.LanguagePreference.Length);
+ Assert.AreEqual(new CultureInfo("en-US"), request.LanguagePreference[0]);
+ Assert.AreEqual(new CultureInfo("es-ES"), request.LanguagePreference[1]);
}
[TestMethod]
diff --git a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs
index 3e599e9..cc02265 100644
--- a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs
@@ -75,7 +75,7 @@ namespace DotNetOpenAuth.Test.OpenId {
Assert.AreEqual(this.uri, ((UriIdentifier)id).Uri.AbsoluteUri);
}
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
public void ParseNull() {
Identifier.Parse(null);
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs
index e140b24..8b0937a 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs
@@ -127,7 +127,7 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
}
}
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
public void GetReturnToArgumentNullKey() {
this.response.GetReturnToArgument(null);
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs
index af9c5db..d4884e8 100644
--- a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Test.OpenId {
using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OpenId;
@@ -36,7 +37,7 @@ namespace DotNetOpenAuth.Test.OpenId {
}
private static Action<OpenIdRelyingParty> WrapAction(Action<OpenIdRelyingParty> action) {
- ErrorUtilities.VerifyArgumentNotNull(action, "action");
+ Contract.Requires<ArgumentNullException>(action != null);
return rp => {
action(rp);
@@ -45,7 +46,7 @@ namespace DotNetOpenAuth.Test.OpenId {
}
private static Action<OpenIdProvider> WrapAction(Action<OpenIdProvider> action) {
- ErrorUtilities.VerifyArgumentNotNull(action, "action");
+ Contract.Requires<ArgumentNullException>(action != null);
return op => {
action(op);
@@ -56,10 +57,12 @@ namespace DotNetOpenAuth.Test.OpenId {
private void EnsurePartiesAreInitialized() {
if (this.RelyingParty == null) {
this.RelyingParty = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore());
+ this.RelyingParty.DiscoveryServices.Add(new MockIdentifierDiscoveryService());
}
if (this.Provider == null) {
this.Provider = new OpenIdProvider(new StandardProviderApplicationStore());
+ this.Provider.DiscoveryServices.Add(new MockIdentifierDiscoveryService());
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs
index 5034b7e..d8f5674 100644
--- a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Test.OpenId {
using System;
+ using System.Collections.Generic;
using System.IO;
using System.Reflection;
using DotNetOpenAuth.Configuration;
@@ -116,17 +117,17 @@ namespace DotNetOpenAuth.Test.OpenId {
}
}
- internal static ServiceEndpoint GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl) {
+ internal static IdentifierDiscoveryResult GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl) {
return GetServiceEndpoint(user, providerVersion, servicePriority, useSsl, false);
}
- internal static ServiceEndpoint GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl, bool delegating) {
+ internal static IdentifierDiscoveryResult GetServiceEndpoint(int user, ProtocolVersion providerVersion, int servicePriority, bool useSsl, bool delegating) {
var providerEndpoint = new ProviderEndpointDescription(
useSsl ? OpenIdTestBase.OPUriSsl : OpenIdTestBase.OPUri,
new string[] { Protocol.Lookup(providerVersion).ClaimedIdentifierServiceTypeURI });
var local_id = useSsl ? OPLocalIdentifiersSsl[user] : OPLocalIdentifiers[user];
var claimed_id = delegating ? (useSsl ? VanityUriSsl : VanityUri) : local_id;
- return ServiceEndpoint.CreateForClaimedIdentifier(
+ return IdentifierDiscoveryResult.CreateForClaimedIdentifier(
claimed_id,
claimed_id,
local_id,
@@ -176,6 +177,12 @@ namespace DotNetOpenAuth.Test.OpenId {
}
}
+ internal IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier) {
+ var rp = this.CreateRelyingParty(true);
+ rp.Channel.WebRequestHandler = this.RequestHandler;
+ return rp.Discover(identifier);
+ }
+
protected Realm GetMockRealm(bool useSsl) {
var rpDescription = new RelyingPartyEndpointDescription(useSsl ? RPUriSsl : RPUri, new string[] { Protocol.V20.RPReturnToTypeURI });
return new MockRealm(useSsl ? RPRealmUriSsl : RPRealmUri, rpDescription);
@@ -190,9 +197,21 @@ namespace DotNetOpenAuth.Test.OpenId {
}
protected Identifier GetMockIdentifier(ProtocolVersion providerVersion, bool useSsl, bool delegating) {
- ServiceEndpoint se = GetServiceEndpoint(0, providerVersion, 10, useSsl, delegating);
+ var se = GetServiceEndpoint(0, providerVersion, 10, useSsl, delegating);
UriIdentifier identityUri = (UriIdentifier)se.ClaimedIdentifier;
- return new MockIdentifier(identityUri, this.MockResponder, new ServiceEndpoint[] { se });
+ return new MockIdentifier(identityUri, this.MockResponder, new IdentifierDiscoveryResult[] { se });
+ }
+
+ protected Identifier GetMockDualIdentifier() {
+ Protocol protocol = Protocol.Default;
+ var opDesc = new ProviderEndpointDescription(OPUri, protocol.Version);
+ var dualResults = new IdentifierDiscoveryResult[] {
+ IdentifierDiscoveryResult.CreateForClaimedIdentifier(VanityUri.AbsoluteUri, OPLocalIdentifiers[0], opDesc, 10, 10),
+ IdentifierDiscoveryResult.CreateForProviderIdentifier(protocol.ClaimedIdentifierForOPIdentifier, opDesc, 20, 20),
+ };
+
+ Identifier dualId = new MockIdentifier(VanityUri, this.MockResponder, dualResults);
+ return dualId;
}
/// <summary>
@@ -211,6 +230,7 @@ namespace DotNetOpenAuth.Test.OpenId {
protected OpenIdRelyingParty CreateRelyingParty(bool stateless) {
var rp = new OpenIdRelyingParty(stateless ? null : new StandardRelyingPartyApplicationStore());
rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler;
+ rp.DiscoveryServices.Add(new MockIdentifierDiscoveryService());
return rp;
}
@@ -221,6 +241,7 @@ namespace DotNetOpenAuth.Test.OpenId {
protected OpenIdProvider CreateProvider() {
var op = new OpenIdProvider(new StandardProviderApplicationStore());
op.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler;
+ op.DiscoveryServices.Add(new MockIdentifierDiscoveryService());
return op;
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs
new file mode 100644
index 0000000..389ef81
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdUtilitiesTests.cs
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdUtilitiesTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OpenIdUtilitiesTests : OpenIdTestBase {
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs
index 0a6cdcc..8528aa7 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.OpenId.Provider {
using System;
using System.IO;
+ using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions;
@@ -74,6 +75,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
/// </summary>
[TestMethod, ExpectedException(typeof(InvalidOperationException))]
public void GetRequestNoContext() {
+ HttpContext.Current = null;
this.provider.GetRequest();
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs
index 4ab9fc3..9f4727d 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs
@@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
timer.Stop();
double executionsPerSecond = GetExecutionsPerSecond(iterations, timer);
TestContext.WriteLine("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond);
- Assert.IsTrue(executionsPerSecond >= 2, "Too slow");
+ Assert.IsTrue(executionsPerSecond >= 2, "Too slow ({0} >= 2 executions per second required.)", executionsPerSecond);
}
[TestMethod]
@@ -70,7 +70,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
timer.Stop();
double executionsPerSecond = GetExecutionsPerSecond(iterations, timer);
TestContext.WriteLine("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond);
- Assert.IsTrue(executionsPerSecond > 1000, "Too slow.");
+ Assert.IsTrue(executionsPerSecond > 1000, "Too slow ({0} > 1000 executions per second required.)", executionsPerSecond);
}
[TestMethod]
@@ -78,7 +78,8 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
Protocol protocol = Protocol.Default;
string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA1;
double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType);
- Assert.IsTrue(executionsPerSecond > 500, "Too slow");
+ TestContext.WriteLine("{0} executions per second.", executionsPerSecond);
+ Assert.IsTrue(executionsPerSecond > 500, "Too slow ({0} > 500 executions per second required.)", executionsPerSecond);
}
[TestMethod]
@@ -86,7 +87,8 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
Protocol protocol = Protocol.Default;
string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA256;
double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType);
- Assert.IsTrue(executionsPerSecond > 400, "Too slow");
+ TestContext.WriteLine("{0} executions per second.", executionsPerSecond);
+ Assert.IsTrue(executionsPerSecond > 400, "Too slow ({0} > 400 executions per second required.)", executionsPerSecond);
}
private static double GetExecutionsPerSecond(int iterations, Stopwatch timer) {
diff --git a/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs b/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs
index 005b8a0..60cd25f 100644
--- a/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Test.OpenId {
using System;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.Messages;
@@ -13,50 +14,15 @@ namespace DotNetOpenAuth.Test.OpenId {
[TestClass]
public class ProviderEndpointDescriptionTests : OpenIdTestBase {
- private ProviderEndpointDescription se;
-
- private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI };
-
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
-
- this.se = new ProviderEndpointDescription(OPUri, Protocol.V20.Version);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsExtensionSupportedNullType() {
- this.se.IsExtensionSupported((Type)null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsExtensionSupportedNullString() {
- this.se.IsExtensionSupported((string)null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void IsExtensionSupportedEmptyString() {
- this.se.IsExtensionSupported(string.Empty);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsExtensionSupportedNullExtension() {
- this.se.IsExtensionSupported((IOpenIdMessageExtension)null);
- }
-
[TestMethod]
- public void IsExtensionSupported() {
- this.se = new ProviderEndpointDescription(OPUri, this.v20TypeUris);
- Assert.IsFalse(this.se.IsExtensionSupported<ClaimsRequest>());
- Assert.IsFalse(this.se.IsExtensionSupported(new ClaimsRequest()));
- Assert.IsFalse(this.se.IsExtensionSupported("http://someextension/typeuri"));
+ public void NonNullCapabilities() {
+ var epd = new ProviderEndpointDescription(OPUri, Protocol.Default.Version);
+ Assert.IsNotNull(epd.Capabilities);
+ }
- this.se = new ProviderEndpointDescription(
- OPUri,
- new[] { Protocol.V20.ClaimedIdentifierServiceTypeURI, "http://someextension", Constants.sreg_ns });
- Assert.IsTrue(this.se.IsExtensionSupported<ClaimsRequest>());
- Assert.IsTrue(this.se.IsExtensionSupported(new ClaimsRequest()));
- Assert.IsTrue(this.se.IsExtensionSupported("http://someextension"));
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void ProtocolDetectionWithoutClues() {
+ new ProviderEndpointDescription(OPUri, new[] { Protocol.V20.HtmlDiscoveryLocalIdKey }); // random type URI irrelevant to detection
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs
index 10497b2..332b0b7 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/AuthenticationRequestTests.cs
@@ -16,6 +16,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.Messages;
using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Test.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -37,7 +38,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
/// </summary>
[TestMethod]
public void IsDirectedIdentity() {
- IAuthenticationRequest_Accessor iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
+ var iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
Assert.IsFalse(iauthRequest.IsDirectedIdentity);
iauthRequest = this.CreateAuthenticationRequest(IdentifierSelect, IdentifierSelect);
@@ -49,7 +50,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
/// </summary>
[TestMethod]
public void ClaimedIdentifier() {
- IAuthenticationRequest_Accessor iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.delegatedLocalId);
+ var iauthRequest = this.CreateAuthenticationRequest(this.claimedId, this.delegatedLocalId);
Assert.AreEqual(this.claimedId, iauthRequest.ClaimedIdentifier);
iauthRequest = this.CreateAuthenticationRequest(IdentifierSelect, IdentifierSelect);
@@ -62,7 +63,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
[TestMethod]
public void ProviderVersion() {
var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
- Assert.AreEqual(this.protocol.Version, authRequest.endpoint.Protocol.Version);
+ Assert.AreEqual(this.protocol.Version, authRequest.DiscoveryResult.Version);
}
/// <summary>
@@ -112,11 +113,11 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
var rp = CreateRelyingParty();
// First verify that delegating identifiers work
- Assert.IsTrue(AuthenticationRequest.Create(id, rp, realm, returnTo, false).Any(), "The delegating identifier should have not generated any results.");
+ Assert.IsTrue(AuthenticationRequest.Create(id, rp, this.realm, this.returnTo, false).Any(), "The delegating identifier should have not generated any results.");
// Now disable them and try again.
rp.SecuritySettings.RejectDelegatingIdentifiers = true;
- Assert.IsFalse(AuthenticationRequest.Create(id, rp, realm, returnTo, false).Any(), "The delegating identifier should have not generated any results.");
+ Assert.IsFalse(AuthenticationRequest.Create(id, rp, this.realm, this.returnTo, false).Any(), "The delegating identifier should have not generated any results.");
}
/// <summary>
@@ -124,7 +125,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
/// </summary>
[TestMethod]
public void Provider() {
- IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
+ var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
Assert.IsNotNull(authRequest.Provider);
Assert.AreEqual(OPUri, authRequest.Provider.Uri);
Assert.AreEqual(this.protocol.Version, authRequest.Provider.Version);
@@ -135,7 +136,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
/// </summary>
[TestMethod]
public void AddCallbackArgument() {
- IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
+ var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
Assert.AreEqual(this.returnTo, authRequest.ReturnToUrl);
authRequest.AddCallbackArguments("p1", "v1");
var req = (SignedResponseRequest)authRequest.RedirectingResponse.OriginalMessage;
@@ -152,7 +153,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
UriBuilder returnToWithArgs = new UriBuilder(this.returnTo);
returnToWithArgs.AppendQueryArgs(new Dictionary<string, string> { { "p1", "v1" } });
this.returnTo = returnToWithArgs.Uri;
- IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
+ var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
authRequest.AddCallbackArguments("p1", "v2");
var req = (SignedResponseRequest)authRequest.RedirectingResponse.OriginalMessage;
NameValueCollection query = HttpUtility.ParseQueryString(req.ReturnTo.Query);
@@ -164,7 +165,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
/// </summary>
[TestMethod]
public void NonIdentityRequest() {
- IAuthenticationRequest_Accessor authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
+ var authRequest = this.CreateAuthenticationRequest(this.claimedId, this.claimedId);
authRequest.IsExtensionOnly = true;
Assert.IsTrue(authRequest.IsExtensionOnly);
var req = (SignedResponseRequest)authRequest.RedirectingResponse.OriginalMessage;
@@ -172,6 +173,24 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
}
/// <summary>
+ /// Verifies that discovery on identifiers that serve as OP identifiers and claimed identifiers
+ /// only generate OP Identifier auth requests.
+ /// </summary>
+ [TestMethod]
+ public void DualIdentifierUsedOnlyAsOPIdentifierForAuthRequest() {
+ var rp = this.CreateRelyingParty(true);
+ var results = AuthenticationRequest.Create(GetMockDualIdentifier(), rp, this.realm, this.returnTo, false).ToList();
+ Assert.AreEqual(1, results.Count);
+ Assert.IsTrue(results[0].IsDirectedIdentity);
+
+ // Also test when dual identiifer support is turned on.
+ rp.SecuritySettings.AllowDualPurposeIdentifiers = true;
+ results = AuthenticationRequest.Create(GetMockDualIdentifier(), rp, this.realm, this.returnTo, false).ToList();
+ Assert.AreEqual(1, results.Count);
+ Assert.IsTrue(results[0].IsDirectedIdentity);
+ }
+
+ /// <summary>
/// Verifies that authentication requests are generated first for OPs that respond
/// to authentication requests.
/// </summary>
@@ -181,13 +200,11 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
Assert.Inconclusive("Not yet implemented.");
}
- private AuthenticationRequest_Accessor CreateAuthenticationRequest(Identifier claimedIdentifier, Identifier providerLocalIdentifier) {
+ private AuthenticationRequest CreateAuthenticationRequest(Identifier claimedIdentifier, Identifier providerLocalIdentifier) {
ProviderEndpointDescription providerEndpoint = new ProviderEndpointDescription(OPUri, this.protocol.Version);
- ServiceEndpoint endpoint = ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, providerLocalIdentifier, providerEndpoint, 10, 5);
- ServiceEndpoint_Accessor endpointAccessor = ServiceEndpoint_Accessor.AttachShadow(endpoint);
+ IdentifierDiscoveryResult endpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, providerLocalIdentifier, providerEndpoint, 10, 5);
OpenIdRelyingParty rp = this.CreateRelyingParty();
- AuthenticationRequest_Accessor authRequest = new AuthenticationRequest_Accessor(endpointAccessor, this.realm, this.returnTo, rp);
- return authRequest;
+ return AuthenticationRequest.CreateForTest(endpoint, this.realm, this.returnTo, rp);
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs
new file mode 100644
index 0000000..1ed281c
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/IdentifierDiscoveryResultTests.cs
@@ -0,0 +1,199 @@
+//-----------------------------------------------------------------------
+// <copyright file="IdentifierDiscoveryResultTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Test.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class IdentifierDiscoveryResultTests : OpenIdTestBase {
+ private UriIdentifier claimedId = new UriIdentifier("http://claimedid.justatest.com");
+ private XriIdentifier claimedXri = new XriIdentifier("=!9B72.7DD1.50A9.5CCD");
+ private XriIdentifier userSuppliedXri = new XriIdentifier("=Arnot");
+ private Uri providerEndpoint = new Uri("http://someprovider.com");
+ private Identifier localId = "http://localid.someprovider.com";
+ private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI };
+ private string[] v11TypeUris = { Protocol.V11.ClaimedIdentifierServiceTypeURI };
+ private int servicePriority = 10;
+ private int uriPriority = 10;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+ }
+
+ [TestMethod]
+ public void Ctor() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreEqual(this.claimedId, se.ClaimedIdentifier);
+ Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
+ Assert.AreSame(this.localId, se.ProviderLocalIdentifier);
+ CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.Capabilities);
+ Assert.AreEqual(this.servicePriority, se.ServicePriority);
+ }
+
+ [TestMethod]
+ public void CtorImpliedLocalIdentifier() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, null, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreEqual(this.claimedId, se.ClaimedIdentifier);
+ Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
+ Assert.AreSame(this.claimedId, se.ProviderLocalIdentifier);
+ CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.Capabilities);
+ }
+
+ [TestMethod]
+ public void ProtocolDetection() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreSame(Protocol.V20, se.Protocol);
+ se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
+ this.claimedId,
+ this.localId,
+ new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.OPIdentifierServiceTypeURI }),
+ this.servicePriority,
+ this.uriPriority);
+ Assert.AreSame(Protocol.V20, se.Protocol);
+ se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreSame(Protocol.V11, se.Protocol);
+ }
+
+ [TestMethod]
+ public void EqualsTests() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ IdentifierDiscoveryResult se2 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), (int?)null, (int?)null);
+ Assert.AreEqual(se2, se);
+ Assert.AreNotEqual(se, null);
+ Assert.AreNotEqual(null, se);
+
+ IdentifierDiscoveryResult se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(new UriIdentifier(this.claimedId + "a"), this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+ se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(new Uri(this.providerEndpoint.AbsoluteUri + "a"), this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+ se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId + "a", new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+ se3 = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
+ Assert.AreNotEqual(se, se3);
+
+ // make sure that Collection<T>.Contains works as desired.
+ var list = new List<IdentifierDiscoveryResult>();
+ list.Add(se);
+ Assert.IsTrue(list.Contains(se2));
+ }
+
+ [TestMethod]
+ public void GetFriendlyIdentifierForDisplay() {
+ Uri providerEndpoint = new Uri("http://someprovider");
+ Identifier localId = "someuser";
+ string[] serviceTypeUris = new string[] {
+ Protocol.V20.ClaimedIdentifierServiceTypeURI,
+ };
+ IdentifierDiscoveryResult se;
+
+ // strip of protocol, port, query and fragment
+ se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
+ "http://someprovider.somedomain.com:79/someuser?query#frag",
+ localId,
+ new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
+ null,
+ null);
+ Assert.AreEqual("someprovider.somedomain.com/someuser", se.FriendlyIdentifierForDisplay);
+
+ // unescape characters
+ Uri foreignUri = new Uri("http://server崎/村");
+ se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(foreignUri, localId, new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), null, null);
+ Assert.AreEqual("server崎/村", se.FriendlyIdentifierForDisplay);
+
+ // restore user supplied identifier to XRIs
+ se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
+ new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
+ new XriIdentifier("=Arnott崎村"),
+ localId,
+ new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
+ null,
+ null);
+ Assert.AreEqual("=Arnott崎村", se.FriendlyIdentifierForDisplay);
+
+ // If UserSuppliedIdentifier is the same as the ClaimedIdentifier, don't display it twice...
+ se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
+ new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
+ new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
+ localId,
+ new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
+ null,
+ null);
+ Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", se.FriendlyIdentifierForDisplay);
+ }
+
+ [TestMethod]
+ public void IsTypeUriPresent() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ Assert.IsTrue(se.IsTypeUriPresent(Protocol.Default.ClaimedIdentifierServiceTypeURI));
+ Assert.IsFalse(se.IsTypeUriPresent("http://someother"));
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void IsTypeUriPresentNull() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ se.IsTypeUriPresent(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void IsTypeUriPresentEmpty() {
+ IdentifierDiscoveryResult se = IdentifierDiscoveryResult.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
+ se.IsTypeUriPresent(string.Empty);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void IsExtensionSupportedNullType() {
+ var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null);
+ se.IsExtensionSupported((Type)null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void IsTypeUriPresentNullString() {
+ var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null);
+ se.IsTypeUriPresent((string)null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void IsTypeUriPresentEmptyString() {
+ var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null);
+ se.IsTypeUriPresent(string.Empty);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void IsExtensionSupportedNullExtension() {
+ var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null);
+ se.IsExtensionSupported((IOpenIdMessageExtension)null);
+ }
+
+ [TestMethod]
+ public void IsExtensionSupported() {
+ var se = IdentifierDiscoveryResult.CreateForProviderIdentifier(OPUri, new ProviderEndpointDescription(OPUri, this.v20TypeUris), null, null);
+ Assert.IsFalse(se.IsExtensionSupported<ClaimsRequest>());
+ Assert.IsFalse(se.IsExtensionSupported(new ClaimsRequest()));
+ Assert.IsFalse(se.IsTypeUriPresent("http://someextension/typeuri"));
+
+ se = IdentifierDiscoveryResult.CreateForProviderIdentifier(
+ OPUri,
+ new ProviderEndpointDescription(OPUri, new[] { Protocol.V20.ClaimedIdentifierServiceTypeURI, "http://someextension", Constants.sreg_ns }),
+ null,
+ null);
+ Assert.IsTrue(se.IsExtensionSupported<ClaimsRequest>());
+ Assert.IsTrue(se.IsExtensionSupported(new ClaimsRequest()));
+ Assert.IsTrue(se.IsTypeUriPresent("http://someextension"));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
index f6a57e7..7f0eb81 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
@@ -23,7 +23,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
[TestMethod]
public void CreateRequestDumbMode() {
- var rp = new OpenIdRelyingParty(null);
+ var rp = this.CreateRelyingParty(true);
Identifier id = this.GetMockIdentifier(ProtocolVersion.V20);
var authReq = rp.CreateRequest(id, RPRealmUri, RPUri);
CheckIdRequest requestMessage = (CheckIdRequest)authReq.RedirectingResponse.OriginalMessage;
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs
new file mode 100644
index 0000000..67255e3
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdTextBoxTests.cs
@@ -0,0 +1,49 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdTextBoxTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OpenIdTextBoxTests : OpenIdTestBase {
+ /// <summary>
+ /// Verifies that the Text and Identifier properties interact correctly.
+ /// </summary>
+ [TestMethod]
+ public void IdentifierTextInteraction() {
+ var box = new OpenIdTextBox();
+ Assert.AreEqual(string.Empty, box.Text);
+ Assert.IsNull(box.Identifier);
+
+ box.Text = "=arnott";
+ Assert.AreEqual("=arnott", box.Text);
+ Assert.AreEqual("=arnott", box.Identifier.ToString());
+
+ box.Identifier = "=bob";
+ Assert.AreEqual("=bob", box.Text);
+ Assert.AreEqual("=bob", box.Identifier.ToString());
+
+ box.Text = string.Empty;
+ Assert.AreEqual(string.Empty, box.Text);
+ Assert.IsNull(box.Identifier);
+
+ box.Text = null;
+ Assert.AreEqual(string.Empty, box.Text);
+ Assert.IsNull(box.Identifier);
+
+ // Invalid identifier case
+ box.Text = "/";
+ Assert.AreEqual("/", box.Text);
+ Assert.IsNull(box.Identifier);
+
+ // blank out the invalid case
+ box.Identifier = null;
+ Assert.AreEqual(string.Empty, box.Text);
+ Assert.IsNull(box.Identifier);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs
index 083b988..38dd0e6 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAuthenticationResponseTests.cs
@@ -38,7 +38,7 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
Assert.AreEqual(AuthenticationStatus.Authenticated, authResponse.Status);
Assert.IsNull(authResponse.Exception);
Assert.AreEqual<string>(assertion.ClaimedIdentifier, authResponse.ClaimedIdentifier);
- Assert.AreEqual<string>(authResponseAccessor.endpoint.FriendlyIdentifierForDisplay, authResponse.FriendlyIdentifierForDisplay);
+ Assert.AreEqual<string>(authResponse.Endpoint.FriendlyIdentifierForDisplay, authResponse.FriendlyIdentifierForDisplay);
Assert.AreSame(extension, authResponse.GetUntrustedExtension(typeof(ClaimsResponse)));
Assert.AreSame(extension, authResponse.GetUntrustedExtension<ClaimsResponse>());
Assert.IsNull(authResponse.GetCallbackArgument("a"));
@@ -46,6 +46,32 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
}
/// <summary>
+ /// Verifies that discovery verification of a positive assertion can match a dual identifier.
+ /// </summary>
+ [TestMethod]
+ public void DualIdentifierMatchesInAssertionVerification() {
+ PositiveAssertionResponse assertion = this.GetPositiveAssertion(true);
+ ClaimsResponse extension = new ClaimsResponse();
+ assertion.Extensions.Add(extension);
+ var rp = CreateRelyingParty();
+ rp.SecuritySettings.AllowDualPurposeIdentifiers = true;
+ new PositiveAuthenticationResponse(assertion, rp); // this will throw if it fails to find a match
+ }
+
+ /// <summary>
+ /// Verifies that discovery verification of a positive assertion cannot match a dual identifier
+ /// if the default settings are in place.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void DualIdentifierNoMatchInAssertionVerificationByDefault() {
+ PositiveAssertionResponse assertion = this.GetPositiveAssertion(true);
+ ClaimsResponse extension = new ClaimsResponse();
+ assertion.Extensions.Add(extension);
+ var rp = CreateRelyingParty();
+ new PositiveAuthenticationResponse(assertion, rp); // this will throw if it fails to find a match
+ }
+
+ /// <summary>
/// Verifies that the RP rejects signed solicited assertions by an OP that
/// makes up a claimed Id that was not part of the original request, and
/// that the OP has no authority to assert positively regarding.
@@ -95,9 +121,13 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
}
private PositiveAssertionResponse GetPositiveAssertion() {
+ return this.GetPositiveAssertion(false);
+ }
+
+ private PositiveAssertionResponse GetPositiveAssertion(bool dualIdentifier) {
Protocol protocol = Protocol.Default;
PositiveAssertionResponse assertion = new PositiveAssertionResponse(protocol.Version, this.returnTo);
- assertion.ClaimedIdentifier = this.GetMockIdentifier(protocol.ProtocolVersion, false);
+ assertion.ClaimedIdentifier = dualIdentifier ? this.GetMockDualIdentifier() : this.GetMockIdentifier(protocol.ProtocolVersion, false);
assertion.LocalIdentifier = OPLocalIdentifiers[0];
assertion.ReturnTo = this.returnTo;
assertion.ProviderEndpoint = OPUri;
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
deleted file mode 100644
index bd09bc9..0000000
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="ServiceEndpointTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
- using DotNetOpenAuth.OpenId.Messages;
- using DotNetOpenAuth.OpenId.RelyingParty;
- using DotNetOpenAuth.Test.Messaging;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ServiceEndpointTests : OpenIdTestBase {
- private UriIdentifier claimedId = new UriIdentifier("http://claimedid.justatest.com");
- private XriIdentifier claimedXri = new XriIdentifier("=!9B72.7DD1.50A9.5CCD");
- private XriIdentifier userSuppliedXri = new XriIdentifier("=Arnot");
- private Uri providerEndpoint = new Uri("http://someprovider.com");
- private Identifier localId = "http://localid.someprovider.com";
- private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI };
- private string[] v11TypeUris = { Protocol.V11.ClaimedIdentifierServiceTypeURI };
- private int servicePriority = 10;
- private int uriPriority = 10;
-
- [TestMethod]
- public void Ctor() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreEqual(this.claimedId, se.ClaimedIdentifier);
- Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
- Assert.AreSame(this.localId, se.ProviderLocalIdentifier);
- CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.ProviderSupportedServiceTypeUris);
- Assert.AreEqual(this.servicePriority, ((IXrdsProviderEndpoint)se).ServicePriority);
- }
-
- [TestMethod]
- public void CtorImpliedLocalIdentifier() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, null, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreEqual(this.claimedId, se.ClaimedIdentifier);
- Assert.AreSame(this.providerEndpoint, se.ProviderEndpoint);
- Assert.AreSame(this.claimedId, se.ProviderLocalIdentifier);
- CollectionAssert<string>.AreEquivalent(this.v20TypeUris, se.ProviderSupportedServiceTypeUris);
- }
-
- [TestMethod]
- public void ProtocolDetection() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreSame(Protocol.V20, se.Protocol);
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- this.claimedId,
- this.localId,
- new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.OPIdentifierServiceTypeURI }),
- this.servicePriority,
- this.uriPriority);
- Assert.AreSame(Protocol.V20, se.Protocol);
- se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreSame(Protocol.V11, se.Protocol);
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void ProtocolDetectionWithoutClues() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(
- this.claimedId,
- this.localId,
- new ProviderEndpointDescription(this.providerEndpoint, new[] { Protocol.V20.HtmlDiscoveryLocalIdKey }), // random type URI irrelevant to detection
- this.servicePriority,
- this.uriPriority);
- }
-
- [TestMethod]
- public void SerializationWithUri() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- StringBuilder sb = new StringBuilder();
- using (StringWriter sw = new StringWriter(sb)) {
- se.Serialize(sw);
- }
- using (StringReader sr = new StringReader(sb.ToString())) {
- ServiceEndpoint se2 = ServiceEndpoint.Deserialize(sr);
- Assert.AreEqual(se, se2);
- Assert.AreEqual(se.Protocol.Version, se2.Protocol.Version, "Particularly interested in this, since type URIs are not serialized but version info is.");
- Assert.AreEqual(se.UserSuppliedIdentifier, se2.UserSuppliedIdentifier);
- Assert.AreEqual(se.FriendlyIdentifierForDisplay, se2.FriendlyIdentifierForDisplay);
- }
- }
-
- [TestMethod]
- public void SerializationWithXri() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- StringBuilder sb = new StringBuilder();
- using (StringWriter sw = new StringWriter(sb)) {
- se.Serialize(sw);
- }
- using (StringReader sr = new StringReader(sb.ToString())) {
- ServiceEndpoint se2 = ServiceEndpoint.Deserialize(sr);
- Assert.AreEqual(se, se2);
- Assert.AreEqual(se.Protocol.Version, se2.Protocol.Version, "Particularly interested in this, since type URIs are not serialized but version info is.");
- Assert.AreEqual(se.UserSuppliedIdentifier, se2.UserSuppliedIdentifier);
- Assert.AreEqual(se.FriendlyIdentifierForDisplay, se2.FriendlyIdentifierForDisplay);
- }
- }
-
- [TestMethod]
- public void EqualsTests() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- ServiceEndpoint se2 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), (int?)null, (int?)null);
- Assert.AreEqual(se2, se);
- Assert.AreNotEqual(se, null);
- Assert.AreNotEqual(null, se);
-
- ServiceEndpoint se3 = ServiceEndpoint.CreateForClaimedIdentifier(new UriIdentifier(this.claimedId + "a"), this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
- se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(new Uri(this.providerEndpoint.AbsoluteUri + "a"), this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
- se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId + "a", new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
- se3 = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedId, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v11TypeUris), this.servicePriority, this.uriPriority);
- Assert.AreNotEqual(se, se3);
-
- // make sure that Collection<T>.Contains works as desired.
- List<ServiceEndpoint> list = new List<ServiceEndpoint>();
- list.Add(se);
- Assert.IsTrue(list.Contains(se2));
- }
-
- [TestMethod]
- public void FriendlyIdentifierForDisplay() {
- Uri providerEndpoint = new Uri("http://someprovider");
- Identifier localId = "someuser";
- string[] serviceTypeUris = new string[] {
- Protocol.V20.ClaimedIdentifierServiceTypeURI,
- };
- ServiceEndpoint se;
-
- // strip of protocol and fragment
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- "http://someprovider.somedomain.com:79/someuser#frag",
- localId,
- new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
- null,
- null);
- Assert.AreEqual("someprovider.somedomain.com:79/someuser", se.FriendlyIdentifierForDisplay);
-
- // unescape characters
- Uri foreignUri = new Uri("http://server崎/村");
- se = ServiceEndpoint.CreateForClaimedIdentifier(foreignUri, localId, new ProviderEndpointDescription(providerEndpoint, serviceTypeUris), null, null);
- Assert.AreEqual("server崎/村", se.FriendlyIdentifierForDisplay);
-
- // restore user supplied identifier to XRIs
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
- new XriIdentifier("=Arnott崎村"),
- localId,
- new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
- null,
- null);
- Assert.AreEqual("=Arnott崎村", se.FriendlyIdentifierForDisplay);
-
- // If UserSuppliedIdentifier is the same as the ClaimedIdentifier, don't display it twice...
- se = ServiceEndpoint.CreateForClaimedIdentifier(
- new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
- new XriIdentifier("=!9B72.7DD1.50A9.5CCD"),
- localId,
- new ProviderEndpointDescription(providerEndpoint, serviceTypeUris),
- null,
- null);
- Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", se.FriendlyIdentifierForDisplay);
- }
-
- [TestMethod]
- public void IsTypeUriPresent() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.IsTrue(se.IsTypeUriPresent(Protocol.Default.ClaimedIdentifierServiceTypeURI));
- Assert.IsFalse(se.IsTypeUriPresent("http://someother"));
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsTypeUriPresentNull() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- se.IsTypeUriPresent(null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void IsTypeUriPresentEmpty() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- se.IsTypeUriPresent(string.Empty);
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
index f1823e6..d504cdf 100644
--- a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
@@ -31,7 +31,7 @@ namespace DotNetOpenAuth.Test.OpenId {
new UriIdentifier((Uri)null);
}
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
public void CtorNullString() {
new UriIdentifier((string)null);
}
@@ -141,62 +141,6 @@ namespace DotNetOpenAuth.Test.OpenId {
}
[TestMethod]
- public void HtmlDiscover_11() {
- this.DiscoverHtml("html10prov", ProtocolVersion.V11, null, "http://a/b");
- this.DiscoverHtml("html10both", ProtocolVersion.V11, "http://c/d", "http://a/b");
- this.FailDiscoverHtml("html10del");
-
- // Verify that HTML discovery generates the 1.x endpoints when appropriate
- this.DiscoverHtml("html2010", ProtocolVersion.V11, "http://g/h", "http://e/f");
- this.DiscoverHtml("html1020", ProtocolVersion.V11, "http://g/h", "http://e/f");
- this.DiscoverHtml("html2010combinedA", ProtocolVersion.V11, "http://c/d", "http://a/b");
- this.DiscoverHtml("html2010combinedB", ProtocolVersion.V11, "http://c/d", "http://a/b");
- this.DiscoverHtml("html2010combinedC", ProtocolVersion.V11, "http://c/d", "http://a/b");
- }
-
- [TestMethod]
- public void HtmlDiscover_20() {
- this.DiscoverHtml("html20prov", ProtocolVersion.V20, null, "http://a/b");
- this.DiscoverHtml("html20both", ProtocolVersion.V20, "http://c/d", "http://a/b");
- this.FailDiscoverHtml("html20del");
- this.DiscoverHtml("html2010", ProtocolVersion.V20, "http://c/d", "http://a/b");
- this.DiscoverHtml("html1020", ProtocolVersion.V20, "http://c/d", "http://a/b");
- this.DiscoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d", "http://a/b");
- this.DiscoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d", "http://a/b");
- this.DiscoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d", "http://a/b");
- this.FailDiscoverHtml("html20relative");
- }
-
- [TestMethod]
- public void XrdsDiscoveryFromHead() {
- this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
- this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null, "http://a/b");
- }
-
- [TestMethod]
- public void XrdsDiscoveryFromHttpHeader() {
- WebHeaderCollection headers = new WebHeaderCollection();
- headers.Add("X-XRDS-Location", new Uri("http://localhost/xrds1020.xml").AbsoluteUri);
- this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
- this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, "http://a/b", headers);
- }
-
- [TestMethod]
- public void XrdsDirectDiscovery_10() {
- this.FailDiscoverXrds("xrds-irrelevant");
- this.DiscoverXrds("xrds10", ProtocolVersion.V10, null, "http://a/b");
- this.DiscoverXrds("xrds11", ProtocolVersion.V11, null, "http://a/b");
- this.DiscoverXrds("xrds1020", ProtocolVersion.V10, null, "http://a/b");
- }
-
- [TestMethod]
- public void XrdsDirectDiscovery_20() {
- this.DiscoverXrds("xrds20", ProtocolVersion.V20, null, "http://a/b");
- this.DiscoverXrds("xrds2010a", ProtocolVersion.V20, null, "http://a/b");
- this.DiscoverXrds("xrds2010b", ProtocolVersion.V20, null, "http://a/b");
- }
-
- [TestMethod]
public void NormalizeCase() {
// only the host name can be normalized in casing safely.
Identifier id = "http://HOST:80/PaTH?KeY=VaLUE#fRag";
@@ -221,21 +165,6 @@ namespace DotNetOpenAuth.Test.OpenId {
}
[TestMethod]
- public void DiscoveryWithRedirects() {
- Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, false);
-
- // Add a couple of chained redirect pages that lead to the claimedId.
- Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
- Uri insecureMidpointUri = new Uri("http://localhost/insecureStop");
- this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
- this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));
-
- // don't require secure SSL discovery for this test.
- Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false);
- Assert.AreEqual(1, userSuppliedIdentifier.Discover(this.RequestHandler).Count());
- }
-
- [TestMethod]
public void TryRequireSslAdjustsIdentifier() {
Identifier secureId;
// Try Parse and ctor without explicit scheme
@@ -256,180 +185,13 @@ namespace DotNetOpenAuth.Test.OpenId {
Assert.IsFalse(id.TryRequireSsl(out secureId));
Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd, "Although the TryRequireSsl failed, the created identifier should retain the Ssl status.");
Assert.AreEqual("http://www.yahoo.com/", secureId.ToString());
- Assert.AreEqual(0, secureId.Discover(this.RequestHandler).Count(), "Since TryRequireSsl failed, the created Identifier should never discover anything.");
+ Assert.AreEqual(0, Discover(secureId).Count(), "Since TryRequireSsl failed, the created Identifier should never discover anything.");
id = new UriIdentifier("http://www.yahoo.com");
Assert.IsFalse(id.TryRequireSsl(out secureId));
Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd);
Assert.AreEqual("http://www.yahoo.com/", secureId.ToString());
- Assert.AreEqual(0, secureId.Discover(this.RequestHandler).Count());
- }
-
- [TestMethod]
- public void DiscoverRequireSslWithSecureRedirects() {
- Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true);
-
- // Add a couple of chained redirect pages that lead to the claimedId.
- // All redirects should be secure.
- Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
- Uri secureMidpointUri = new Uri("https://localhost/secureStop");
- this.MockResponder.RegisterMockRedirect(userSuppliedUri, secureMidpointUri);
- this.MockResponder.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString()));
-
- Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
- Assert.AreEqual(1, userSuppliedIdentifier.Discover(this.RequestHandler).Count());
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void DiscoverRequireSslWithInsecureRedirect() {
- Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true);
-
- // Add a couple of chained redirect pages that lead to the claimedId.
- // Include an insecure HTTP jump in those redirects to verify that
- // the ultimate endpoint is never found as a result of high security profile.
- Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
- Uri insecureMidpointUri = new Uri("http://localhost/insecureStop");
- this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
- this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));
-
- Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
- userSuppliedIdentifier.Discover(this.RequestHandler);
- }
-
- [TestMethod]
- public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() {
- var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
- Uri secureClaimedUri = new Uri("https://localhost/secureId");
-
- string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource);
- this.MockResponder.RegisterMockResponse(secureClaimedUri, "text/html", html);
-
- Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true);
- Assert.AreEqual(0, userSuppliedIdentifier.Discover(this.RequestHandler).Count());
- }
-
- [TestMethod]
- public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() {
- var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
-
- string html = "<html><head></head><body></body></html>";
- WebHeaderCollection headers = new WebHeaderCollection {
- { "X-XRDS-Location", insecureXrdsSource }
- };
- this.MockResponder.RegisterMockResponse(VanityUriSsl, VanityUriSsl, "text/html", headers, html);
-
- Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true);
- Assert.AreEqual(0, userSuppliedIdentifier.Discover(this.RequestHandler).Count());
- }
-
- [TestMethod]
- public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() {
- var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
- string html = string.Format(
- @"
- <html><head>
- <meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored -->
- <link rel='openid2.provider' href='{1}' />
- <link rel='openid2.local_id' href='{2}' />
- </head><body></body></html>",
- HttpUtility.HtmlEncode(insecureXrdsSource),
- HttpUtility.HtmlEncode(OPUriSsl.AbsoluteUri),
- HttpUtility.HtmlEncode(OPLocalIdentifiersSsl[1].AbsoluteUri));
- this.MockResponder.RegisterMockResponse(VanityUriSsl, "text/html", html);
-
- Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true);
-
- // We verify that the XRDS was ignored and the LINK tags were used
- // because the XRDS OP-LocalIdentifier uses different local identifiers.
- Assert.AreEqual(OPLocalIdentifiersSsl[1], userSuppliedIdentifier.Discover(this.RequestHandler).Single().ProviderLocalIdentifier);
- }
-
- [TestMethod]
- public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() {
- var insecureEndpoint = GetServiceEndpoint(0, ProtocolVersion.V20, 10, false);
- var secureEndpoint = GetServiceEndpoint(1, ProtocolVersion.V20, 20, true);
- UriIdentifier secureClaimedId = new UriIdentifier(VanityUriSsl, true);
- this.MockResponder.RegisterMockXrdsResponse(secureClaimedId, new ServiceEndpoint[] { insecureEndpoint, secureEndpoint });
- Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, secureClaimedId.Discover(this.RequestHandler).Single().ProviderLocalIdentifier);
- }
-
- private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect) {
- this.Discover(url, version, expectedLocalId, providerEndpoint, expectSreg, useRedirect, null);
- }
-
- private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect, WebHeaderCollection headers) {
- Protocol protocol = Protocol.Lookup(version);
- Uri baseUrl = new Uri("http://localhost/");
- UriIdentifier claimedId = new Uri(baseUrl, url);
- UriIdentifier userSuppliedIdentifier = new Uri(baseUrl, "Discovery/htmldiscovery/redirect.aspx?target=" + url);
- if (expectedLocalId == null) {
- expectedLocalId = claimedId;
- }
- Identifier idToDiscover = useRedirect ? userSuppliedIdentifier : claimedId;
-
- string contentType;
- if (url.EndsWith("html")) {
- contentType = "text/html";
- } else if (url.EndsWith("xml")) {
- contentType = "application/xrds+xml";
- } else {
- throw new InvalidOperationException();
- }
- this.MockResponder.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType, headers ?? new WebHeaderCollection(), LoadEmbeddedFile(url));
-
- ServiceEndpoint expected = ServiceEndpoint.CreateForClaimedIdentifier(
- claimedId,
- expectedLocalId,
- new ProviderEndpointDescription(new Uri(providerEndpoint), new string[] { protocol.ClaimedIdentifierServiceTypeURI }), // services aren't checked by Equals
- null,
- null);
-
- ServiceEndpoint se = idToDiscover.Discover(this.RequestHandler).FirstOrDefault(ep => ep.Equals(expected));
- Assert.IsNotNull(se, url + " failed to be discovered.");
-
- // Do extra checking of service type URIs, which aren't included in
- // the ServiceEndpoint.Equals method.
- Assert.AreEqual(expectSreg ? 2 : 1, se.ProviderSupportedServiceTypeUris.Count);
- Assert.IsTrue(se.ProviderSupportedServiceTypeUris.Contains(protocol.ClaimedIdentifierServiceTypeURI));
- Assert.AreEqual(expectSreg, se.IsExtensionSupported<ClaimsRequest>());
- }
-
- private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) {
- this.DiscoverXrds(page, version, expectedLocalId, providerEndpoint, null);
- }
-
- private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, WebHeaderCollection headers) {
- if (!page.Contains(".")) {
- page += ".xml";
- }
- this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, false, headers);
- this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, true, headers);
- }
-
- private void DiscoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool useRedirect) {
- this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, providerEndpoint, false, useRedirect);
- }
-
- private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) {
- string page = scenario + ".html";
- this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, false);
- this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, true);
- }
-
- private void FailDiscover(string url) {
- UriIdentifier userSuppliedId = new Uri(new Uri("http://localhost"), url);
-
- this.MockResponder.RegisterMockResponse(new Uri(userSuppliedId), userSuppliedId, "text/html", LoadEmbeddedFile(url));
-
- Assert.AreEqual(0, userSuppliedId.Discover(this.RequestHandler).Count()); // ... but that no endpoint info is discoverable
- }
-
- private void FailDiscoverHtml(string scenario) {
- this.FailDiscover("/Discovery/htmldiscovery/" + scenario + ".html");
- }
-
- private void FailDiscoverXrds(string scenario) {
- this.FailDiscover("/Discovery/xrdsdiscovery/" + scenario + ".xml");
+ Assert.AreEqual(0, Discover(secureId).Count());
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs
index 87ecd4b..d5a51cf 100644
--- a/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/XriIdentifierTests.cs
@@ -22,7 +22,7 @@ namespace DotNetOpenAuth.Test.OpenId {
base.SetUp();
}
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
public void CtorNull() {
new XriIdentifier(null);
}
@@ -88,386 +88,10 @@ namespace DotNetOpenAuth.Test.OpenId {
Assert.AreEqual(this.goodXri, new XriIdentifier(this.goodXri));
}
- [TestMethod]
- public void Discover() {
- string xrds = @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'>
- <Query>*Arnott</Query>
- <Status ceid='off' cid='verified' code='100'/>
- <Expires>2008-07-14T02:03:24.000Z</Expires>
- <ProviderID>xri://=</ProviderID>
- <LocalID>!9b72.7dd1.50a9.5ccd</LocalID>
- <CanonicalID>=!9B72.7DD1.50A9.5CCD</CanonicalID>
-
- <Service priority='10'>
- <ProviderID>xri://!!1008</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='default' select='false'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null' select='false'/>
- <URI append='qxri' priority='1'>http://1id.com/contact/</URI>
-
- </Service>
- <Service priority='10'>
- <ProviderID>xri://!!1008</ProviderID>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Type match='null' select='false'/>
- <URI append='qxri' priority='1'>http://1id.com/</URI>
- </Service>
-
- <Service priority='10'>
- <ProviderID>xri://!!1008</ProviderID>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <URI append='none' priority='10'>http://1id.com/sso</URI>
- </Service>
-</XRD>";
- Dictionary<string, string> mocks = new Dictionary<string, string> {
- { "https://xri.net/=Arnott?_xrd_r=application/xrd%2Bxml;sep=false", xrds },
- { "https://xri.net/=!9B72.7DD1.50A9.5CCD?_xrd_r=application/xrd%2Bxml;sep=false", xrds },
- };
- this.MockResponder.RegisterMockXrdsResponses(mocks);
-
- string expectedCanonicalId = "=!9B72.7DD1.50A9.5CCD";
- ServiceEndpoint se = this.VerifyCanonicalId("=Arnott", expectedCanonicalId);
- Assert.AreEqual(Protocol.V10, se.Protocol);
- Assert.AreEqual("http://1id.com/sso", se.ProviderEndpoint.ToString());
- Assert.AreEqual(se.ClaimedIdentifier, se.ProviderLocalIdentifier);
- Assert.AreEqual("=Arnott", se.FriendlyIdentifierForDisplay);
- }
-
- [TestMethod]
- public void DiscoverCommunityInameCanonicalIDs() {
- string llliResponse = @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'>
- <Query>*llli</Query>
- <Status ceid='off' cid='verified' code='100'/>
- <Expires>2008-07-14T02:21:06.000Z</Expires>
- <ProviderID>xri://@</ProviderID>
- <LocalID>!72cd.a072.157e.a9c6</LocalID>
- <CanonicalID>@!72CD.A072.157E.A9C6</CanonicalID>
- <Service priority='10'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <URI append='none' priority='1'>https://login.llli.org/server/</URI>
- </Service>
- <Service priority='1'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type match='null' select='false'/>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Path match='default'/>
- <Path>(+index)</Path>
- <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
- </Service>
- <Service priority='10'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://$res*auth*($v*2.0)</Type>
- <MediaType>application/xrds+xml;trust=none</MediaType>
- <URI priority='10'>http://resolve.ezibroker.net/resolve/@llli/</URI>
- </Service>
- <Service priority='10'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null'/>
- <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
- </Service>
-</XRD>
-";
- string llliAreaResponse = @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD xmlns='xri://$xrd*($v*2.0)'>
- <Query>*area</Query>
- <Status cid='verified' code='100'>SUCCESS</Status>
- <ServerStatus code='100'>SUCCESS</ServerStatus>
- <Expires>2008-07-15T01:21:07.000Z</Expires>
- <ProviderID>xri://!!1003</ProviderID>
- <LocalID>0000.0000.3B9A.CA0C</LocalID>
- <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C</CanonicalID>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <URI append='none' priority='1'>https://login.llli.org/server/</URI>
- </Service>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null'/>
- <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
- </Service>
- <Service priority='1'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Type match='null' select='false'/>
- <Path>(+index)</Path>
- <Path match='default'/>
- <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
- </Service>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://$res*auth*($v*2.0)</Type>
- <MediaType>application/xrds+xml;trust=none</MediaType>
- <URI>http://resolve.ezibroker.net/resolve/@llli*area/</URI>
- </Service>
-</XRD>";
- string llliAreaCanadaUnattachedResponse = @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD xmlns='xri://$xrd*($v*2.0)'>
- <Query>*canada.unattached</Query>
- <Status cid='verified' code='100'>SUCCESS</Status>
- <ServerStatus code='100'>SUCCESS</ServerStatus>
- <Expires>2008-07-15T01:21:08.000Z</Expires>
- <ProviderID>xri://!!1003</ProviderID>
- <LocalID>0000.0000.3B9A.CA41</LocalID>
- <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41</CanonicalID>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <URI append='none' priority='1'>https://login.llli.org/server/</URI>
- </Service>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null'/>
- <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
- </Service>
- <Service priority='1'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Type match='null' select='false'/>
- <Path>(+index)</Path>
- <Path match='default'/>
- <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
- </Service>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://$res*auth*($v*2.0)</Type>
- <MediaType>application/xrds+xml;trust=none</MediaType>
- <URI>http://resolve.ezibroker.net/resolve/@llli*area*canada.unattached/</URI>
- </Service>
-</XRD>";
- string llliAreaCanadaUnattachedAdaResponse = @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD xmlns='xri://$xrd*($v*2.0)'>
- <Query>*ada</Query>
- <Status cid='verified' code='100'>SUCCESS</Status>
- <ServerStatus code='100'>SUCCESS</ServerStatus>
- <Expires>2008-07-15T01:21:10.000Z</Expires>
- <ProviderID>xri://!!1003</ProviderID>
- <LocalID>0000.0000.3B9A.CA01</LocalID>
- <CanonicalID>@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01</CanonicalID>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <URI append='none' priority='1'>https://login.llli.org/server/</URI>
- </Service>
- <Service>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null'/>
- <URI append='authority' priority='1'>http://linksafe-contact.ezibroker.net/contact/</URI>
- </Service>
- <Service priority='1'>
- <ProviderID>xri://!!1003!103</ProviderID>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Type match='null' select='false'/>
- <Path>(+index)</Path>
- <Path match='default'/>
- <URI append='qxri' priority='1'>http://linksafe-forward.ezibroker.net/forwarding/</URI>
- </Service>
-</XRD>";
- string webResponse = @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD version='2.0' xmlns='xri://$xrd*($v*2.0)'>
- <Query>*Web</Query>
- <Status ceid='off' cid='verified' code='100'/>
- <Expires>2008-07-14T02:21:12.000Z</Expires>
- <ProviderID>xri://=</ProviderID>
- <LocalID>!91f2.8153.f600.ae24</LocalID>
- <CanonicalID>=!91F2.8153.F600.AE24</CanonicalID>
- <Service priority='10'>
- <Type select='true'>xri://+i-service*(+locator)*($v*1.0)</Type>
- <Path select='true'>(+locator)</Path>
- <MediaType match='default' select='false'/>
- <URI append='qxri'>http://locator.fullxri.com/locator/</URI>
- </Service>
- <Service priority='10'>
- <ProviderID>xri://=web</ProviderID>
- <Type select='true'>xri://$res*auth*($v*2.0)</Type>
- <Type select='true'>xri://$res*auth*($v*2.0)</Type>
- <MediaType select='true'>application/xrds+xml</MediaType>
- <URI append='qxri' priority='1'>https://resolve.freexri.com/ns/=web/</URI>
- <URI append='qxri' priority='2'>http://resolve.freexri.com/ns/=web/</URI>
- </Service>
- <Service priority='10'>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <Type select='true'>http://specs.openid.net/auth/2.0/signon</Type>
- <Path select='true'>(+login)</Path>
- <Path match='default' select='false'/>
- <MediaType match='default' select='false'/>
- <URI append='none' priority='2'>http://authn.fullxri.com/authentication/</URI>
- <URI append='none' priority='1'>https://authn.fullxri.com/authentication/</URI>
- </Service>
- <Service priority='10'>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null' select='false'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null' select='false'/>
- <MediaType match='default' select='false'/>
- <URI append='qxri'>http://contact.fullxri.com/contact/</URI>
- </Service>
- <KeyInfo xmlns='http://www.w3.org/2000/09/xmldsig#'>
- <X509Data>
- <X509Certificate>
-MIIExzCCA6+gAwIBAgIJAM+MlFr0Sth6MA0GCSqGSIb3DQEBBQUAMIGdMR8wHQYD
-VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE
-CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs
-YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs
-YWluLmVkdTAeFw0wNjA4MTcxOTU5NTNaFw0xMTA4MTYxOTU5NTNaMIGdMR8wHQYD
-VQQDExZTdXBlcnZpbGxhaW46IFRoZSBSb290MQswCQYDVQQGEwJVUzERMA8GA1UE
-CBMITmV3IFlvcmsxDzANBgNVBAcTBkdvdGhhbTEgMB4GA1UEChMXU3VwZXJ2aWxs
-YWluIFVuaXZlcnNpdHkxJzAlBgkqhkiG9w0BCQEWGHBlbmd1aW5Ac3VwZXJ2aWxs
-YWluLmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL6uFqas4dK6
-A2wTZL0viRQNJrPyFnFBDSZGib/2ijhgzed/vvmZIBM9sFpwahcuR5hvyKUe37/c
-/RSZXoNDi/eiNOx4qb0l9UB6bd8qvc4V1PnLE7L+ZYcmwrvTKm4x8qXMgEv1wca2
-FPsreHNPdLiTUZ8v0tDTWi3Mgi7y47VTzJaTkcfmO1nL6xAtln5sLdH0PbMM3LAp
-T1d3nwI3VdbhqqZ+6+OKEuC8gk5iH4lfrbr6C9bYS6vzIKrotHpZ3N2aIC3NMjJD
-PMw/mfCuADfRNlHXgZW+0zyUkwGTMDea8qgsoAMWJGdeTIw8I1I3RhnbgLzdsNQl
-b/1ZXx1uJRUCAwEAAaOCAQYwggECMB0GA1UdDgQWBBQe+xSjYTrlfraJARjMxscb
-j36jvDCB0gYDVR0jBIHKMIHHgBQe+xSjYTrlfraJARjMxscbj36jvKGBo6SBoDCB
-nTEfMB0GA1UEAxMWU3VwZXJ2aWxsYWluOiBUaGUgUm9vdDELMAkGA1UEBhMCVVMx
-ETAPBgNVBAgTCE5ldyBZb3JrMQ8wDQYDVQQHEwZHb3RoYW0xIDAeBgNVBAoTF1N1
-cGVydmlsbGFpbiBVbml2ZXJzaXR5MScwJQYJKoZIhvcNAQkBFhhwZW5ndWluQHN1
-cGVydmlsbGFpbi5lZHWCCQDPjJRa9ErYejAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
-DQEBBQUAA4IBAQC4SPBDGYAxfbXd8N5OvG0drM7a5hjXfcCZpiILlPSRpxp79yh7
-I5vVWxBxUfolwbei7PTBVy7CE27SUbSICeqWjcDCfjNjiZk6mLS80rm/TdLrHSyM
-+Ujlw9MGcBGaLI+sdziDUMtTQDpeAyQTaGVbh1mx5874Hlo1VXqGYNo0RwR+iLfs
-x48VuO6GbWVyxtktkE2ypz1KLWiyI056YynydRvuBCBHeRqGUixPlH9CrmeSCP2S
-sfbiKnMOGXjIYbvbsTAMdW2iqg6IWa/fgxhvZoAXChM9bkhisJQc0qD0J5TJQwgr
-uEyb50RJ7DWmXctSC0b3eymZ2lSXxAWNOsNy
- </X509Certificate>
- </X509Data>
- </KeyInfo>
-</XRD>";
- this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> {
- { "https://xri.net/@llli?_xrd_r=application/xrd%2Bxml;sep=false", llliResponse },
- { "https://xri.net/@llli*area?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaResponse },
- { "https://xri.net/@llli*area*canada.unattached?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedResponse },
- { "https://xri.net/@llli*area*canada.unattached*ada?_xrd_r=application/xrd%2Bxml;sep=false", llliAreaCanadaUnattachedAdaResponse },
- { "https://xri.net/=Web?_xrd_r=application/xrd%2Bxml;sep=false", webResponse },
- });
- this.VerifyCanonicalId("@llli", "@!72CD.A072.157E.A9C6");
- this.VerifyCanonicalId("@llli*area", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C");
- this.VerifyCanonicalId("@llli*area*canada.unattached", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41");
- this.VerifyCanonicalId("@llli*area*canada.unattached*ada", "@!72CD.A072.157E.A9C6!0000.0000.3B9A.CA0C!0000.0000.3B9A.CA41!0000.0000.3B9A.CA01");
- this.VerifyCanonicalId("=Web", "=!91F2.8153.F600.AE24");
- }
-
- [TestMethod]
- public void DiscoveryCommunityInameDelegateWithoutCanonicalID() {
- this.MockResponder.RegisterMockXrdsResponses(new Dictionary<string, string> {
- { "https://xri.net/=Web*andrew.arnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD xmlns='xri://$xrd*($v*2.0)'>
- <Query>*andrew.arnott</Query>
- <Status cid='absent' code='100'>Success</Status>
- <ServerStatus code='100'>Success</ServerStatus>
- <Expires>2008-07-14T03:30:59.722Z</Expires>
- <ProviderID>=!91F2.8153.F600.AE24</ProviderID>
- <Service>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <Path select='true'>(+login)</Path>
- <Path match='default'/>
- <MediaType match='default'/>
- <URI append='none' priority='2'>http://www.myopenid.com/server</URI>
- <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate>
- </Service>
- <Service>
- <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null'/>
- <MediaType match='default'/>
- <URI append='qxri'>http://contact.freexri.com/contact/</URI>
- </Service>
- <Service>
- <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Path select='true'>(+index)</Path>
- <Path match='default'/>
- <MediaType match='default'/>
- <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI>
- </Service>
- <Service>
- <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <Path select='true'>(+login)</Path>
- <Path match='default'/>
- <MediaType match='default'/>
- <URI append='none' priority='2'>http://authn.freexri.com/authentication/</URI>
- <URI append='none' priority='1'>https://authn.freexri.com/authentication/</URI>
- </Service>
- <ServedBy>OpenXRI</ServedBy>
-</XRD>" },
- { "https://xri.net/@id*andrewarnott?_xrd_r=application/xrd%2Bxml;sep=false", @"<?xml version='1.0' encoding='UTF-8'?>
-<XRD xmlns='xri://$xrd*($v*2.0)'>
- <Query>*andrewarnott</Query>
- <Status cid='absent' code='100'>Success</Status>
- <ServerStatus code='100'>Success</ServerStatus>
- <Expires>2008-07-14T03:31:00.466Z</Expires>
- <ProviderID>@!B1E8.C27B.E41C.25C3</ProviderID>
- <Service>
- <Type select='true'>http://openid.net/signon/1.0</Type>
- <Path select='true'>(+login)</Path>
- <Path match='default'/>
- <MediaType match='default'/>
- <URI append='none' priority='2'>http://www.myopenid.com/server</URI>
- <openid:Delegate xmlns:openid='http://openid.net/xmlns/1.0'>http://blog.nerdbank.net</openid:Delegate>
- </Service>
- <Service>
- <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
- <Type select='true'>xri://+i-service*(+contact)*($v*1.0)</Type>
- <Type match='null'/>
- <Path select='true'>(+contact)</Path>
- <Path match='null'/>
- <MediaType match='default'/>
- <URI append='qxri'>http://contact.freexri.com/contact/</URI>
- </Service>
- <Service>
- <ProviderID>@!7F6F.F50.A4E4.1133</ProviderID>
- <Type select='true'>xri://+i-service*(+forwarding)*($v*1.0)</Type>
- <Path select='true'>(+index)</Path>
- <Path match='default'/>
- <MediaType match='default'/>
- <URI append='qxri'>http://forwarding.freexri.com/forwarding/</URI>
- </Service>
- <ServedBy>OpenXRI</ServedBy>
-</XRD>" },
- });
- // Consistent with spec section 7.3.2.3, we do not permit
- // delegation on XRI discovery when there is no CanonicalID present.
- this.VerifyCanonicalId("=Web*andrew.arnott", null);
- this.VerifyCanonicalId("@id*andrewarnott", null);
- }
-
[TestMethod, Ignore] // XRI parsing and normalization is not implemented (yet).
public void NormalizeCase() {
Identifier id = "=!9B72.7dd1.50a9.5ccd";
Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", id.ToString());
}
-
- private ServiceEndpoint VerifyCanonicalId(Identifier iname, string expectedClaimedIdentifier) {
- ServiceEndpoint se = iname.Discover(this.RequestHandler).FirstOrDefault();
- if (expectedClaimedIdentifier != null) {
- Assert.IsNotNull(se);
- Assert.AreEqual(expectedClaimedIdentifier, se.ClaimedIdentifier.ToString(), "i-name {0} discovery resulted in unexpected CanonicalId", iname);
- Assert.IsTrue(se.ProviderSupportedServiceTypeUris.Count > 0);
- } else {
- Assert.IsNull(se);
- }
- return se;
- }
}
}
diff --git a/src/DotNetOpenAuth.Test/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.Test/Properties/AssemblyInfo.cs
index d6fd020..4744bfe 100644
--- a/src/DotNetOpenAuth.Test/Properties/AssemblyInfo.cs
+++ b/src/DotNetOpenAuth.Test/Properties/AssemblyInfo.cs
@@ -29,4 +29,4 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("aef0bb13-b79c-4854-a69a-de58b8feb5d1")]
-[assembly: ContractVerification(false)] \ No newline at end of file
+[assembly: ContractVerification(true)] \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Test/TestBase.cs b/src/DotNetOpenAuth.Test/TestBase.cs
index f9db40c..c6508f6 100644
--- a/src/DotNetOpenAuth.Test/TestBase.cs
+++ b/src/DotNetOpenAuth.Test/TestBase.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test {
using System.IO;
using System.Reflection;
+ using System.Web;
using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.OAuth.Messages;
using log4net;
@@ -48,6 +49,7 @@ namespace DotNetOpenAuth.Test {
log4net.Config.XmlConfigurator.Configure(Assembly.GetExecutingAssembly().GetManifestResourceStream("DotNetOpenAuth.Test.Logging.config"));
MessageBase.LowSecurityMode = true;
this.messageDescriptions = new MessageDescriptionCollection();
+ SetMockHttpContext();
}
/// <summary>
@@ -58,6 +60,16 @@ namespace DotNetOpenAuth.Test {
log4net.LogManager.Shutdown();
}
+ /// <summary>
+ /// Sets HttpContext.Current to some empty (but non-null!) value.
+ /// </summary>
+ protected internal static void SetMockHttpContext() {
+ HttpContext.Current = new HttpContext(
+ new HttpRequest("mock", "http://mock", "mock"),
+ new HttpResponse(new StringWriter()));
+ }
+
+#pragma warning disable 0618
protected internal static void SuspendLogging() {
LogManager.GetLoggerRepository().Threshold = LogManager.GetLoggerRepository().LevelMap["OFF"];
}
@@ -65,5 +77,6 @@ namespace DotNetOpenAuth.Test {
protected internal static void ResumeLogging() {
LogManager.GetLoggerRepository().Threshold = LogManager.GetLoggerRepository().LevelMap["ALL"];
}
+#pragma warning restore 0618
}
}
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln
index 2759c9b..5a5f6d2 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -7,7 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Test", "DotN
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20B5E173-C3C4-49F8-BD25-E69044075B4D}"
ProjectSection(SolutionItems) = preProject
+ ..\build.proj = ..\build.proj
+ ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent = ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent
DotNetOpenAuth.vsmdi = DotNetOpenAuth.vsmdi
+ ..\LICENSE.txt = ..\LICENSE.txt
LocalTestRun.testrunconfig = LocalTestRun.testrunconfig
..\doc\README.Bin.html = ..\doc\README.Bin.html
..\doc\README.html = ..\doc\README.html
@@ -16,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}"
ProjectSection(SolutionItems) = preProject
+ ..\doc\specs\ICAM_OpenID20Profile.pdf = ..\doc\specs\ICAM_OpenID20Profile.pdf
..\doc\specs\OAuth Core 1.0.htm = ..\doc\specs\OAuth Core 1.0.htm
..\doc\specs\OAuth Core 1.0a (Draft 3).htm = ..\doc\specs\OAuth Core 1.0a (Draft 3).htm
..\doc\specs\OpenID OAuth Extension.htm = ..\doc\specs\OpenID OAuth Extension.htm
@@ -77,7 +81,7 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "InfoCardRelyingParty", "..\
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
- VWDPort = "4490"
+ VWDPort = "59719"
DefaultWebSiteLanguage = "Visual Basic"
EndProjectSection
EndProject
@@ -134,7 +138,7 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OAuthConsumer", "..\samples
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
- VWDPort = "10335"
+ VWDPort = "59721"
EndProjectSection
EndProject
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OAuthServiceProvider", "..\samples\OAuthServiceProvider", "{7ADCCD5C-AC2B-4340-9410-FE3A31A48191}"
@@ -163,6 +167,25 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdOfflineProvider", "..
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{E9ED920D-1F83-48C0-9A4B-09CCE505FE6D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project Templates", "Project Templates", "{B9EB8729-4B54-4453-B089-FE6761BA3057}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFormsRelyingParty", "..\projecttemplates\WebFormsRelyingParty\WebFormsRelyingParty.csproj", "{A78F8FC6-7B03-4230-BE41-761E400D6810}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RelyingPartyLogic", "..\projecttemplates\RelyingPartyLogic\RelyingPartyLogic.csproj", "{17932639-1F50-48AF-B0A5-E2BF832F82CC}"
+ ProjectSection(ProjectDependencies) = postProject
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}
+ EndProjectSection
+EndProject
+Project("{C8D11400-126E-41CD-887F-60BD40844F9E}") = "RelyingPartyDatabase", "..\projecttemplates\RelyingPartyDatabase\RelyingPartyDatabase.dbproj", "{2B4261AC-25AC-4B8D-B459-1C42B6B1401D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcRelyingParty", "..\projecttemplates\MvcRelyingParty\MvcRelyingParty.csproj", "{152B7BAB-E884-4A59-8067-440971A682B3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoRelyingParty", "..\samples\OpenIdWebRingSsoRelyingParty\OpenIdWebRingSsoRelyingParty.csproj", "{B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoProvider", "..\samples\OpenIdWebRingSsoProvider\OpenIdWebRingSsoProvider.csproj", "{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}"
+EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "OpenIdRelyingPartyWebFormsVB", "..\samples\OpenIdRelyingPartyWebFormsVB\OpenIdRelyingPartyWebFormsVB.vbproj", "{F289B925-4307-4BEC-B411-885CE70E3379}"
+EndProject
Global
GlobalSection(TestCaseManagementSettings) = postSolution
CategoryFile = DotNetOpenAuth.vsmdi
@@ -257,6 +280,51 @@ Global
{5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A78F8FC6-7B03-4230-BE41-761E400D6810}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {A78F8FC6-7B03-4230-BE41-761E400D6810}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {A78F8FC6-7B03-4230-BE41-761E400D6810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A78F8FC6-7B03-4230-BE41-761E400D6810}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A78F8FC6-7B03-4230-BE41-761E400D6810}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A78F8FC6-7B03-4230-BE41-761E400D6810}.Release|Any CPU.Build.0 = Release|Any CPU
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.CodeAnalysis|Any CPU.Deploy.0 = Debug|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -272,10 +340,17 @@ Global
{1E8AEA89-BF69-47A1-B290-E8B0FE588700} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{2A59DE0A-B76A-4B42-9A33-04D34548353D} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
+ {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
+ {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
+ {F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277}
{6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
{9ADBE36D-9960-48F6-82E9-B4AC559E9AC3} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
{7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
{5C65603B-235F-47E6-B536-06385C60DE7F} = {E9ED920D-1F83-48C0-9A4B-09CCE505FE6D}
+ {A78F8FC6-7B03-4230-BE41-761E400D6810} = {B9EB8729-4B54-4453-B089-FE6761BA3057}
+ {17932639-1F50-48AF-B0A5-E2BF832F82CC} = {B9EB8729-4B54-4453-B089-FE6761BA3057}
+ {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {B9EB8729-4B54-4453-B089-FE6761BA3057}
+ {152B7BAB-E884-4A59-8067-440971A682B3} = {B9EB8729-4B54-4453-B089-FE6761BA3057}
EndGlobalSection
EndGlobal
diff --git a/src/DotNetOpenAuth.vsmdi b/src/DotNetOpenAuth.vsmdi
index 03d376c..2de6386 100644
--- a/src/DotNetOpenAuth.vsmdi
+++ b/src/DotNetOpenAuth.vsmdi
@@ -3,506 +3,528 @@
<TestList name="Hosted ASP.NET tests" id="5a637793-05cb-476f-9f0c-18e7e7a4b205" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
<Description>Tests that require hosting the test ASP.NET web site to run. These tests tend to be a bit slower than the rest due to the overhead of loading ASP.NET.</Description>
<TestLinks>
- <TestLink id="896fd3db-5edb-ed16-b11a-e01769735f89" name="BadRequestsGenerateValidErrorResponsesHosted" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a6d6decf-0c19-16d8-edf6-ee70e56877e1" name="AspHostBasicTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="896fd3db-5edb-ed16-b11a-e01769735f89" name="BadRequestsGenerateValidErrorResponsesHosted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a6d6decf-0c19-16d8-edf6-ee70e56877e1" name="AspHostBasicTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
</TestLinks>
</TestList>
<TestList name="Fast" id="7e880f0f-2224-4f4c-a555-910a3bc2c0e5" parentListId="f0eeb325-0558-48a3-9a99-952133d8148e">
<Description>Fast running unit tests</Description>
<TestLinks>
- <TestLink id="89de77d8-729a-7efe-9667-71b1f5d78859" name="CtorBadXri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="06b350b0-79d1-9393-7620-cd919061898c" name="ParseEndUserSuppliedUriIdentifier" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="300ae1f7-fc61-1d41-b262-f8c830b6e115" name="RemoveTest1" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="734dd45c-6320-26a9-e412-62ecacfd285a" name="CtorNullAttribute" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="70c08ce3-cbd0-d553-61c0-a6d2ca203dc4" name="IsExtensionSupportedNullExtension" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1d5fb5a9-e15c-d99c-7a7e-95a4c4d123c2" name="DirectRequestsUsePost" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6b218bf7-a4e9-8dac-d2c2-9bc3ee3ffc3e" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b32b6295-d4a9-3369-f072-28a71e84d4e8" name="SerializationWithUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1c531011-403a-0821-d630-d5433d968f31" name="CtorFromRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="73c6c979-205d-2216-d98d-2dd136b352c6" name="UtcCreationDateConvertsToUniversal" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e7aacb49-62ef-637d-ada2-0a12d836414d" name="ExtensionFactory" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="49a266cf-4ab6-3fdc-f4fd-21533f42c7cb" name="CtorWithProtocolMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d12e8df0-1195-ab75-2275-7c8f854ddf98" name="UserSetupUrl" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="21cf1f9a-063f-395a-f8aa-92c190c69146" name="SignaturesMatchKnownGood" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d570770a-74e4-50ec-8eb9-91bd81c093ad" name="ParseNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a9f7897c-b299-807b-0612-384732cd10c9" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f4bec8d2-0531-34ab-8d50-bca260b58c61" name="ReadFromRequestWithContext" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a314e3b9-36a5-bfbb-3e15-e5003f22cf87" name="Serialize" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1bfbe1e1-3827-824f-27ad-4c990b0e22ab" name="Defaults" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ca9f3da7-e19f-b58b-54fe-54fa56ab9556" name="AddByKeyAndValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d766edce-59de-a03d-830a-0f0477521cff" name="ApplyHeadersToResponseNullAspNetResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="84e718d7-bb82-e7d1-31be-471e2c154053" name="Item" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="889ba616-43dc-8a7f-ee13-46288969d617" name="ParameterNames" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fdf5b3df-239b-26fd-c1a2-152057195b7e" name="ReadFromRequestForm" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="555edc3b-5abf-7e46-b4f6-ddf44800b5df" name="SpreadSregToAXBasic" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="149a95cf-a538-f853-e11b-3133c15579c5" name="RequestTokenUriTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f3cbbcda-49ff-fc43-140b-f362081654c3" name="CtorNullTypeUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fa52f2db-fc1e-ba31-cc5e-0bcc05998187" name="NoValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="65752c29-fa1f-7b88-bbec-5329af8db4d8" name="IsValid" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cd1142a5-f77a-5626-a739-65eb0228bf7d" name="ProtocolDetection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="68532d6d-a0cf-5883-17e2-6060707ba9ae" name="DecodeOobToNullUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="188ce83b-3117-adb5-4b89-12f2b09be1de" name="CtorSimpleConsumer" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="93c157e8-1293-3aff-f616-66502872b37d" name="DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f6ecb459-cc64-36ee-438c-4514e9413586" name="AddAttributeByPrimitives" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="03e293d0-dbe8-ad09-1ddd-de7be2cf9276" name="CopyTo" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c87bee54-0edd-1051-b5f8-2233692249ba" name="DiscoverCommunityInameCanonicalIDs" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5fa10f12-3de5-1783-0a97-9802d5469dfa" name="AddAttributeNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5ab06bb5-d047-8c3a-6b91-5951e0a55cc5" name="ToStringTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="40e1121e-8ff3-df73-203b-04baab671a0c" name="ImplicitConversionToStringTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="779b1f99-fe67-185c-f165-66787bf6e39a" name="BasicEncodingTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="456c3468-9081-4879-7e7e-8299bd8c7f68" name="IsReadOnly" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d6951a97-9d0b-31c1-7a29-01fbb986c5a9" name="SpoofedClaimedIdDetectionSolicited" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="315b5857-7697-8222-f94c-f6f10d539491" name="BaseSignatureStringTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1cde79f8-99b7-7090-f898-ba96a607875f" name="IsReturnUrlDiscoverableValidButNoMatch" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="910f8448-5454-8ae5-cba3-690c7f375576" name="ParameterNames" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="83aba528-c8ea-f464-177e-2ea8ae2cfd0b" name="Birthdates" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3c4e0438-94f5-a132-3949-8d94718e4839" name="PassThruCollection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b63c4b89-3889-6dcf-8890-c92fc44c0b10" name="VerifyBadTimestampIsRejected" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9126d9e0-14dc-490b-3cd3-d3e424d38f9e" name="BinarySerialization" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4c399759-263f-5eba-8855-de14f197e647" name="QueryStringContainPrefixedParametersNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1757957f-17bb-ef9f-39f8-c008863ec033" name="AssuranceLevels" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c11e5541-0a92-85ab-4f90-0db7766ebdcb" name="CtorUnsolicited" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8aecb3a5-2cb5-143d-aa99-9514fa8dfacb" name="AddAttributeByValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ef6cebca-f8da-edf6-0217-8bb854710090" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b09311d4-4dea-6786-3e59-9c62fe16e301" name="ParameterNames" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="54eae9ed-bed1-eeda-b6ea-045c8f7e2ba5" name="SendIndirectMessage301GetNullFields" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2d0ee03a-f082-768c-a0db-574ac8efeffb" name="Valid" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6c20a52a-bab7-e84e-faca-fd79ec5303d9" name="CtorCountZero" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7fb8d29c-c8ea-7f88-ed42-ae7368d6a429" name="CtorNullStore" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="50986611-9de6-a112-2fe8-691210989f45" name="IsTypeUriPresent" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="163c8ba8-f829-c21e-a5a1-3c4565ec4425" name="UnifyExtensionsAsSregNoExtensions" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="054484ce-12c5-83ad-49a4-b241cd81557d" name="ClaimedIdentifier" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e8337858-a320-8aad-51aa-402e65a90b75" name="ReplayDetectionTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="507cd1b6-1010-0bca-cf7f-f96e3f4f6c6c" name="QueryBeforeSettingUrl" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1dcbaac6-0b11-8d8f-50d7-237574abbab1" name="ToDictionaryWithSkippedNullKey" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="715dcbdd-28f5-3c33-7d88-e0a1b648d89a" name="CreateRequestDumbMode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a1ff4ada-fe5d-d2f3-b7fb-8e72db02b3c3" name="Full" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="85a71d28-5f2f-75ce-9008-94982438bb5f" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f334cc44-b2d0-2d67-358a-532def3bee80" name="ContainsKeyValuePair" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fa05cc5f-2aaf-da22-ff52-caf1c3c6bb08" name="InsecureIdentifiersRejectedWithRequireSsl" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7debb527-142a-6ca6-3b9b-1e131c18e801" name="AccessTokenUriTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="98e7a0f9-ab6c-7ff1-3a2c-00d8244e1bec" name="CommonMethods" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f583b298-139a-e733-dde6-f9dc4b73d4bf" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="af7cb01c-950e-23d7-0f32-082b7af8b382" name="CtorNullToObject" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a260d196-066f-b0ae-a40e-fb9d962b28a4" name="XrdsDirectDiscovery_20" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="13acd546-c82e-324c-220d-34f42a6d705e" name="DeserializeSimple" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b56cdf04-0d29-8b13-468c-fb4b4258c619" name="CtorNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d474830d-3636-522c-1564-1b83e7a844d3" name="EmptyLine" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="63e5025b-5ccf-5f13-6e05-d1e44502a6e9" name="RequestBadPreferredScheme" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f20bd439-e277-dc27-4ec4-5d5949d1c6bf" name="RequestUsingAuthorizationHeaderScattered" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3fc3ac8d-7772-b620-0927-f4bd3a24ce2f" name="SendNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5f3758b3-1410-c742-e623-b964c01b0633" name="AuthenticationTimeUtcConvertsToUtc" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9d4a230d-9e74-dc1b-ecdc-bf875b56e1b3" name="CtorNullVersion" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f8d8d555-7a04-ab6e-918a-3dae32f4b52b" name="IsExtensionSupportedNullString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7b1fb2c4-39c0-0d39-700c-96d992f5a01f" name="AuthenticationTimeUtcSetUnspecified" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="00089858-d849-1e5f-4fb5-31d8d0590233" name="VerifyArgumentNotNullThrows" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="54a65e0b-1857-72b9-797b-fe3d9a082131" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f17424d2-ed4b-1ea0-a339-733f5092d9d0" name="MaximumAuthenticationAgeTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d067c55c-3715-ed87-14a2-c07349813c94" name="IsDirectedIdentity" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8fc08a6d-6dcf-6256-42ff-073d4e4b6859" name="RequireDirectedIdentity" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3aa4e498-fd14-8274-22da-895436c1659e" name="AssociateUnencrypted" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1f46ce86-bc66-3f5c-4061-3f851cf6dd7f" name="HtmlDiscover_20" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2f9d176e-4137-63bd-ee2a-6b79fde70d0d" name="Clear" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fc7af2d7-6262-d761-335b-ef3ec029484d" name="DeserializeVerifyElementOrdering" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="df159af7-abf5-089c-b592-e6f535dab1c1" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="577b8522-8516-4f62-22db-76227bf82f4c" name="UserSetupUrlNotRequiredInV1SetupOrV2" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ac4ff1af-8333-e54e-0322-27d8824d7573" name="RequestUsingAuthorizationHeader" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e78ab82c-3b49-468a-b2ad-ca038e98ff07" name="GetEnumerator" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8e86c2fd-24b9-44c5-7cda-d66aa7cd4418" name="Serializable" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c0d7cfcc-4f7e-e7df-3de2-b578c4c3d6ee" name="SpreadSregToAxMultipleSchemas" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e2287de6-cbd2-4298-3fb8-297013749e70" name="SendIndirectMessageFormPostNullFields" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="db8d66cc-8206-57cc-0ce5-c8117813d77c" name="UnifyExtensionsasSregFromSchemaOpenIdNet" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5b451648-5ca1-4395-333d-bbcb098f4a45" name="NoRequestedExtensions" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="decb3fef-ef61-6794-5bc6-f7ff722a146e" name="EqualsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0d99e0a9-295e-08a6-bc31-2abb79c00ff8" name="IsReturnUrlDiscoverableRequireSsl" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f41ce7ab-5500-7eea-ab4d-8c646bffff23" name="HttpSchemePrepended" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="55b078e4-3933-d4e0-1151-a0a61321638e" name="ReadFromRequestAuthorization" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="38239ff0-1dfd-1116-55df-2790243dc768" name="IsValid" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="04be6602-31a2-f4ae-8fdb-b9ad2ac370be" name="PrepareMessageForReceiving" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2c2b48d0-8009-e7e0-9ff4-34f9973f59da" name="EqualsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5dd2e6c9-ff0f-48de-3a1a-cbab61370843" name="SetCountNegative" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="182203f3-5a16-b736-ea8c-b59f6bf7df66" name="InvalidRealmTwoWildcards2" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4fd5baa2-8f39-8bf6-db8f-aa92592bfc06" name="CtorRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="385c302d-b546-c164-2a59-2e35f75d7d60" name="RemoveStructDeclaredProperty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="870cce9d-5b17-953c-028f-827ec8b56da2" name="GetInvalidMessageType" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="97f0277a-86e6-5b5a-8419-c5253cabf2e0" name="UserAuthorizationUriTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c9d67d40-1903-8319-0f7c-d70db4846380" name="SendWithoutAspNetContext" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="643add47-e9f3-20b8-d8e0-69e3f8926d33" name="CreateRequestsWithEndpointFilter" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="bc3e979b-09ea-c45d-5714-2d1fb00244cf" name="IncomingMessageMissingRequiredParameters" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d66a3b7a-1738-f6b3-aed1-e9bc80734ae9" name="CtorNullString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f3f84a10-317f-817a-1988-fddc10b75c20" name="AddTwoAttributes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e03f0038-5bb7-92f2-87a7-00a7d2c31a77" name="MessageExpirationWithoutTamperResistance" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1f3ea08b-9880-635f-368f-9fcd3e25f3cd" name="ReadFromRequestNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="643d722c-2c2b-fbd8-a499-5a852ef14dc7" name="PrepareMessageForSending" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f49bcd49-76fb-bfea-b119-4e0f70159f80" name="OpenIdProvider" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f4b313bb-cebc-a854-ffbd-6c955d850a05" name="VerifyGoodTimestampIsAccepted" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="77047207-0571-72d5-71bd-586b878bcc0c" name="Base64Member" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f919a731-cc5c-88c6-5582-639b272d64fc" name="IsReturnUrlDiscoverableValidResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4a00f3ab-f405-95a7-d745-2fcf7787eb56" name="GetNonexistentHandle" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f4537b23-bb5e-5c6f-da53-64b34472f0dc" name="ChannelGetter" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="75fa4664-bb0e-3a54-de29-c18ac712b231" name="Mode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="94ba9fd3-851d-13b2-e273-6294b167c13e" name="HttpsSignatureVerification" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ff78d828-b437-aaeb-e48a-85a5ad1fe396" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5460f9c6-ec9d-969d-5aff-b946d6776e25" name="CtorWithNullProtocolMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d5f4e610-eabe-1dc0-ab3f-7c9dcb17fcc3" name="CtorImpliedLocalIdentifier" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="64d8c630-76c6-e420-937b-19c889dd7f59" name="CtorNonMessageType" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fba4d9a6-d8c7-a063-7c07-4a27c38c94a9" name="InvalidRealmBadWildcard3" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="be41e9c1-ecde-cc80-37d0-4126225e4cda" name="CtorNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="48115dc0-1323-bab0-c540-695a2160e0a3" name="CtorNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a79e43c9-ad5a-5543-51ff-22271ec87ab0" name="PrepareMessageForSendingNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7ca16e07-126d-58ac-2ac5-a09a8bf77592" name="InvalidRealmBadWildcard1" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="832dbf28-5bf2-bd95-9029-bf798349d917" name="GetCallbackArguments" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="735b7a56-0f6f-77d8-8968-6708792a7ce8" name="UnifyExtensionsAsSregWithAXSchemaOpenIdNet" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="53cbbf4a-89d3-122b-0d88-662f3022ce26" name="OpenIdMaxAuthenticationTime" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="20646985-c84a-db8e-f982-ec55d61eaacd" name="ResponseNonceSetter" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c2c78c43-7f50-ffc3-affb-e60de2b76c94" name="CreateQueryStringNullDictionary" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="dc6af36d-0efc-9291-a603-2af7bac2a269" name="ErrorMessagesAsHttp400" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cc584a29-684c-75e8-3d77-96201d9ba537" name="ProviderTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ef20222d-b2e2-d593-17fa-512041020643" name="InvalidRealmNullString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b2e1bba0-ab24-cdd5-906c-a3655814ab2d" name="SendSetsTimestamp" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2f1a3fc4-77ec-2ae3-668c-9e18f9ab0ebe" name="SendIndirectMessage301Get" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e4403d9e-73c1-967d-345c-4a2c83880d4e" name="EqualsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6ce37652-2f47-6952-fb6d-568c2ca85224" name="TransformAXFormatTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a145f430-8062-5ad7-0cf5-b51eba0f8de7" name="HttpsSignatureGeneration" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="bb542259-4c10-4b88-1b3c-f842b0bb49a9" name="ImmediateVsSetupModes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="71564ca7-7845-92b3-7433-2f2beeb6b9f7" name="VerifyNonZeroLengthOnNonEmpty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7ea157db-cf32-529f-f1d3-b3351f17725a" name="CtorSimpleServiceProvider" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c905ca57-e427-3833-c2dd-17ca9f6962cd" name="SendIndirectMessageFormPost" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="12304550-5647-5e61-64b4-a5e051f20a03" name="IsExtensionSupported" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="50594141-1a00-b4ab-d794-5b06e67327e5" name="IsTypeUriPresentNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="44ced969-83dd-201d-a660-e3744ee81cf8" name="ConstructorTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c7f6459d-9e6e-b4bc-cae8-65f5a3785403" name="SendIndirectMessageNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="aa79cdf5-e0bc-194e-fdbb-78369c19c30f" name="ConstantFieldMemberInvalidValues" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="dd9e3279-2d7e-e88e-ccfa-ef213055fc3d" name="SendDirectedNoRecipientMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b384002f-26a9-7dde-c3f6-9ceff34dd8e2" name="GetRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="81f670d0-d314-c53c-9d91-c0765dfc30c1" name="MessagePartsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="efd570c9-5e74-17e4-f332-ac257c8e8aff" name="RealmReturnToMismatchV1" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fda58c48-e03a-73a3-4294-9a49e776ffb6" name="CtorWithTextMessageAndInnerException" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e7a41771-7dda-be44-0755-e06300f3cd92" name="IsSaneTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ae384709-e9a4-0142-20ba-6adb6b40b3e2" name="CtorStringHttpsSchemeSecure" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3b70dd09-384d-5b99-222b-dc8ce8e791f2" name="SecuritySettingsSetNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f787ae5d-b8fc-0862-a527-9157d11bbed7" name="UntrustedWebRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8df5d75f-bd4d-ce4e-2faf-6106b623de42" name="AddAttributeRequestStrangeUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="51a08d94-c327-4d28-1f0c-f7920ea54870" name="ValidMessageTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="051a85ed-eef9-9437-507d-d6208b6a8f74" name="DiscoveryWithRedirects" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="be6a14aa-c0d9-cf61-286a-236b92239597" name="EnumerableCache" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5a4df395-962e-0b7c-de71-abcb7e8930db" name="CreateFiltersDelegatingIdentifiers" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="bf73c7f2-33b1-8e18-c4f6-cb8609388754" name="DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="feb0a53e-1592-b878-b70c-1a272d9c6908" name="SpreadSregToAxTargetedAtOPFormat" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e071a119-c7e9-1a55-b132-72e161fea598" name="CtorAndProperties" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a6e464af-42df-1ba4-17e5-b955352664b5" name="RPOnlyRenegotiatesOnce" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0290975f-02ce-d8a7-d723-5dae623cab46" name="CtorNullTokenManager" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e1958fc5-a979-88b2-b593-3bc89ad6ad4e" name="GetEnumeratorUntyped" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3b535521-90c8-7f49-545f-bcfc4ad16d40" name="UnresponsiveProvidersComeLast" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4bd86299-18d7-abbe-e5d2-1afad17279e9" name="Parse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0443f5f8-aa08-80d5-dcc6-261802debe5a" name="XrdsDirectDiscovery_10" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6c95f443-463e-2856-f500-b9029645e44c" name="RequestNullRecipient" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="afafb5ef-662e-2da3-35b8-1d67bb0d79ce" name="AddPolicies" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d0a92f93-9bb4-1821-81cf-e9b50e3e7d62" name="SendDirectMessageResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="17267cde-a296-8293-5bd1-9ca629817e4b" name="OpenIdRelyingParty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cab73921-470b-331f-e601-b44805b67c81" name="GetAttributeValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ebd84587-bbc2-9889-c500-b6fbdf2bf209" name="GetRequestNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="565140c9-c9fe-9466-1e39-740d7e368cb5" name="TryParse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e9bc3f63-aeb1-d84d-8abc-fc6ed77955e6" name="SignedResponsesIncludeExtraDataInSignature" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9e209599-5924-f624-48de-ed31588cb425" name="EncodeDecode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="95e1fc36-2500-2721-1919-35e9e8349a1c" name="AddPolicyMultipleTimes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9af943f7-b289-1a24-8e3e-bfbd7a55b4c7" name="CtorGoodUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ae1ef27c-fbfe-c57e-a1e0-c1ef9de4ea23" name="CommonProperties" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0f80456a-5465-dd68-bfb0-ba27b676187c" name="EqualsTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="bb68f1dc-3854-fc11-2ea6-d45f892d76fa" name="NistAssuranceLevelSetVarious" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="88aaa032-b18a-b334-937b-66837c5f987c" name="AssociateRenegotiateBitLength" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="dd5be0e2-a1fc-3369-0b11-78b728eeaba5" name="CtorNullUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0362a92c-a21c-f718-6b1e-3d154c14acd0" name="RequestUsingPost" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="264cd371-e830-c09b-5511-18f54d4c69d5" name="RespondSimpleValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4a009f39-66b1-9cc5-ea8b-13b75ab22a5b" name="ContainsKey" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9bdc56c0-33ce-b46c-4031-bd3252b499a6" name="PrivateAssociationPositive" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="32604ca2-2577-9c33-f778-ff7e4c985ce5" name="RequestTokenUriWithOAuthParametersTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fdf439d0-3b74-4d32-d395-d5a2559ed88b" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7f9c4a9e-de7a-555c-543d-db89b757588e" name="AppendQueryArgs" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3df1f62b-4fb4-d399-cf7f-40b72001d9d6" name="CtorUnsolicited" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="90f06a50-7b81-05ec-3dc0-7b3e8ade8cfa" name="NormalizeCase" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8b11aa63-4c0f-41ff-f70c-882aacf939fe" name="CtorCountNegative" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="83271647-7da8-70b1-48a3-f1253a636088" name="IsExtensionSupportedEmptyString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b671ea40-3b8c-58d5-7788-f776810c49be" name="UnicodeTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="501fa941-c1ac-d4ef-56e7-46827788b571" name="GetRequestNoContext" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="01e33554-07cc-ff90-46f8-7d0ca036c9f6" name="ToDictionaryNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0215f125-3936-484e-a8d0-d940d85bbc27" name="AppendQueryArgsNullDictionary" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7c048c58-c456-3406-995f-adb742cc2501" name="DeserializeInvalidMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="704a32d0-3f50-d462-f767-fd9cf1981b7f" name="ProviderVersion" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f362baf3-da5b-1b8c-39ae-7c9b2051270a" name="AuthenticationTimeUtcSetUtc" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3bb818b4-5423-ad91-8cd9-8606ec85d2cb" name="ReadFromRequestAuthorizationScattered" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cbfeb75b-d031-7df3-c281-3c9e1c450042" name="CtorFromRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9302541c-9c30-9ce7-66db-348ee4e9f6ee" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="997253fb-7591-c151-1705-02976b400f27" name="AddAttributeTwice" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9bf0528f-c3ab-9a38-fd8a-fd14bade0d0b" name="EnumerableCacheCurrentThrowsAfter" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2fb2563b-b908-2fad-5efc-522a68c76780" name="ValidMessageNoNonceReceivedTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3c438474-63f3-b56c-dcba-1ed923fcdbdd" name="CreateResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="47706bc6-7bee-0385-62b4-4f9cec6cc702" name="CtorWithTextMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2495fc9b-d766-5ae7-7324-f044c4ce1242" name="AddNullValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fb6c270f-ff72-73f4-b8b3-82851537427c" name="MultiVersionedMessageTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="10245b55-8130-e0aa-e211-4a16fa14d0b1" name="ClearValues" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3fe0b432-dbb4-b334-e504-a83fe5ffdbaf" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="47e8fae9-542d-1ebb-e17c-568cf9594539" name="RelativeUriDecodeFails" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a2b3835e-8edb-89aa-ba6c-f10b28a3af81" name="ReadFromRequestQueryString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e97cee09-4163-d83f-f65f-14e424294172" name="ExtensionsAreIdentifiedAsSignedOrUnsigned" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f4fd129a-a7c3-dc1e-2b4a-5059a4207a8a" name="Send" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cc0031b8-1fdb-cd87-97c1-c6f893c296e0" name="TooManyBindingElementsProvidingSameProtection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="152e7a3a-21f9-eabf-0065-08597a0cc9a6" name="AuthorizationHeaderScheme" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="93041654-1050-3878-6b90-656a7e2e3cfd" name="CtorDefault" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e2ab77b2-a6dc-f165-1485-140b9b3d916f" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="035cd43a-23d5-af91-12ee-0a0ce78b3548" name="XrdsDiscoveryFromHttpHeader" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f70b368e-da33-bc64-6096-1b467d49a9d4" name="NonIdentityRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="dbf7855c-0cc6-309f-b5f5-022e0b95fe3b" name="QueryStringLookupWithoutQuery" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="abb0610a-c06f-0767-ac99-f37a2b573d1b" name="ParameterPrefix" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="32532d1f-d817-258d-ca72-021772bfc185" name="UriEncodeDecode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f3345901-2e76-34dd-32f1-0b312d6e1c1e" name="IsReturnUrlDiscoverableNotSsl" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1deb0ca9-923a-8ef7-7a24-d5d5af04acdf" name="SpecAppendixAExample" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c63c9935-54a0-398a-f44b-214e17faf1f1" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ee052b02-681f-2303-3cc6-37f7b2319579" name="RequireAssociation" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fb0e478e-0f55-b257-75fe-2ab60b57292e" name="SendInvalidMessageTransport" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a4aa113a-57b5-a52c-c4e3-f70d6702badb" name="Default" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1da7c479-bf01-2d12-8645-b4f7007dcfec" name="LanguagePreferenceEncoding" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="547cfee6-bbb4-6138-a114-fc0eb6cc94f6" name="PrivateAssociationTampered" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5803e93d-e256-86ba-e10e-499d2f813c6d" name="Trivial" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="beb086e9-5eb7-fb8f-480a-70ede9efd70d" name="CreateRequests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="78f622a3-750c-12c5-afc6-470c1bf71d85" name="ProtocolDetectionWithoutClues" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0c1a0323-092a-34b3-1601-1f941569efab" name="CtorGoodXri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8538caf8-48bd-7cf8-6ad8-15e1c3766f92" name="CtorNullType" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1b66e135-bdab-c2ed-18d8-aa89b46a57fc" name="RPRejectsUnrecognizedAssociationType" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f4893153-bb84-bf45-7889-8350a7e1db66" name="DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5218fba2-d1af-e1f4-7641-9ae1d4975430" name="DirectResponsesSentUsingKeyValueForm" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a1a0178c-cd4a-1651-8535-3c9ee3d40821" name="ToDictionaryWithNullKey" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="19d2219e-c04d-fa3a-5e26-92448f35f21d" name="RespondNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="eb932fc7-76c7-b63f-e1e6-a59dea8e4da1" name="AddAttribute" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a59c5dc0-de4d-8136-8545-2e2e9616de46" name="SerializationWithXri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0aa1bc22-0b26-3977-5983-5dc4a454cea5" name="OptionalNullableStruct" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c205832e-711c-62d0-5f5e-78f1250ea7cc" name="AuthenticationTimeUtcSetNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3536ba12-fdb0-2ac9-3fef-00a2dd8e9a65" name="SharedAssociationTampered" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e7b8770d-b26c-e7b3-e80e-fac46285f59d" name="PassThruList" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="14acb719-f090-018f-b870-9a5acb1d7179" name="AddAuthLevelTypes" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="aef95d4e-ad69-0eca-6528-7fce78512336" name="EqualityTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1ea8bd07-75a5-bfc0-5f8c-1a78d04240c2" name="TryGetValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7b89844d-f60a-fb66-c48d-e483864c66b5" name="RespondTooManyValues" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b9cda1a0-83cd-cf4b-b61f-4faa75fa37ba" name="ReceivedReplayProtectedMessageTwice" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="02333934-cfea-2fb6-5e08-7a24be050f44" name="CreateRequestsOnNonOpenID" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cf58b7b9-9718-f6cd-1839-fc53174598f2" name="IsExtensionSupportedNullType" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d6b120b7-fc16-6815-927e-af382cd44bbd" name="ReceivedInvalidSignature" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b71d12f6-58a1-cf82-d06e-e57c0a3ea55c" name="RPRejectsUnencryptedSuggestion" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="77e5af06-b02d-692e-b32f-40ea39e77fbd" name="FriendlyIdentifierForDisplay" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3c67903e-15ce-9ed4-34c8-f77059af79ca" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="87593646-8db5-fb47-3a5b-bf84d7d828c2" name="InvalidMessageTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4a5b601d-475d-e6cc-1fec-19a2850681ad" name="Serializable" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="bdba0004-be80-f5c1-1aae-487db09bdf04" name="GetReturnToArgumentDoesNotReturnExtraArgs" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f3af5fd8-f661-dc4f-4539-947b081a8b54" name="ReceivedReplayProtectedMessageJustOnce" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a14ddf08-796b-6cf1-a9bf-856dd50520fa" name="RequiredProtection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5b4fee50-7c15-8c6b-3398-c82279646e5f" name="RequiredOptionalLists" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4b44b825-36cc-77f8-3a4a-5892c540f577" name="GetValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="59295023-d248-e9c4-68b9-65f6ea38490c" name="VerifyArgumentNotNullDoesNotThrow" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e137b84a-d2a7-9af6-d15d-a92417668ccf" name="Transport" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a94ee2ec-02df-b535-1d2e-0c5db9c76b49" name="ReceiveUnrecognizedMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5dcd69c3-e979-7316-4551-a73fe4645dcd" name="SecuritySettings" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cea48223-04e2-d336-0ac4-255c514bd188" name="RoundTripFullStackTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ba35acc7-78d2-6710-57ac-6843210d4202" name="UserSetupUrlRequiredInV1Immediate" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f18b514c-4f78-5421-8bdf-8b0f1fdf2282" name="HandleLifecycle" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1e2ae78c-d2f3-a808-2b82-eca9f9f2e458" name="Keys" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9173c754-a358-91cc-a8f0-2c2703a55da8" name="AssertionWithEndpointFilter" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6badbaa8-33d1-13c4-c1f9-aef73a9ac5bf" name="InvalidRawBirthdate" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0435e38a-71f2-d58d-9c07-d97d830a1578" name="ExtensionResponsesAreSigned" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2f2ea001-a4f8-ff0d-5d12-74180e0bf610" name="HttpsSignatureVerificationNotApplicable" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fe55cc74-98eb-c6c7-622f-77ad3e304c10" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4735a071-3c06-509b-05f5-912ab0e39f13" name="InvalidRealmBadProtocol" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6ef9df5a-d069-0103-5260-593808f232da" name="XrdsDiscoveryFromHead" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6fbd433d-cd54-b206-6df3-fbd591690a4d" name="HtmlDiscover_11" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="175b6eb8-a448-4e07-7fed-001355edc128" name="PassThruArray" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="777af676-ee70-0e16-799b-85b9ec33cd63" name="IsValid" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d083396b-db68-1318-e62b-6dc9f89e26bd" name="CtorDefault" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2f6a81c5-cd04-0ca0-22ee-d4213f9cf147" name="EqualityTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="72d3f240-67f2-0b04-bd31-a99c3f7a8e12" name="SharedAssociationPositive" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3cd9447e-9ffd-f706-37bb-e7eb5828e430" name="InvalidRealmEmpty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="28fe030c-d36e-13cf-475c-7813210bf886" name="AddAttributeRequestAgain" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2e23dc5a-93ea-11a5-d00d-02d294794e5f" name="AssociateDiffieHellmanOverHttps" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8d0df47c-c381-0487-6c19-77548ad7fc13" name="UnifyExtensionsAsSregWithBothSregAndAX" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2237b8ce-94ce-28c1-7eb2-14e59f47e926" name="UnifyExtensionsAsSregFromAXSchemaOrg" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="457d6b32-d224-8a06-5e34-dbef3e935655" name="HttpSignatureVerification" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ef8a2274-4e58-0dde-4c5c-7f286865fc3a" name="SendReplayProtectedMessageSetsNonce" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2e1b27e8-2e3e-0290-2bee-d88e2914efd9" name="SpreadSregToAXNoExtensions" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c15c3ab5-e969-efc9-366d-78ebc43ce08f" name="Fetch" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7bf8e806-68a1-86bc-8d91-9a99d237d35c" name="CreateRequestMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a883dc73-d6be-e59a-6da2-0db1d4452679" name="BindingElementsOrdering" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="196be55e-a3e5-adf3-9f15-13ba6cce0701" name="ValidRealmsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f533bf9e-daa1-b26a-4789-372f3a9291d6" name="TryRequireSslAdjustsIdentifier" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f44fb549-fc8a-7469-6eed-09d9f86cebff" name="SendDirectMessageResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="313faac6-6357-5468-2d4d-4c9fba001678" name="TryParseNoThrow" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7cbe4350-38d0-db7e-335c-93d9398fc95b" name="ExtensionOnlyFacadeLevel" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2f5cfa57-bcb4-39af-e769-2d7c34e2598e" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="88ae5661-da27-91c5-4d78-1f43cd716127" name="EqualsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c351c660-d583-d869-0129-2e312665d815" name="CtorBlank" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f063a3c6-5a36-2801-53d7-5142416199a9" name="ImplicitConversionFromStringTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9026e58c-8582-0852-3c3c-9eadfd544cbc" name="VerifyNonZeroLengthOnEmpty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="068dcefa-8f2b-52c3-fe79-576c84c5648b" name="CtorBlank" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="782d64c8-46af-a624-b3f6-a65aeaa57bfe" name="LastLineNotTerminatedLoose" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cd219db4-4f6e-8ff4-f957-c8428d38c118" name="HttpSignatureGeneration" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="864578a5-61a2-bc5d-1d19-17093885bea3" name="InvalidRealmTwoWildcards1" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5298ecb0-bcad-9022-8b93-87793eb2c669" name="UnsolicitedDelegatingIdentifierRejection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2599d559-d036-5dd2-0b5b-fb229c3bf486" name="InvalidRealmBadWildcard2" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8a5c9404-1e77-68cf-229a-ef7ed413e6e7" name="OptionalNonNullableStruct" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="937b85f4-1ef3-84d1-a567-8bba079a33a9" name="Properties" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="29e45877-ca7a-85de-5c39-6d43befe1a1e" name="DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c057a3e5-b527-62a9-c19b-abb82e6be621" name="SendIndirectMessage301GetEmptyRecipient" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0dc9284e-cba4-9d87-8955-19639578c70d" name="Serializable" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="384fecbf-f18e-edcb-a2eb-fb0322f031aa" name="ApplyHeadersToResponseNullListenerResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e3a3b3b6-e05f-0a99-e20c-af91a9065819" name="AssociateRequestDeterminedBySecuritySettings" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d3c4624f-f78a-2ff3-199a-77c922059718" name="Best" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7f3144c7-95a1-affa-1a37-9e6169c19be6" name="SharedAssociationNegative" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="aedfde98-4357-5b63-7dca-cced838ee416" name="Provider" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b71e8878-b20e-5d96-bce4-7f10831ceaf8" name="AddPolicies" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ddf4f3ec-07bb-09e8-b5e8-0837cb8cb684" name="IsReturnUrlDiscoverableNoResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a18ae750-318b-bb1f-c2b3-c31da845c085" name="Count" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cb9a8325-abf5-5d97-a94e-a6d34f2b51e1" name="AssociateRenegotiateLimitedByRPSecuritySettings" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="115283b9-d95c-9a92-2197-96685ee8e96a" name="TwoExtensionsSameTypeUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9fae8ab4-8436-eba1-3e4b-51511998fa8e" name="UnsolicitedAssertionRejected" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7cf52613-a212-8a0f-843f-37f08740c125" name="SpreadSregToAxNoOpIfOPSupportsSreg" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7c8eac5a-0455-e038-0e9a-10e59d459452" name="CtorUriHttpSchemeSecure" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f69f1c0c-e258-95fb-4fcb-ad14bfc40e3c" name="Discover" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ccfda025-cb1a-a2ff-78bd-5e9af885ae0b" name="ToDictionary" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="64142858-d52e-be06-d11f-6be326c6176b" name="RespondTwoValues" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="03b47440-3d09-ab28-97f1-39809f5703b6" name="NormalizeCase" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="34357633-4745-6fba-9316-493d3c6c5b90" name="ParseEmpty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="85a0dec0-983c-8f21-b093-a2179624cc88" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5c66a1b8-5b20-2e3b-8427-d6ff4640ac53" name="BadRequestsGenerateValidErrorResponses" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a6ea74e5-8681-4eb4-a51b-5051e5f7603c" name="NonFieldOrPropertyMember" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f6979feb-7016-4e2b-14e2-e6c2c392419f" name="RemoveByKeyValuePair" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="46ec24da-deb7-27c7-6dc6-52090e4fd1fb" name="Serialize" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e9a5efc6-fde8-8fa4-0bda-2675a4a7e06b" name="DefaultReferenceTypeDeclaredPropertyHasNoKey" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b70b4bd5-6dae-b4ad-349c-c3ad70603773" name="ReadFromRequestQueryString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2a7b77c3-27d5-7788-e664-5d20118d223b" name="OPRejectsHttpNoEncryptionAssociateRequests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5f02e24c-2972-c598-ca71-ea362b2fe7d8" name="SecuritySettingsSetNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="98a2ece8-c9e6-e6f3-c65e-f915b22077fa" name="RequestUsingGet" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2cfefc4a-918a-3e16-0670-53eb33634525" name="GeneratesOnlyRequiredElements" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="da8fcfa9-bd2c-eca0-ecbf-90364f84e8e5" name="AddExtraFieldThatAlreadyExists" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fb91e9dd-fc3b-d8a7-a5d7-d215d5ba880f" name="CtorStringHttpSchemeSecure" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5211652f-1c25-fd4b-890d-05d2178a60e2" name="ExtensionFactories" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7650ec62-b144-f36f-8b56-31ad20521d0e" name="DoesNotStripFragment" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e2b1ae2a-8f30-b6b3-bca6-ef28fc5a0175" name="ClaimedIdAndLocalIdSpecifiedIsValid" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="58d69d1e-3bd2-3379-0af1-188f9cff2dd0" name="IsTypeUriPresentEmpty" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="495dd486-08dd-d365-7a84-67d96fef8460" name="SendIndirectedUndirectedMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cc9200bf-1399-d40a-9754-6415f0b7bcf8" name="CreateRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7cdabb8a-aefa-e90e-c32e-047404b64c2d" name="SerializeTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ee7a04ba-0419-e08f-b838-01ec0f2a838e" name="UnsolicitedAssertion" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="58df167c-cf19-351c-cb09-5c52ae9f97be" name="DeserializeNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a778f331-f14e-9d6e-f942-a023423540f6" name="Ctor" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ae8b7cba-696e-2362-d5e1-79a9c202a994" name="EmptyLineLoose" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="be00d3ef-f24d-eb8a-d251-4d691736ee6f" name="AddAttributeRequestNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="593e1d86-a6c2-c937-a1b4-6d25a595a1f1" name="EnumerableCacheCurrentThrowsBefore" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="030ac3cf-cfb6-ca47-f822-ec1d2979f0b3" name="Defaults" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d5d2c553-97db-8d6c-c984-982299d6091d" name="Serializable" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="628a417f-4ddb-5965-bd4a-86c8de565c8f" name="AssociateDiffieHellmanOverHttp" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="10c44d72-2789-2afe-3b27-091dea97546e" name="RequestNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="24506e06-a678-66cc-48ee-b7f11f18a6e8" name="StripXriScheme" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c79dd056-8fff-3393-f125-4b83cf02cb3b" name="RequireSsl" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5a0d31d9-9c70-2a28-3e8c-46e8e047ac2d" name="ReceiveNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ca5360e1-ca08-d00f-6ade-7c9441db4294" name="CreateQueryStringEmptyCollection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f17128c1-5953-5391-ed75-c33774eacbfc" name="LastLineNotTerminated" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="671ddaf5-238d-a517-b0f3-d79bd591a396" name="EmptyMailAddress" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d8118997-ecf7-7130-f068-5e2bc867786d" name="SerializeNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e9c2087b-1c52-5bb9-bf4e-9046cf281e36" name="DiscoverRequireSslWithInsecureRedirect" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="25e2c593-2e69-6215-90c0-67f269939865" name="CtorEmptyTypeUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e1e9dde8-30e6-6ce0-d5a6-4e22e0347ac4" name="UnifyExtensionsAsSregWithAX" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f50a0bdb-380e-30f6-492a-a6dd9664d0f0" name="ExtensionOnlyChannelLevel" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="44afc59c-60fc-3179-b5a6-1e58e7752d54" name="ApplyHeadersToResponseNullHeaders" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2d82ac4b-99b4-a132-eb62-d943e02d1498" name="ApplyHeadersToResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="599add9e-e9eb-5e8a-ce6b-6dc73c2bb408" name="DataContractNamespace" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="643c9887-3f12-300e-fdac-17ae59652712" name="Mode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ed7efca3-c3c1-bc4a-cef7-eaf984749355" name="ValidMessageReceivedTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5435ab79-de25-e2fc-0b2d-b05d5686d27d" name="IsUrlWithinRealmTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1c5d54e2-d96a-d3a6-aeac-95f137b96421" name="CommonMethods" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e9cceef5-383d-92f0-a8bb-f3e207582836" name="RealmReturnToMismatchV2" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e6b412e5-3a53-e717-6393-254e1c93e239" name="PassThruDoubleCache" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0f36556d-ece7-eb70-8597-a9d085165c2c" name="Sign" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="924b5295-0d39-5c89-8794-22518091e05a" name="CtorNullToString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a63c169c-4e9a-bcba-b7cd-c4c5280cd652" name="PrepareMessageForSendingNonExtendableMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="63944cb8-4c61-c42c-906f-986fa793370b" name="SignatureTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="309fdc0f-150c-5992-9a79-63be5f479d89" name="RequiredProtection" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3d0effa3-894a-630c-02b0-ada4b5cef795" name="CtorNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9bcc2d64-870f-7675-a314-fbb975446817" name="IsApprovedDeterminesReturnedMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="53bc1962-e7c2-04b6-cafa-0f6fde7592a9" name="ReadFromRequestNoContext" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0f56721c-ef8f-84be-28b7-d909614c2f85" name="EqualsTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="41ff051f-03d5-5f06-c6e4-615360cac08a" name="ReadFromRequestDisallowedHttpMethod" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="62c6ee5b-ac29-461c-2373-bf620e948825" name="InvalidRealmNoScheme" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="352d9fd6-cf38-4b72-478f-e3e17ace55f5" name="NoValueLoose" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b2b54c72-1d26-8c28-ebf5-7a5a4beeec43" name="VerifyNonZeroLengthOnNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9684f7bf-cdda-a2c5-0822-29cb0add3835" name="ResponseNonceGetter" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c4001e1c-75ad-236b-284f-318905d2bc3a" name="CreateRequestOnNonOpenID" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b58e4791-68c0-1bc0-2e48-e1351459ee46" name="UserSetupUrlSetForV1Immediate" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="30f3c12b-e510-de63-5acd-ae8e32866592" name="CreateQueryString" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="32e95494-d0bb-cfc7-a8d6-652f8816c6b4" name="ReadFromResponse" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f967c0af-c04c-d156-4faf-8978bfcab5d7" name="RequiredNullableStruct" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="248f0afc-979f-a86f-e7de-fdeb4f9dd3ea" name="CtorBadUri" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="536ecd26-4bda-a35e-5af8-666eb9b44940" name="NullValueEncoding" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3e2f1dad-3684-587c-9039-8d116582be10" name="GetReturnToArgumentEmptyKey" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="00ed61cd-46cd-9c0e-f044-38d784c8bcfb" name="DecodeEmptyStringFails" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="09b892f2-96e9-45b7-d082-b0bb512c1dd4" name="RequiredNonNullableStruct" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d5912d3e-441c-a20e-20a2-0b9f0220a762" name="ParameterNames" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a6295302-c78f-4122-ce88-94fc30980262" name="CtorStringNoSchemeSecure" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f12bf351-584c-bc51-c315-a67f1076927c" name="ReturnToDoesNotMatchRecipient" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b4b00582-dcc9-7672-0c02-52432b074a92" name="GetNullType" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f1e1aa37-c712-6096-22fa-394008f0820a" name="CtorNull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cb48421f-f4ff-3994-3abc-4be35f8bfd99" name="AssociateQuietlyFailsAfterHttpError" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="660ad25a-b02b-1b17-7d6e-3af3303fa7bc" name="ModeEncoding" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="736a09b4-f56e-0176-6c1c-81db0fbe3412" name="CtorUriHttpsSchemeSecure" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9f880280-aa8f-91bb-4a5f-3fe044b6815a" name="CreateVerificationCode" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="10a8b8e5-e147-838c-0708-be98d5e4490e" name="CtorFull" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6daa360b-71e4-a972-143f-01b801fada84" name="DeserializeWithExtraFields" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8bbc6a02-b5a4-ea8e-2a77-8d1b6671ceb5" name="ImplicitConverstionFromUriTests" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d6088ffe-ccf5-9738-131b-0fc1bc7e3707" name="TrimFragment" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="90d3c411-8895-a07f-7a21-258b9d43c5b2" name="InvalidMessageNoNonceReceivedTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="121983e3-1336-70cb-8d2a-498629e92bec" name="GetReturnToArgumentNullKey" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5e0c892d-7ad8-6d56-1f1d-2fb6236670d6" name="CtorDefault" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c23e762d-4162-cb9e-47b3-455a568b5072" name="SendIndirectMessageFormPostEmptyRecipient" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="809afd59-8f10-ce37-6630-06b59351a05a" name="CommonProperties" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b191e585-49d9-df8e-c156-307f798db169" name="AddAttributeRequest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3772f97f-3fe6-3fc0-350d-4085e7c4329e" name="Test" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="44091d36-98db-2115-8647-7bd7cd308796" name="ToStringTest" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9e59b8d8-2fc4-b425-b5c4-c0a9fde3bf4d" name="SetValue" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="90557d85-db17-e9ab-e17b-32d6cc9fd437" name="TrimFragment" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="534bee09-36e1-c3e0-f6af-bc191b10aa48" name="CtorNullSigner" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9986fea9-8d64-9ada-60cb-ab95adb50fb7" name="ToStringDeferredEmptyMultiLine" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="de1cdd00-a226-0d43-62b6-0c1ad325be8c" name="RequiredMinAndMaxVersions" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cbdfd707-7ba8-4b8f-9d58-17b125aa4cd4" name="SendIndirectMessage301GetNullMessage" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5aa4dfa9-9691-bfe0-7d81-587cfa519a55" name="DirectResponsesReceivedAsKeyValueForm" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8fd673c8-977a-7b66-72cb-38c7054796c7" name="DiscoverRequireSslWithSecureRedirects" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5271f941-565f-5977-6884-82cef09161db" name="ParseEndUserSuppliedXriIdentifer" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="89de77d8-729a-7efe-9667-71b1f5d78859" name="CtorBadXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="06b350b0-79d1-9393-7620-cd919061898c" name="ParseEndUserSuppliedUriIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="300ae1f7-fc61-1d41-b262-f8c830b6e115" name="RemoveTest1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="734dd45c-6320-26a9-e412-62ecacfd285a" name="CtorNullAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1d5fb5a9-e15c-d99c-7a7e-95a4c4d123c2" name="DirectRequestsUsePost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6b218bf7-a4e9-8dac-d2c2-9bc3ee3ffc3e" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="74f2623d-98c5-7c2b-a2af-02dfa7ff5601" name="GetHttpVerbTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1c531011-403a-0821-d630-d5433d968f31" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="73c6c979-205d-2216-d98d-2dd136b352c6" name="UtcCreationDateConvertsToUniversal" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d39dbdbd-7daf-9d4c-9ef1-8bbdeda2ffb0" name="DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="49a266cf-4ab6-3fdc-f4fd-21533f42c7cb" name="CtorWithProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d12e8df0-1195-ab75-2275-7c8f854ddf98" name="UserSetupUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="21cf1f9a-063f-395a-f8aa-92c190c69146" name="SignaturesMatchKnownGood" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d570770a-74e4-50ec-8eb9-91bd81c093ad" name="ParseNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a9f7897c-b299-807b-0612-384732cd10c9" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f4bec8d2-0531-34ab-8d50-bca260b58c61" name="ReadFromRequestWithContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a314e3b9-36a5-bfbb-3e15-e5003f22cf87" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1bfbe1e1-3827-824f-27ad-4c990b0e22ab" name="Defaults" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ca9f3da7-e19f-b58b-54fe-54fa56ab9556" name="AddByKeyAndValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d766edce-59de-a03d-830a-0f0477521cff" name="ApplyHeadersToResponseNullAspNetResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9fae8ab4-8436-eba1-3e4b-51511998fa8e" name="UnsolicitedAssertionRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="84e718d7-bb82-e7d1-31be-471e2c154053" name="Item" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="889ba616-43dc-8a7f-ee13-46288969d617" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fdf5b3df-239b-26fd-c1a2-152057195b7e" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="555edc3b-5abf-7e46-b4f6-ddf44800b5df" name="SpreadSregToAXBasic" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1c2226d3-5cf9-dba6-80b9-ff2710808567" name="IsExtensionSupportedNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f8e523fa-a36b-5b4b-5f9d-b4f257f14ac5" name="XrdsDiscoveryFromHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="149a95cf-a538-f853-e11b-3133c15579c5" name="RequestTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f3cbbcda-49ff-fc43-140b-f362081654c3" name="CtorNullTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="352d9fd6-cf38-4b72-478f-e3e17ace55f5" name="NoValueLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fa52f2db-fc1e-ba31-cc5e-0bcc05998187" name="NoValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="65752c29-fa1f-7b88-bbec-5329af8db4d8" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="68532d6d-a0cf-5883-17e2-6060707ba9ae" name="DecodeOobToNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="188ce83b-3117-adb5-4b89-12f2b09be1de" name="CtorSimpleConsumer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="782d64c8-46af-a624-b3f6-a65aeaa57bfe" name="LastLineNotTerminatedLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f6ecb459-cc64-36ee-438c-4514e9413586" name="AddAttributeByPrimitives" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="03e293d0-dbe8-ad09-1ddd-de7be2cf9276" name="CopyTo" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5fa10f12-3de5-1783-0a97-9802d5469dfa" name="AddAttributeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5ab06bb5-d047-8c3a-6b91-5951e0a55cc5" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="40e1121e-8ff3-df73-203b-04baab671a0c" name="ImplicitConversionToStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="779b1f99-fe67-185c-f165-66787bf6e39a" name="BasicEncodingTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ed7efca3-c3c1-bc4a-cef7-eaf984749355" name="ValidMessageReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="456c3468-9081-4879-7e7e-8299bd8c7f68" name="IsReadOnly" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d6951a97-9d0b-31c1-7a29-01fbb986c5a9" name="SpoofedClaimedIdDetectionSolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1cde79f8-99b7-7090-f898-ba96a607875f" name="IsReturnUrlDiscoverableValidButNoMatch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="910f8448-5454-8ae5-cba3-690c7f375576" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="83aba528-c8ea-f464-177e-2ea8ae2cfd0b" name="Birthdates" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3c4e0438-94f5-a132-3949-8d94718e4839" name="PassThruCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b63c4b89-3889-6dcf-8890-c92fc44c0b10" name="VerifyBadTimestampIsRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9126d9e0-14dc-490b-3cd3-d3e424d38f9e" name="BinarySerialization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4c399759-263f-5eba-8855-de14f197e647" name="QueryStringContainPrefixedParametersNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1757957f-17bb-ef9f-39f8-c008863ec033" name="AssuranceLevels" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c11e5541-0a92-85ab-4f90-0db7766ebdcb" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8aecb3a5-2cb5-143d-aa99-9514fa8dfacb" name="AddAttributeByValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b09311d4-4dea-6786-3e59-9c62fe16e301" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="54eae9ed-bed1-eeda-b6ea-045c8f7e2ba5" name="SendIndirectMessage301GetNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2d0ee03a-f082-768c-a0db-574ac8efeffb" name="Valid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6c20a52a-bab7-e84e-faca-fd79ec5303d9" name="CtorCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7fb8d29c-c8ea-7f88-ed42-ae7368d6a429" name="CtorNullStore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="264cd371-e830-c09b-5511-18f54d4c69d5" name="RespondSimpleValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="77cb0e92-f1d8-44ba-eb16-83fdce2fb762" name="ProtocolDetectionWithoutClues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="163c8ba8-f829-c21e-a5a1-3c4565ec4425" name="UnifyExtensionsAsSregNoExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="054484ce-12c5-83ad-49a4-b241cd81557d" name="ClaimedIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e8337858-a320-8aad-51aa-402e65a90b75" name="ReplayDetectionTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="507cd1b6-1010-0bca-cf7f-f96e3f4f6c6c" name="QueryBeforeSettingUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1dcbaac6-0b11-8d8f-50d7-237574abbab1" name="ToDictionaryWithSkippedNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5c66a1b8-5b20-2e3b-8427-d6ff4640ac53" name="BadRequestsGenerateValidErrorResponses" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8c3f05ce-10de-f3f0-fac6-1471d1f146ed" name="IsTypeUriPresentEmptyString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4e3cef90-06f2-f5dd-3510-a8595b552af5" name="GetHttpVerbOutOfRangeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="715dcbdd-28f5-3c33-7d88-e0a1b648d89a" name="CreateRequestDumbMode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a1ff4ada-fe5d-d2f3-b7fb-8e72db02b3c3" name="Full" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="85a71d28-5f2f-75ce-9008-94982438bb5f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f334cc44-b2d0-2d67-358a-532def3bee80" name="ContainsKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fa05cc5f-2aaf-da22-ff52-caf1c3c6bb08" name="InsecureIdentifiersRejectedWithRequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c1c7e292-3e87-4fe5-1f6f-0ef2f86ebbce" name="GetHttpDeliveryMethodOutOfRangeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7debb527-142a-6ca6-3b9b-1e131c18e801" name="AccessTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="98e7a0f9-ab6c-7ff1-3a2c-00d8244e1bec" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a25369b4-b1b8-09a1-7a47-aacc1480e51c" name="FilePartAsFile" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="af7cb01c-950e-23d7-0f32-082b7af8b382" name="CtorNullToObject" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="13acd546-c82e-324c-220d-34f42a6d705e" name="DeserializeSimple" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b56cdf04-0d29-8b13-468c-fb4b4258c619" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d474830d-3636-522c-1564-1b83e7a844d3" name="EmptyLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8fc08a6d-6dcf-6256-42ff-073d4e4b6859" name="RequireDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d80bf2a0-bcd5-f1c1-4835-8e5ceb523387" name="GetPublicFacingUrlSSLForwarder3" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f20bd439-e277-dc27-4ec4-5d5949d1c6bf" name="RequestUsingAuthorizationHeaderScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3fc3ac8d-7772-b620-0927-f4bd3a24ce2f" name="SendNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5f3758b3-1410-c742-e623-b964c01b0633" name="AuthenticationTimeUtcConvertsToUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9d4a230d-9e74-dc1b-ecdc-bf875b56e1b3" name="CtorNullVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7b1fb2c4-39c0-0d39-700c-96d992f5a01f" name="AuthenticationTimeUtcSetUnspecified" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="00089858-d849-1e5f-4fb5-31d8d0590233" name="VerifyArgumentNotNullThrows" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="54a65e0b-1857-72b9-797b-fe3d9a082131" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f17424d2-ed4b-1ea0-a339-733f5092d9d0" name="MaximumAuthenticationAgeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d067c55c-3715-ed87-14a2-c07349813c94" name="IsDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2a7b77c3-27d5-7788-e664-5d20118d223b" name="OPRejectsHttpNoEncryptionAssociateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a66588b5-989d-8f8e-4994-4a066220516b" name="FileSerializeMatchesLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2f9d176e-4137-63bd-ee2a-6b79fde70d0d" name="Clear" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5c6d64b8-6368-5609-aa2c-ca32e273cdfd" name="IsTypeUriPresentNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="df159af7-abf5-089c-b592-e6f535dab1c1" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e137b84a-d2a7-9af6-d15d-a92417668ccf" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ac4ff1af-8333-e54e-0322-27d8824d7573" name="RequestUsingAuthorizationHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e78ab82c-3b49-468a-b2ad-ca038e98ff07" name="GetEnumerator" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8e86c2fd-24b9-44c5-7cda-d66aa7cd4418" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="62096188-a8d2-d540-0157-57974e058035" name="HtmlDiscover_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2e473003-940f-259e-ea10-3ddd8360e74d" name="IsTypeUriPresentEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5435ab79-de25-e2fc-0b2d-b05d5686d27d" name="IsUrlWithinRealmTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e2287de6-cbd2-4298-3fb8-297013749e70" name="SendIndirectMessageFormPostNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="db8d66cc-8206-57cc-0ce5-c8117813d77c" name="UnifyExtensionsasSregFromSchemaOpenIdNet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ef8a2274-4e58-0dde-4c5c-7f286865fc3a" name="SendReplayProtectedMessageSetsNonce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="63e5025b-5ccf-5f13-6e05-d1e44502a6e9" name="RequestBadPreferredScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e2ab77b2-a6dc-f165-1485-140b9b3d916f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2eb7c3dd-08aa-bfc0-edc8-e14bd82f7507" name="IdentifierTextInteraction" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a098eccc-1909-2f95-6fdf-848b7b7fd7d6" name="DiscoverRequireSslWithInsecureRedirect" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f41ce7ab-5500-7eea-ab4d-8c646bffff23" name="HttpSchemePrepended" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="38239ff0-1dfd-1116-55df-2790243dc768" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="04be6602-31a2-f4ae-8fdb-b9ad2ac370be" name="PrepareMessageForReceiving" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2c2b48d0-8009-e7e0-9ff4-34f9973f59da" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5dd2e6c9-ff0f-48de-3a1a-cbab61370843" name="SetCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="182203f3-5a16-b736-ea8c-b59f6bf7df66" name="InvalidRealmTwoWildcards2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4fd5baa2-8f39-8bf6-db8f-aa92592bfc06" name="CtorRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="385c302d-b546-c164-2a59-2e35f75d7d60" name="RemoveStructDeclaredProperty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="870cce9d-5b17-953c-028f-827ec8b56da2" name="GetInvalidMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="97f0277a-86e6-5b5a-8419-c5253cabf2e0" name="UserAuthorizationUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="643add47-e9f3-20b8-d8e0-69e3f8926d33" name="CreateRequestsWithEndpointFilter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d66a3b7a-1738-f6b3-aed1-e9bc80734ae9" name="CtorNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f3f84a10-317f-817a-1988-fddc10b75c20" name="AddTwoAttributes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2d82ac4b-99b4-a132-eb62-d943e02d1498" name="ApplyHeadersToResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5e2555a0-c07a-6488-c0e9-40ececd5021f" name="Serbian" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e03f0038-5bb7-92f2-87a7-00a7d2c31a77" name="MessageExpirationWithoutTamperResistance" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1f3ea08b-9880-635f-368f-9fcd3e25f3cd" name="ReadFromRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f49bcd49-76fb-bfea-b119-4e0f70159f80" name="OpenIdProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f4b313bb-cebc-a854-ffbd-6c955d850a05" name="VerifyGoodTimestampIsAccepted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="77047207-0571-72d5-71bd-586b878bcc0c" name="Base64Member" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f919a731-cc5c-88c6-5582-639b272d64fc" name="IsReturnUrlDiscoverableValidResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4a00f3ab-f405-95a7-d745-2fcf7787eb56" name="GetNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f4537b23-bb5e-5c6f-da53-64b34472f0dc" name="ChannelGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="75fa4664-bb0e-3a54-de29-c18ac712b231" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="94ba9fd3-851d-13b2-e273-6294b167c13e" name="HttpsSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5460f9c6-ec9d-969d-5aff-b946d6776e25" name="CtorWithNullProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="64d8c630-76c6-e420-937b-19c889dd7f59" name="CtorNonMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7c2d7790-f8fa-9af7-cd23-cfb360ae2c7b" name="IsExtensionSupported" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="be41e9c1-ecde-cc80-37d0-4126225e4cda" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="48115dc0-1323-bab0-c540-695a2160e0a3" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a79e43c9-ad5a-5543-51ff-22271ec87ab0" name="PrepareMessageForSendingNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7ca16e07-126d-58ac-2ac5-a09a8bf77592" name="InvalidRealmBadWildcard1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="832dbf28-5bf2-bd95-9029-bf798349d917" name="GetCallbackArguments" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="53cbbf4a-89d3-122b-0d88-662f3022ce26" name="OpenIdMaxAuthenticationTime" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="20646985-c84a-db8e-f982-ec55d61eaacd" name="ResponseNonceSetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c2c78c43-7f50-ffc3-affb-e60de2b76c94" name="CreateQueryStringNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="dc6af36d-0efc-9291-a603-2af7bac2a269" name="ErrorMessagesAsHttp400" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cc584a29-684c-75e8-3d77-96201d9ba537" name="ProviderTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9684f7bf-cdda-a2c5-0822-29cb0add3835" name="ResponseNonceGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b2e1bba0-ab24-cdd5-906c-a3655814ab2d" name="SendSetsTimestamp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2f1a3fc4-77ec-2ae3-668c-9e18f9ab0ebe" name="SendIndirectMessage301Get" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e4403d9e-73c1-967d-345c-4a2c83880d4e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6ce37652-2f47-6952-fb6d-568c2ca85224" name="TransformAXFormatTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a145f430-8062-5ad7-0cf5-b51eba0f8de7" name="HttpsSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="bb542259-4c10-4b88-1b3c-f842b0bb49a9" name="ImmediateVsSetupModes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="71564ca7-7845-92b3-7433-2f2beeb6b9f7" name="VerifyNonZeroLengthOnNonEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7ea157db-cf32-529f-f1d3-b3351f17725a" name="CtorSimpleServiceProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c905ca57-e427-3833-c2dd-17ca9f6962cd" name="SendIndirectMessageFormPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ca6a3643-2978-a695-2c7e-819c8dfebfc9" name="IsTypeUriPresentNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c7f6459d-9e6e-b4bc-cae8-65f5a3785403" name="SendIndirectMessageNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="aa79cdf5-e0bc-194e-fdbb-78369c19c30f" name="ConstantFieldMemberInvalidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="dd9e3279-2d7e-e88e-ccfa-ef213055fc3d" name="SendDirectedNoRecipientMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b384002f-26a9-7dde-c3f6-9ceff34dd8e2" name="GetRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="81f670d0-d314-c53c-9d91-c0765dfc30c1" name="MessagePartsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="efd570c9-5e74-17e4-f332-ac257c8e8aff" name="RealmReturnToMismatchV1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="60cf73e0-c801-a9e1-43c7-83998350953b" name="IsExtensionSupportedNullExtension" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="660ad25a-b02b-1b17-7d6e-3af3303fa7bc" name="ModeEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ae384709-e9a4-0142-20ba-6adb6b40b3e2" name="CtorStringHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3b70dd09-384d-5b99-222b-dc8ce8e791f2" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f787ae5d-b8fc-0862-a527-9157d11bbed7" name="UntrustedWebRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8df5d75f-bd4d-ce4e-2faf-6106b623de42" name="AddAttributeRequestStrangeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="dc821571-7c36-7ac2-1a8f-ae946b8352da" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e0606da1-e4fd-45ee-114c-a2878b89b615" name="XrdsDiscoveryFromHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3f4f8ac9-84c4-3933-71b0-fa3367e34ce1" name="DiscoverCommunityInameCanonicalIDs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f7733adb-f021-669a-a89e-95cd56666648" name="DualIdentifierOffByDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5a4df395-962e-0b7c-de71-abcb7e8930db" name="CreateFiltersDelegatingIdentifiers" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="feb0a53e-1592-b878-b70c-1a272d9c6908" name="SpreadSregToAxTargetedAtOPFormat" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e071a119-c7e9-1a55-b132-72e161fea598" name="CtorAndProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e9cdffd6-e5f9-2d24-b1c6-8195de7f3c40" name="DiscoverRequireSslWithSecureRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a6e464af-42df-1ba4-17e5-b955352664b5" name="RPOnlyRenegotiatesOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0290975f-02ce-d8a7-d723-5dae623cab46" name="CtorNullTokenManager" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="45003a0c-fdb3-863c-3331-d5c5af704c66" name="DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3b535521-90c8-7f49-545f-bcfc4ad16d40" name="UnresponsiveProvidersComeLast" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4bd86299-18d7-abbe-e5d2-1afad17279e9" name="Parse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="03b47440-3d09-ab28-97f1-39809f5703b6" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6c95f443-463e-2856-f500-b9029645e44c" name="RequestNullRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="afafb5ef-662e-2da3-35b8-1d67bb0d79ce" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="be6a14aa-c0d9-cf61-286a-236b92239597" name="EnumerableCache" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="17267cde-a296-8293-5bd1-9ca629817e4b" name="OpenIdRelyingParty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5a21f2d2-f916-fb16-1460-caff8c24a9fc" name="GetHttpDeliveryMethodTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cab73921-470b-331f-e601-b44805b67c81" name="GetAttributeValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ebd84587-bbc2-9889-c500-b6fbdf2bf209" name="GetRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="565140c9-c9fe-9466-1e39-740d7e368cb5" name="TryParse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e9bc3f63-aeb1-d84d-8abc-fc6ed77955e6" name="SignedResponsesIncludeExtraDataInSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9e209599-5924-f624-48de-ed31588cb425" name="EncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="51a08d94-c327-4d28-1f0c-f7920ea54870" name="ValidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="25e2c593-2e69-6215-90c0-67f269939865" name="CtorEmptyTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="95e1fc36-2500-2721-1919-35e9e8349a1c" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9af943f7-b289-1a24-8e3e-bfbd7a55b4c7" name="CtorGoodUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ae1ef27c-fbfe-c57e-a1e0-c1ef9de4ea23" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="196be55e-a3e5-adf3-9f15-13ba6cce0701" name="ValidRealmsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="bb68f1dc-3854-fc11-2ea6-d45f892d76fa" name="NistAssuranceLevelSetVarious" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="88aaa032-b18a-b334-937b-66837c5f987c" name="AssociateRenegotiateBitLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="dd5be0e2-a1fc-3369-0b11-78b728eeaba5" name="CtorNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0362a92c-a21c-f718-6b1e-3d154c14acd0" name="RequestUsingPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4a009f39-66b1-9cc5-ea8b-13b75ab22a5b" name="ContainsKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9bdc56c0-33ce-b46c-4031-bd3252b499a6" name="PrivateAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="32604ca2-2577-9c33-f778-ff7e4c985ce5" name="RequestTokenUriWithOAuthParametersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fdf439d0-3b74-4d32-d395-d5a2559ed88b" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3df1f62b-4fb4-d399-cf7f-40b72001d9d6" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7c688207-e58a-86ed-90d0-687e28ee7e38" name="MultiPartPostAscii" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8b11aa63-4c0f-41ff-f70c-882aacf939fe" name="CtorCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fba4d9a6-d8c7-a063-7c07-4a27c38c94a9" name="InvalidRealmBadWildcard3" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f583b298-139a-e733-dde6-f9dc4b73d4bf" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b671ea40-3b8c-58d5-7788-f776810c49be" name="UnicodeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="501fa941-c1ac-d4ef-56e7-46827788b571" name="GetRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="01e33554-07cc-ff90-46f8-7d0ca036c9f6" name="ToDictionaryNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="33dd2f79-cd69-f1c0-0e47-f7a17b5a8a8b" name="MultiPartPostMultiByteCharacters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0215f125-3936-484e-a8d0-d940d85bbc27" name="AppendQueryArgsNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7c048c58-c456-3406-995f-adb742cc2501" name="DeserializeInvalidMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="704a32d0-3f50-d462-f767-fd9cf1981b7f" name="ProviderVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f362baf3-da5b-1b8c-39ae-7c9b2051270a" name="AuthenticationTimeUtcSetUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="decb3fef-ef61-6794-5bc6-f7ff722a146e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3bb818b4-5423-ad91-8cd9-8606ec85d2cb" name="ReadFromRequestAuthorizationScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cbfeb75b-d031-7df3-c281-3c9e1c450042" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9302541c-9c30-9ce7-66db-348ee4e9f6ee" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="997253fb-7591-c151-1705-02976b400f27" name="AddAttributeTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9bf0528f-c3ab-9a38-fd8a-fd14bade0d0b" name="EnumerableCacheCurrentThrowsAfter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2fb2563b-b908-2fad-5efc-522a68c76780" name="ValidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3c438474-63f3-b56c-dcba-1ed923fcdbdd" name="CreateResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="47706bc6-7bee-0385-62b4-4f9cec6cc702" name="CtorWithTextMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2495fc9b-d766-5ae7-7324-f044c4ce1242" name="AddNullValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fb6c270f-ff72-73f4-b8b3-82851537427c" name="MultiVersionedMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="10245b55-8130-e0aa-e211-4a16fa14d0b1" name="ClearValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c2119f75-468b-8440-5ba1-2bcf6f62d91a" name="DualIdentifierNoMatchInAssertionVerificationByDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c9d67d40-1903-8319-0f7c-d70db4846380" name="SendWithoutAspNetContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="aa93fd70-1b5f-1ce7-c9dd-3c70f9e271ac" name="PostMultipart" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e97cee09-4163-d83f-f65f-14e424294172" name="ExtensionsAreIdentifiedAsSignedOrUnsigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f4fd129a-a7c3-dc1e-2b4a-5059a4207a8a" name="Send" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cc0031b8-1fdb-cd87-97c1-c6f893c296e0" name="TooManyBindingElementsProvidingSameProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e7aacb49-62ef-637d-ada2-0a12d836414d" name="ExtensionFactory" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="152e7a3a-21f9-eabf-0065-08597a0cc9a6" name="AuthorizationHeaderScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="93041654-1050-3878-6b90-656a7e2e3cfd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c7bdb960-68ca-50dd-8020-609224f93213" name="DualIdentifierMatchesInAssertionVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="dbf7855c-0cc6-309f-b5f5-022e0b95fe3b" name="QueryStringLookupWithoutQuery" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="abb0610a-c06f-0767-ac99-f37a2b573d1b" name="ParameterPrefix" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="32532d1f-d817-258d-ca72-021772bfc185" name="UriEncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f3345901-2e76-34dd-32f1-0b312d6e1c1e" name="IsReturnUrlDiscoverableNotSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a2b3835e-8edb-89aa-ba6c-f10b28a3af81" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1deb0ca9-923a-8ef7-7a24-d5d5af04acdf" name="SpecAppendixAExample" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="81442cf1-8c5f-7937-dc78-79474e3d3e80" name="DualIdentifierUsedOnlyAsOPIdentifierForAuthRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c63c9935-54a0-398a-f44b-214e17faf1f1" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ff47215f-1c38-8c55-e411-bf5608dd98f5" name="XrdsDirectDiscovery_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fb0e478e-0f55-b257-75fe-2ab60b57292e" name="SendInvalidMessageTransport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a4aa113a-57b5-a52c-c4e3-f70d6702badb" name="Default" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="547cfee6-bbb4-6138-a114-fc0eb6cc94f6" name="PrivateAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5803e93d-e256-86ba-e10e-499d2f813c6d" name="Trivial" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="beb086e9-5eb7-fb8f-480a-70ede9efd70d" name="CreateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="88ae5661-da27-91c5-4d78-1f43cd716127" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0c1a0323-092a-34b3-1601-1f941569efab" name="CtorGoodXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8538caf8-48bd-7cf8-6ad8-15e1c3766f92" name="CtorNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e1958fc5-a979-88b2-b593-3bc89ad6ad4e" name="GetEnumeratorUntyped" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="536ecd26-4bda-a35e-5af8-666eb9b44940" name="NullValueEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5218fba2-d1af-e1f4-7641-9ae1d4975430" name="DirectResponsesSentUsingKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a1a0178c-cd4a-1651-8535-3c9ee3d40821" name="ToDictionaryWithNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0f36556d-ece7-eb70-8597-a9d085165c2c" name="Sign" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="19d2219e-c04d-fa3a-5e26-92448f35f21d" name="RespondNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1d7932c1-22de-b750-4602-7ed419f9d7f8" name="DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0d99e0a9-295e-08a6-bc31-2abb79c00ff8" name="IsReturnUrlDiscoverableRequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c205832e-711c-62d0-5f5e-78f1250ea7cc" name="AuthenticationTimeUtcSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3536ba12-fdb0-2ac9-3fef-00a2dd8e9a65" name="SharedAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e7b8770d-b26c-e7b3-e80e-fac46285f59d" name="PassThruList" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="14acb719-f090-018f-b870-9a5acb1d7179" name="AddAuthLevelTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="aef95d4e-ad69-0eca-6528-7fce78512336" name="EqualityTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1b66e135-bdab-c2ed-18d8-aa89b46a57fc" name="RPRejectsUnrecognizedAssociationType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2f5cfa57-bcb4-39af-e769-2d7c34e2598e" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1ea8bd07-75a5-bfc0-5f8c-1a78d04240c2" name="TryGetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7b89844d-f60a-fb66-c48d-e483864c66b5" name="RespondTooManyValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b9cda1a0-83cd-cf4b-b61f-4faa75fa37ba" name="ReceivedReplayProtectedMessageTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7f9c4a9e-de7a-555c-543d-db89b757588e" name="AppendQueryArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e6b412e5-3a53-e717-6393-254e1c93e239" name="PassThruDoubleCache" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d6b120b7-fc16-6815-927e-af382cd44bbd" name="ReceivedInvalidSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b71d12f6-58a1-cf82-d06e-e57c0a3ea55c" name="RPRejectsUnencryptedSuggestion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2f6a81c5-cd04-0ca0-22ee-d4213f9cf147" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3c67903e-15ce-9ed4-34c8-f77059af79ca" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="87593646-8db5-fb47-3a5b-bf84d7d828c2" name="InvalidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4a5b601d-475d-e6cc-1fec-19a2850681ad" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="bd130894-b7ba-9d89-5170-a67e8a2add7c" name="NonNullCapabilities" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f3af5fd8-f661-dc4f-4539-947b081a8b54" name="ReceivedReplayProtectedMessageJustOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a14ddf08-796b-6cf1-a9bf-856dd50520fa" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3aa4e498-fd14-8274-22da-895436c1659e" name="AssociateUnencrypted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7203b777-33d6-a659-564a-8c7294f4c28b" name="Discover" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5b4fee50-7c15-8c6b-3398-c82279646e5f" name="RequiredOptionalLists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="59295023-d248-e9c4-68b9-65f6ea38490c" name="VerifyArgumentNotNullDoesNotThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a94ee2ec-02df-b535-1d2e-0c5db9c76b49" name="ReceiveUnrecognizedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cea48223-04e2-d336-0ac4-255c514bd188" name="RoundTripFullStackTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ba35acc7-78d2-6710-57ac-6843210d4202" name="UserSetupUrlRequiredInV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f18b514c-4f78-5421-8bdf-8b0f1fdf2282" name="HandleLifecycle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1e2ae78c-d2f3-a808-2b82-eca9f9f2e458" name="Keys" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9173c754-a358-91cc-a8f0-2c2703a55da8" name="AssertionWithEndpointFilter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6badbaa8-33d1-13c4-c1f9-aef73a9ac5bf" name="InvalidRawBirthdate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d3c4624f-f78a-2ff3-199a-77c922059718" name="Best" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0435e38a-71f2-d58d-9c07-d97d830a1578" name="ExtensionResponsesAreSigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2f2ea001-a4f8-ff0d-5d12-74180e0bf610" name="HttpsSignatureVerificationNotApplicable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fe55cc74-98eb-c6c7-622f-77ad3e304c10" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ca5360e1-ca08-d00f-6ade-7c9441db4294" name="CreateQueryStringEmptyCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4735a071-3c06-509b-05f5-912ab0e39f13" name="InvalidRealmBadProtocol" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="577b8522-8516-4f62-22db-76227bf82f4c" name="UserSetupUrlNotRequiredInV1SetupOrV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="175b6eb8-a448-4e07-7fed-001355edc128" name="PassThruArray" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="777af676-ee70-0e16-799b-85b9ec33cd63" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d083396b-db68-1318-e62b-6dc9f89e26bd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="72d3f240-67f2-0b04-bd31-a99c3f7a8e12" name="SharedAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3cd9447e-9ffd-f706-37bb-e7eb5828e430" name="InvalidRealmEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="28fe030c-d36e-13cf-475c-7813210bf886" name="AddAttributeRequestAgain" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e7a41771-7dda-be44-0755-e06300f3cd92" name="IsSaneTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2e23dc5a-93ea-11a5-d00d-02d294794e5f" name="AssociateDiffieHellmanOverHttps" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8d0df47c-c381-0487-6c19-77548ad7fc13" name="UnifyExtensionsAsSregWithBothSregAndAX" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2237b8ce-94ce-28c1-7eb2-14e59f47e926" name="UnifyExtensionsAsSregFromAXSchemaOrg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a2f79410-e593-9155-e03d-8168cbb9f091" name="GetPublicFacingUrlSSLForwarder1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7cdabb8a-aefa-e90e-c32e-047404b64c2d" name="SerializeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="457d6b32-d224-8a06-5e34-dbef3e935655" name="HttpSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="309fdc0f-150c-5992-9a79-63be5f479d89" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2e1b27e8-2e3e-0290-2bee-d88e2914efd9" name="SpreadSregToAXNoExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c15c3ab5-e969-efc9-366d-78ebc43ce08f" name="Fetch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7bf8e806-68a1-86bc-8d91-9a99d237d35c" name="CreateRequestMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a883dc73-d6be-e59a-6da2-0db1d4452679" name="BindingElementsOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="056e3331-9ffb-bd58-a668-68c3dd8091d1" name="XrdsDirectDiscovery_10" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5dcd69c3-e979-7316-4551-a73fe4645dcd" name="SecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f533bf9e-daa1-b26a-4789-372f3a9291d6" name="TryRequireSslAdjustsIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f44fb549-fc8a-7469-6eed-09d9f86cebff" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="313faac6-6357-5468-2d4d-4c9fba001678" name="TryParseNoThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7cbe4350-38d0-db7e-335c-93d9398fc95b" name="ExtensionOnlyFacadeLevel" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5f02e24c-2972-c598-ca71-ea362b2fe7d8" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ef20222d-b2e2-d593-17fa-512041020643" name="InvalidRealmNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c351c660-d583-d869-0129-2e312665d815" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f063a3c6-5a36-2801-53d7-5142416199a9" name="ImplicitConversionFromStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8ae44d86-85d0-8eef-7aff-c1a3b517df79" name="EqualsTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="02333934-cfea-2fb6-5e08-7a24be050f44" name="CreateRequestsOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5298ecb0-bcad-9022-8b93-87793eb2c669" name="UnsolicitedDelegatingIdentifierRejection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b97c4a66-a832-401c-a98b-6342ad7bcdcf" name="LanguagePreferenceEncodingDecoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="068dcefa-8f2b-52c3-fe79-576c84c5648b" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="315b5857-7697-8222-f94c-f6f10d539491" name="BaseSignatureStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cd219db4-4f6e-8ff4-f957-c8428d38c118" name="HttpSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="864578a5-61a2-bc5d-1d19-17093885bea3" name="InvalidRealmTwoWildcards1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2599d559-d036-5dd2-0b5b-fb229c3bf486" name="InvalidRealmBadWildcard2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4b55986d-de09-7176-4565-af763f30861d" name="DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e0b191fa-aa68-bd0f-c9d0-f8cba23003f3" name="RequestUsingHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f341c47f-ac23-2fc0-fcf5-c999fe8d2611" name="SetBodyToByteStream" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8a5c9404-1e77-68cf-229a-ef7ed413e6e7" name="OptionalNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="937b85f4-1ef3-84d1-a567-8bba079a33a9" name="Properties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="53bc1962-e7c2-04b6-cafa-0f6fde7592a9" name="ReadFromRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ce1b9b53-2cb8-f764-bf2f-1458cb576773" name="ProtocolDetection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0dc9284e-cba4-9d87-8955-19639578c70d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="384fecbf-f18e-edcb-a2eb-fb0322f031aa" name="ApplyHeadersToResponseNullListenerResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e3a3b3b6-e05f-0a99-e20c-af91a9065819" name="AssociateRequestDeterminedBySecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c057a3e5-b527-62a9-c19b-abb82e6be621" name="SendIndirectMessage301GetEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="30f3c12b-e510-de63-5acd-ae8e32866592" name="CreateQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7f3144c7-95a1-affa-1a37-9e6169c19be6" name="SharedAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="aedfde98-4357-5b63-7dca-cced838ee416" name="Provider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b71e8878-b20e-5d96-bce4-7f10831ceaf8" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ddf4f3ec-07bb-09e8-b5e8-0837cb8cb684" name="IsReturnUrlDiscoverableNoResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a18ae750-318b-bb1f-c2b3-c31da845c085" name="Count" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5b451648-5ca1-4395-333d-bbcb098f4a45" name="NoRequestedExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cb9a8325-abf5-5d97-a94e-a6d34f2b51e1" name="AssociateRenegotiateLimitedByRPSecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5271f941-565f-5977-6884-82cef09161db" name="ParseEndUserSuppliedXriIdentifer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7cf52613-a212-8a0f-843f-37f08740c125" name="SpreadSregToAxNoOpIfOPSupportsSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7c8eac5a-0455-e038-0e9a-10e59d459452" name="CtorUriHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="bdba0004-be80-f5c1-1aae-487db09bdf04" name="GetReturnToArgumentDoesNotReturnExtraArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ccfda025-cb1a-a2ff-78bd-5e9af885ae0b" name="ToDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="64142858-d52e-be06-d11f-6be326c6176b" name="RespondTwoValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e7e85e0f-5dfb-ae98-f796-ea23c27195ea" name="HtmlDiscover_11" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="34357633-4745-6fba-9316-493d3c6c5b90" name="ParseEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="85a0dec0-983c-8f21-b093-a2179624cc88" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fc7af2d7-6262-d761-335b-ef3ec029484d" name="DeserializeVerifyElementOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a6ea74e5-8681-4eb4-a51b-5051e5f7603c" name="NonFieldOrPropertyMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f6979feb-7016-4e2b-14e2-e6c2c392419f" name="RemoveByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="44afc59c-60fc-3179-b5a6-1e58e7752d54" name="ApplyHeadersToResponseNullHeaders" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="46ec24da-deb7-27c7-6dc6-52090e4fd1fb" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e9a5efc6-fde8-8fa4-0bda-2675a4a7e06b" name="DefaultReferenceTypeDeclaredPropertyHasNoKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b70b4bd5-6dae-b4ad-349c-c3ad70603773" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="809afd59-8f10-ce37-6630-06b59351a05a" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="98a2ece8-c9e6-e6f3-c65e-f915b22077fa" name="RequestUsingGet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2cfefc4a-918a-3e16-0670-53eb33634525" name="GeneratesOnlyRequiredElements" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="da8fcfa9-bd2c-eca0-ecbf-90364f84e8e5" name="AddExtraFieldThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fb91e9dd-fc3b-d8a7-a5d7-d215d5ba880f" name="CtorStringHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5211652f-1c25-fd4b-890d-05d2178a60e2" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b7441cac-9ee6-b2f4-d096-0f52c6eea5f6" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7650ec62-b144-f36f-8b56-31ad20521d0e" name="DoesNotStripFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e2b1ae2a-8f30-b6b3-bca6-ef28fc5a0175" name="ClaimedIdAndLocalIdSpecifiedIsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="495dd486-08dd-d365-7a84-67d96fef8460" name="SendIndirectedUndirectedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="10a8b8e5-e147-838c-0708-be98d5e4490e" name="CtorFull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="24fb403f-be35-278e-9beb-e11177f2cd1e" name="FormDataSerializeMatchesLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ee7a04ba-0419-e08f-b838-01ec0f2a838e" name="UnsolicitedAssertion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="47e8fae9-542d-1ebb-e17c-568cf9594539" name="RelativeUriDecodeFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e8094568-0c57-b752-2bc5-a630ebf075b8" name="DiscoveryWithRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a778f331-f14e-9d6e-f942-a023423540f6" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="90f06a50-7b81-05ec-3dc0-7b3e8ade8cfa" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ee052b02-681f-2303-3cc6-37f7b2319579" name="RequireAssociation" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="593e1d86-a6c2-c937-a1b4-6d25a595a1f1" name="EnumerableCacheCurrentThrowsBefore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="030ac3cf-cfb6-ca47-f822-ec1d2979f0b3" name="Defaults" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3fe0b432-dbb4-b334-e504-a83fe5ffdbaf" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d5d2c553-97db-8d6c-c984-982299d6091d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="628a417f-4ddb-5965-bd4a-86c8de565c8f" name="AssociateDiffieHellmanOverHttp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="10c44d72-2789-2afe-3b27-091dea97546e" name="RequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="24506e06-a678-66cc-48ee-b7f11f18a6e8" name="StripXriScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c79dd056-8fff-3393-f125-4b83cf02cb3b" name="RequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5a0d31d9-9c70-2a28-3e8c-46e8e047ac2d" name="ReceiveNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4b44b825-36cc-77f8-3a4a-5892c540f577" name="GetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f17128c1-5953-5391-ed75-c33774eacbfc" name="LastLineNotTerminated" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="671ddaf5-238d-a517-b0f3-d79bd591a396" name="EmptyMailAddress" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="98f169da-4e46-340a-cd72-ec0b1e504472" name="GetFriendlyIdentifierForDisplay" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d8118997-ecf7-7130-f068-5e2bc867786d" name="SerializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6fc900ea-f861-a6ec-47ec-709d0b91e003" name="IsTypeUriPresent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e1e9dde8-30e6-6ce0-d5a6-4e22e0347ac4" name="UnifyExtensionsAsSregWithAX" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f50a0bdb-380e-30f6-492a-a6dd9664d0f0" name="ExtensionOnlyChannelLevel" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="55b078e4-3933-d4e0-1151-a0a61321638e" name="ReadFromRequestAuthorization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="eb932fc7-76c7-b63f-e1e6-a59dea8e4da1" name="AddAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="599add9e-e9eb-5e8a-ce6b-6dc73c2bb408" name="DataContractNamespace" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="643c9887-3f12-300e-fdac-17ae59652712" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="44ced969-83dd-201d-a660-e3744ee81cf8" name="ConstructorTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1c5d54e2-d96a-d3a6-aeac-95f137b96421" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="58df167c-cf19-351c-cb09-5c52ae9f97be" name="DeserializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cfc4032d-936a-6532-09d5-4a1267e57d63" name="GetPublicFacingUrlSSLForwarder2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e9cceef5-383d-92f0-a8bb-f3e207582836" name="RealmReturnToMismatchV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f3b7d131-2c26-86e0-6654-ad6b2e4b706d" name="DualIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="bc3e979b-09ea-c45d-5714-2d1fb00244cf" name="IncomingMessageMissingRequiredParameters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="924b5295-0d39-5c89-8794-22518091e05a" name="CtorNullToString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a63c169c-4e9a-bcba-b7cd-c4c5280cd652" name="PrepareMessageForSendingNonExtendableMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="63944cb8-4c61-c42c-906f-986fa793370b" name="SignatureTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="735b7a56-0f6f-77d8-8968-6708792a7ce8" name="UnifyExtensionsAsSregWithAXSchemaOpenIdNet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3d0effa3-894a-630c-02b0-ada4b5cef795" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9bcc2d64-870f-7675-a314-fbb975446817" name="IsApprovedDeterminesReturnedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9026e58c-8582-0852-3c3c-9eadfd544cbc" name="VerifyNonZeroLengthOnEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0f56721c-ef8f-84be-28b7-d909614c2f85" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ae8b7cba-696e-2362-d5e1-79a9c202a994" name="EmptyLineLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="41ff051f-03d5-5f06-c6e4-615360cac08a" name="ReadFromRequestDisallowedHttpMethod" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="62c6ee5b-ac29-461c-2373-bf620e948825" name="InvalidRealmNoScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="115283b9-d95c-9a92-2197-96685ee8e96a" name="TwoExtensionsSameTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d24a246f-5017-49f0-8030-e44a68ba534d" name="GetPublicFacingUrlSSLForwarder4" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="96382f34-8075-73d1-e8cd-21dcb9235c84" name="CtorImpliedLocalIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b2b54c72-1d26-8c28-ebf5-7a5a4beeec43" name="VerifyNonZeroLengthOnNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c4001e1c-75ad-236b-284f-318905d2bc3a" name="CreateRequestOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b58e4791-68c0-1bc0-2e48-e1351459ee46" name="UserSetupUrlSetForV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c0d7cfcc-4f7e-e7df-3de2-b578c4c3d6ee" name="SpreadSregToAxMultipleSchemas" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="32e95494-d0bb-cfc7-a8d6-652f8816c6b4" name="ReadFromResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f967c0af-c04c-d156-4faf-8978bfcab5d7" name="RequiredNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="248f0afc-979f-a86f-e7de-fdeb4f9dd3ea" name="CtorBadUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3e2f1dad-3684-587c-9039-8d116582be10" name="GetReturnToArgumentEmptyKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="00ed61cd-46cd-9c0e-f044-38d784c8bcfb" name="DecodeEmptyStringFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="09b892f2-96e9-45b7-d082-b0bb512c1dd4" name="RequiredNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d5912d3e-441c-a20e-20a2-0b9f0220a762" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a6295302-c78f-4122-ce88-94fc30980262" name="CtorStringNoSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f12bf351-584c-bc51-c315-a67f1076927c" name="ReturnToDoesNotMatchRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b4b00582-dcc9-7672-0c02-52432b074a92" name="GetNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f1e1aa37-c712-6096-22fa-394008f0820a" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cb48421f-f4ff-3994-3abc-4be35f8bfd99" name="AssociateQuietlyFailsAfterHttpError" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0aa1bc22-0b26-3977-5983-5dc4a454cea5" name="OptionalNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="736a09b4-f56e-0176-6c1c-81db0fbe3412" name="CtorUriHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9f880280-aa8f-91bb-4a5f-3fe044b6815a" name="CreateVerificationCode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="be00d3ef-f24d-eb8a-d251-4d691736ee6f" name="AddAttributeRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6daa360b-71e4-a972-143f-01b801fada84" name="DeserializeWithExtraFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8bbc6a02-b5a4-ea8e-2a77-8d1b6671ceb5" name="ImplicitConverstionFromUriTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d6088ffe-ccf5-9738-131b-0fc1bc7e3707" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="90d3c411-8895-a07f-7a21-258b9d43c5b2" name="InvalidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="121983e3-1336-70cb-8d2a-498629e92bec" name="GetReturnToArgumentNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d0a92f93-9bb4-1821-81cf-e9b50e3e7d62" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5e0c892d-7ad8-6d56-1f1d-2fb6236670d6" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="643d722c-2c2b-fbd8-a499-5a852ef14dc7" name="PrepareMessageForSending" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c23e762d-4162-cb9e-47b3-455a568b5072" name="SendIndirectMessageFormPostEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f70b368e-da33-bc64-6096-1b467d49a9d4" name="NonIdentityRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b191e585-49d9-df8e-c156-307f798db169" name="AddAttributeRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3772f97f-3fe6-3fc0-350d-4085e7c4329e" name="Test" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="44091d36-98db-2115-8647-7bd7cd308796" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9e59b8d8-2fc4-b425-b5c4-c0a9fde3bf4d" name="SetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="90557d85-db17-e9ab-e17b-32d6cc9fd437" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="534bee09-36e1-c3e0-f6af-bc191b10aa48" name="CtorNullSigner" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9986fea9-8d64-9ada-60cb-ab95adb50fb7" name="ToStringDeferredEmptyMultiLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="de1cdd00-a226-0d43-62b6-0c1ad325be8c" name="RequiredMinAndMaxVersions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cbdfd707-7ba8-4b8f-9d58-17b125aa4cd4" name="SendIndirectMessage301GetNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5aa4dfa9-9691-bfe0-7d81-587cfa519a55" name="DirectResponsesReceivedAsKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fda58c48-e03a-73a3-4294-9a49e776ffb6" name="CtorWithTextMessageAndInnerException" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cc9200bf-1399-d40a-9754-6415f0b7bcf8" name="CreateRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
</TestLinks>
</TestList>
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
@@ -511,11 +533,11 @@
<TestList name="Slow" id="c426db8a-259c-4eac-b887-152be3e06ece" parentListId="f0eeb325-0558-48a3-9a99-952133d8148e">
<Description>All tests</Description>
<TestLinks>
- <TestLink id="b350ddb1-f4e5-e79c-af5e-f4195767f294" name="TestPublic" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7c3603c8-3686-807b-7840-1f04f8f307f5" name="AssociateDH" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7fa99410-3aa3-10c3-10a0-27bb9288c900" name="CheckIdSharedHmacSha1Association" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="85469029-ffe0-f6f7-b56a-9ffd48fa137b" name="AssociateClearText" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e6dadcc3-60ff-f60c-0c9a-2ebd5cf91df0" name="CheckIdSharedHmacSha256Association" storage="..\bin\codeanalysis\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b350ddb1-f4e5-e79c-af5e-f4195767f294" name="TestPublic" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7c3603c8-3686-807b-7840-1f04f8f307f5" name="AssociateDH" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7fa99410-3aa3-10c3-10a0-27bb9288c900" name="CheckIdSharedHmacSha1Association" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="85469029-ffe0-f6f7-b56a-9ffd48fa137b" name="AssociateClearText" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e6dadcc3-60ff-f60c-0c9a-2ebd5cf91df0" name="CheckIdSharedHmacSha256Association" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
</TestLinks>
</TestList>
<TestList name="Unit tests" id="f0eeb325-0558-48a3-9a99-952133d8148e" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6" />
diff --git a/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs b/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs
index 1c8c555..864d001 100644
--- a/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs
+++ b/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs
@@ -16,6 +16,7 @@ namespace DotNetOpenAuth.ComponentModel {
/// A type that generates suggested strings for Intellisense,
/// but doesn't actually convert between strings and other types.
/// </summary>
+ [ContractClass(typeof(SuggestedStringsConverterContract))]
public abstract class SuggestedStringsConverter : ConverterBase<string> {
/// <summary>
/// Initializes a new instance of the <see cref="SuggestedStringsConverter"/> class.
@@ -35,6 +36,9 @@ namespace DotNetOpenAuth.ComponentModel {
/// <param name="type">The type to reflect over.</param>
/// <returns>A collection of values.</returns>
internal static ICollection GetStandardValuesForCacheShared(Type type) {
+ Contract.Requires<ArgumentNullException>(type != null);
+ Contract.Ensures(Contract.Result<ICollection>() != null);
+
var fields = from field in type.GetFields(BindingFlags.Static | BindingFlags.Public)
select field.GetValue(null);
var properties = from prop in type.GetProperties(BindingFlags.Static | BindingFlags.Public)
diff --git a/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverterContract.cs b/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverterContract.cs
new file mode 100644
index 0000000..1573208
--- /dev/null
+++ b/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverterContract.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="SuggestedStringsConverterContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.ComponentModel {
+ using System;
+ using System.Collections;
+ using System.ComponentModel.Design.Serialization;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Reflection;
+
+ /// <summary>
+ /// Contract class for the <see cref="SuggestedStringsConverter"/> class.
+ /// </summary>
+ [ContractClassFor(typeof(SuggestedStringsConverter))]
+ internal abstract class SuggestedStringsConverterContract : SuggestedStringsConverter {
+ /// <summary>
+ /// Gets the type to reflect over for the well known values.
+ /// </summary>
+ protected override Type WellKnownValuesType {
+ get {
+ Contract.Ensures(Contract.Result<Type>() != null);
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/ComponentModel/UriConverter.cs b/src/DotNetOpenAuth/ComponentModel/UriConverter.cs
index cf8dde3..5e7c22b 100644
--- a/src/DotNetOpenAuth/ComponentModel/UriConverter.cs
+++ b/src/DotNetOpenAuth/ComponentModel/UriConverter.cs
@@ -101,9 +101,13 @@ namespace DotNetOpenAuth.ComponentModel {
protected override ICollection GetStandardValuesForCache() {
if (this.WellKnownValuesType != null) {
var fields = from field in this.WellKnownValuesType.GetFields(BindingFlags.Static | BindingFlags.Public)
- select new Uri((string)field.GetValue(null));
+ let value = (string)field.GetValue(null)
+ where value != null
+ select new Uri(value);
var properties = from prop in this.WellKnownValuesType.GetProperties(BindingFlags.Static | BindingFlags.Public)
- select new Uri((string)prop.GetValue(null, null));
+ let value = (string)prop.GetValue(null, null)
+ where value != null
+ select new Uri(value);
return (fields.Concat(properties)).ToArray();
} else {
return new Uri[0];
diff --git a/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs b/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs
index 56c7389..881fcdb 100644
--- a/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs
+++ b/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs
@@ -54,8 +54,7 @@ namespace DotNetOpenAuth.Configuration {
/// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
- Contract.Assume(element != null); // this should be Contract.Requires in base class.
- return ((AssociationTypeElement)element).AssociationType;
+ return ((AssociationTypeElement)element).AssociationType ?? string.Empty;
}
}
}
diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd
index ea32f4c..18e1c5c 100644
--- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd
+++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd
@@ -4,28 +4,68 @@
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="dotNetOpenAuth">
+ <xs:annotation>
+ <xs:documentation>
+ Customizations and configuration of DotNetOpenAuth behavior.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="messaging">
+ <xs:annotation>
+ <xs:documentation>
+ Options for general messaging protocols, such as whitelist/blacklist hosts and maximum message age.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="untrustedWebRequest">
+ <xs:annotation>
+ <xs:documentation>
+ Restrictions and settings to apply to outgoing HTTP requests to hosts that are not
+ trusted by this web site. Useful for OpenID-supporting hosts because HTTP connections
+ are initiated based on user input to arbitrary servers.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="whitelistHosts">
+ <xs:annotation>
+ <xs:documentation>
+ A set of host names (including domain names) to allow outgoing connections to
+ that would otherwise not be allowed based on security restrictions.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="add">
<xs:complexType>
- <xs:attribute name="name" type="xs:string" use="required" />
+ <xs:attribute name="name" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The host name to trust. For example: "localhost" or "www.mypartners.com".
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="remove">
<xs:complexType>
- <xs:attribute name="name" type="xs:string" use="required" />
+ <xs:attribute name="name" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The host name to NOT trust. For example: "localhost" or "www.mypartners.com".
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="clear">
+ <xs:annotation>
+ <xs:documentation>
+ Clears all hosts from the whitelist.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<!--tag is empty-->
</xs:complexType>
@@ -55,19 +95,42 @@
</xs:complexType>
</xs:element>
<xs:element name="blacklistHosts">
+ <xs:annotation>
+ <xs:documentation>
+ A set of host names (including domain names) to disallow outgoing connections to
+ that would otherwise be allowed based on security restrictions.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="add">
<xs:complexType>
- <xs:attribute name="name" type="xs:string" use="required" />
+ <xs:attribute name="name" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The host name known to add to the blacklist. For example: "localhost" or "www.mypartners.com".
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="remove">
<xs:complexType>
- <xs:attribute name="name" type="xs:string" use="required" />
+ <xs:attribute name="name" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The host name known to remove to the blacklist. For example: "localhost" or "www.mypartners.com".
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="clear">
+ <xs:annotation>
+ <xs:documentation>
+ Clears all hosts from the blacklist.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<!--tag is empty-->
</xs:complexType>
@@ -97,27 +160,92 @@
</xs:complexType>
</xs:element>
</xs:choice>
- <xs:attribute name="timeout" type="xs:string" />
- <xs:attribute name="readWriteTimeout" type="xs:string" />
- <xs:attribute name="maximumBytesToRead" type="xs:int" />
- <xs:attribute name="maximumRedirections" type="xs:int" />
+ <xs:attribute name="timeout" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum time to allow for an outgoing HTTP request to complete before giving up.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="readWriteTimeout" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum time to allow for an outgoing HTTP request to either send or receive data before giving up.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="maximumBytesToRead" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum bytes to read from an untrusted server during an outgoing HTTP request before cutting off the response.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="maximumRedirections" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum redirection instructions to follow before giving up.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
</xs:choice>
- <xs:attribute name="lifetime" type="xs:string" />
- <xs:attribute name="clockSkew" type="xs:string" />
+ <xs:attribute name="lifetime" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum time allowed between a message being sent to when it is received before
+ it is considered expired.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="clockSkew" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum time to consider a safe difference in server clocks.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="openid">
+ <xs:annotation>
+ <xs:documentation>
+ Configuration for OpenID authentication (relying parties and providers).
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="relyingParty">
+ <xs:annotation>
+ <xs:documentation>
+ Configuration specific for OpenID relying parties.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="security">
+ <xs:annotation>
+ <xs:documentation>
+ Security settings that apply to OpenID relying parties.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
- <xs:attribute name="requireSsl" type="xs:boolean" default="false" />
+ <xs:attribute name="requireSsl" type="xs:boolean" default="false">
+ <xs:annotation>
+ <xs:documentation>
+ Restricts OpenID logins to identifiers that use HTTPS throughout the discovery process,
+ and only uses HTTPS OpenID Provider endpoints.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="minimumRequiredOpenIdVersion">
+ <xs:annotation>
+ <xs:documentation>
+ Optionally restricts interoperability with remote parties that
+ implement older versions of OpenID.
+ </xs:documentation>
+ </xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="V10" />
@@ -126,28 +254,125 @@
</xs:restriction>
</xs:simpleType>
</xs:attribute>
- <xs:attribute name="minimumHashBitLength" type="xs:int" />
- <xs:attribute name="maximumHashBitLength" type="xs:int" />
- <xs:attribute name="privateSecretMaximumAge" type="xs:string" />
- <xs:attribute name="requireDirectedIdentity" type="xs:boolean" />
- <xs:attribute name="requireAssociation" type="xs:boolean" />
- <xs:attribute name="rejectUnsolicitedAssertions" type="xs:boolean" />
- <xs:attribute name="rejectDelegatingIdentifiers" type="xs:boolean" />
- <xs:attribute name="ignoreUnsignedExtensions" type="xs:boolean" />
+ <xs:attribute name="minimumHashBitLength" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>
+ Shared associations with OpenID Providers will only be formed or used if they
+ are willing to form associations equal to or greater than a given level of protection.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="maximumHashBitLength" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>
+ Shared associaitons with OpenID Providers will only be formed or used if they
+ are willing to form associations equal to or less than a given level of protection.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="privateSecretMaximumAge" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum age of a secret used for private signing before it is renewed.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="requireDirectedIdentity" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Requires that OpenID identifiers upon which authentication requests are created
+ are to be OP Identifiers. Claimed Identifiers are not allowed.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="requireAssociation" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Requires that the relying party can form a shared association with an
+ OpenID Provider before creating an authentication request for it.
+ Note that this does not require that the Provider actually use a
+ shared association in its response.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rejectUnsolicitedAssertions" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Requires that users begin their login experience at the relying party
+ rather than at a Provider or using other forms of unsolicited assertions.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rejectDelegatingIdentifiers" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Requires that the claimed identifiers used to log into the relying party
+ be the same ones that are originally issued by the Provider.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ignoreUnsignedExtensions" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Makes it impossible for the relying party to read authentication response
+ extensions that are not signed by the Provider.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="allowDualPurposeIdentifiers" type="xs:boolean" />
</xs:complexType>
</xs:element>
<xs:element name="behaviors">
+ <xs:annotation>
+ <xs:documentation>
+ Manipulates the set of custom behaviors that are automatically applied
+ to incoming and outgoing OpenID messages.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="add">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="optional" />
+ <xs:attribute name="type" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>
+ The fully-qualified name of the type that implements the IRelyingPartyBehavior interface.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="xaml" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="remove">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="required" />
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The fully-qualified name of the type that implements the IRelyingPartyBehavior interface.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="clear">
+ <xs:complexType>
+ <!--tag is empty-->
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="discoveryServices">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="add">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="remove">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="clear">
@@ -159,31 +384,76 @@
</xs:complexType>
</xs:element>
<xs:element name="store">
+ <xs:annotation>
+ <xs:documentation>
+ A custom implementation of IRelyingPartyApplicationStore to use by default for new
+ instances of OpenIdRelyingParty.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
- <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ A fully-qualified type name of the custom implementation of IRelyingPartyApplicationStore.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="provider">
+ <xs:annotation>
+ <xs:documentation>
+ Configuration specific for OpenID providers.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="security">
+ <xs:annotation>
+ <xs:documentation>
+ Security settings that apply to OpenID providers.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="associations">
+ <xs:annotation>
+ <xs:documentation>
+ Sets maximum ages for shared associations of various strengths.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="add">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="required" />
- <xs:attribute name="lifetime" type="xs:string" use="required" />
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The OpenID association type (i.e. HMAC-SHA1 or HMAC-SHA256)
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="lifetime" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The lifetime a shared association of this type will be used for.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="remove">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="required" />
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The OpenID association type (i.e. HMAC-SHA1 or HMAC-SHA256)
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="clear">
@@ -195,18 +465,92 @@
</xs:complexType>
</xs:element>
</xs:choice>
- <xs:attribute name="requireSsl" type="xs:boolean" default="false" />
- <xs:attribute name="protectDownlevelReplayAttacks" type="xs:boolean" />
- <xs:attribute name="minimumHashBitLength" type="xs:int" />
- <xs:attribute name="maximumHashBitLength" type="xs:int" />
+ <xs:attribute name="requireSsl" type="xs:boolean" default="false">
+ <xs:annotation>
+ <xs:documentation>
+ Requires that relying parties' realm URLs be protected by HTTPS,
+ ensuring that the RP discovery step is not vulnerable to DNS poisoning attacks.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="protectDownlevelReplayAttacks" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Provides automatic security protections to OpenID 1.x relying parties
+ so security is comparable to OpenID 2.0 relying parties.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="unsolicitedAssertionVerification">
+ <xs:annotation>
+ <xs:documentation>
+ The level of verification done on a claimed identifier before an unsolicited
+ assertion for that identifier is issued by this Provider.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="RequireSuccess">
+ <xs:annotation>
+ <xs:documentation>
+ The claimed identifier being asserted must delegate to this Provider
+ and this must be verifiable by the Provider to send the assertion.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="LogWarningOnFailure">
+ <xs:annotation>
+ <xs:documentation>
+ The claimed identifier being asserted is checked for delegation to this Provider
+ and an warning is logged, but the assertion is allowed to go through.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="NeverVerify">
+ <xs:annotation>
+ <xs:documentation>
+ The claimed identifier being asserted is not checked to see that this Provider
+ has authority to assert its identity.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="minimumHashBitLength" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>
+ The minimum shared association strength to form with relying parties.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="maximumHashBitLength" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum shared association strength to form with relying parties.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="behaviors">
+ <xs:annotation>
+ <xs:documentation>
+ Manipulates the set of custom behaviors that are automatically applied
+ to incoming and outgoing OpenID messages.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="add">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="optional" />
+ <xs:attribute name="type" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>
+ The fully-qualified name of the type that implements the IRelyingPartyBehavior interface.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="xaml" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
@@ -224,25 +568,54 @@
</xs:complexType>
</xs:element>
<xs:element name="store">
+ <xs:annotation>
+ <xs:documentation>
+ A custom implementation of IProviderApplicationStore to use by default for new
+ instances of OpenIdRelyingParty.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
- <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ A fully-qualified type name of the custom implementation of IProviderApplicationStore.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="extensionFactories">
+ <xs:annotation>
+ <xs:documentation>
+ Adjusts the list of known OpenID extensions via the registration of extension factories.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="add">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="optional" />
+ <xs:attribute name="type" type="xs:string" use="optional">
+ <xs:annotation>
+ <xs:documentation>
+ The fully-qualified name of the type that implements IOpenIdExtensionFactory.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="xaml" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="remove">
<xs:complexType>
- <xs:attribute name="type" type="xs:string" use="required" />
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>
+ The fully-qualified name of the type that implements IOpenIdExtensionFactory.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="clear">
@@ -254,13 +627,203 @@
</xs:complexType>
</xs:element>
<xs:element name="xriResolver">
+ <xs:annotation>
+ <xs:documentation>
+ Controls XRI resolution to XRDS documents.
+ </xs:documentation>
+ </xs:annotation>
<xs:complexType>
- <xs:attribute name="enabled" type="xs:boolean" />
- <xs:attribute name="proxy" type="xs:string" />
+ <xs:attribute name="enabled" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Controls whether XRI identifiers are allowed at all.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="proxy" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The XRI proxy resolver to use for obtaining XRDS documents from an XRI.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
</xs:choice>
- <xs:attribute name="maxAuthenticationTime" type="xs:string" />
+ <xs:attribute name="maxAuthenticationTime" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum time a user can take at the Provider while logging in before a relying party considers
+ the authentication lost.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="oauth">
+ <xs:annotation>
+ <xs:documentation>
+ Settings for OAuth consumers and service providers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="consumer">
+ <xs:annotation>
+ <xs:documentation>
+ Settings applicable to OAuth Consumers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="security">
+ <xs:annotation>
+ <xs:documentation>
+ Security settings applicable to OAuth Consumers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="serviceProvider">
+ <xs:annotation>
+ <xs:documentation>
+ Settings applicable to OAuth Service Providers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="security">
+ <xs:annotation>
+ <xs:documentation>
+ Security settings applicable to OAuth Service Providers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="minimumRequiredOAuthVersion" default="V10">
+ <xs:annotation>
+ <xs:documentation>
+ Optionally restricts interoperability with OAuth consumers that implement
+ older versions of OAuth.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="V10">
+ <xs:annotation>
+ <xs:documentation>
+ The initial version of OAuth, now known to be vulnerable to certain social engineering attacks.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="V10a">
+ <xs:annotation>
+ <xs:documentation>
+ The OAuth version that protects against social engineering attacks by introducing
+ the oauth_verifier parameter.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="maxAuthorizationTime" type="xs:string" default="0:05">
+ <xs:annotation>
+ <xs:documentation>
+ The maximum time allowed for users to authorize a consumer before request tokens expire.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="store">
+ <xs:annotation>
+ <xs:documentation>
+ Sets the custom type that implements the INonceStore interface to use for nonce checking.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="type" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ A fully-qualified type name of the custom implementation of INonceStore.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="reporting">
+ <xs:annotation>
+ <xs:documentation>
+ Adjusts statistical reports DotNetOpenAuth may send to the library authors to
+ assist with future development of the library.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="enabled" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Controls whether reporting is active at all or entirely inactive.
+ Note that even if active, the reports may be more or less empty based
+ on other settings.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="minimumReportingInterval" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ Controls how frequently reports are collected and transmitted.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="minimumFlushInterval" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>
+ Controls how frequently the statistics that are collected in memory are persisted to disk.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="includeFeatureUsage" type="xs:boolean" default="true">
+ <xs:annotation>
+ <xs:documentation>
+ Whether a list of features in DotNetOpenAuth that are actually used by this host
+ are included in the report.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="includeEventStatistics" type="xs:boolean" default="true">
+ <xs:annotation>
+ <xs:documentation>
+ Whether a set of counters that track how often certain events (such as an
+ successful or failed authentication) is included in the report.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="includeLocalRequestUris" type="xs:boolean" default="true">
+ <xs:annotation>
+ <xs:documentation>
+ Whether to include a few of this host's URLs that contain DotNetOpenAuth components.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="includeCultures" type="xs:boolean" default="true">
+ <xs:annotation>
+ <xs:documentation>
+ Whether to include the cultures as set on the user agents of incoming requests to pages
+ that contain DotNetOpenAuth components.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
</xs:choice>
diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs
index 7bd84d9..aa956d1 100644
--- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs
+++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs
@@ -35,6 +35,11 @@ namespace DotNetOpenAuth.Configuration {
private const string OAuthElementName = "oauth";
/// <summary>
+ /// The name of the &lt;reporting&gt; sub-element.
+ /// </summary>
+ private const string ReportingElementName = "reporting";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="DotNetOpenAuthSection"/> class.
/// </summary>
internal DotNetOpenAuthSection() {
@@ -75,5 +80,14 @@ namespace DotNetOpenAuth.Configuration {
get { return (OAuthElement)this[OAuthElementName] ?? new OAuthElement(); }
set { this[OAuthElementName] = value; }
}
+
+ /// <summary>
+ /// Gets or sets the configuration for reporting.
+ /// </summary>
+ [ConfigurationProperty(ReportingElementName)]
+ internal ReportingElement Reporting {
+ get { return (ReportingElement)this[ReportingElementName] ?? new ReportingElement(); }
+ set { this[ReportingElementName] = value; }
+ }
}
}
diff --git a/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs b/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs
index 88a1615..c7d963b 100644
--- a/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs
+++ b/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs
@@ -64,7 +64,7 @@ namespace DotNetOpenAuth.Configuration {
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
Contract.Assume(element != null); // this should be Contract.Requires in base class.
- return ((HostNameElement)element).Name;
+ return ((HostNameElement)element).Name ?? string.Empty;
}
}
}
diff --git a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs
index 5ff528d..8e910a0 100644
--- a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs
@@ -6,12 +6,18 @@
namespace DotNetOpenAuth.Configuration {
using System.Configuration;
+ using DotNetOpenAuth.Messaging.Bindings;
/// <summary>
/// Represents the &lt;oauth/serviceProvider&gt; element in the host's .config file.
/// </summary>
internal class OAuthServiceProviderElement : ConfigurationElement {
/// <summary>
+ /// The name of the custom store sub-element.
+ /// </summary>
+ private const string StoreConfigName = "store";
+
+ /// <summary>
/// Gets the name of the security sub-element.
/// </summary>
private const string SecuritySettingsConfigName = "security";
@@ -23,6 +29,15 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the type to use for storing application state.
+ /// </summary>
+ [ConfigurationProperty(StoreConfigName)]
+ public TypeConfigurationElement<INonceStore> ApplicationStore {
+ get { return (TypeConfigurationElement<INonceStore>)this[StoreConfigName] ?? new TypeConfigurationElement<INonceStore>(); }
+ set { this[StoreConfigName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the security settings.
/// </summary>
[ConfigurationProperty(SecuritySettingsConfigName)]
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
index 58a8276..69994e6 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
@@ -63,8 +63,17 @@ namespace DotNetOpenAuth.Configuration {
[ConfigurationProperty(MaxAuthenticationTimePropertyName, DefaultValue = "0:05")] // 5 minutes
[PositiveTimeSpanValidator]
internal TimeSpan MaxAuthenticationTime {
- get { return (TimeSpan)this[MaxAuthenticationTimePropertyName]; }
- set { this[MaxAuthenticationTimePropertyName] = value; }
+ get {
+ Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
+ TimeSpan result = (TimeSpan)this[MaxAuthenticationTimePropertyName];
+ Contract.Assume(result > TimeSpan.Zero); // our PositiveTimeSpanValidator should take care of this
+ return result;
+ }
+
+ set {
+ Contract.Requires<ArgumentOutOfRangeException>(value > TimeSpan.Zero);
+ this[MaxAuthenticationTimePropertyName] = value;
+ }
}
/// <summary>
@@ -104,10 +113,10 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
- /// Gets or sets the registered OpenID extensions.
+ /// Gets or sets the registered OpenID extension factories.
/// </summary>
[ConfigurationProperty(ExtensionFactoriesElementName, IsDefaultCollection = false)]
- [ConfigurationCollection(typeof(TypeConfigurationCollection<IOpenIdMessageExtension>))]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IOpenIdExtensionFactory>))]
internal TypeConfigurationCollection<IOpenIdExtensionFactory> ExtensionFactories {
get { return (TypeConfigurationCollection<IOpenIdExtensionFactory>)this[ExtensionFactoriesElementName] ?? new TypeConfigurationCollection<IOpenIdExtensionFactory>(); }
set { this[ExtensionFactoriesElementName] = value; }
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
index 457955c..3545fc5 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
@@ -41,6 +41,11 @@ namespace DotNetOpenAuth.Configuration {
private const string RequireSslConfigName = "requireSsl";
/// <summary>
+ /// Gets the name of the @unsolicitedAssertionVerification attribute.
+ /// </summary>
+ private const string UnsolicitedAssertionVerificationConfigName = "unsolicitedAssertionVerification";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdProviderSecuritySettingsElement"/> class.
/// </summary>
public OpenIdProviderSecuritySettingsElement() {
@@ -84,6 +89,17 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the level of verification a Provider performs on an identifier before
+ /// sending an unsolicited assertion for it.
+ /// </summary>
+ /// <value>The default value is <see cref="ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.RequireSuccess"/>.</value>
+ [ConfigurationProperty(UnsolicitedAssertionVerificationConfigName, DefaultValue = ProviderSecuritySettings.UnsolicitedAssertionVerificationDefault)]
+ public ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel UnsolicitedAssertionVerification {
+ get { return (ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel)this[UnsolicitedAssertionVerificationConfigName]; }
+ set { this[UnsolicitedAssertionVerificationConfigName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the configured lifetimes of the various association types.
/// </summary>
[ConfigurationProperty(AssociationsConfigName, IsDefaultCollection = false)]
@@ -109,6 +125,7 @@ namespace DotNetOpenAuth.Configuration {
settings.MinimumHashBitLength = this.MinimumHashBitLength;
settings.MaximumHashBitLength = this.MaximumHashBitLength;
settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks;
+ settings.UnsolicitedAssertionVerification = this.UnsolicitedAssertionVerification;
foreach (AssociationTypeElement element in this.AssociationLifetimes) {
Contract.Assume(element != null);
settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime);
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
index cdf4fd3..2ee2e91 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
@@ -5,8 +5,10 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Configuration {
+ using System;
using System.Configuration;
using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
@@ -25,11 +27,21 @@ namespace DotNetOpenAuth.Configuration {
private const string SecuritySettingsConfigName = "security";
/// <summary>
- /// Gets the name of the &lt;behaviors&gt; sub-element.
+ /// The name of the &lt;behaviors&gt; sub-element.
/// </summary>
private const string BehaviorsElementName = "behaviors";
/// <summary>
+ /// The name of the &lt;discoveryServices&gt; sub-element.
+ /// </summary>
+ private const string DiscoveryServicesElementName = "discoveryServices";
+
+ /// <summary>
+ /// The built-in set of identifier discovery services.
+ /// </summary>
+ private static readonly TypeConfigurationCollection<IIdentifierDiscoveryService> defaultDiscoveryServices = new TypeConfigurationCollection<IIdentifierDiscoveryService>(new Type[] { typeof(UriDiscoveryService), typeof(XriDiscoveryProxyService) });
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingPartyElement"/> class.
/// </summary>
public OpenIdRelyingPartyElement() {
@@ -62,5 +74,25 @@ namespace DotNetOpenAuth.Configuration {
get { return (TypeConfigurationElement<IRelyingPartyApplicationStore>)this[StoreConfigName] ?? new TypeConfigurationElement<IRelyingPartyApplicationStore>(); }
set { this[StoreConfigName] = value; }
}
+
+ /// <summary>
+ /// Gets or sets the services to use for discovering service endpoints for identifiers.
+ /// </summary>
+ /// <remarks>
+ /// If no discovery services are defined in the (web) application's .config file,
+ /// the default set of discovery services built into the library are used.
+ /// </remarks>
+ [ConfigurationProperty(DiscoveryServicesElementName, IsDefaultCollection = false)]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IIdentifierDiscoveryService>))]
+ internal TypeConfigurationCollection<IIdentifierDiscoveryService> DiscoveryServices {
+ get {
+ var configResult = (TypeConfigurationCollection<IIdentifierDiscoveryService>)this[DiscoveryServicesElementName];
+ return configResult != null && configResult.Count > 0 ? configResult : defaultDiscoveryServices;
+ }
+
+ set {
+ this[DiscoveryServicesElementName] = value;
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
index d10d9bd..0d8b768 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
@@ -66,6 +66,11 @@ namespace DotNetOpenAuth.Configuration {
private const string PrivateSecretMaximumAgeConfigName = "privateSecretMaximumAge";
/// <summary>
+ /// Gets the name of the @allowDualPurposeIdentifiers attribute.
+ /// </summary>
+ private const string AllowDualPurposeIdentifiersConfigName = "allowDualPurposeIdentifiers";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingPartySecuritySettingsElement"/> class.
/// </summary>
public OpenIdRelyingPartySecuritySettingsElement() {
@@ -183,6 +188,19 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets a value indicating whether identifiers that are both OP Identifiers and Claimed Identifiers
+ /// should ever be recognized as claimed identifiers.
+ /// </summary>
+ /// <value>
+ /// The default value is <c>false</c>, per the OpenID 2.0 spec.
+ /// </value>
+ [ConfigurationProperty(AllowDualPurposeIdentifiersConfigName, DefaultValue = false)]
+ public bool AllowDualPurposeIdentifiers {
+ get { return (bool)this[AllowDualPurposeIdentifiersConfigName]; }
+ set { this[AllowDualPurposeIdentifiersConfigName] = value; }
+ }
+
+ /// <summary>
/// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file.
/// </summary>
/// <returns>The newly created security settings object.</returns>
@@ -200,6 +218,7 @@ namespace DotNetOpenAuth.Configuration {
settings.RejectUnsolicitedAssertions = this.RejectUnsolicitedAssertions;
settings.RejectDelegatingIdentifiers = this.RejectDelegatingIdentifiers;
settings.IgnoreUnsignedExtensions = this.IgnoreUnsignedExtensions;
+ settings.AllowDualPurposeIdentifiers = this.AllowDualPurposeIdentifiers;
return settings;
}
diff --git a/src/DotNetOpenAuth/Configuration/ReportingElement.cs b/src/DotNetOpenAuth/Configuration/ReportingElement.cs
new file mode 100644
index 0000000..2374448
--- /dev/null
+++ b/src/DotNetOpenAuth/Configuration/ReportingElement.cs
@@ -0,0 +1,139 @@
+//-----------------------------------------------------------------------
+// <copyright file="ReportingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System;
+ using System.Collections.Generic;
+ using System.Configuration;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// Represents the &lt;reporting&gt; element in the host's .config file.
+ /// </summary>
+ internal class ReportingElement : ConfigurationElement {
+ /// <summary>
+ /// The name of the @enabled attribute.
+ /// </summary>
+ private const string EnabledAttributeName = "enabled";
+
+ /// <summary>
+ /// The name of the @minimumReportingInterval attribute.
+ /// </summary>
+ private const string MinimumReportingIntervalAttributeName = "minimumReportingInterval";
+
+ /// <summary>
+ /// The name of the @minimumFlushInterval attribute.
+ /// </summary>
+ private const string MinimumFlushIntervalAttributeName = "minimumFlushInterval";
+
+ /// <summary>
+ /// The name of the @includeFeatureUsage attribute.
+ /// </summary>
+ private const string IncludeFeatureUsageAttributeName = "includeFeatureUsage";
+
+ /// <summary>
+ /// The name of the @includeEventStatistics attribute.
+ /// </summary>
+ private const string IncludeEventStatisticsAttributeName = "includeEventStatistics";
+
+ /// <summary>
+ /// The name of the @includeLocalRequestUris attribute.
+ /// </summary>
+ private const string IncludeLocalRequestUrisAttributeName = "includeLocalRequestUris";
+
+ /// <summary>
+ /// The name of the @includeCultures attribute.
+ /// </summary>
+ private const string IncludeCulturesAttributeName = "includeCultures";
+
+ /// <summary>
+ /// The default value for the @minimumFlushInterval attribute.
+ /// </summary>
+#if DEBUG
+ private const string MinimumFlushIntervalDefault = "0";
+#else
+ private const string MinimumFlushIntervalDefault = "0:15";
+#endif
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ReportingElement"/> class.
+ /// </summary>
+ internal ReportingElement() {
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this reporting is enabled.
+ /// </summary>
+ /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
+ [ConfigurationProperty(EnabledAttributeName, DefaultValue = true)]
+ internal bool Enabled {
+ get { return (bool)this[EnabledAttributeName]; }
+ set { this[EnabledAttributeName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum frequency that reports will be published.
+ /// </summary>
+ [ConfigurationProperty(MinimumReportingIntervalAttributeName, DefaultValue = "1")] // 1 day default
+ internal TimeSpan MinimumReportingInterval {
+ get { return (TimeSpan)this[MinimumReportingIntervalAttributeName]; }
+ set { this[MinimumReportingIntervalAttributeName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum frequency the set can be flushed to disk.
+ /// </summary>
+ [ConfigurationProperty(MinimumFlushIntervalAttributeName, DefaultValue = MinimumFlushIntervalDefault)]
+ internal TimeSpan MinimumFlushInterval {
+ get { return (TimeSpan)this[MinimumFlushIntervalAttributeName]; }
+ set { this[MinimumFlushIntervalAttributeName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to include a list of library features used in the report.
+ /// </summary>
+ /// <value><c>true</c> to include a report of features used; otherwise, <c>false</c>.</value>
+ [ConfigurationProperty(IncludeFeatureUsageAttributeName, DefaultValue = true)]
+ internal bool IncludeFeatureUsage {
+ get { return (bool)this[IncludeFeatureUsageAttributeName]; }
+ set { this[IncludeFeatureUsageAttributeName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to include statistics of certain events such as
+ /// authentication success and failure counting, and can include remote endpoint URIs.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> to include event counters in the report; otherwise, <c>false</c>.
+ /// </value>
+ [ConfigurationProperty(IncludeEventStatisticsAttributeName, DefaultValue = true)]
+ internal bool IncludeEventStatistics {
+ get { return (bool)this[IncludeEventStatisticsAttributeName]; }
+ set { this[IncludeEventStatisticsAttributeName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to include a few URLs to pages on the hosting
+ /// web site that host DotNetOpenAuth components.
+ /// </summary>
+ [ConfigurationProperty(IncludeLocalRequestUrisAttributeName, DefaultValue = true)]
+ internal bool IncludeLocalRequestUris {
+ get { return (bool)this[IncludeLocalRequestUrisAttributeName]; }
+ set { this[IncludeLocalRequestUrisAttributeName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to include the cultures requested by the user agent
+ /// on pages that host DotNetOpenAuth components.
+ /// </summary>
+ [ConfigurationProperty(IncludeCulturesAttributeName, DefaultValue = true)]
+ internal bool IncludeCultures {
+ get { return (bool)this[IncludeCulturesAttributeName]; }
+ set { this[IncludeCulturesAttributeName] = value; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs
index d928c87..000cb6a 100644
--- a/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs
+++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs
@@ -18,7 +18,8 @@ namespace DotNetOpenAuth.Configuration {
/// </summary>
/// <typeparam name="T">The type that all types specified in the elements must derive from.</typeparam>
[ContractVerification(true)]
- internal class TypeConfigurationCollection<T> : ConfigurationElementCollection {
+ internal class TypeConfigurationCollection<T> : ConfigurationElementCollection
+ where T : class {
/// <summary>
/// Initializes a new instance of the TypeConfigurationCollection class.
/// </summary>
@@ -30,8 +31,7 @@ namespace DotNetOpenAuth.Configuration {
/// </summary>
/// <param name="elements">The elements that should be added to the collection initially.</param>
internal TypeConfigurationCollection(IEnumerable<Type> elements) {
- Contract.Requires(elements != null);
- ErrorUtilities.VerifyArgumentNotNull(elements, "elements");
+ Contract.Requires<ArgumentNullException>(elements != null);
foreach (Type element in elements) {
this.BaseAdd(new TypeConfigurationElement<T> { TypeName = element.FullName });
@@ -70,7 +70,7 @@ namespace DotNetOpenAuth.Configuration {
protected override object GetElementKey(ConfigurationElement element) {
Contract.Assume(element != null); // this should be Contract.Requires in base class.
TypeConfigurationElement<T> typedElement = (TypeConfigurationElement<T>)element;
- return !string.IsNullOrEmpty(typedElement.TypeName) ? typedElement.TypeName : typedElement.XamlSource;
+ return (!string.IsNullOrEmpty(typedElement.TypeName) ? typedElement.TypeName : typedElement.XamlSource) ?? string.Empty;
}
}
}
diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
index 0c350cb..0335af5 100644
--- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
+++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Configuration {
using System;
using System.Configuration;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Reflection;
using System.Web;
@@ -18,7 +19,8 @@ namespace DotNetOpenAuth.Configuration {
/// the full type that provides some service used by this library.
/// </summary>
/// <typeparam name="T">A constraint on the type the user may provide.</typeparam>
- internal class TypeConfigurationElement<T> : ConfigurationElement {
+ internal class TypeConfigurationElement<T> : ConfigurationElement
+ where T : class {
/// <summary>
/// The name of the attribute whose value is the full name of the type the user is specifying.
/// </summary>
@@ -75,6 +77,8 @@ namespace DotNetOpenAuth.Configuration {
/// <param name="defaultValue">The value to return if no type is given in the .config file.</param>
/// <returns>The newly instantiated type.</returns>
public T CreateInstance(T defaultValue) {
+ Contract.Ensures(Contract.Result<T>() != null || Contract.Result<T>() == defaultValue);
+
return this.CreateInstance(defaultValue, false);
}
@@ -85,6 +89,8 @@ namespace DotNetOpenAuth.Configuration {
/// <param name="allowInternals">if set to <c>true</c> then internal types may be instantiated.</param>
/// <returns>The newly instantiated type.</returns>
public T CreateInstance(T defaultValue, bool allowInternals) {
+ Contract.Ensures(Contract.Result<T>() != null || Contract.Result<T>() == defaultValue);
+
if (this.CustomType != null) {
if (!allowInternals) {
// Although .NET will usually prevent our instantiating non-public types,
@@ -100,7 +106,7 @@ namespace DotNetOpenAuth.Configuration {
source = HttpContext.Current.Server.MapPath(source);
}
using (Stream xamlFile = File.OpenRead(source)) {
- return this.CreateInstanceFromXaml(xamlFile);
+ return CreateInstanceFromXaml(xamlFile);
}
} else {
return defaultValue;
@@ -119,7 +125,8 @@ namespace DotNetOpenAuth.Configuration {
/// XamlSource attribute is never used, the PresentationFramework.dll never need
/// be present.
/// </remarks>
- private T CreateInstanceFromXaml(Stream xaml) {
+ private static T CreateInstanceFromXaml(Stream xaml) {
+ Contract.Ensures(Contract.Result<T>() != null);
return (T)XamlReader.Load(xaml);
}
}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index d795b0b..cf3badb 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
+ <ProjectRoot Condition="'$(ProjectRoot)' == ''">$(MSBuildProjectDirectory)\..\..\</ProjectRoot>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ </PropertyGroup>
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" />
+ <PropertyGroup>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</ProjectGuid>
@@ -12,20 +16,25 @@
<AssemblyName>DotNetOpenAuth</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
+ <StandardCopyright>
+Copyright (c) 2009, Andrew Arnott. All rights reserved.
+Code licensed under the Ms-PL License:
+http://opensource.org/licenses/ms-pl.html
+</StandardCopyright>
+ <ApplicationIcon>DotNetOpenAuth.ico</ApplicationIcon>
+ <DocumentationFile>$(OutputPath)DotNetOpenAuth.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
- <OutputPath>..\..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <DocumentationFile>..\..\bin\Debug\DotNetOpenAuth.xml</DocumentationFile>
<RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
- <CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsCustomRewriterAssembly>
</CodeContractsCustomRewriterAssembly>
<CodeContractsCustomRewriterClass>
@@ -48,24 +57,27 @@
<CodeContractsShowSquigglies>True</CodeContractsShowSquigglies>
<CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
<CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
- <OutputPath>..\..\bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <DocumentationFile>..\..\bin\Release\DotNetOpenAuth.xml</DocumentationFile>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
- <CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsCustomRewriterAssembly>
</CodeContractsCustomRewriterAssembly>
<CodeContractsCustomRewriterClass>
</CodeContractsCustomRewriterClass>
- <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsRuntimeCheckingLevel>ReleaseRequires</CodeContractsRuntimeCheckingLevel>
<CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
<CodeContractsBuildReferenceAssembly>True</CodeContractsBuildReferenceAssembly>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
@@ -81,17 +93,17 @@
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Sign)' == 'true' ">
- <SignAssembly>true</SignAssembly>
- <AssemblyOriginatorKeyFile>..\official-build-key.pfx</AssemblyOriginatorKeyFile>
- <DefineConstants>$(DefineConstants);StrongNameSigned</DefineConstants>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
- <OutputPath>..\..\bin\CodeAnalysis\</OutputPath>
- <DefineConstants>CONTRACTS_FULL;DEBUG;TRACE</DefineConstants>
- <DocumentationFile>..\..\bin\CodeAnalysis\DotNetOpenAuth.xml</DocumentationFile>
+ <DefineConstants>$(DefineConstants);CONTRACTS_FULL;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
@@ -105,7 +117,7 @@
</CodeContractsCustomRewriterClass>
<CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
<CodeContractsRunCodeAnalysis>True</CodeContractsRunCodeAnalysis>
- <CodeContractsBuildReferenceAssembly>False</CodeContractsBuildReferenceAssembly>
+ <CodeContractsBuildReferenceAssembly>True</CodeContractsBuildReferenceAssembly>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
<CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
<CodeContractsLibPaths>
@@ -120,6 +132,13 @@
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>True</CodeContractsShowSquigglies>
<RunCodeAnalysis>true</RunCodeAnalysis>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
@@ -146,15 +165,27 @@
<Reference Include="System.IdentityModel.Selectors">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Runtime.Serialization">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
<Reference Include="System.ServiceModel">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.ServiceModel.Web">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\System.Web.Abstractions.dll</HintPath>
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Web.Extensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web.Extensions.Design">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
<Reference Include="System.Web.Mobile" />
<Reference Include="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -176,6 +207,7 @@
<ItemGroup>
<Compile Include="ComponentModel\ClaimTypeSuggestions.cs" />
<Compile Include="ComponentModel\ConverterBase.cs" />
+ <Compile Include="ComponentModel\SuggestedStringsConverterContract.cs" />
<Compile Include="ComponentModel\IssuersSuggestions.cs" />
<Compile Include="ComponentModel\IdentifierConverter.cs" />
<Compile Include="ComponentModel\SuggestedStringsConverter.cs" />
@@ -194,6 +226,7 @@
<Compile Include="Configuration\OpenIdProviderSecuritySettingsElement.cs" />
<Compile Include="Configuration\OpenIdRelyingPartyElement.cs" />
<Compile Include="Configuration\OpenIdRelyingPartySecuritySettingsElement.cs" />
+ <Compile Include="Configuration\ReportingElement.cs" />
<Compile Include="Configuration\TypeConfigurationCollection.cs" />
<Compile Include="Configuration\TypeConfigurationElement.cs" />
<Compile Include="Configuration\UntrustedWebRequestElement.cs" />
@@ -231,9 +264,11 @@
<Compile Include="Messaging\EmptyList.cs" />
<Compile Include="Messaging\ErrorUtilities.cs" />
<Compile Include="Messaging\IMessageWithEvents.cs" />
+ <Compile Include="Messaging\IncomingWebResponseContract.cs" />
<Compile Include="Messaging\IProtocolMessageWithExtensions.cs" />
<Compile Include="Messaging\InternalErrorException.cs" />
<Compile Include="Messaging\KeyedCollectionDelegate.cs" />
+ <Compile Include="Messaging\MultipartPostPart.cs" />
<Compile Include="Messaging\NetworkDirectWebResponse.cs" />
<Compile Include="Messaging\OutgoingWebResponseActionResult.cs" />
<Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
@@ -264,6 +299,7 @@
<Compile Include="OAuth\ConsumerSecuritySettings.cs" />
<Compile Include="OAuth\DesktopConsumer.cs" />
<Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="Messaging\IMessageWithBinaryData.cs" />
<Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" />
<Compile Include="Messaging\ChannelEventArgs.cs" />
<Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" />
@@ -353,6 +389,7 @@
<Compile Include="OpenId\ChannelElements\OpenIdMessageFactory.cs" />
<Compile Include="OpenId\ChannelElements\ReturnToSignatureBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\SkipSecurityBindingElement.cs" />
+ <Compile Include="OpenId\AssociationContract.cs" />
<Compile Include="OpenId\Extensions\AliasManager.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeRequest.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeValues.cs" />
@@ -390,15 +427,18 @@
<Compile Include="OpenId\Extensions\UI\UIUtilities.cs" />
<Compile Include="OpenId\Extensions\UI\UIModes.cs" />
<Compile Include="OpenId\Extensions\UI\UIRequest.cs" />
+ <Compile Include="OpenId\HostMetaDiscoveryService.cs" />
<Compile Include="OpenId\Identifier.cs" />
<Compile Include="OpenId\IdentifierContract.cs" />
<Compile Include="OpenId\Extensions\ExtensionsInteropHelper.cs" />
<Compile Include="OpenId\Interop\AuthenticationResponseShim.cs" />
<Compile Include="OpenId\Interop\ClaimsResponseShim.cs" />
<Compile Include="OpenId\Interop\OpenIdRelyingPartyShim.cs" />
+ <Compile Include="OpenId\IIdentifierDiscoveryService.cs" />
<Compile Include="OpenId\Messages\CheckAuthenticationRequest.cs" />
<Compile Include="OpenId\Messages\CheckAuthenticationResponse.cs" />
<Compile Include="OpenId\Messages\CheckIdRequest.cs" />
+ <Compile Include="OpenId\Messages\AssociateSuccessfulResponseContract.cs" />
<Compile Include="OpenId\Messages\IErrorMessage.cs" />
<Compile Include="OpenId\Messages\IndirectResponseBase.cs" />
<Compile Include="OpenId\Messages\IndirectSignedResponse.cs" />
@@ -461,8 +501,15 @@
<Compile Include="OpenId\RelyingParty\AssociationPreference.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequest.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequestMode.cs" />
+ <Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" />
+ <Compile Include="OpenId\RelyingParty\SelectorButtonContract.cs" />
+ <Compile Include="OpenId\RelyingParty\SelectorProviderButton.cs" />
+ <Compile Include="OpenId\RelyingParty\SelectorOpenIdButton.cs" />
+ <Compile Include="OpenId\RelyingParty\SelectorInfoCardButton.cs" />
<Compile Include="OpenId\RelyingParty\IRelyingPartyBehavior.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdSelector.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.cs" />
+ <Compile Include="OpenId\RelyingParty\IAuthenticationRequestContract.cs" />
<Compile Include="OpenId\RelyingParty\NegativeAuthenticationResponse.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdAjaxTextBox.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdButton.cs" />
@@ -478,9 +525,7 @@
<Compile Include="OpenId\RelyingParty\FailedAuthenticationResponse.cs" />
<Compile Include="OpenId\RelyingParty\IAuthenticationRequest.cs" />
<Compile Include="OpenId\RelyingParty\IAuthenticationResponse.cs" />
- <Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" />
<Compile Include="OpenId\RelyingParty\ISetupRequiredAuthenticationResponse.cs" />
- <Compile Include="OpenId\RelyingParty\IXrdsProviderEndpoint.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdRelyingParty.cs" />
<Compile Include="OpenId\OpenIdStrings.Designer.cs">
<DependentUpon>OpenIdStrings.resx</DependentUpon>
@@ -491,23 +536,28 @@
<Compile Include="OpenId\ProviderEndpointDescription.cs" />
<Compile Include="OpenId\Provider\ProviderSecuritySettings.cs" />
<Compile Include="OpenId\RelyingParty\IRelyingPartyApplicationStore.cs" />
- <Compile Include="OpenId\RelyingParty\AuthenticationResponseSnapshot.cs" />
+ <Compile Include="OpenId\RelyingParty\PositiveAuthenticationResponseSnapshot.cs" />
<Compile Include="OpenId\RelyingParty\PrivateSecretManager.cs" />
<Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettings.cs" />
- <Compile Include="OpenId\RelyingParty\ServiceEndpoint.cs" />
+ <Compile Include="OpenId\RelyingParty\SelectorButton.cs" />
+ <Compile Include="OpenId\IdentifierDiscoveryResult.cs" />
<Compile Include="OpenId\OpenIdXrdsHelper.cs" />
<Compile Include="OpenId\RelyingParty\SimpleXrdsProviderEndpoint.cs" />
<Compile Include="OpenId\RelyingParty\StandardRelyingPartyApplicationStore.cs" />
+ <Compile Include="OpenId\Behaviors\GsaIcamProfile.cs" />
<Compile Include="OpenId\RelyingParty\WellKnownProviders.cs" />
<Compile Include="OpenId\SecuritySettings.cs" />
<Compile Include="Messaging\UntrustedWebRequestHandler.cs" />
+ <Compile Include="OpenId\UriDiscoveryService.cs" />
<Compile Include="OpenId\UriIdentifier.cs" />
+ <Compile Include="OpenId\XriDiscoveryProxyService.cs" />
<Compile Include="OpenId\XriIdentifier.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="OAuth\Messages\UnauthorizedTokenRequest.cs" />
<Compile Include="OAuth\ChannelElements\RsaSha1SigningBindingElement.cs" />
<Compile Include="Messaging\StandardWebRequestHandler.cs" />
<Compile Include="Messaging\MessageReceivingEndpoint.cs" />
+ <Compile Include="Reporting.cs" />
<Compile Include="SimpleApiAuth\ChannelElements\SimpleAuthChannel.cs" />
<Compile Include="SimpleApiAuth\ChannelElements\SimpleAuthMessageFactory.cs" />
<Compile Include="SimpleApiAuth\ChannelElements\UriOrOutOfBandEncoding.cs" />
@@ -565,6 +615,8 @@
<None Include="Messaging\Bindings\Bindings.cd" />
<None Include="Messaging\Exceptions.cd" />
<None Include="Messaging\Messaging.cd" />
+ <None Include="OpenId\RelyingParty\Controls.cd" />
+ <None Include="OpenId\RelyingParty\OpenIdRelyingParty.cd" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Messaging\MessagingStrings.resx">
@@ -596,7 +648,9 @@
<EmbeddedResource Include="OpenId\RelyingParty\login_failure.png" />
<EmbeddedResource Include="OpenId\RelyingParty\login_success %28lock%29.png" />
<EmbeddedResource Include="OpenId\RelyingParty\login_success.png" />
- <EmbeddedResource Include="OpenId\RelyingParty\OpenIdAjaxTextBox.js" />
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdAjaxTextBox.js">
+ <Copyright>$(StandardCopyright)</Copyright>
+ </EmbeddedResource>
<EmbeddedResource Include="OpenId\RelyingParty\spinner.gif" />
</ItemGroup>
<ItemGroup>
@@ -617,22 +671,47 @@
<EmbeddedResource Include="InfoCard\infocard_71x50.png" />
<EmbeddedResource Include="InfoCard\infocard_81x57.png" />
<EmbeddedResource Include="InfoCard\infocard_92x64.png" />
- <EmbeddedResource Include="InfoCard\SupportingScript.js" />
+ <EmbeddedResource Include="InfoCard\SupportingScript.js">
+ <Copyright>$(StandardCopyright)</Copyright>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyControlBase.js" />
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyControlBase.js">
+ <Copyright>$(StandardCopyright)</Copyright>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
+ <EmbeddedResource Include="InfoCard\InfoCardStrings.sr.resx" />
+ <EmbeddedResource Include="Messaging\MessagingStrings.sr.resx" />
+ <EmbeddedResource Include="OAuth\OAuthStrings.sr.resx" />
<EmbeddedResource Include="OpenId\Behaviors\BehaviorStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>BehaviorStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
- <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.js" />
+ <EmbeddedResource Include="OpenId\Behaviors\BehaviorStrings.sr.resx" />
+ <EmbeddedResource Include="OpenId\OpenIdStrings.sr.resx" />
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.js">
+ <Copyright>$(StandardCopyright)</Copyright>
+ </EmbeddedResource>
<EmbeddedResource Include="SimpleApiAuth\SimpleAuthStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>SimpleAuthStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
+ <EmbeddedResource Include="Strings.sr.resx" />
+ <EmbeddedResource Include="Xrds\XrdsStrings.sr.resx" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdAjaxTextBox.css" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdSelector.js" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdSelector.css" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="DotNetOpenAuth.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
-</Project> \ No newline at end of file
+ <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" />
+</Project>
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.ico b/src/DotNetOpenAuth/DotNetOpenAuth.ico
new file mode 100644
index 0000000..e227dbe
--- /dev/null
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.ico
Binary files differ
diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs
index 07fabc3..e436846 100644
--- a/src/DotNetOpenAuth/GlobalSuppressions.cs
+++ b/src/DotNetOpenAuth/GlobalSuppressions.cs
@@ -44,3 +44,16 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.UI")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Reflection")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "oauthverifier", Scope = "resource", Target = "DotNetOpenAuth.OAuth.OAuthStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "whitelist", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "icam", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "idmanagement", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "no-pii", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "req", Scope = "member", Target = "DotNetOpenAuth.OpenId.Provider.IAuthenticationRequestContract.#DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest.ClaimedIdentifier")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "runat", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationRequest)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.OnIncomingPositiveAssertion(DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationResponse)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.RelyingParty.RelyingPartySecuritySettings)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnOutgoingResponse(DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnIncomingRequest(DotNetOpenAuth.OpenId.Provider.IRequest)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.Provider.ProviderSecuritySettings)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")]
diff --git a/src/DotNetOpenAuth/InfoCard/ClaimType.cs b/src/DotNetOpenAuth/InfoCard/ClaimType.cs
index 5a25ef8..9d3056a 100644
--- a/src/DotNetOpenAuth/InfoCard/ClaimType.cs
+++ b/src/DotNetOpenAuth/InfoCard/ClaimType.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.InfoCard {
using System;
using System.ComponentModel;
+ using System.Diagnostics.Contracts;
using System.IdentityModel.Claims;
using System.Web.UI;
@@ -15,6 +16,7 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
[PersistChildren(false)]
[Serializable]
+ [ContractVerification(true)]
public class ClaimType {
/// <summary>
/// Initializes a new instance of the <see cref="ClaimType"/> class.
@@ -47,7 +49,7 @@ namespace DotNetOpenAuth.InfoCard {
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public override string ToString() {
- return this.Name != null ? this.Name : null;
+ return this.Name ?? "<no name>";
}
}
}
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardImage.cs b/src/DotNetOpenAuth/InfoCard/InfoCardImage.cs
index 2b7b25f..247f461 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardImage.cs
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardImage.cs
@@ -23,6 +23,7 @@
namespace DotNetOpenAuth.InfoCard {
using System;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
/// <summary>
@@ -129,6 +130,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <returns>The manifest resource stream name.</returns>
internal static string GetImageManifestResourceStreamName(InfoCardImageSize size) {
string imageSize = size.ToString();
+ Contract.Assume(imageSize.Length >= 6);
imageSize = imageSize.Substring(4);
return String.Format(CultureInfo.InvariantCulture, UrlFormatString, imageSize);
}
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
index a17f1e0..86c1118 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
@@ -5,7 +5,7 @@
// </copyright>
//-----------------------------------------------------------------------
-[assembly: System.Web.UI.WebResource("DotNetOpenAuth.InfoCard.SupportingScript.js", "text/javascript")]
+[assembly: System.Web.UI.WebResource(DotNetOpenAuth.InfoCard.InfoCardSelector.ScriptResourceName, "text/javascript")]
namespace DotNetOpenAuth.InfoCard {
using System;
@@ -16,6 +16,7 @@ namespace DotNetOpenAuth.InfoCard {
using System.Drawing.Design;
using System.Globalization;
using System.Linq;
+ using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
@@ -48,6 +49,11 @@ namespace DotNetOpenAuth.InfoCard {
[ToolboxData("<{0}:InfoCardSelector runat=\"server\"><ClaimsRequested><{0}:ClaimType Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier\" /></ClaimsRequested><UnsupportedTemplate><p>Your browser does not support Information Cards.</p></UnsupportedTemplate></{0}:InfoCardSelector>")]
[ContractVerification(true)]
public class InfoCardSelector : CompositeControl, IPostBackEventHandler {
+ /// <summary>
+ /// The resource name for getting at the SupportingScript.js embedded manifest stream.
+ /// </summary>
+ internal const string ScriptResourceName = "DotNetOpenAuth.InfoCard.SupportingScript.js";
+
#region Property constants
/// <summary>
@@ -172,11 +178,6 @@ namespace DotNetOpenAuth.InfoCard {
#endregion
/// <summary>
- /// The resource name for getting at the SupportingScript.js embedded manifest stream.
- /// </summary>
- private const string ScriptResourceName = "DotNetOpenAuth.InfoCard.SupportingScript.js";
-
- /// <summary>
/// The panel containing the controls to display if InfoCard is supported in the user agent.
/// </summary>
private Panel infoCardSupportedPanel;
@@ -194,6 +195,14 @@ namespace DotNetOpenAuth.InfoCard {
private bool audienceSet;
/// <summary>
+ /// Initializes a new instance of the <see cref="InfoCardSelector"/> class.
+ /// </summary>
+ public InfoCardSelector() {
+ this.ToolTip = InfoCardStrings.SelectorClickPrompt;
+ Reporting.RecordFeatureUse(this);
+ }
+
+ /// <summary>
/// Occurs when an InfoCard has been submitted but not decoded yet.
/// </summary>
[Category(InfoCardCategory)]
@@ -214,7 +223,7 @@ namespace DotNetOpenAuth.InfoCard {
#region Properties
/// <summary>
- /// Gets the set of claims that are requested from the Information Card..
+ /// Gets the set of claims that are requested from the Information Card.
/// </summary>
[Description("Specifies the required and optional claims.")]
[PersistenceMode(PersistenceMode.InnerProperty), Category(InfoCardCategory)]
@@ -257,23 +266,28 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
[Description("The URL to this site's privacy policy.")]
[Category(InfoCardCategory), DefaultValue(PrivacyUrlDefault)]
+ [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "We construct a Uri to validate the format of the string.")]
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "That overload is NOT the same.")]
public string PrivacyUrl {
get {
return (string)this.ViewState[PrivacyUrlViewStateKey] ?? PrivacyUrlDefault;
}
set {
- if (this.Page != null && !this.DesignMode) {
- // Validate new value by trying to construct a Uri based on it.
- new Uri(new HttpRequestInfo(HttpContext.Current.Request).UrlBeforeRewriting, this.Page.ResolveUrl(value)); // throws an exception on failure.
- } else {
- // We can't fully test it, but it should start with either ~/ or a protocol.
- if (Regex.IsMatch(value, @"^https?://")) {
- new Uri(value); // make sure it's fully-qualified, but ignore wildcards
- } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
- // this is valid too
+ ErrorUtilities.VerifyOperation(string.IsNullOrEmpty(value) || this.Page == null || this.DesignMode || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired);
+ if (!string.IsNullOrEmpty(value)) {
+ if (this.Page != null && !this.DesignMode) {
+ // Validate new value by trying to construct a Uri based on it.
+ new Uri(new HttpRequestInfo(HttpContext.Current.Request).UrlBeforeRewriting, this.Page.ResolveUrl(value)); // throws an exception on failure.
} else {
- throw new UriFormatException();
+ // We can't fully test it, but it should start with either ~/ or a protocol.
+ if (Regex.IsMatch(value, @"^https?://")) {
+ new Uri(value); // make sure it's fully-qualified, but ignore wildcards
+ } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
+ // this is valid too
+ } else {
+ throw new UriFormatException();
+ }
}
}
@@ -404,7 +418,16 @@ namespace DotNetOpenAuth.InfoCard {
/// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
/// </summary>
/// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
- public void RaisePostBackEvent(string eventArgument) {
+ void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
+ this.RaisePostBackEvent(eventArgument);
+ }
+
+ /// <summary>
+ /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
+ /// </summary>
+ /// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Predefined signature.")]
+ protected virtual void RaisePostBackEvent(string eventArgument) {
if (!string.IsNullOrEmpty(this.TokenXml)) {
try {
ReceivingTokenEventArgs receivingArgs = this.OnReceivingToken(this.TokenXml);
@@ -430,8 +453,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <returns>The event arguments sent to the event handlers.</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "decryptor", Justification = "By design")]
protected virtual ReceivingTokenEventArgs OnReceivingToken(string tokenXml) {
- Contract.Requires(tokenXml != null);
- ErrorUtilities.VerifyArgumentNotNull(tokenXml, "tokenXml");
+ Contract.Requires<ArgumentNullException>(tokenXml != null);
var args = new ReceivingTokenEventArgs(tokenXml);
var receivingToken = this.ReceivingToken;
@@ -447,8 +469,7 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
/// <param name="token">The token, if it was decrypted.</param>
protected virtual void OnReceivedToken(Token token) {
- Contract.Requires(token != null);
- ErrorUtilities.VerifyArgumentNotNull(token, "token");
+ Contract.Requires<ArgumentNullException>(token != null);
var receivedInfoCard = this.ReceivedToken;
if (receivedInfoCard != null) {
@@ -462,8 +483,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="unprocessedToken">The unprocessed token.</param>
/// <param name="ex">The exception generated while processing the token.</param>
protected virtual void OnTokenProcessingError(string unprocessedToken, Exception ex) {
- Contract.Requires(unprocessedToken != null);
- Contract.Requires(ex != null);
+ Contract.Requires<ArgumentNullException>(unprocessedToken != null);
+ Contract.Requires<ArgumentNullException>(ex != null);
var tokenProcessingError = this.TokenProcessingError;
if (tokenProcessingError != null) {
@@ -514,6 +535,8 @@ namespace DotNetOpenAuth.InfoCard {
// the privacy URL is present but the privacy version is not.
ErrorUtilities.VerifyOperation(string.IsNullOrEmpty(this.PrivacyUrl) || !string.IsNullOrEmpty(this.PrivacyVersion), InfoCardStrings.PrivacyVersionRequiredWithPrivacyUrl);
}
+
+ this.RegisterInfoCardSelectorObjectScript();
}
/// <summary>
@@ -522,12 +545,18 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns>The control that renders to the Param tag.</returns>
- private static Control CreateParam(string name, string value) {
- Contract.Ensures(Contract.Result<Control>() != null);
- HtmlGenericControl control = new HtmlGenericControl(HtmlTextWriterTag.Param.ToString());
- control.Attributes.Add(HtmlTextWriterAttribute.Name.ToString(), name);
- control.Attributes.Add(HtmlTextWriterAttribute.Value.ToString(), value);
- return control;
+ private static string CreateParamJs(string name, string value) {
+ Contract.Ensures(Contract.Result<string>() != null);
+ string scriptFormat = @" objp = document.createElement('param');
+ objp.name = {0};
+ objp.value = {1};
+ obj.appendChild(objp);
+";
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ scriptFormat,
+ MessagingUtilities.GetSafeJavascriptValue(name),
+ MessagingUtilities.GetSafeJavascriptValue(value));
}
/// <summary>
@@ -547,17 +576,7 @@ namespace DotNetOpenAuth.InfoCard {
supportedPanel.Style[HtmlTextWriterStyle.Display] = "none";
}
- supportedPanel.Controls.Add(this.CreateInfoCardSelectorObject());
-
- // add clickable image
- Image image = new Image();
- image.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(typeof(InfoCardSelector), InfoCardImage.GetImageManifestResourceStreamName(this.ImageSize));
- image.AlternateText = InfoCardStrings.SelectorClickPrompt;
- image.ToolTip = InfoCardStrings.SelectorClickPrompt;
- image.Style[HtmlTextWriterStyle.Cursor] = "hand";
-
- image.Attributes["onclick"] = this.GetInfoCardSelectorActivationScript(false);
- supportedPanel.Controls.Add(image);
+ supportedPanel.Controls.Add(this.CreateInfoCardImage());
// trigger the selector at page load?
if (this.AutoPopup && !this.Page.IsPostBack) {
@@ -612,47 +631,74 @@ namespace DotNetOpenAuth.InfoCard {
}
/// <summary>
- /// Creates the info card selector &lt;object&gt; HTML tag.
+ /// Adds the javascript that adds the info card selector &lt;object&gt; HTML tag to the page.
/// </summary>
- /// <returns>A control that renders to the &lt;object&gt; tag.</returns>
[Pure]
- private Control CreateInfoCardSelectorObject() {
- HtmlGenericControl cardSpaceControl = new HtmlGenericControl(HtmlTextWriterTag.Object.ToString());
- cardSpaceControl.Attributes.Add(HtmlTextWriterAttribute.Type.ToString(), "application/x-informationcard");
- cardSpaceControl.Attributes.Add(HtmlTextWriterAttribute.Id.ToString(), this.ClientID + "_cs");
+ private void RegisterInfoCardSelectorObjectScript() {
+ string scriptFormat = @"{{
+ var obj = document.createElement('object');
+ obj.type = 'application/x-informationcard';
+ obj.id = {0};
+ obj.style.display = 'none';
+";
+ StringBuilder script = new StringBuilder();
+ script.AppendFormat(
+ CultureInfo.InvariantCulture,
+ scriptFormat,
+ MessagingUtilities.GetSafeJavascriptValue(this.ClientID + "_cs"));
if (!string.IsNullOrEmpty(this.Issuer)) {
- cardSpaceControl.Controls.Add(CreateParam("issuer", this.Issuer));
+ script.AppendLine(CreateParamJs("issuer", this.Issuer));
}
if (!string.IsNullOrEmpty(this.IssuerPolicy)) {
- cardSpaceControl.Controls.Add(CreateParam("issuerPolicy", this.IssuerPolicy));
+ script.AppendLine(CreateParamJs("issuerPolicy", this.IssuerPolicy));
}
if (!string.IsNullOrEmpty(this.TokenType)) {
- cardSpaceControl.Controls.Add(CreateParam("tokenType", this.TokenType));
+ script.AppendLine(CreateParamJs("tokenType", this.TokenType));
}
string requiredClaims, optionalClaims;
this.GetRequestedClaims(out requiredClaims, out optionalClaims);
ErrorUtilities.VerifyArgument(!string.IsNullOrEmpty(requiredClaims) || !string.IsNullOrEmpty(optionalClaims), InfoCardStrings.EmptyClaimListNotAllowed);
if (!string.IsNullOrEmpty(requiredClaims)) {
- cardSpaceControl.Controls.Add(CreateParam("requiredClaims", requiredClaims));
+ script.AppendLine(CreateParamJs("requiredClaims", requiredClaims));
}
if (!string.IsNullOrEmpty(optionalClaims)) {
- cardSpaceControl.Controls.Add(CreateParam("optionalClaims", optionalClaims));
+ script.AppendLine(CreateParamJs("optionalClaims", optionalClaims));
}
if (!string.IsNullOrEmpty(this.PrivacyUrl)) {
string privacyUrl = this.DesignMode ? this.PrivacyUrl : new Uri(Page.Request.Url, Page.ResolveUrl(this.PrivacyUrl)).AbsoluteUri;
- cardSpaceControl.Controls.Add(CreateParam("privacyUrl", privacyUrl));
+ script.AppendLine(CreateParamJs("privacyUrl", privacyUrl));
}
if (!string.IsNullOrEmpty(this.PrivacyVersion)) {
- cardSpaceControl.Controls.Add(CreateParam("privacyVersion", this.PrivacyVersion));
+ script.AppendLine(CreateParamJs("privacyVersion", this.PrivacyVersion));
}
- return cardSpaceControl;
+ script.AppendLine(@"if (document.infoCard.isSupported()) { document.write(obj.outerHTML); }
+}");
+
+ this.Page.ClientScript.RegisterClientScriptBlock(typeof(InfoCardSelector), this.ClientID + "tag", script.ToString(), true);
+ }
+
+ /// <summary>
+ /// Creates the info card clickable image.
+ /// </summary>
+ /// <returns>An Image object.</returns>
+ [Pure]
+ private Image CreateInfoCardImage() {
+ // add clickable image
+ Image image = new Image();
+ image.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(typeof(InfoCardSelector), InfoCardImage.GetImageManifestResourceStreamName(this.ImageSize));
+ image.AlternateText = InfoCardStrings.SelectorClickPrompt;
+ image.ToolTip = this.ToolTip;
+ image.Style[HtmlTextWriterStyle.Cursor] = "hand";
+
+ image.Attributes["onclick"] = this.GetInfoCardSelectorActivationScript(false);
+ return image;
}
/// <summary>
@@ -663,7 +709,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="optional">A space-delimited list of claim type URIs for claims that may optionally be included in a submitted Information Card.</param>
[Pure]
private void GetRequestedClaims(out string required, out string optional) {
- Contract.Requires(this.ClaimsRequested != null);
+ Contract.Requires<InvalidOperationException>(this.ClaimsRequested != null);
Contract.Ensures(Contract.ValueAtReturn<string>(out required) != null);
Contract.Ensures(Contract.ValueAtReturn<string>(out optional) != null);
@@ -678,10 +724,10 @@ namespace DotNetOpenAuth.InfoCard {
string[] requiredClaimsArray = requiredClaims.ToArray();
string[] optionalClaimsArray = optionalClaims.ToArray();
- Contract.Assume(requiredClaimsArray != null);
- Contract.Assume(optionalClaimsArray != null);
required = string.Join(" ", requiredClaimsArray);
optional = string.Join(" ", optionalClaimsArray);
+ Contract.Assume(required != null);
+ Contract.Assume(optional != null);
}
/// <summary>
@@ -689,7 +735,7 @@ namespace DotNetOpenAuth.InfoCard {
/// or to downgrade gracefully if the user agent lacks an Information Card selector.
/// </summary>
private void RenderSupportingScript() {
- Contract.Requires(this.infoCardSupportedPanel != null);
+ Contract.Requires<InvalidOperationException>(this.infoCardSupportedPanel != null);
this.Page.ClientScript.RegisterClientScriptResource(typeof(InfoCardSelector), ScriptResourceName);
@@ -708,4 +754,4 @@ namespace DotNetOpenAuth.InfoCard {
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardStrings.sr.resx b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.sr.resx
new file mode 100644
index 0000000..9df0429
--- /dev/null
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardStrings.sr.resx
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="AudienceMismatch" xml:space="preserve">
+ <value>Token je neispravan: restrikcije u prijemu se ne slažu sa Relying Party.</value>
+ </data>
+ <data name="EmptyClaimListNotAllowed" xml:space="preserve">
+ <value>Tražena lista zahteva za uključivanje u InfoCard ne sme biti prazna.</value>
+ </data>
+ <data name="EncryptionAlgorithmNotFound" xml:space="preserve">
+ <value>encryptionAlgorithm nije pronađen.</value>
+ </data>
+ <data name="PpidClaimRequired" xml:space="preserve">
+ <value>Ova operacija zahteva da PPID zahtev bude uključen u InfoCard token.</value>
+ </data>
+ <data name="SelectorClickPrompt" xml:space="preserve">
+ <value>Kliknite ovde da odaberete vaš Information Card.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs b/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs
index 1511e2d..f325ff9 100644
--- a/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs
+++ b/src/DotNetOpenAuth/InfoCard/ReceivedTokenEventArgs.cs
@@ -31,9 +31,10 @@ namespace DotNetOpenAuth.InfoCard {
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
- protected void ObjectInvariant() {
+ private void ObjectInvariant() {
Contract.Invariant(this.Token != null);
}
#endif
diff --git a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs
index f3722d7..124f9f8 100644
--- a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs
+++ b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs
@@ -21,7 +21,7 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
/// <param name="tokenXml">The raw token XML, prior to any decryption.</param>
internal ReceivingTokenEventArgs(string tokenXml) {
- Contract.Requires(tokenXml != null);
+ Contract.Requires<ArgumentNullException>(tokenXml != null);
this.TokenXml = tokenXml;
this.IsEncrypted = Token.IsEncrypted(this.TokenXml);
@@ -63,7 +63,7 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
/// <param name="securityToken">The security token.</param>
public void AddDecryptingToken(SecurityToken securityToken) {
- Contract.Requires(securityToken != null);
+ Contract.Requires<ArgumentNullException>(securityToken != null);
this.DecryptingTokens.Add(securityToken);
}
@@ -72,8 +72,8 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
/// <param name="certificate">The certificate.</param>
public void AddDecryptingToken(X509Certificate2 certificate) {
- Contract.Requires(certificate != null);
- Contract.Requires(certificate.HasPrivateKey);
+ Contract.Requires<ArgumentNullException>(certificate != null);
+ Contract.Requires<ArgumentException>(certificate.HasPrivateKey);
this.AddDecryptingToken(new X509SecurityToken(certificate));
}
@@ -81,9 +81,10 @@ namespace DotNetOpenAuth.InfoCard {
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
- protected void ObjectInvariant() {
+ private void ObjectInvariant() {
Contract.Invariant(this.TokenXml != null);
Contract.Invariant(this.DecryptingTokens != null);
}
diff --git a/src/DotNetOpenAuth/InfoCard/SupportingScript.js b/src/DotNetOpenAuth/InfoCard/SupportingScript.js
index ce4b02d..a883cd7 100644
--- a/src/DotNetOpenAuth/InfoCard/SupportingScript.js
+++ b/src/DotNetOpenAuth/InfoCard/SupportingScript.js
@@ -1,5 +1,7 @@
+/*jslint white: true, onevar: true, browser: true, undef: true, nomen: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true */
+"use strict";
document.infoCard = {
- isSupported: function() {
+ isSupported: function () {
/// <summary>
/// Determines if information cards are supported by the
/// browser.
@@ -7,19 +9,20 @@ document.infoCard = {
/// <returns>
/// true-if the browser supports information cards.
///</returns>
+ var IEVer, embed, x, event;
- var IEVer = -1;
- if (navigator.appName == 'Microsoft Internet Explorer') {
- if (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) {
+ IEVer = -1;
+ if (navigator.appName === 'Microsoft Internet Explorer') {
+ if (new RegExp("MSIE ([0-9]{1,}[\\.0-9]{0,})").exec(navigator.userAgent) !== null) {
IEVer = parseFloat(RegExp.$1);
}
}
// Look for IE 7+.
if (IEVer >= 7) {
- var embed = document.createElement("object");
+ embed = document.createElement("object");
embed.type = "application/x-informationcard";
- return !(embed.issuerPolicy === undefined) && embed.isInstalled;
+ return embed.issuerPolicy !== undefined && embed.isInstalled;
}
// not IE (any version)
@@ -32,22 +35,23 @@ document.infoCard = {
// check for the IdentitySelector event handler is there.
if (document.addEventListener) {
- var event = document.createEvent("Events");
+ event = document.createEvent("Events");
event.initEvent("IdentitySelectorAvailable", true, true);
top.dispatchEvent(event);
- if (top.IdentitySelectorAvailable == true) {
+ if (top.IdentitySelectorAvailable === true) {
return true;
}
}
}
-
+
return false;
},
- activate: function(selectorId, hiddenFieldName) {
- var selector = document.getElementById(selectorId);
- var hiddenField = document.getElementsByName(hiddenFieldName)[0];
+ activate: function (selectorId, hiddenFieldName) {
+ var selector, hiddenField;
+ selector = document.getElementById(selectorId);
+ hiddenField = document.getElementsByName(hiddenFieldName)[0];
try {
hiddenField.value = selector.value;
} catch (e) {
@@ -71,7 +75,7 @@ document.infoCard = {
}
},
- showStatic: function(divName) {
+ showStatic: function (divName) {
var div = document.getElementById(divName);
if (div) {
div.style.visibility = 'visible';
@@ -95,26 +99,26 @@ document.infoCard = {
checkDynamic: function (controlDiv, unsupportedDiv) {
if (this.isSupported()) {
this.showDynamic(controlDiv);
- if (unsupportedDiv != '') {
+ if (unsupportedDiv) {
this.hideDynamic(unsupportedDiv);
}
} else {
this.hideDynamic(controlDiv);
- if (unsupportedDiv != '') {
+ if (unsupportedDiv) {
this.showDynamic(unsupportedDiv);
}
}
},
- checkStatic: function(controlDiv, unsupportedDiv) {
+ checkStatic: function (controlDiv, unsupportedDiv) {
if (this.isSupported()) {
this.showStatic(controlDiv);
- if (unsupportedDiv != '') {
+ if (unsupportedDiv) {
this.hideStatic(unsupportedDiv);
}
} else {
this.hideStatic(controlDiv);
- if (unsupportedDiv != '') {
+ if (unsupportedDiv) {
this.showDynamic(unsupportedDiv);
}
}
diff --git a/src/DotNetOpenAuth/InfoCard/Token/Token.cs b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
index 5c955ed..7fa9a95 100644
--- a/src/DotNetOpenAuth/InfoCard/Token/Token.cs
+++ b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
@@ -42,19 +42,20 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="decryptor">The decryptor to use to decrypt the token, if necessary..</param>
/// <exception cref="InformationCardException">Thrown for any problem decoding or decrypting the token.</exception>
private Token(string tokenXml, Uri audience, TokenDecryptor decryptor) {
- Contract.Requires(tokenXml != null && tokenXml.Length > 0);
- Contract.Requires(decryptor != null || !IsEncrypted(tokenXml));
- ErrorUtilities.VerifyNonZeroLength(tokenXml, "tokenXml");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(tokenXml));
+ Contract.Requires<ArgumentException>(decryptor != null || !IsEncrypted(tokenXml));
+ Contract.Ensures(this.AuthorizationContext != null);
byte[] decryptedBytes;
string decryptedString;
using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) {
+ Contract.Assume(tokenReader != null); // BCL contract should say XmlReader.Create result != null
if (IsEncrypted(tokenReader)) {
Logger.InfoCard.DebugFormat("Incoming SAML token, before decryption: {0}", tokenXml);
- ErrorUtilities.VerifyArgumentNotNull(decryptor, "decryptor");
decryptedBytes = decryptor.DecryptToken(tokenReader);
decryptedString = Encoding.UTF8.GetString(decryptedBytes);
+ Contract.Assume(decryptedString != null); // BCL contracts should be enhanced here
} else {
decryptedBytes = Encoding.UTF8.GetBytes(tokenXml);
decryptedString = tokenXml;
@@ -106,7 +107,7 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
public string SiteSpecificId {
get {
- Contract.Requires(this.Claims.ContainsKey(ClaimTypes.PPID));
+ Contract.Requires<InvalidOperationException>(this.Claims.ContainsKey(ClaimTypes.PPID) && !string.IsNullOrEmpty(this.Claims[ClaimTypes.PPID]));
string ppidValue;
ErrorUtilities.VerifyOperation(this.Claims.TryGetValue(ClaimTypes.PPID, out ppidValue) && ppidValue != null, InfoCardStrings.PpidClaimRequired);
return TokenUtility.CalculateSiteSpecificID(ppidValue);
@@ -132,7 +133,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="tokenXml">The token XML.</param>
/// <returns>The deserialized token.</returns>
public static Token Read(string tokenXml) {
- Contract.Requires(!String.IsNullOrEmpty(tokenXml));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(tokenXml));
return Read(tokenXml, (Uri)null);
}
@@ -143,7 +144,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="audience">The URI that this token must have been crafted to be sent to. Use <c>null</c> to accept any intended audience.</param>
/// <returns>The deserialized token.</returns>
public static Token Read(string tokenXml, Uri audience) {
- Contract.Requires(!String.IsNullOrEmpty(tokenXml));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(tokenXml));
return Read(tokenXml, audience, Enumerable.Empty<SecurityToken>());
}
@@ -154,8 +155,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="decryptionTokens">Any X.509 certificates that may be used to decrypt the token, if necessary.</param>
/// <returns>The deserialized token.</returns>
public static Token Read(string tokenXml, IEnumerable<SecurityToken> decryptionTokens) {
- Contract.Requires(!String.IsNullOrEmpty(tokenXml));
- Contract.Requires(decryptionTokens != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(tokenXml));
+ Contract.Requires<ArgumentNullException>(decryptionTokens != null);
return Read(tokenXml, null, decryptionTokens);
}
@@ -167,8 +168,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="decryptionTokens">Any X.509 certificates that may be used to decrypt the token, if necessary.</param>
/// <returns>The deserialized token.</returns>
public static Token Read(string tokenXml, Uri audience, IEnumerable<SecurityToken> decryptionTokens) {
- Contract.Requires(!String.IsNullOrEmpty(tokenXml));
- Contract.Requires(decryptionTokens != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(tokenXml));
+ Contract.Requires<ArgumentNullException>(decryptionTokens != null);
Contract.Ensures(Contract.Result<Token>() != null);
TokenDecryptor decryptor = null;
@@ -190,10 +191,10 @@ namespace DotNetOpenAuth.InfoCard {
/// </returns>
[Pure]
internal static bool IsEncrypted(string tokenXml) {
- Contract.Requires(tokenXml != null);
- ErrorUtilities.VerifyArgumentNotNull(tokenXml, "tokenXml");
+ Contract.Requires<ArgumentNullException>(tokenXml != null);
using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) {
+ Contract.Assume(tokenReader != null); // CC missing for XmlReader.Create
return IsEncrypted(tokenReader);
}
}
@@ -206,11 +207,22 @@ namespace DotNetOpenAuth.InfoCard {
/// <c>true</c> if the specified token XML is encrypted; otherwise, <c>false</c>.
/// </returns>
private static bool IsEncrypted(XmlReader tokenXmlReader) {
- Contract.Requires(tokenXmlReader != null);
- ErrorUtilities.VerifyArgumentNotNull(tokenXmlReader, "tokenXmlReader");
+ Contract.Requires<ArgumentNullException>(tokenXmlReader != null);
return tokenXmlReader.IsStartElement(TokenDecryptor.XmlEncryptionStrings.EncryptedData, TokenDecryptor.XmlEncryptionStrings.Namespace);
}
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.AuthorizationContext != null);
+ }
+#endif
+
/// <summary>
/// Flattens the claims into a dictionary
/// </summary>
diff --git a/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs b/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs
index 1038ad7..9424480 100644
--- a/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs
+++ b/src/DotNetOpenAuth/InfoCard/Token/TokenDecryptor.cs
@@ -89,9 +89,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="reader">The encrypted token XML reader.</param>
/// <returns>A byte array of the contents of the encrypted token</returns>
internal byte[] DecryptToken(XmlReader reader) {
- Contract.Requires(reader != null);
+ Contract.Requires<ArgumentNullException>(reader != null);
Contract.Ensures(Contract.Result<byte[]>() != null);
- ErrorUtilities.VerifyArgumentNotNull(reader, "reader");
byte[] securityTokenData;
string encryptionAlgorithm;
@@ -148,9 +147,10 @@ namespace DotNetOpenAuth.InfoCard {
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
- protected void ObjectInvariant() {
+ private void ObjectInvariant() {
Contract.Invariant(this.Tokens != null);
}
#endif
diff --git a/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs b/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs
index 85b951d..48b7794 100644
--- a/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs
+++ b/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs
@@ -48,6 +48,8 @@ namespace DotNetOpenAuth.InfoCard {
/// The authorization context carried by the token.
/// </returns>
internal static AuthorizationContext AuthenticateToken(XmlReader reader, Uri audience) {
+ Contract.Ensures(Contract.Result<AuthorizationContext>() != null);
+
// Extensibility Point:
// in order to accept different token types, you would need to add additional
// code to create an authenticationcontext from the security token.
@@ -160,8 +162,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="authzContext">The Authorization Context</param>
/// <returns>A unique ID for this user at this web site.</returns>
internal static string GetUniqueName(AuthorizationContext authzContext) {
- Contract.Requires(authzContext != null);
- ErrorUtilities.VerifyArgumentNotNull(authzContext, "authzContext");
+ Contract.Requires<ArgumentNullException>(authzContext != null);
Claim uniqueIssuerClaim = null;
Claim uniqueUserClaim = null;
@@ -217,9 +218,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="ppid">The personal private identifier.</param>
/// <returns>A string containing the XXX-XXXX-XXX cosmetic value.</returns>
internal static string CalculateSiteSpecificID(string ppid) {
- Contract.Requires(ppid != null);
- ErrorUtilities.VerifyArgumentNotNull(ppid, "ppid");
- Contract.Ensures(Contract.Result<string>() != null && Contract.Result<string>().Length > 0);
+ Contract.Requires<ArgumentNullException>(ppid != null);
+ Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
int callSignChars = 10;
char[] charMap = "QL23456789ABCDEFGHJKMNPRSTUVWXYZ".ToCharArray();
@@ -246,8 +246,7 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="cs">the claimset which contains the claim</param>
/// <returns>a RSA claim</returns>
private static Claim GetUniqueRsaClaim(ClaimSet cs) {
- Contract.Requires(cs != null);
- ErrorUtilities.VerifyArgumentNotNull(cs, "cs");
+ Contract.Requires<ArgumentNullException>(cs != null);
Claim rsa = null;
@@ -269,11 +268,9 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="claimValue">the claim value to hash with.</param>
/// <returns>A base64 representation of the combined ID.</returns>
private static string ComputeCombinedId(RSA issuerKey, string claimValue) {
- Contract.Requires(issuerKey != null);
- Contract.Requires(claimValue != null);
+ Contract.Requires<ArgumentNullException>(issuerKey != null);
+ Contract.Requires<ArgumentNullException>(claimValue != null);
Contract.Ensures(Contract.Result<string>() != null);
- ErrorUtilities.VerifyArgumentNotNull(issuerKey, "issuerKey");
- ErrorUtilities.VerifyArgumentNotNull(claimValue, "claimValue");
int nameLength = Encoding.UTF8.GetByteCount(claimValue);
RSAParameters rsaParams = issuerKey.ExportParameters(false);
diff --git a/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs b/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs
index 1132ac0..0f17b63 100644
--- a/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs
+++ b/src/DotNetOpenAuth/InfoCard/TokenProcessingErrorEventArgs.cs
@@ -18,8 +18,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="tokenXml">The token XML.</param>
/// <param name="exception">The exception.</param>
internal TokenProcessingErrorEventArgs(string tokenXml, Exception exception) {
- Contract.Requires(tokenXml != null);
- Contract.Requires(exception != null);
+ Contract.Requires<ArgumentNullException>(tokenXml != null);
+ Contract.Requires<ArgumentNullException>(exception != null);
this.TokenXml = tokenXml;
this.Exception = exception;
}
@@ -38,9 +38,10 @@ namespace DotNetOpenAuth.InfoCard {
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
- protected void ObjectInvariant() {
+ private void ObjectInvariant() {
Contract.Invariant(this.TokenXml != null);
Contract.Invariant(this.Exception != null);
}
diff --git a/src/DotNetOpenAuth/Logger.cs b/src/DotNetOpenAuth/Logger.cs
index 6015df5..48007ed 100644
--- a/src/DotNetOpenAuth/Logger.cs
+++ b/src/DotNetOpenAuth/Logger.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth {
using System;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using DotNetOpenAuth.Loggers;
using DotNetOpenAuth.Messaging;
@@ -59,6 +60,11 @@ namespace DotNetOpenAuth {
private static readonly ILog http = Create("DotNetOpenAuth.Http");
/// <summary>
+ /// Backing field for the <see cref="Controls"/> property.
+ /// </summary>
+ private static readonly ILog controls = Create("DotNetOpenAuth.Controls");
+
+ /// <summary>
/// Backing field for the <see cref="OpenId"/> property.
/// </summary>
private static readonly ILog openId = Create("DotNetOpenAuth.OpenId");
@@ -109,6 +115,11 @@ namespace DotNetOpenAuth {
internal static ILog Http { get { return http; } }
/// <summary>
+ /// Gets the logger for events logged by ASP.NET controls.
+ /// </summary>
+ internal static ILog Controls { get { return controls; } }
+
+ /// <summary>
/// Gets the logger for high-level OpenID events.
/// </summary>
internal static ILog OpenId { get { return openId; } }
@@ -131,7 +142,7 @@ namespace DotNetOpenAuth {
/// <param name="name">A name that will be included in the log file.</param>
/// <returns>The <see cref="ILog"/> instance created with the given name.</returns>
internal static ILog Create(string name) {
- ErrorUtilities.VerifyNonZeroLength(name, "name");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(name));
return InitializeFacade(name);
}
@@ -142,6 +153,7 @@ namespace DotNetOpenAuth {
/// <param name="name">A name that will be included in the log file.</param>
/// <returns>The <see cref="ILog"/> instance created with the given name.</returns>
internal static ILog CreateWithBanner(string name) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(name));
ILog log = Create(name);
log.Info(Util.LibraryVersion);
return log;
@@ -153,9 +165,7 @@ namespace DotNetOpenAuth {
/// <param name="type">A type whose full name that will be included in the log file.</param>
/// <returns>The <see cref="ILog"/> instance created with the given type name.</returns>
internal static ILog Create(Type type) {
- if (type == null) {
- throw new ArgumentNullException("type");
- }
+ Contract.Requires<ArgumentNullException>(type != null);
return Create(type.FullName);
}
diff --git a/src/DotNetOpenAuth/Loggers/ILog.cs b/src/DotNetOpenAuth/Loggers/ILog.cs
index 4ddbd49..8094296 100644
--- a/src/DotNetOpenAuth/Loggers/ILog.cs
+++ b/src/DotNetOpenAuth/Loggers/ILog.cs
@@ -21,7 +21,7 @@
// This interface is designed to look like log4net's ILog interface.
// We have this as a facade in front of it to avoid crashing if the
// hosting web site chooses not to deploy log4net.dll along with
-// dotnetopenid.dll.
+// DotNetOpenAuth.dll.
namespace DotNetOpenAuth.Loggers
{
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs b/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
index 417c98e..73ce289 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Messaging.Bindings {
using System;
+ using System.Diagnostics.Contracts;
using System.Globalization;
/// <summary>
@@ -20,6 +21,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <param name="faultedMessage">The expired message.</param>
public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
: base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToLocalTime(), DateTime.Now), faultedMessage) {
+ Contract.Requires<ArgumentException>(utcExpirationDate.Kind == DateTimeKind.Utc);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs b/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs
index fff251a..6b6e2e1 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/INonceStore.cs
@@ -19,11 +19,12 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// The context SHOULD be treated as case-sensitive.
/// The value will never be <c>null</c> but may be the empty string.</param>
/// <param name="nonce">A series of random characters.</param>
- /// <param name="timestamp">The timestamp that together with the nonce string make it unique.
+ /// <param name="timestampUtc">The UTC timestamp that together with the nonce string make it unique
+ /// within the given <paramref name="context"/>.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
/// <returns>
- /// True if the nonce+timestamp (combination) was not previously in the database.
- /// False if the nonce was stored previously with the same timestamp.
+ /// True if the context+nonce+timestamp (combination) was not previously in the database.
+ /// False if the nonce was stored previously with the same timestamp and context.
/// </returns>
/// <remarks>
/// The nonce must be stored for no less than the maximum time window a message may
@@ -33,6 +34,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// property, accessible via the <see cref="DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration"/>
/// property.
/// </remarks>
- bool StoreNonce(string context, string nonce, DateTime timestamp);
+ bool StoreNonce(string context, string nonce, DateTime timestampUtc);
}
}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs b/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs
index 3d624a6..6e64acc 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/NonceMemoryStore.cs
@@ -47,6 +47,13 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <summary>
/// Initializes a new instance of the <see cref="NonceMemoryStore"/> class.
/// </summary>
+ internal NonceMemoryStore()
+ : this(StandardExpirationBindingElement.MaximumMessageAge) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NonceMemoryStore"/> class.
+ /// </summary>
/// <param name="maximumMessageAge">The maximum age a message can be before it is discarded.</param>
internal NonceMemoryStore(TimeSpan maximumMessageAge) {
this.maximumMessageAge = maximumMessageAge;
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
index 7b00e34..ddfa88a 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Messaging.Bindings {
using System;
+ using DotNetOpenAuth.Configuration;
/// <summary>
/// A message expiration enforcing binding element that supports messages
@@ -13,11 +14,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// </summary>
internal class StandardExpirationBindingElement : IChannelBindingElement {
/// <summary>
- /// The default maximum message age to use if the default constructor is called.
- /// </summary>
- internal static readonly TimeSpan DefaultMaximumMessageAge = TimeSpan.FromMinutes(13);
-
- /// <summary>
/// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class.
/// </summary>
internal StandardExpirationBindingElement() {
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
index c8d5873..bb56cfd 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging.Bindings {
using System;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
/// <summary>
/// A binding element that checks/verifies a nonce message part.
@@ -41,7 +42,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
/// <param name="allowEmptyNonces">A value indicating whether zero-length nonces will be allowed.</param>
internal StandardReplayProtectionBindingElement(INonceStore nonceStore, bool allowEmptyNonces) {
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
+ Contract.Requires<ArgumentNullException>(nonceStore != null);
this.nonceStore = nonceStore;
this.AllowZeroLengthNonce = allowEmptyNonces;
@@ -124,6 +125,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
ErrorUtilities.VerifyProtocol(nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce, MessagingStrings.InvalidNonceReceived);
if (!this.nonceStore.StoreNonce(nonceMessage.NonceContext, nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
+ Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", nonceMessage.Nonce, nonceMessage.UtcCreationDate);
throw new ReplayedMessageException(message);
}
diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
index 65cbba1..dd34d90 100644
--- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
@@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Cached details on the response from a direct web request to a remote party.
/// </summary>
+ [ContractVerification(true)]
[DebuggerDisplay("{Status} {ContentType.MediaType}, length: {ResponseStream.Length}")]
internal class CachedDirectWebResponse : IncomingWebResponse {
/// <summary>
@@ -36,6 +37,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="maximumBytesToRead">The maximum bytes to read.</param>
internal CachedDirectWebResponse(Uri requestUri, HttpWebResponse response, int maximumBytesToRead)
: base(requestUri, response) {
+ Contract.Requires<ArgumentNullException>(requestUri != null);
+ Contract.Requires<ArgumentNullException>(response != null);
this.responseStream = CacheNetworkStreamAndClose(response, maximumBytesToRead);
// BUGBUG: if the response was exactly maximumBytesToRead, we'll incorrectly believe it was truncated.
@@ -54,8 +57,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="responseStream">The response stream.</param>
internal CachedDirectWebResponse(Uri requestUri, Uri responseUri, WebHeaderCollection headers, HttpStatusCode statusCode, string contentType, string contentEncoding, MemoryStream responseStream)
: base(requestUri, responseUri, headers, statusCode, contentType, contentEncoding) {
- ErrorUtilities.VerifyArgumentNotNull(responseStream, "responseStream");
-
+ Contract.Requires<ArgumentNullException>(requestUri != null);
+ Contract.Requires<ArgumentNullException>(responseStream != null);
this.responseStream = responseStream;
}
@@ -149,11 +152,14 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="maximumBytesToRead">The maximum bytes to cache.</param>
/// <returns>The seekable Stream instance that contains a copy of what was returned in the HTTP response.</returns>
private static MemoryStream CacheNetworkStreamAndClose(HttpWebResponse response, int maximumBytesToRead) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
+ Contract.Ensures(Contract.Result<MemoryStream>() != null);
// Now read and cache the network stream
Stream networkStream = response.GetResponseStream();
MemoryStream cachedStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : Math.Min((int)response.ContentLength, maximumBytesToRead));
+ Contract.Assume(networkStream.CanRead, "HttpWebResponse.GetResponseStream() always returns a readable stream."); // CC missing
+ Contract.Assume(cachedStream.CanWrite, "This is a MemoryStream -- it's always writable."); // CC missing
networkStream.CopyTo(cachedStream);
cachedStream.Seek(0, SeekOrigin.Begin);
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 2e0f1a8..1bddf02 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -28,15 +28,15 @@ namespace DotNetOpenAuth.Messaging {
[ContractClass(typeof(ChannelContract))]
public abstract class Channel : IDisposable {
/// <summary>
- /// The content-type used on HTTP POST requests where the POST entity is a
- /// URL-encoded series of key=value pairs.
+ /// The encoding to use when writing out POST entity strings.
/// </summary>
- protected internal const string HttpFormUrlEncoded = "application/x-www-form-urlencoded";
+ internal static readonly Encoding PostEntityEncoding = new UTF8Encoding(false);
/// <summary>
- /// The encoding to use when writing out POST entity strings.
+ /// The content-type used on HTTP POST requests where the POST entity is a
+ /// URL-encoded series of key=value pairs.
/// </summary>
- private static readonly Encoding PostEntityEncoding = new UTF8Encoding(false);
+ protected internal const string HttpFormUrlEncoded = "application/x-www-form-urlencoded";
/// <summary>
/// The maximum allowable size for a 301 Redirect response before we send
@@ -112,8 +112,7 @@ namespace DotNetOpenAuth.Messaging {
/// </param>
/// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) {
- Contract.Requires(messageTypeProvider != null);
- ErrorUtilities.VerifyArgumentNotNull(messageTypeProvider, "messageTypeProvider");
+ Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
this.messageTypeProvider = messageTypeProvider;
this.WebRequestHandler = new StandardWebRequestHandler();
@@ -146,13 +145,11 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
internal MessageDescriptionCollection MessageDescriptions {
get {
- Contract.Ensures(Contract.Result<MessageDescriptionCollection>() != null);
return this.messageDescriptions;
}
set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.messageDescriptions = value;
}
}
@@ -180,7 +177,11 @@ namespace DotNetOpenAuth.Messaging {
/// Gets the binding elements used by this channel, in the order applied to incoming messages.
/// </summary>
protected internal ReadOnlyCollection<IChannelBindingElement> IncomingBindingElements {
- get { return this.incomingBindingElements.AsReadOnly(); }
+ get {
+ Contract.Ensures(Contract.Result<ReadOnlyCollection<IChannelBindingElement>>().All(be => be.Channel != null));
+ Contract.Ensures(Contract.Result<ReadOnlyCollection<IChannelBindingElement>>().All(be => be != null));
+ return this.incomingBindingElements.AsReadOnly();
+ }
}
/// <summary>
@@ -209,8 +210,7 @@ namespace DotNetOpenAuth.Messaging {
}
set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.cachePolicy = value;
}
}
@@ -226,8 +226,8 @@ namespace DotNetOpenAuth.Messaging {
/// Requires an HttpContext.Current context.
/// </remarks>
public void Send(IProtocolMessage message) {
- Contract.Requires(HttpContext.Current != null);
- Contract.Requires(message != null);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.CurrentHttpContextRequired);
+ Contract.Requires<ArgumentNullException>(message != null);
this.PrepareResponse(message).Send();
}
@@ -238,9 +238,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="message">The one-way message to send</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
public OutgoingWebResponse PrepareResponse(IProtocolMessage message) {
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
this.ProcessOutgoingMessage(message);
Logger.Channel.DebugFormat("Sending message: {0}", message.GetType().Name);
@@ -309,7 +308,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
public bool TryReadFromRequest<TRequest>(HttpRequestInfo httpRequest, out TRequest request)
where TRequest : class, IProtocolMessage {
- Contract.Requires(httpRequest != null);
+ Contract.Requires<ArgumentNullException>(httpRequest != null);
Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn<TRequest>(out request) != null));
IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
@@ -350,7 +349,7 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest)
where TRequest : class, IProtocolMessage {
- Contract.Requires(httpRequest != null);
+ Contract.Requires<ArgumentNullException>(httpRequest != null);
TRequest request;
if (this.TryReadFromRequest<TRequest>(httpRequest, out request)) {
return request;
@@ -365,8 +364,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="httpRequest">The request to search for an embedded message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) {
- Contract.Requires(httpRequest != null);
- ErrorUtilities.VerifyArgumentNotNull(httpRequest, "httpRequest");
+ Contract.Requires<ArgumentNullException>(httpRequest != null);
if (Logger.Channel.IsInfoEnabled && httpRequest.UrlBeforeRewriting != null) {
Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.UrlBeforeRewriting.AbsoluteUri);
@@ -393,7 +391,7 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
where TResponse : class, IProtocolMessage {
- Contract.Requires(requestMessage != null);
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
Contract.Ensures(Contract.Result<TResponse>() != null);
IProtocolMessage response = this.Request(requestMessage);
@@ -412,8 +410,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The remote party's response. Guaranteed to never be null.</returns>
/// <exception cref="ProtocolException">Thrown if the response does not include a protocol message.</exception>
public IProtocolMessage Request(IDirectedProtocolMessage requestMessage) {
- Contract.Requires(requestMessage != null);
- ErrorUtilities.VerifyArgumentNotNull(requestMessage, "requestMessage");
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
this.ProcessOutgoingMessage(requestMessage);
Logger.Channel.DebugFormat("Sending {0} request.", requestMessage.GetType().Name);
@@ -448,9 +445,14 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
protected internal virtual HttpRequestInfo GetRequestFromContext() {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
Contract.Ensures(Contract.Result<HttpRequestInfo>() != null);
- ErrorUtilities.VerifyHttpContext();
+ Contract.Ensures(Contract.Result<HttpRequestInfo>().Url != null);
+ Contract.Ensures(Contract.Result<HttpRequestInfo>().RawUrl != null);
+ Contract.Ensures(Contract.Result<HttpRequestInfo>().UrlBeforeRewriting != null);
+ Contract.Assume(HttpContext.Current.Request.Url != null);
+ Contract.Assume(HttpContext.Current.Request.RawUrl != null);
return new HttpRequestInfo(HttpContext.Current.Request);
}
@@ -492,8 +494,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="message">The message about to be encoded and sent.</param>
protected virtual void OnSending(IProtocolMessage message) {
- Contract.Requires(message != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
var sending = this.Sending;
if (sending != null) {
@@ -508,8 +509,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The response to the web request.</returns>
/// <exception cref="ProtocolException">Thrown on network or protocol errors.</exception>
protected virtual IncomingWebResponse GetDirectResponse(HttpWebRequest webRequest) {
- Contract.Requires(webRequest != null);
- ErrorUtilities.VerifyArgumentNotNull(webRequest, "webRequest");
+ Contract.Requires<ArgumentNullException>(webRequest != null);
return this.WebRequestHandler.GetResponse(webRequest);
}
@@ -524,7 +524,9 @@ namespace DotNetOpenAuth.Messaging {
/// this method to eliminate all use of an HTTP transport.
/// </remarks>
protected virtual IProtocolMessage RequestCore(IDirectedProtocolMessage request) {
- Contract.Requires(request != null);
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(request.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
+
HttpWebRequest webRequest = this.CreateHttpRequest(request);
IDictionary<string, string> responseFields;
IDirectResponseProtocolMessage responseMessage;
@@ -535,6 +537,9 @@ namespace DotNetOpenAuth.Messaging {
}
responseFields = this.ReadFromResponseCore(response);
+ if (responseFields == null) {
+ return null;
+ }
responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
if (responseMessage == null) {
@@ -564,8 +569,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request to search for an embedded message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.UrlBeforeRewriting.AbsoluteUri);
@@ -576,7 +580,15 @@ namespace DotNetOpenAuth.Messaging {
fields = request.QueryStringBeforeRewriting.ToDictionary();
}
- return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
+ MessageReceivingEndpoint recipient;
+ try {
+ recipient = request.GetRecipient();
+ } catch (ArgumentException ex) {
+ Logger.Messaging.WarnFormat("Unrecognized HTTP request: " + ex.ToString());
+ return null;
+ }
+
+ return (IDirectedProtocolMessage)this.Receive(fields, recipient);
}
/// <summary>
@@ -586,8 +598,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="recipient">Information about where the message was directed. Null for direct response messages.</param>
/// <returns>The deserialized message, or null if no message could be recognized in the provided data.</returns>
protected virtual IProtocolMessage Receive(Dictionary<string, string> fields, MessageReceivingEndpoint recipient) {
- Contract.Requires(fields != null);
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+ Contract.Requires<ArgumentNullException>(fields != null);
IProtocolMessage message = this.MessageFactory.GetNewRequestMessage(recipient, fields);
@@ -613,11 +624,13 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="message">The message to send.</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
protected virtual OutgoingWebResponse PrepareIndirectResponse(IDirectedProtocolMessage message) {
- Contract.Requires(message != null && message.Recipient != null);
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<ArgumentException>(message.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Assert(message != null && message.Recipient != null);
var messageAccessor = this.MessageDescriptions.GetAccessor(message);
+ Contract.Assert(message != null && message.Recipient != null);
var fields = messageAccessor.Serialize();
// First try creating a 301 redirect, and fallback to a form POST
@@ -637,15 +650,13 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="message">The message to forward.</param>
/// <param name="fields">The pre-serialized fields from the message.</param>
/// <returns>The encoded HTTP response.</returns>
+ [Pure]
protected virtual OutgoingWebResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
- Contract.Requires(message != null && message.Recipient != null);
- Contract.Requires(fields != null);
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<ArgumentException>(message.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
+ Contract.Requires<ArgumentNullException>(fields != null);
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyArgumentNamed(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
WebHeaderCollection headers = new WebHeaderCollection();
UriBuilder builder = new UriBuilder(message.Recipient);
MessagingUtilities.AppendQueryArgs(builder, fields);
@@ -669,12 +680,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="fields">The pre-serialized fields from the message.</param>
/// <returns>The encoded HTTP response.</returns>
protected virtual OutgoingWebResponse CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
- Contract.Requires(message != null && message.Recipient != null);
- Contract.Requires(fields != null);
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<ArgumentException>(message.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
+ Contract.Requires<ArgumentNullException>(fields != null);
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyArgumentNamed(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
WebHeaderCollection headers = new WebHeaderCollection();
headers.Add(HttpResponseHeader.ContentType, "text/html");
@@ -719,7 +728,8 @@ namespace DotNetOpenAuth.Messaging {
/// is overridden and does not require this method.
/// </remarks>
protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
- Contract.Requires(request != null);
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(request.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
throw new NotImplementedException();
}
@@ -744,8 +754,7 @@ namespace DotNetOpenAuth.Messaging {
/// except when sending ONE WAY request messages.
/// </remarks>
protected void ProcessOutgoingMessage(IProtocolMessage message) {
- Contract.Requires(message != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
Logger.Channel.DebugFormat("Preparing to send {0} ({1}) message.", message.GetType().Name, message.Version);
this.OnSending(message);
@@ -758,6 +767,7 @@ namespace DotNetOpenAuth.Messaging {
MessageProtections appliedProtection = MessageProtections.None;
foreach (IChannelBindingElement bindingElement in this.outgoingBindingElements) {
+ Contract.Assume(bindingElement.Channel != null);
MessageProtections? elementProtection = bindingElement.ProcessOutgoingMessage(message);
if (elementProtection.HasValue) {
Logger.Bindings.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
@@ -803,8 +813,8 @@ namespace DotNetOpenAuth.Messaging {
/// This method satisfies OAuth 1.0 section 5.2, item #3.
/// </remarks>
protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
- Contract.Requires(requestMessage != null);
- ErrorUtilities.VerifyArgumentNotNull(requestMessage, "requestMessage");
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
+ Contract.Requires<ArgumentException>(requestMessage.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
var messageAccessor = this.MessageDescriptions.GetAccessor(requestMessage);
var fields = messageAccessor.Serialize();
@@ -817,6 +827,24 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a HEAD request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP HEAD request with the message parts serialized to the query string.
+ /// This method satisfies OAuth 1.0 section 5.2, item #3.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsHead(IDirectedProtocolMessage requestMessage) {
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
+ Contract.Requires<ArgumentException>(requestMessage.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
+
+ HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = "HEAD";
+ return request;
+ }
+
+ /// <summary>
/// Prepares to send a request to the Service Provider as the payload of a POST request.
/// </summary>
/// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
@@ -827,9 +855,8 @@ namespace DotNetOpenAuth.Messaging {
/// This method satisfies OAuth 1.0 section 5.2, item #2 and OpenID 2.0 section 4.1.2.
/// </remarks>
protected virtual HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
- Contract.Requires(requestMessage != null);
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
- ErrorUtilities.VerifyArgumentNotNull(requestMessage, "requestMessage");
var messageAccessor = this.MessageDescriptions.GetAccessor(requestMessage);
var fields = messageAccessor.Serialize();
@@ -837,12 +864,57 @@ namespace DotNetOpenAuth.Messaging {
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
httpRequest.CachePolicy = this.CachePolicy;
httpRequest.Method = "POST";
- this.SendParametersInEntity(httpRequest, fields);
+
+ var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData;
+ if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) {
+ var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData);
+
+ // When sending multi-part, all data gets send as multi-part -- even the non-binary data.
+ multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value)));
+ this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields);
+ } else {
+ ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart);
+ this.SendParametersInEntity(httpRequest, fields);
+ }
return httpRequest;
}
/// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a PUT request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP PUT request with the message parts serialized to the query string.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsPut(IDirectedProtocolMessage requestMessage) {
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
+ Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
+
+ HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = "PUT";
+ return request;
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a DELETE request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP DELETE request with the message parts serialized to the query string.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsDelete(IDirectedProtocolMessage requestMessage) {
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
+ Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
+
+ HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = "DELETE";
+ return request;
+ }
+
+ /// <summary>
/// Sends the given parameters in the entity stream of an HTTP request.
/// </summary>
/// <param name="httpRequest">The HTTP request.</param>
@@ -852,10 +924,8 @@ namespace DotNetOpenAuth.Messaging {
/// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>.
/// </remarks>
protected void SendParametersInEntity(HttpWebRequest httpRequest, IDictionary<string, string> fields) {
- Contract.Requires(httpRequest != null);
- Contract.Requires(fields != null);
- ErrorUtilities.VerifyArgumentNotNull(httpRequest, "httpRequest");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+ Contract.Requires<ArgumentNullException>(httpRequest != null);
+ Contract.Requires<ArgumentNullException>(fields != null);
httpRequest.ContentType = HttpFormUrlEncoded;
@@ -881,6 +951,19 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Sends the given parameters in the entity stream of an HTTP request in multi-part format.
+ /// </summary>
+ /// <param name="httpRequest">The HTTP request.</param>
+ /// <param name="fields">The parameters to send.</param>
+ /// <remarks>
+ /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes
+ /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>.
+ /// </remarks>
+ protected void SendParametersInEntityAsMultipart(HttpWebRequest httpRequest, IEnumerable<MultipartPostPart> fields) {
+ httpRequest.PostMultipartNoGetResponse(this.WebRequestHandler, fields);
+ }
+
+ /// <summary>
/// Verifies the integrity and applicability of an incoming message.
/// </summary>
/// <param name="message">The message just received.</param>
@@ -889,7 +972,7 @@ namespace DotNetOpenAuth.Messaging {
/// This can be due to tampering, replay attack or expiration, among other things.
/// </exception>
protected virtual void ProcessIncomingMessage(IProtocolMessage message) {
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
if (Logger.Channel.IsInfoEnabled) {
var messageAccessor = this.MessageDescriptions.GetAccessor(message);
@@ -902,7 +985,8 @@ namespace DotNetOpenAuth.Messaging {
}
MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.incomingBindingElements) {
+ foreach (IChannelBindingElement bindingElement in this.IncomingBindingElements) {
+ Contract.Assume(bindingElement.Channel != null); // CC bug: this.IncomingBindingElements ensures this... why must we assume it here?
MessageProtections? elementProtection = bindingElement.ProcessIncomingMessage(message);
if (elementProtection.HasValue) {
Logger.Bindings.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
@@ -963,10 +1047,8 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
/// <exception cref="ArgumentException">Thrown if a binding element is new or missing in one of the ordered lists.</exception>
protected void CustomizeBindingElementOrder(IEnumerable<IChannelBindingElement> outgoingOrder, IEnumerable<IChannelBindingElement> incomingOrder) {
- Contract.Requires(outgoingOrder != null);
- Contract.Requires(incomingOrder != null);
- ErrorUtilities.VerifyArgumentNotNull(outgoingOrder, "outgoingOrder");
- ErrorUtilities.VerifyArgumentNotNull(incomingOrder, "incomingOrder");
+ Contract.Requires<ArgumentNullException>(outgoingOrder != null);
+ Contract.Requires<ArgumentNullException>(incomingOrder != null);
ErrorUtilities.VerifyArgument(this.IsBindingElementOrderValid(outgoingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
ErrorUtilities.VerifyArgument(this.IsBindingElementOrderValid(incomingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
@@ -984,14 +1066,12 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The properly ordered list of elements.</returns>
/// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
- Contract.Requires(elements == null || elements.All(e => e != null));
+ Contract.Requires<ArgumentException>(elements == null || elements.All(e => e != null));
Contract.Ensures(Contract.Result<IEnumerable<IChannelBindingElement>>() != null);
if (elements == null) {
return new IChannelBindingElement[0];
}
- ErrorUtilities.VerifyArgumentNamed(!elements.Contains(null), "elements", MessagingStrings.SequenceContainsNullElement);
-
// Filter the elements between the mere transforming ones and the protection ones.
var transformationElements = new List<IChannelBindingElement>(
elements.Where(element => element.Protection == MessageProtections.None));
@@ -1027,8 +1107,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="protection1">The first protection type to compare.</param>
/// <param name="protection2">The second protection type to compare.</param>
/// <returns>
- /// -1 if <paramref name="element1"/> should be applied to an outgoing message before <paramref name="element2"/>.
- /// 1 if <paramref name="element2"/> should be applied to an outgoing message before <paramref name="element1"/>.
+ /// -1 if <paramref name="protection1"/> should be applied to an outgoing message before <paramref name="protection2"/>.
+ /// 1 if <paramref name="protection2"/> should be applied to an outgoing message before <paramref name="protection1"/>.
/// 0 if it doesn't matter.
/// </returns>
private static int BindingElementOutgoingMessageApplicationOrder(MessageProtections protection1, MessageProtections protection2) {
@@ -1038,6 +1118,18 @@ namespace DotNetOpenAuth.Messaging {
return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
}
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.MessageDescriptions != null);
+ }
+#endif
+
/// <summary>
/// Verifies that all required message parts are initialized to values
/// prior to sending the message to a remote party.
@@ -1047,7 +1139,7 @@ namespace DotNetOpenAuth.Messaging {
/// Thrown when any required message part does not have a value.
/// </exception>
private void EnsureValidMessageParts(IProtocolMessage message) {
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(message);
MessageDescription description = this.MessageDescriptions.Get(message);
description.EnsureMessagePartsPassBasicValidation(dictionary);
@@ -1063,8 +1155,7 @@ namespace DotNetOpenAuth.Messaging {
/// </returns>
[Pure]
private bool IsBindingElementOrderValid(IEnumerable<IChannelBindingElement> order) {
- Contract.Requires(order != null);
- ErrorUtilities.VerifyArgumentNotNull(order, "order");
+ Contract.Requires<ArgumentNullException>(order != null);
// Check that the same number of binding elements are defined.
if (order.Count() != this.OutgoingBindingElements.Count) {
diff --git a/src/DotNetOpenAuth/Messaging/ChannelContract.cs b/src/DotNetOpenAuth/Messaging/ChannelContract.cs
index 551d7c4..9b85d28 100644
--- a/src/DotNetOpenAuth/Messaging/ChannelContract.cs
+++ b/src/DotNetOpenAuth/Messaging/ChannelContract.cs
@@ -30,7 +30,7 @@ namespace DotNetOpenAuth.Messaging {
/// </returns>
/// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) {
- Contract.Requires(response != null);
+ Contract.Requires<ArgumentNullException>(response != null);
throw new NotImplementedException();
}
@@ -46,7 +46,7 @@ namespace DotNetOpenAuth.Messaging {
/// This method implements spec V1.0 section 5.3.
/// </remarks>
protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
- Contract.Requires(response != null);
+ Contract.Requires<ArgumentNullException>(response != null);
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
throw new NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/Messaging/ChannelEventArgs.cs b/src/DotNetOpenAuth/Messaging/ChannelEventArgs.cs
index 29310dc..1e71bf2 100644
--- a/src/DotNetOpenAuth/Messaging/ChannelEventArgs.cs
+++ b/src/DotNetOpenAuth/Messaging/ChannelEventArgs.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
+ using System.Diagnostics.Contracts;
/// <summary>
/// The data packet sent with Channel events.
@@ -16,7 +17,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="message">The message behind the fired event..</param>
internal ChannelEventArgs(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
this.Message = message;
}
diff --git a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs
index 22c947a..9db5169 100644
--- a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Linq;
/// <summary>
@@ -153,6 +154,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="T:System.NotSupportedException">
/// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </exception>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void Add(KeyValuePair<TKey, TValue> item) {
throw new NotSupportedException();
}
@@ -196,7 +198,7 @@ namespace DotNetOpenAuth.Messaging {
/// -or-
/// The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
/// -or-
- /// Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.
+ /// Type cannot be cast automatically to the type of the destination <paramref name="array"/>.
/// </exception>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
}
diff --git a/src/DotNetOpenAuth/Messaging/EmptyList.cs b/src/DotNetOpenAuth/Messaging/EmptyList.cs
index b418623..68cdabd 100644
--- a/src/DotNetOpenAuth/Messaging/EmptyList.cs
+++ b/src/DotNetOpenAuth/Messaging/EmptyList.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
/// <summary>
/// An empty, read-only list.
@@ -98,6 +99,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="T:System.NotSupportedException">
/// The <see cref="T:System.Collections.Generic.IList`1"/> is read-only.
/// </exception>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void RemoveAt(int index) {
throw new ArgumentOutOfRangeException("index");
}
@@ -113,6 +115,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="T:System.NotSupportedException">
/// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </exception>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void Add(T item) {
throw new NotSupportedException();
}
@@ -156,7 +159,7 @@ namespace DotNetOpenAuth.Messaging {
/// -or-
/// The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
/// -or-
- /// Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.
+ /// Type <typeparamref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.
/// </exception>
public void CopyTo(T[] array, int arrayIndex) {
}
diff --git a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
index d343410..29ec533 100644
--- a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
+++ b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
/// <summary>
/// Extension methods for <see cref="IEnumerable&lt;T&gt;"/> types.
@@ -34,6 +35,8 @@ namespace DotNetOpenAuth.Messaging {
/// to avoid double-caching.</para>
/// </remarks>
public static IEnumerable<T> CacheGeneratedResults<T>(this IEnumerable<T> sequence) {
+ Contract.Requires<ArgumentNullException>(sequence != null);
+
// Don't create a cache for types that don't need it.
if (sequence is IList<T> ||
sequence is ICollection<T> ||
@@ -80,9 +83,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="generator">The generator.</param>
internal EnumerableCache(IEnumerable<T> generator) {
- if (generator == null) {
- throw new ArgumentNullException("generator");
- }
+ Contract.Requires<ArgumentNullException>(generator != null);
this.generator = generator;
}
@@ -136,13 +137,11 @@ namespace DotNetOpenAuth.Messaging {
private int cachePosition = -1;
/// <summary>
- /// Initializes a new instance of the <see cref="EnumerableCache&lt;T&gt;.EnumeratorCache"/> class.
+ /// Initializes a new instance of the <see cref="EnumeratorCache"/> class.
/// </summary>
/// <param name="parent">The parent cached enumerable whose GetEnumerator method is calling this constructor.</param>
internal EnumeratorCache(EnumerableCache<T> parent) {
- if (parent == null) {
- throw new ArgumentNullException("parent");
- }
+ Contract.Requires<ArgumentNullException>(parent != null);
this.parent = parent;
}
diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
index 0a60b19..1807f54 100644
--- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
@@ -26,7 +26,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The newly constructed (unthrown) exception.</returns>
[Pure]
internal static Exception Wrap(Exception inner, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Assume(errorMessage != null);
return new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), inner);
}
@@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InternalErrorException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyInternal(bool condition, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<InternalErrorException>(!condition);
Contract.Assume(errorMessage != null);
@@ -118,11 +118,11 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="condition">The condition to check.</param>
/// <param name="errorMessage">The message to include in the exception, if created.</param>
- /// <param name="args">The string formatting arguments for <paramref name="message"/>.</param>
+ /// <param name="args">The string formatting arguments for <paramref name="errorMessage"/>.</param>
/// <exception cref="NotSupportedException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifySupported(bool condition, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<NotSupportedException>(!condition);
Contract.Assume(errorMessage != null);
@@ -140,7 +140,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InvalidOperationException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyOperation(bool condition, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<InvalidOperationException>(!condition);
Contract.Assume(errorMessage != null);
@@ -160,7 +160,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InfoCard.InformationCardException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyInfoCard(bool condition, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<InfoCard.InformationCardException>(!condition);
Contract.Assume(errorMessage != null);
@@ -179,7 +179,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="HostErrorException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyHost(bool condition, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<ProtocolException>(!condition);
Contract.Assume(errorMessage != null);
@@ -198,7 +198,8 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ProtocolException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyProtocol(bool condition, IProtocolMessage faultedMessage, string errorMessage, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
+ Contract.Requires<ArgumentNullException>(faultedMessage != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<ProtocolException>(!condition);
Contract.Assume(errorMessage != null);
@@ -216,7 +217,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ProtocolException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyProtocol(bool condition, string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<ProtocolException>(!condition);
Contract.Assume(message != null);
@@ -248,7 +249,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ProtocolException">Always thrown.</exception>
[Pure]
internal static Exception ThrowProtocol(string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Assume(message != null);
VerifyProtocol(false, message, args);
@@ -264,7 +265,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>Nothing. It's just here so the caller can throw this method for C# compilation check.</returns>
[Pure]
internal static Exception ThrowFormat(string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Assume(message != null);
throw new FormatException(string.Format(CultureInfo.CurrentCulture, message, args));
}
@@ -278,7 +279,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="FormatException">Thrown when <paramref name="condition"/> is <c>false</c>.</exception>
[Pure]
internal static void VerifyFormat(bool condition, string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<FormatException>(!condition);
Contract.Assume(message != null);
@@ -296,7 +297,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyArgument(bool condition, string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<ArgumentException>(!condition);
Contract.Assume(message != null);
@@ -306,48 +307,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Verifies something about the argument supplied to a method.
- /// </summary>
- /// <param name="condition">The condition that must evaluate to true to avoid an exception.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
- [Pure]
- internal static void VerifyArgumentInRange(bool condition, string parameterName) {
- Contract.Requires(condition);
- if (!condition) {
- throw new ArgumentOutOfRangeException(parameterName);
- }
- }
-
- /// <summary>
- /// Verifies something about the argument supplied to a method.
- /// </summary>
- /// <param name="condition">The condition that must evaluate to true to avoid an exception.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <param name="message">The unformatted message.</param>
- /// <param name="args">The string formatting arguments.</param>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
- [Pure]
- internal static void VerifyArgumentInRange(bool condition, string parameterName, string message, params object[] args) {
- Contract.Requires(condition);
- Contract.Requires(args != null);
- Contract.Assume(message != null);
- if (!condition) {
- throw new ArgumentOutOfRangeException(parameterName, string.Format(CultureInfo.CurrentCulture, message, args));
- }
- }
-
- /// <summary>
- /// Verifies something about the argument supplied to a method.
+ /// Throws an <see cref="ArgumentException"/>.
/// </summary>
/// <param name="parameterName">Name of the parameter.</param>
/// <param name="message">The message to use in the exception if the condition is false.</param>
/// <param name="args">The string formatting arguments, if any.</param>
/// <returns>Never returns anything. It always throws.</returns>
- /// <exception cref="ArgumentException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static Exception ThrowArgumentNamed(string parameterName, string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Assume(message != null);
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, message, args), parameterName);
}
@@ -362,7 +330,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyArgumentNamed(bool condition, string parameterName, string message, params object[] args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(condition);
Contract.EnsuresOnThrow<ArgumentException>(!condition);
Contract.Assume(message != null);
@@ -379,7 +347,8 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
[Pure]
internal static void VerifyArgumentNotNull(object value, string paramName) {
- Contract.Requires(value != null);
+ Contract.Ensures(value != null);
+ Contract.EnsuresOnThrow<ArgumentNullException>(value == null);
if (value == null) {
throw new ArgumentNullException(paramName);
}
@@ -394,7 +363,8 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> has zero length.</exception>
[Pure]
internal static void VerifyNonZeroLength(string value, string paramName) {
- Contract.Requires((value != null && value.Length > 0) || !string.IsNullOrEmpty(value));
+ Contract.Ensures((value != null && value.Length > 0) && !string.IsNullOrEmpty(value));
+ Contract.EnsuresOnThrow<ArgumentException>(value == null || value.Length == 0);
VerifyArgumentNotNull(value, paramName);
if (value.Length == 0) {
throw new ArgumentException(MessagingStrings.UnexpectedEmptyString, paramName);
@@ -409,9 +379,7 @@ namespace DotNetOpenAuth.Messaging {
internal static void VerifyHttpContext() {
Contract.Ensures(HttpContext.Current != null);
Contract.Ensures(HttpContext.Current.Request != null);
-
- ErrorUtilities.VerifyOperation(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
- ErrorUtilities.VerifyOperation(HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ ErrorUtilities.VerifyOperation(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
}
}
}
diff --git a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
index cbbe28e..1443fff 100644
--- a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
@@ -34,5 +34,25 @@ namespace DotNetOpenAuth.Messaging {
/// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
/// </summary>
GetRequest = 0x4,
+
+ /// <summary>
+ /// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
+ /// </summary>
+ PutRequest = 0x8,
+
+ /// <summary>
+ /// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
+ /// </summary>
+ DeleteRequest = 0x10,
+
+ /// <summary>
+ /// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
+ /// </summary>
+ HeadRequest = 0x20,
+
+ /// <summary>
+ /// The flags that control HTTP verbs.
+ /// </summary>
+ HttpVerbMask = PostRequest | GetRequest | PutRequest | DeleteRequest | HeadRequest,
}
}
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
index 14666f2..94990c8 100644
--- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
@@ -50,8 +50,15 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The ASP.NET structure to copy from.</param>
public HttpRequestInfo(HttpRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Ensures(this.HttpMethod == request.HttpMethod);
+ Contract.Ensures(this.Url == request.Url);
+ Contract.Ensures(this.RawUrl == request.RawUrl);
+ Contract.Ensures(this.UrlBeforeRewriting != null);
+ Contract.Ensures(this.Headers != null);
+ Contract.Ensures(this.InputStream == request.InputStream);
+ Contract.Ensures(this.form == request.Form);
+ Contract.Ensures(this.queryString == request.QueryString);
this.HttpMethod = request.HttpMethod;
this.Url = request.Url;
@@ -66,6 +73,8 @@ namespace DotNetOpenAuth.Messaging {
// these as well.
this.form = request.Form;
this.queryString = request.QueryString;
+
+ Reporting.RecordRequestStatistics(this);
}
/// <summary>
@@ -78,14 +87,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="headers">Headers in the HTTP request.</param>
/// <param name="inputStream">The entity stream, if any. (POST requests typically have these). Use <c>null</c> for GET requests.</param>
public HttpRequestInfo(string httpMethod, Uri requestUrl, string rawUrl, WebHeaderCollection headers, Stream inputStream) {
- Contract.Requires(!string.IsNullOrEmpty(httpMethod));
- Contract.Requires(requestUrl != null);
- Contract.Requires(rawUrl != null);
- Contract.Requires(headers != null);
- ErrorUtilities.VerifyNonZeroLength(httpMethod, "httpMethod");
- ErrorUtilities.VerifyArgumentNotNull(requestUrl, "requestUrl");
- ErrorUtilities.VerifyArgumentNotNull(rawUrl, "rawUrl");
- ErrorUtilities.VerifyArgumentNotNull(headers, "headers");
+ Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(httpMethod));
+ Contract.Requires<ArgumentNullException>(requestUrl != null);
+ Contract.Requires<ArgumentNullException>(rawUrl != null);
+ Contract.Requires<ArgumentNullException>(headers != null);
this.HttpMethod = httpMethod;
this.Url = requestUrl;
@@ -93,6 +98,8 @@ namespace DotNetOpenAuth.Messaging {
this.RawUrl = rawUrl;
this.Headers = headers;
this.InputStream = inputStream;
+
+ Reporting.RecordRequestStatistics(this);
}
/// <summary>
@@ -100,8 +107,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="listenerRequest">Details on the incoming HTTP request.</param>
public HttpRequestInfo(HttpListenerRequest listenerRequest) {
- Contract.Requires(listenerRequest != null);
- ErrorUtilities.VerifyArgumentNotNull(listenerRequest, "listenerRequest");
+ Contract.Requires<ArgumentNullException>(listenerRequest != null);
this.HttpMethod = listenerRequest.HttpMethod;
this.Url = listenerRequest.Url;
@@ -113,6 +119,8 @@ namespace DotNetOpenAuth.Messaging {
}
this.InputStream = listenerRequest.InputStream;
+
+ Reporting.RecordRequestStatistics(this);
}
/// <summary>
@@ -121,22 +129,25 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The WCF incoming request structure to get the HTTP information from.</param>
/// <param name="requestUri">The URI of the service endpoint.</param>
public HttpRequestInfo(HttpRequestMessageProperty request, Uri requestUri) {
- Contract.Requires(request != null);
- Contract.Requires(requestUri != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(requestUri != null);
this.HttpMethod = request.Method;
this.Headers = request.Headers;
this.Url = requestUri;
this.UrlBeforeRewriting = requestUri;
this.RawUrl = MakeUpRawUrlFromUrl(requestUri);
+
+ Reporting.RecordRequestStatistics(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestInfo"/> class.
/// </summary>
internal HttpRequestInfo() {
+ Contract.Ensures(this.HttpMethod == "GET");
+ Contract.Ensures(this.Headers != null);
+
this.HttpMethod = "GET";
this.Headers = new WebHeaderCollection();
}
@@ -146,8 +157,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The HttpWebRequest (that was never used) to copy from.</param>
internal HttpRequestInfo(WebRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
this.HttpMethod = request.Method;
this.Url = request.RequestUri;
@@ -155,6 +165,8 @@ namespace DotNetOpenAuth.Messaging {
this.RawUrl = MakeUpRawUrlFromUrl(request.RequestUri);
this.Headers = GetHeaderCollection(request.Headers);
this.InputStream = null;
+
+ Reporting.RecordRequestStatistics(this);
}
/// <summary>
@@ -164,11 +176,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="httpMethod">The HTTP method that the incoming request came in on, whether or not <paramref name="message"/> is null.</param>
internal HttpRequestInfo(IDirectedProtocolMessage message, HttpDeliveryMethods httpMethod) {
this.message = message;
- if ((httpMethod & HttpDeliveryMethods.GetRequest) != 0) {
- this.HttpMethod = "GET";
- } else if ((httpMethod & HttpDeliveryMethods.PostRequest) != 0) {
- this.HttpMethod = "POST";
- }
+ this.HttpMethod = MessagingUtilities.GetHttpVerb(httpMethod);
}
/// <summary>
@@ -293,28 +301,22 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Gets the query or form data from the original request (before any URL rewriting has occurred.)
- /// </summary>
- /// <returns>A set of name=value pairs.</returns>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")]
- internal NameValueCollection GetQueryOrFormFromContext() {
- NameValueCollection query;
- if (this.HttpMethod == "GET") {
- query = this.QueryStringBeforeRewriting;
- } else {
- query = this.Form;
- }
- return query;
- }
-
- /// <summary>
/// Gets the public facing URL for the given incoming HTTP request.
/// </summary>
/// <param name="request">The request.</param>
- /// <returns>The URI that the outside world used to create this request.</returns>
- private static Uri GetPublicFacingUrl(HttpRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ /// <param name="serverVariables">The server variables to consider part of the request.</param>
+ /// <returns>
+ /// The URI that the outside world used to create this request.
+ /// </returns>
+ /// <remarks>
+ /// Although the <paramref name="serverVariables"/> value can be obtained from
+ /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them
+ /// in so we can simulate injected values from our unit tests since the actual property
+ /// is a read-only kind of <see cref="NameValueCollection"/>.
+ /// </remarks>
+ internal static Uri GetPublicFacingUrl(HttpRequest request, NameValueCollection serverVariables) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(serverVariables != null);
// Due to URL rewriting, cloud computing (i.e. Azure)
// and web farms, etc., we have to be VERY careful about what
@@ -323,15 +325,14 @@ namespace DotNetOpenAuth.Messaging {
// HttpRequest.Url gives us the internal URL in a cloud environment,
// So we use a variable that (at least from what I can tell) gives us
// the public URL:
- if (request.ServerVariables["HTTP_HOST"] != null) {
+ if (serverVariables["HTTP_HOST"] != null) {
ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols.");
+ string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme;
+ Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]);
UriBuilder publicRequestUri = new UriBuilder(request.Url);
- Uri hostAndPort = new Uri(request.Url.Scheme + Uri.SchemeDelimiter + request.ServerVariables["HTTP_HOST"]);
+ publicRequestUri.Scheme = scheme;
publicRequestUri.Host = hostAndPort.Host;
- publicRequestUri.Port = hostAndPort.Port;
- if (request.ServerVariables["HTTP_X_FORWARDED_PROTO"] != null) {
- publicRequestUri.Scheme = request.ServerVariables["HTTP_X_FORWARDED_PROTO"];
- }
+ publicRequestUri.Port = hostAndPort.Port; // CC missing Uri.Port contract that's on UriBuilder.Port
return publicRequestUri.Uri;
} else {
// Failover to the method that works for non-web farm enviroments.
@@ -347,12 +348,37 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets the query or form data from the original request (before any URL rewriting has occurred.)
+ /// </summary>
+ /// <returns>A set of name=value pairs.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")]
+ internal NameValueCollection GetQueryOrFormFromContext() {
+ NameValueCollection query;
+ if (this.HttpMethod == "GET") {
+ query = this.QueryStringBeforeRewriting;
+ } else {
+ query = this.Form;
+ }
+ return query;
+ }
+
+ /// <summary>
+ /// Gets the public facing URL for the given incoming HTTP request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>The URI that the outside world used to create this request.</returns>
+ private static Uri GetPublicFacingUrl(HttpRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ return GetPublicFacingUrl(request, request.ServerVariables);
+ }
+
+ /// <summary>
/// Makes up a reasonable guess at the raw URL from the possibly rewritten URL.
/// </summary>
/// <param name="url">A full URL.</param>
/// <returns>A raw URL that might have come in on the HTTP verb.</returns>
private static string MakeUpRawUrlFromUrl(Uri url) {
- Contract.Requires(url != null);
+ Contract.Requires<ArgumentNullException>(url != null);
return url.AbsolutePath + url.Query + url.Fragment;
}
@@ -362,14 +388,34 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="pairs">The collection a HTTP headers.</param>
/// <returns>A new collection of the given headers.</returns>
private static WebHeaderCollection GetHeaderCollection(NameValueCollection pairs) {
- Debug.Assert(pairs != null, "pairs == null");
+ Contract.Requires<ArgumentNullException>(pairs != null);
WebHeaderCollection headers = new WebHeaderCollection();
foreach (string key in pairs) {
- headers.Add(key, pairs[key]);
+ try {
+ headers.Add(key, pairs[key]);
+ } catch (ArgumentException ex) {
+ Logger.Messaging.WarnFormat(
+ "{0} thrown when trying to add web header \"{1}: {2}\". {3}",
+ ex.GetType().Name,
+ key,
+ pairs[key],
+ ex.Message);
+ }
}
return headers;
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/Messaging/IChannelBindingElement.cs b/src/DotNetOpenAuth/Messaging/IChannelBindingElement.cs
index 5ae07c3..db5dd24 100644
--- a/src/DotNetOpenAuth/Messaging/IChannelBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/IChannelBindingElement.cs
@@ -107,8 +107,8 @@ namespace DotNetOpenAuth.Messaging {
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) {
- Contract.Requires(((IChannelBindingElement)this).Channel != null);
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null);
throw new NotImplementedException();
}
@@ -130,8 +130,8 @@ namespace DotNetOpenAuth.Messaging {
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) {
- Contract.Requires(((IChannelBindingElement)this).Channel != null);
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null);
throw new NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
index 380e2d5..3420de9 100644
--- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
@@ -5,7 +5,9 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
+ using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using DotNetOpenAuth.Messaging;
@@ -16,6 +18,7 @@ namespace DotNetOpenAuth.Messaging {
/// <remarks>
/// Implementations of this interface must be thread safe.
/// </remarks>
+ [ContractClass(typeof(IDirectWebRequestHandlerContract))]
public interface IDirectWebRequestHandler {
/// <summary>
/// Determines whether this instance can support the specified options.
@@ -24,6 +27,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>
/// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
/// </returns>
+ [Pure]
bool CanSupport(DirectWebRequestOptions options);
/// <summary>
@@ -98,4 +102,122 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options);
}
+
+ /// <summary>
+ /// Code contract for the <see cref="IDirectWebRequestHandler"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IDirectWebRequestHandler))]
+ internal abstract class IDirectWebRequestHandlerContract : IDirectWebRequestHandler {
+ #region IDirectWebRequestHandler Members
+
+ /// <summary>
+ /// Determines whether this instance can support the specified options.
+ /// </summary>
+ /// <param name="options">The set of options that might be given in a subsequent web request.</param>
+ /// <returns>
+ /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
+ /// </returns>
+ bool IDirectWebRequestHandler.CanSupport(DirectWebRequestOptions options) {
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
+ /// </summary>
+ /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
+ /// <returns>
+ /// The stream the caller should write out the entity data to.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
+ /// and any other appropriate properties <i>before</i> calling this method.
+ /// Callers <i>must</i> close and dispose of the request stream when they are done
+ /// writing to it to avoid taking up the connection too long and causing long waits on
+ /// subsequent requests.</para>
+ /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch.</para>
+ /// </remarks>
+ Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
+ /// </summary>
+ /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
+ /// <param name="options">The options to apply to this web request.</param>
+ /// <returns>
+ /// The stream the caller should write out the entity data to.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
+ /// and any other appropriate properties <i>before</i> calling this method.
+ /// Callers <i>must</i> close and dispose of the request stream when they are done
+ /// writing to it to avoid taking up the connection too long and causing long waits on
+ /// subsequent requests.</para>
+ /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch.</para>
+ /// </remarks>
+ Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<NotSupportedException>(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported);
+ ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name));
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Processes an <see cref="HttpWebRequest"/> and converts the
+ /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
+ /// </summary>
+ /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
+ /// <returns>
+ /// An instance of <see cref="IncomingWebResponse"/> describing the response.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
+ /// value, if set, shoud be Closed before throwing.
+ /// </remarks>
+ IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Processes an <see cref="HttpWebRequest"/> and converts the
+ /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
+ /// </summary>
+ /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
+ /// <param name="options">The options to apply to this web request.</param>
+ /// <returns>
+ /// An instance of <see cref="IncomingWebResponse"/> describing the response.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
+ /// value, if set, shoud be Closed before throwing.
+ /// </remarks>
+ IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<NotSupportedException>(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
+
+ ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name));
+ throw new System.NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/Messaging/IMessageFactory.cs b/src/DotNetOpenAuth/Messaging/IMessageFactory.cs
index 9ce5f89..3718545 100644
--- a/src/DotNetOpenAuth/Messaging/IMessageFactory.cs
+++ b/src/DotNetOpenAuth/Messaging/IMessageFactory.cs
@@ -7,11 +7,13 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
/// <summary>
/// A tool to analyze an incoming message to figure out what concrete class
/// is designed to deserialize it and instantiates that class.
/// </summary>
+ [ContractClass(typeof(IMessageFactoryContract))]
public interface IMessageFactory {
/// <summary>
/// Analyzes an incoming request message payload to discover what kind of
@@ -39,4 +41,47 @@ namespace DotNetOpenAuth.Messaging {
/// </returns>
IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields);
}
+
+ /// <summary>
+ /// Code contract for the <see cref="IMessageFactory"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IMessageFactory))]
+ internal class IMessageFactoryContract : IMessageFactory {
+ #region IMessageFactory Members
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="recipient">The intended or actual recipient of the request message.</param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ IDirectedProtocolMessage IMessageFactory.GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
+ Contract.Requires<ArgumentNullException>(recipient != null);
+ Contract.Requires<ArgumentNullException>(fields != null);
+
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="request">The message that was sent as a request that resulted in the response.</param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ IDirectResponseProtocolMessage IMessageFactory.GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(fields != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs
new file mode 100644
index 0000000..f411cf5
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs
@@ -0,0 +1,152 @@
+//-----------------------------------------------------------------------
+// <copyright file="IMessageWithBinaryData.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as protocol or extension messages that uses POST multi-part data for binary content.
+ /// </summary>
+ [ContractClass(typeof(IMessageWithBinaryDataContract))]
+ public interface IMessageWithBinaryData : IDirectedProtocolMessage {
+ /// <summary>
+ /// Gets the parts of the message that carry binary data.
+ /// </summary>
+ /// <value>A list of parts. Never null.</value>
+ IList<MultipartPostPart> BinaryData { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this message should be sent as multi-part POST.
+ /// </summary>
+ bool SendAsMultipart { get; }
+ }
+
+ /// <summary>
+ /// The contract class for the <see cref="IMessageWithBinaryData"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IMessageWithBinaryData))]
+ internal sealed class IMessageWithBinaryDataContract : IMessageWithBinaryData {
+ #region IMessageWithBinaryData Members
+
+ /// <summary>
+ /// Gets the parts of the message that carry binary data.
+ /// </summary>
+ /// <value>A list of parts. Never null.</value>
+ IList<MultipartPostPart> IMessageWithBinaryData.BinaryData {
+ get {
+ Contract.Ensures(Contract.Result<IList<MultipartPostPart>>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message should be sent as multi-part POST.
+ /// </summary>
+ bool IMessageWithBinaryData.SendAsMultipart {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ Version IMessage.Version {
+ get {
+ Contract.Ensures(Contract.Result<Version>() != null);
+ return default(Version); // dummy return
+ }
+ }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> IMessage.ExtraData {
+ get {
+ Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
+ return default(IDictionary<string, string>);
+ }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <remarks>
+ /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent:
+ /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript
+ /// to automate submission.
+ /// </remarks>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IProtocolMessage Members
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport IProtocolMessage.Transport {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IMessage methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void IMessage.EnsureValidMessage() {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs b/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs
index ba25c12..87ea553 100644
--- a/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs
@@ -6,12 +6,14 @@
namespace DotNetOpenAuth.Messaging {
using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.OAuth.ChannelElements;
/// <summary>
/// An interface that must be implemented by message transforms/validators in order
/// to be included in the channel stack.
/// </summary>
+ [ContractClass(typeof(ITamperProtectionChannelBindingElementContract))]
public interface ITamperProtectionChannelBindingElement : IChannelBindingElement {
/// <summary>
/// Gets or sets the delegate that will initialize the non-serialized properties necessary on a
@@ -25,4 +27,102 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The cloned instance.</returns>
ITamperProtectionChannelBindingElement Clone();
}
+
+ /// <summary>
+ /// Contract class for the <see cref="ITamperProtectionChannelBindingElement"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(ITamperProtectionChannelBindingElement))]
+ internal abstract class ITamperProtectionChannelBindingElementContract : ITamperProtectionChannelBindingElement {
+ #region ITamperProtectionChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets or sets the delegate that will initialize the non-serialized properties necessary on a
+ /// signable message so that its signature can be correctly calculated or verified.
+ /// </summary>
+ Action<ITamperResistantOAuthMessage> ITamperProtectionChannelBindingElement.SignatureCallback {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ Channel IChannelBindingElement.Channel {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the protection commonly offered (if any) by this binding element.
+ /// </summary>
+ /// <remarks>
+ /// This value is used to assist in sorting binding elements in the channel stack.
+ /// </remarks>
+ MessageProtections IChannelBindingElement.Protection {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) {
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Performs any transformation on an incoming message that may be necessary and/or
+ /// validates an incoming message based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) {
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region ITamperProtectionChannelBindingElement Methods
+
+ /// <summary>
+ /// Clones this instance.
+ /// </summary>
+ /// <returns>The cloned instance.</returns>
+ ITamperProtectionChannelBindingElement ITamperProtectionChannelBindingElement.Clone() {
+ Contract.Ensures(Contract.Result<ITamperProtectionChannelBindingElement>() != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
index dee81dc..70b1032 100644
--- a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
@@ -6,8 +6,8 @@
namespace DotNetOpenAuth.Messaging {
using System;
- using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Net;
@@ -17,6 +17,8 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Details on the incoming response from a direct web request to a remote party.
/// </summary>
+ [ContractVerification(true)]
+ [ContractClass(typeof(IncomingWebResponseContract))]
public abstract class IncomingWebResponse : IDisposable {
/// <summary>
/// The encoding to use in reading a response that does not declare its own content encoding.
@@ -37,8 +39,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="requestUri">The original request URI.</param>
/// <param name="response">The response to initialize from. The network stream is used by this class directly.</param>
protected IncomingWebResponse(Uri requestUri, HttpWebResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(requestUri != null);
+ Contract.Requires<ArgumentNullException>(response != null);
this.RequestUri = requestUri;
if (!string.IsNullOrEmpty(response.ContentType)) {
@@ -64,7 +66,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="contentType">Type of the content.</param>
/// <param name="contentEncoding">The content encoding.</param>
protected IncomingWebResponse(Uri requestUri, Uri responseUri, WebHeaderCollection headers, HttpStatusCode statusCode, string contentType, string contentEncoding) {
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
+ Contract.Requires<ArgumentNullException>(requestUri != null);
+
this.RequestUri = requestUri;
this.Status = statusCode;
if (!string.IsNullOrEmpty(contentType)) {
@@ -101,7 +104,7 @@ namespace DotNetOpenAuth.Messaging {
/// This can be different from the <see cref="RequestUri"/> in cases of
/// redirection during the request.
/// </remarks>
- public Uri FinalUri { get; private set; }
+ public Uri FinalUri { get; internal set; }
/// <summary>
/// Gets the headers that must be included in the response to the user agent.
diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs
new file mode 100644
index 0000000..e9c1941
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+// <copyright file="IncomingWebResponseContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+
+ /// <summary>
+ /// Code contract for the <see cref="IncomingWebResponse"/> class.
+ /// </summary>
+ [ContractClassFor(typeof(IncomingWebResponse))]
+ internal abstract class IncomingWebResponseContract : IncomingWebResponse {
+ /// <summary>
+ /// Gets the body of the HTTP response.
+ /// </summary>
+ /// <value></value>
+ public override Stream ResponseStream {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Creates a text reader for the response stream.
+ /// </summary>
+ /// <returns>
+ /// The text reader, initialized for the proper encoding.
+ /// </returns>
+ public override StreamReader GetResponseReader() {
+ Contract.Ensures(Contract.Result<StreamReader>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets an offline snapshot version of this instance.
+ /// </summary>
+ /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
+ /// <returns>A snapshot version of this instance.</returns>
+ /// <remarks>
+ /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
+ /// will automatically close and dispose of the underlying response stream.
+ /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
+ /// be the self same instance.
+ /// </remarks>
+ internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
+ Contract.Requires<ArgumentOutOfRangeException>(maximumBytesToCache >= 0);
+ Contract.Requires<InvalidOperationException>(this.RequestUri != null);
+ Contract.Ensures(Contract.Result<CachedDirectWebResponse>() != null);
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/KeyedCollectionDelegate.cs b/src/DotNetOpenAuth/Messaging/KeyedCollectionDelegate.cs
index 16786e3..5555f9c 100644
--- a/src/DotNetOpenAuth/Messaging/KeyedCollectionDelegate.cs
+++ b/src/DotNetOpenAuth/Messaging/KeyedCollectionDelegate.cs
@@ -27,8 +27,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="getKeyForItemDelegate">The delegate that gets the key for a given item.</param>
internal KeyedCollectionDelegate(Func<TItem, TKey> getKeyForItemDelegate) {
- Contract.Requires(getKeyForItemDelegate != null);
- ErrorUtilities.VerifyArgumentNotNull(getKeyForItemDelegate, "getKeyForItemDelegate");
+ Contract.Requires<ArgumentNullException>(getKeyForItemDelegate != null);
this.getKeyForItemDelegate = getKeyForItemDelegate;
}
@@ -39,7 +38,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="item">The element from which to extract the key.</param>
/// <returns>The key for the specified element.</returns>
protected override TKey GetKeyForItem(TItem item) {
- ErrorUtilities.VerifyArgumentNotNull(item, "item");
+ ErrorUtilities.VerifyArgumentNotNull(item, "item"); // null items not supported.
return this.getKeyForItemDelegate(item);
}
}
diff --git a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs
index 79a1107..e39a047 100644
--- a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs
+++ b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
/// <summary>
/// An immutable description of a URL that receives messages.
@@ -20,7 +21,11 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="locationUri">The URL of this endpoint.</param>
/// <param name="method">The HTTP method(s) allowed.</param>
public MessageReceivingEndpoint(string locationUri, HttpDeliveryMethods method)
- : this(new Uri(locationUri), method) { }
+ : this(new Uri(locationUri), method) {
+ Contract.Requires<ArgumentNullException>(locationUri != null);
+ Contract.Requires<ArgumentOutOfRangeException>(method != HttpDeliveryMethods.None);
+ Contract.Requires<ArgumentOutOfRangeException>((method & HttpDeliveryMethods.HttpVerbMask) != 0, MessagingStrings.GetOrPostFlagsRequired);
+ }
/// <summary>
/// Initializes a new instance of the <see cref="MessageReceivingEndpoint"/> class.
@@ -28,9 +33,9 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="location">The URL of this endpoint.</param>
/// <param name="method">The HTTP method(s) allowed.</param>
public MessageReceivingEndpoint(Uri location, HttpDeliveryMethods method) {
- ErrorUtilities.VerifyArgumentNotNull(location, "location");
- ErrorUtilities.VerifyArgumentInRange(method != HttpDeliveryMethods.None, "method");
- ErrorUtilities.VerifyArgumentInRange((method & (HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.GetRequest)) != 0, "method", MessagingStrings.GetOrPostFlagsRequired);
+ Contract.Requires<ArgumentNullException>(location != null);
+ Contract.Requires<ArgumentOutOfRangeException>(method != HttpDeliveryMethods.None);
+ Contract.Requires<ArgumentOutOfRangeException>((method & HttpDeliveryMethods.HttpVerbMask) != 0, MessagingStrings.GetOrPostFlagsRequired);
this.Location = location;
this.AllowedMethods = method;
diff --git a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
index 1aafc11..ccda5d5 100644
--- a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
@@ -33,17 +33,9 @@ namespace DotNetOpenAuth.Messaging {
/// that will be serialized and deserialized using this class.</param>
[ContractVerification(false)] // bugs/limitations in CC static analysis
private MessageSerializer(Type messageType) {
- Contract.Requires(messageType != null);
- Contract.Requires(typeof(IMessage).IsAssignableFrom(messageType));
+ Contract.Requires<ArgumentNullException>(messageType != null);
+ Contract.Requires<ArgumentException>(typeof(IMessage).IsAssignableFrom(messageType));
Contract.Ensures(this.messageType != null);
-
- ErrorUtilities.VerifyArgumentNamed(
- typeof(IMessage).IsAssignableFrom(messageType),
- "messageType",
- MessagingStrings.UnexpectedType,
- typeof(IMessage).FullName,
- messageType.FullName);
-
this.messageType = messageType;
}
@@ -54,9 +46,8 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>A message serializer for the given message type.</returns>
[ContractVerification(false)] // bugs/limitations in CC static analysis
internal static MessageSerializer Get(Type messageType) {
- Contract.Requires(messageType != null);
- Contract.Requires(typeof(IMessage).IsAssignableFrom(messageType));
- ErrorUtilities.VerifyArgumentNotNull(messageType, "messageType");
+ Contract.Requires<ArgumentNullException>(messageType != null);
+ Contract.Requires<ArgumentException>(typeof(IMessage).IsAssignableFrom(messageType));
return new MessageSerializer(messageType);
}
@@ -66,11 +57,11 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="messageDictionary">The message to be serialized.</param>
/// <returns>The dictionary of values to send for the message.</returns>
+ [Pure]
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
internal IDictionary<string, string> Serialize(MessageDictionary messageDictionary) {
- Contract.Requires(messageDictionary != null);
+ Contract.Requires<ArgumentNullException>(messageDictionary != null);
Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
- ErrorUtilities.VerifyArgumentNotNull(messageDictionary, "messageDictionary");
// Rather than hand back the whole message dictionary (which
// includes keys with blank values), create a new dictionary
@@ -100,10 +91,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="messageDictionary">The message to deserialize into.</param>
/// <exception cref="ProtocolException">Thrown when protocol rules are broken by the incoming message.</exception>
internal void Deserialize(IDictionary<string, string> fields, MessageDictionary messageDictionary) {
- Contract.Requires(fields != null);
- Contract.Requires(messageDictionary != null);
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
- ErrorUtilities.VerifyArgumentNotNull(messageDictionary, "messageDictionary");
+ Contract.Requires<ArgumentNullException>(fields != null);
+ Contract.Requires<ArgumentNullException>(messageDictionary != null);
var messageDescription = messageDictionary.Description;
@@ -124,9 +113,10 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
- protected void ObjectInvariant() {
+ private void ObjectInvariant() {
Contract.Invariant(this.messageType != null);
}
#endif
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
index 13e83e1..5654c00 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
@@ -70,6 +70,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false..
+ /// </summary>
+ internal static string BinaryDataRequiresMultipart {
+ get {
+ return ResourceManager.GetString("BinaryDataRequiresMultipart", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed..
/// </summary>
internal static string CurrentHttpContextRequired {
@@ -115,7 +124,7 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Looks up a localized string similar to The given set of options ({0}) is not supported by {1}..
+ /// Looks up a localized string similar to The given set of options is not supported by this web request handler..
/// </summary>
internal static string DirectWebRequestOptionsNotSupported {
get {
@@ -304,6 +313,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to A non-empty string was expected..
+ /// </summary>
+ internal static string NonEmptyStringExpected {
+ get {
+ return ResourceManager.GetString("NonEmptyStringExpected", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to A message response is already queued for sending in the response stream..
/// </summary>
internal static string QueuedMessageResponseAlreadyExists {
@@ -394,6 +412,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to The stream must have a known length..
+ /// </summary>
+ internal static string StreamMustHaveKnownLength {
+ get {
+ return ResourceManager.GetString("StreamMustHaveKnownLength", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The stream&apos;s CanRead property returned false..
/// </summary>
internal static string StreamUnreadable {
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
index 4bc92a7..34385d4 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
@@ -136,7 +136,7 @@
<value>The directed message's Recipient property must not be null.</value>
</data>
<data name="DirectWebRequestOptionsNotSupported" xml:space="preserve">
- <value>The given set of options ({0}) is not supported by {1}.</value>
+ <value>The given set of options is not supported by this web request handler.</value>
</data>
<data name="ErrorDeserializingMessage" xml:space="preserve">
<value>Error while deserializing message {0}.</value>
@@ -291,4 +291,13 @@
<data name="UnsupportedHttpVerb" xml:space="preserve">
<value>The HTTP verb '{0}' is unrecognized and unsupported.</value>
</data>
+ <data name="NonEmptyStringExpected" xml:space="preserve">
+ <value>A non-empty string was expected.</value>
+ </data>
+ <data name="StreamMustHaveKnownLength" xml:space="preserve">
+ <value>The stream must have a known length.</value>
+ </data>
+ <data name="BinaryDataRequiresMultipart" xml:space="preserve">
+ <value>Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.sr.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.sr.resx
new file mode 100644
index 0000000..5b7b716
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.sr.resx
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ArgumentPropertyMissing" xml:space="preserve">
+ <value>Svojstvo {0}.{1} argumenta je neophodno, ali je ono prazno ili nepostojeće.</value>
+ </data>
+ <data name="CurrentHttpContextRequired" xml:space="preserve">
+ <value>HttpContext.Current je nepostojeći. Mora postojati ASP.NET zahtev u procesu da bi ova operacija bila uspešna.</value>
+ </data>
+ <data name="DataContractMissingFromMessageType" xml:space="preserve">
+ <value>DataContractSerializer se ne može inicijalizovati na tipu poruke {0}. Da li nedostaje [DataContract] atribut?</value>
+ </data>
+ <data name="DataContractMissingNamespace" xml:space="preserve">
+ <value>DataContractSerializer se ne može inicijalizovati na tipu poruke {0} jer svojstvo DataContractAttribute.Namespace property nije podešeno.</value>
+ </data>
+ <data name="DerivedTypeNotExpected" xml:space="preserve">
+ <value>Instanca tipa {0} je bila očekivana, a primljena je neočekivana izvedena instanca tipa {1}.</value>
+ </data>
+ <data name="DirectedMessageMissingRecipient" xml:space="preserve">
+ <value>Svojstvo Recipient usmerene poruke ne sme biti nepostojeće.</value>
+ </data>
+ <data name="DirectWebRequestOptionsNotSupported" xml:space="preserve">
+ <value>Dati set opcija ({0}) nije podržan od strane {1}.</value>
+ </data>
+ <data name="ErrorDeserializingMessage" xml:space="preserve">
+ <value>Greška prilikom deserijalizacije poruke {0}.</value>
+ </data>
+ <data name="ErrorInRequestReplyMessage" xml:space="preserve">
+ <value>Greška se desila tokom slanja usmerene poruke ili tokom primanja odgovora.</value>
+ </data>
+ <data name="ExceptionNotConstructedForTransit" xml:space="preserve">
+ <value>Ovaj izuzetak nije napravljen sa početnom porukom koja ga je izazvala.</value>
+ </data>
+ <data name="ExpectedMessageNotReceived" xml:space="preserve">
+ <value>Očekivana je poruka {0} a primljena poruka nije prepoznata.</value>
+ </data>
+ <data name="ExpiredMessage" xml:space="preserve">
+ <value>Poruka ističe u {0} a sada je {1}.</value>
+ </data>
+ <data name="GetOrPostFlagsRequired" xml:space="preserve">
+ <value>Bar jedan od GET ili POST flegova mora biti prisutan.</value>
+ </data>
+ <data name="HttpContextRequired" xml:space="preserve">
+ <value>Ovaj metod zahteva tekući HttpContext. Kao alternativa, koristite preklopljeni metod koji dozvoljava da se prosledi informacija bez HttpContext-a.</value>
+ </data>
+ <data name="IndirectMessagesMustImplementIDirectedProtocolMessage" xml:space="preserve">
+ <value>Poruke koje ukazuju na indirektni transport moraju implementirati {0} interfejs.</value>
+ </data>
+ <data name="InsecureWebRequestWithSslRequired" xml:space="preserve">
+ <value>Nebezbedan web zahtev za '{0}' prekinut zbog bezbednosnih zahteva koji zahtevaju HTTPS.</value>
+ </data>
+ <data name="InsufficientMessageProtection" xml:space="preserve">
+ <value>Poruka {0} je zahtevala zaštite {{{1}}} ali prenosni kanal nije mogao primeniti {{{2}}}.</value>
+ </data>
+ <data name="InvalidCustomBindingElementOrder" xml:space="preserve">
+ <value>Redosled prilagođenih vezujućih elemenata je neispravan.</value>
+ </data>
+ <data name="InvalidMessageParts" xml:space="preserve">
+ <value>Neki deo ili delovi poruke imaju nevalidne vrednosti: {0}</value>
+ </data>
+ <data name="InvalidNonceReceived" xml:space="preserve">
+ <value>Primljena poruka imala je neispravan ili nedostajući jedinstveni identifikator.</value>
+ </data>
+ <data name="KeyAlreadyExists" xml:space="preserve">
+ <value>Element sa istom vrednošću ključa je već dodat.</value>
+ </data>
+ <data name="MessageNotExtensible" xml:space="preserve">
+ <value>Poruka {0} ne podržava ekstenzije.</value>
+ </data>
+ <data name="MessagePartEncoderWrongType" xml:space="preserve">
+ <value>Vrednost za {0}.{1} člana {1} je trebala da bude izvedena od {2} ali je izvedena od {3}.</value>
+ </data>
+ <data name="MessagePartReadFailure" xml:space="preserve">
+ <value>Greška prilikom čitanja poruke '{0}' parametar '{1}' sa vrednošću '{2}'.</value>
+ </data>
+ <data name="MessagePartValueBase64DecodingFault" xml:space="preserve">
+ <value>Parametar poruke '{0}' sa vrednošću '{1}' nije se base64-dekodovao.</value>
+ </data>
+ <data name="MessagePartWriteFailure" xml:space="preserve">
+ <value>Greška prilikom pripremanja poruke '{0}' parametra '{1}' za slanje.</value>
+ </data>
+ <data name="QueuedMessageResponseAlreadyExists" xml:space="preserve">
+ <value>Poruka-odgovor je već u redu za slanje u stream-u za odgovore.</value>
+ </data>
+ <data name="ReplayAttackDetected" xml:space="preserve">
+ <value>Ova poruka je već obrađena. Ovo može ukazivati na replay napad u toku.</value>
+ </data>
+ <data name="ReplayProtectionNotSupported" xml:space="preserve">
+ <value>Ovaj kanal ne podržava replay zaštitu.</value>
+ </data>
+ <data name="RequiredNonEmptyParameterWasEmpty" xml:space="preserve">
+ <value>Sledeći zahtevani parametri koji ne smeju biti prazni su bili prazni u {0} poruke: {1}</value>
+ </data>
+ <data name="RequiredParametersMissing" xml:space="preserve">
+ <value>Sledeći zahtevani parametri nedostaju u {0} poruke: {1}</value>
+ </data>
+ <data name="RequiredProtectionMissing" xml:space="preserve">
+ <value>Povezujući element koji nudi {0} zaštitu zahteva drugu zaštitu koja nije ponuđena.</value>
+ </data>
+ <data name="SequenceContainsNoElements" xml:space="preserve">
+ <value>Lista je prazna.</value>
+ </data>
+ <data name="SequenceContainsNullElement" xml:space="preserve">
+ <value>Lista sadrži prazan (null) element.</value>
+ </data>
+ <data name="SignatureInvalid" xml:space="preserve">
+ <value>Potpis poruke je neispravan.</value>
+ </data>
+ <data name="SigningNotSupported" xml:space="preserve">
+ <value>Ovaj kanal ne podržava potpisivanje poruka. Da bi podržao potpisivanje poruka, izvedeni tip Channel mora preklopiti Sign i IsSignatureValid metode.</value>
+ </data>
+ <data name="StreamUnreadable" xml:space="preserve">
+ <value>Svojstvo stream-a CanRead je vratilo false.</value>
+ </data>
+ <data name="StreamUnwritable" xml:space="preserve">
+ <value>Svojstvo stream-a CanWrite je vratilo false.</value>
+ </data>
+ <data name="TooManyBindingsOfferingSameProtection" xml:space="preserve">
+ <value>Očekivano je da najviše 1 povezujući element primeni zaštitu {0}, ali je više njih primenjeno.</value>
+ </data>
+ <data name="TooManyRedirects" xml:space="preserve">
+ <value>Maksimalno dozvoljeni broj redirekcija je prekoračen u toku zahtevanja '{0}'.</value>
+ </data>
+ <data name="UnexpectedEmptyArray" xml:space="preserve">
+ <value>Niz ne sme biti prazan.</value>
+ </data>
+ <data name="UnexpectedEmptyString" xml:space="preserve">
+ <value>Prazan string nije dozvoljen.</value>
+ </data>
+ <data name="UnexpectedMessagePartValue" xml:space="preserve">
+ <value>Parametar poruke '{0}' ima neočekivanu '{1}'.</value>
+ </data>
+ <data name="UnexpectedMessagePartValueForConstant" xml:space="preserve">
+ <value>Očekivano je da od poruke {0} parametar '{1}' ima vrednost '{2}' ali je imao vrednost '{3}'.</value>
+ </data>
+ <data name="UnexpectedMessageReceived" xml:space="preserve">
+ <value>Očekivana je poruka {0} ali je umesto nje primljena {1}.</value>
+ </data>
+ <data name="UnexpectedMessageReceivedOfMany" xml:space="preserve">
+ <value>Poruka neočekivanog tipa je primljena.</value>
+ </data>
+ <data name="UnexpectedNullKey" xml:space="preserve">
+ <value>null ključ je uključen a nije dozvoljen.</value>
+ </data>
+ <data name="UnexpectedNullOrEmptyKey" xml:space="preserve">
+ <value>null ili prazan ključ je uključen a nije dozvoljen.</value>
+ </data>
+ <data name="UnexpectedNullValue" xml:space="preserve">
+ <value>null vrednost je uključena za ključ '{0}' a nije dozvoljena.</value>
+ </data>
+ <data name="UnexpectedType" xml:space="preserve">
+ <value>Tip {0} ili izvedeni tip je očekivan, a dat je {1}.</value>
+ </data>
+ <data name="UnrecognizedEnumValue" xml:space="preserve">
+ <value>{0} svojstvo ima nepoznatu vrednost {1}.</value>
+ </data>
+ <data name="UnsafeWebRequestDetected" xml:space="preserve">
+ <value>URL '{0}' je rangiran kao nebezbedan i ne može se zahtevati na ovaj način.</value>
+ </data>
+ <data name="UntrustedRedirectsOnPOSTNotSupported" xml:space="preserve">
+ <value>Redirekcije na POST zahteve usmerene ka serverima kojima se ne veruje nisu podržane.</value>
+ </data>
+ <data name="WebRequestFailed" xml:space="preserve">
+ <value>Web zahtev za '{0}' nije uspeo.</value>
+ </data>
+ <data name="ExceptionUndeliverable" xml:space="preserve">
+ <value>Ovaj izuzetak mora se kreirati zajedno sa primaocem koji će primiti poruku o grešci ili sa instancom poruke direktnog zahteva na koju će ovaj izuzetak odgovoriti.</value>
+ </data>
+ <data name="UnsupportedHttpVerbForMessageType" xml:space="preserve">
+ <value>'{0}' poruka ne može biti primljeno sa HTTP glagolom '{1}'.</value>
+ </data>
+ <data name="UnexpectedHttpStatusCode" xml:space="preserve">
+ <value>Očekivano je da direktan odgovor koristi HTTP status kod {0} a korišćen je {1}.</value>
+ </data>
+ <data name="UnsupportedHttpVerb" xml:space="preserve">
+ <value>HTTP glagol '{0}' je neprepoznat i nije podržan.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index 7d89b4e..fe8d6c2 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.Messaging {
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -90,8 +91,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="response">The response to send to the uesr agent.</param>
/// <returns>The <see cref="ActionResult"/> instance to be returned by the Controller's action method.</returns>
public static ActionResult AsActionResult(this OutgoingWebResponse response) {
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
return new OutgoingWebResponseActionResult(response);
}
@@ -103,7 +103,7 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "The Uri merging requires use of a string value.")]
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call should not be a property.")]
public static Uri GetRequestUrlFromContext() {
- ErrorUtilities.VerifyHttpContext();
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
HttpContext context = HttpContext.Current;
// We use Request.Url for the full path to the server, and modify it
@@ -123,8 +123,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="prefix">The prefix for parameters to remove. A period is NOT automatically appended.</param>
/// <returns>Either a new Uri with the parameters removed if there were any to remove, or the same Uri instance if no parameters needed to be removed.</returns>
public static Uri StripQueryArgumentsWithPrefix(this Uri uri, string prefix) {
- ErrorUtilities.VerifyArgumentNotNull(uri, "uri");
- ErrorUtilities.VerifyNonZeroLength(prefix, "prefix");
+ Contract.Requires<ArgumentNullException>(uri != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(prefix));
NameValueCollection queryArgs = HttpUtility.ParseQueryString(uri.Query);
var matchingKeys = queryArgs.Keys.OfType<string>().Where(key => key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList();
@@ -141,11 +141,27 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Sends a multipart HTTP POST request (useful for posting files).
+ /// </summary>
+ /// <param name="request">The HTTP request.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="parts">The parts to include in the POST entity.</param>
+ /// <returns>The HTTP response.</returns>
+ public static IncomingWebResponse PostMultipart(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Requires<ArgumentNullException>(parts != null);
+
+ PostMultipartNoGetResponse(request, requestHandler, parts);
+ return requestHandler.GetResponse(request);
+ }
+
+ /// <summary>
/// Assembles a message comprised of the message on a given exception and all inner exceptions.
/// </summary>
/// <param name="exception">The exception.</param>
/// <returns>The assembled message.</returns>
- internal static string GetAllMessages(this Exception exception) {
+ public static string ToStringDescriptive(this Exception exception) {
// The input being null is probably bad, but since this method is called
// from a catch block, we don't really want to throw a new exception and
// hide the details of this one.
@@ -166,6 +182,60 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it.
+ /// </summary>
+ /// <param name="request">The HTTP request.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="parts">The parts to include in the POST entity.</param>
+ internal static void PostMultipartNoGetResponse(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Requires<ArgumentNullException>(parts != null);
+
+ Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart");
+ parts = parts.CacheGeneratedResults();
+ string boundary = Guid.NewGuid().ToString();
+ string initialPartLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "--{0}\r\n", boundary);
+ string partLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}\r\n", boundary);
+ string finalTrailingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}--\r\n", boundary);
+
+ request.Method = "POST";
+ request.ContentType = "multipart/form-data; boundary=" + boundary;
+ long contentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length;
+ if (parts.Any()) {
+ contentLength -= 2; // the initial part leading boundary has no leading \r\n
+ }
+ request.ContentLength = contentLength;
+
+ // Setting the content-encoding to "utf-8" causes Google to reply
+ // with a 415 UnsupportedMediaType. But adding it doesn't buy us
+ // anything specific, so we disable it until we know how to get it right.
+ ////request.Headers[HttpRequestHeader.ContentEncoding] = Channel.PostEntityEncoding.WebName;
+
+ var requestStream = requestHandler.GetRequestStream(request);
+ try {
+ StreamWriter writer = new StreamWriter(requestStream, Channel.PostEntityEncoding);
+ bool firstPart = true;
+ foreach (var part in parts) {
+ writer.Write(firstPart ? initialPartLeadingBoundary : partLeadingBoundary);
+ firstPart = false;
+ part.Serialize(writer);
+ part.Dispose();
+ }
+
+ writer.Write(finalTrailingBoundary);
+ writer.Flush();
+ } finally {
+ // We need to be sure to close the request stream...
+ // unless it is a MemoryStream, which is a clue that we're in
+ // a mock stream situation and closing it would preclude reading it later.
+ if (!(requestStream is MemoryStream)) {
+ requestStream.Dispose();
+ }
+ }
+ }
+
+ /// <summary>
/// Gets a buffer of random data (not cryptographically strong).
/// </summary>
/// <param name="length">The length of the sequence to generate.</param>
@@ -206,8 +276,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="allowableCharacters">The allowable characters.</param>
/// <returns>A random string.</returns>
internal static string GetRandomString(int length, string allowableCharacters) {
- Contract.Requires(length >= 0);
- Contract.Requires(allowableCharacters != null && allowableCharacters.Length >= 2);
+ Contract.Requires<ArgumentOutOfRangeException>(length >= 0);
+ Contract.Requires<ArgumentException>(allowableCharacters != null && allowableCharacters.Length >= 2);
char[] randomString = new char[length];
for (int i = 0; i < length; i++) {
@@ -225,8 +295,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="headers">The headers to add.</param>
/// <param name="response">The <see cref="HttpResponse"/> instance to set the appropriate values to.</param>
internal static void ApplyHeadersToResponse(WebHeaderCollection headers, HttpResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(headers, "headers");
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(headers != null);
+ Contract.Requires<ArgumentNullException>(response != null);
foreach (string headerName in headers) {
switch (headerName) {
@@ -250,8 +320,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="headers">The headers to add.</param>
/// <param name="response">The <see cref="HttpListenerResponse"/> instance to set the appropriate values to.</param>
internal static void ApplyHeadersToResponse(WebHeaderCollection headers, HttpListenerResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(headers, "headers");
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(headers != null);
+ Contract.Requires<ArgumentNullException>(response != null);
foreach (string headerName in headers) {
switch (headerName) {
@@ -278,6 +348,10 @@ namespace DotNetOpenAuth.Messaging {
/// The positions are NOT reset after copying is complete.
/// </remarks>
internal static int CopyTo(this Stream copyFrom, Stream copyTo) {
+ Contract.Requires<ArgumentNullException>(copyFrom != null);
+ Contract.Requires<ArgumentNullException>(copyTo != null);
+ Contract.Requires<ArgumentException>(copyFrom.CanRead, MessagingStrings.StreamUnreadable);
+ Contract.Requires<ArgumentException>(copyTo.CanWrite, MessagingStrings.StreamUnwritable);
return CopyTo(copyFrom, copyTo, int.MaxValue);
}
@@ -293,10 +367,10 @@ namespace DotNetOpenAuth.Messaging {
/// The positions are NOT reset after copying is complete.
/// </remarks>
internal static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) {
- ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
- ErrorUtilities.VerifyArgumentNotNull(copyTo, "copyTo");
- ErrorUtilities.VerifyArgument(copyFrom.CanRead, MessagingStrings.StreamUnreadable);
- ErrorUtilities.VerifyArgument(copyTo.CanWrite, MessagingStrings.StreamUnwritable, "copyTo");
+ Contract.Requires<ArgumentNullException>(copyFrom != null);
+ Contract.Requires<ArgumentNullException>(copyTo != null);
+ Contract.Requires<ArgumentException>(copyFrom.CanRead, MessagingStrings.StreamUnreadable);
+ Contract.Requires<ArgumentException>(copyTo.CanWrite, MessagingStrings.StreamUnwritable);
byte[] buffer = new byte[1024];
int readBytes;
@@ -317,7 +391,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="copyFrom">The stream to copy bytes from.</param>
/// <returns>A seekable stream with the same contents as the original.</returns>
internal static Stream CreateSnapshot(this Stream copyFrom) {
- ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
+ Contract.Requires<ArgumentNullException>(copyFrom != null);
+ Contract.Requires<ArgumentException>(copyFrom.CanRead);
MemoryStream copyTo = new MemoryStream(copyFrom.CanSeek ? (int)copyFrom.Length : 4 * 1024);
copyFrom.CopyTo(copyTo);
@@ -331,7 +406,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request to clone.</param>
/// <returns>The newly created instance.</returns>
internal static HttpWebRequest Clone(this HttpWebRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(request.RequestUri != null);
return Clone(request, request.RequestUri);
}
@@ -342,8 +418,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="newRequestUri">The new recipient of the request.</param>
/// <returns>The newly created instance.</returns>
internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(newRequestUri, "newRequestUri");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(newRequestUri != null);
var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri);
@@ -409,8 +485,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="second">The second array in the comparison. May not be null.</param>
/// <returns>True if the arrays equal; false otherwise.</returns>
internal static bool AreEquivalent<T>(T[] first, T[] second) {
- ErrorUtilities.VerifyArgumentNotNull(first, "first");
- ErrorUtilities.VerifyArgumentNotNull(second, "second");
+ Contract.Requires<ArgumentNullException>(first != null);
+ Contract.Requires<ArgumentNullException>(second != null);
if (first.Length != second.Length) {
return false;
}
@@ -499,6 +575,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="second">The second dictionary in the comparison. May not be null.</param>
/// <returns>True if the arrays equal; false otherwise.</returns>
internal static bool AreEquivalent<TKey, TValue>(IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second) {
+ Contract.Requires<ArgumentNullException>(first != null);
+ Contract.Requires<ArgumentNullException>(second != null);
return AreEquivalent(first.ToArray(), second.ToArray());
}
@@ -510,9 +588,9 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="args">The dictionary of key/values to read from.</param>
/// <returns>The formulated querystring style string.</returns>
internal static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args) {
- Contract.Requires(args != null);
+ Contract.Requires<ArgumentNullException>(args != null);
Contract.Ensures(Contract.Result<string>() != null);
- ErrorUtilities.VerifyArgumentNotNull(args, "args");
+
if (args.Count() == 0) {
return string.Empty;
}
@@ -546,7 +624,7 @@ namespace DotNetOpenAuth.Messaging {
/// in the query string, the existing ones are <i>not</i> replaced.
/// </remarks>
internal static void AppendQueryArgs(this UriBuilder builder, IEnumerable<KeyValuePair<string, string>> args) {
- ErrorUtilities.VerifyArgumentNotNull(builder, "builder");
+ Contract.Requires<ArgumentNullException>(builder != null);
if (args != null && args.Count() > 0) {
StringBuilder sb = new StringBuilder(50 + (args.Count() * 10));
@@ -570,7 +648,7 @@ namespace DotNetOpenAuth.Messaging {
/// If null, <paramref name="builder"/> is not changed.
/// </param>
internal static void AppendAndReplaceQueryArgs(this UriBuilder builder, IEnumerable<KeyValuePair<string, string>> args) {
- ErrorUtilities.VerifyArgumentNotNull(builder, "builder");
+ Contract.Requires<ArgumentNullException>(builder != null);
if (args != null && args.Count() > 0) {
NameValueCollection aggregatedArgs = HttpUtility.ParseQueryString(builder.Query);
@@ -587,8 +665,54 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The request to get recipient information from.</param>
/// <returns>The recipient.</returns>
+ /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception>
internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
- return new MessageReceivingEndpoint(request.UrlBeforeRewriting, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
+ return new MessageReceivingEndpoint(request.UrlBeforeRewriting, GetHttpDeliveryMethod(request.HttpMethod));
+ }
+
+ /// <summary>
+ /// Gets the <see cref="HttpDeliveryMethods"/> enum value for a given HTTP verb.
+ /// </summary>
+ /// <param name="httpVerb">The HTTP verb.</param>
+ /// <returns>A <see cref="HttpDeliveryMethods"/> enum value that is within the <see cref="HttpDeliveryMethods.HttpVerbMask"/>.</returns>
+ /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception>
+ internal static HttpDeliveryMethods GetHttpDeliveryMethod(string httpVerb) {
+ if (httpVerb == "GET") {
+ return HttpDeliveryMethods.GetRequest;
+ } else if (httpVerb == "POST") {
+ return HttpDeliveryMethods.PostRequest;
+ } else if (httpVerb == "PUT") {
+ return HttpDeliveryMethods.PutRequest;
+ } else if (httpVerb == "DELETE") {
+ return HttpDeliveryMethods.DeleteRequest;
+ } else if (httpVerb == "HEAD") {
+ return HttpDeliveryMethods.HeadRequest;
+ } else {
+ throw ErrorUtilities.ThrowArgumentNamed("httpVerb", MessagingStrings.UnsupportedHttpVerb, httpVerb);
+ }
+ }
+
+ /// <summary>
+ /// Gets the HTTP verb to use for a given <see cref="HttpDeliveryMethods"/> enum value.
+ /// </summary>
+ /// <param name="httpMethod">The HTTP method.</param>
+ /// <returns>An HTTP verb, such as GET, POST, PUT, or DELETE.</returns>
+ internal static string GetHttpVerb(HttpDeliveryMethods httpMethod) {
+ if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.GetRequest) {
+ return "GET";
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PostRequest) {
+ return "POST";
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PutRequest) {
+ return "PUT";
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.DeleteRequest) {
+ return "DELETE";
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.HeadRequest) {
+ return "HEAD";
+ } else if ((httpMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) {
+ return "GET"; // if AuthorizationHeaderRequest is specified without an explicit HTTP verb, assume GET.
+ } else {
+ throw ErrorUtilities.ThrowArgumentNamed("httpMethod", MessagingStrings.UnsupportedHttpVerb, httpMethod);
+ }
}
/// <summary>
@@ -597,7 +721,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="messageDictionary">The message to copy the extra data into.</param>
/// <param name="extraParameters">The extra data to copy into the message. May be null to do nothing.</param>
internal static void AddExtraParameters(this MessageDictionary messageDictionary, IDictionary<string, string> extraParameters) {
- ErrorUtilities.VerifyArgumentNotNull(messageDictionary, "messageAccessor");
+ Contract.Requires<ArgumentNullException>(messageDictionary != null);
if (extraParameters != null) {
foreach (var pair in extraParameters) {
@@ -664,6 +788,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="comparer">A comparison function to compare keys.</param>
/// <returns>An System.Linq.IOrderedEnumerable&lt;TElement&gt; whose elements are sorted according to a key.</returns>
internal static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Comparison<TKey> comparer) {
+ Contract.Requires<ArgumentNullException>(source != null);
+ Contract.Requires<ArgumentNullException>(comparer != null);
+ Contract.Requires<ArgumentNullException>(keySelector != null);
+ Contract.Ensures(Contract.Result<IOrderedEnumerable<TSource>>() != null);
return System.Linq.Enumerable.OrderBy<TSource, TKey>(source, keySelector, new ComparisonHelper<TKey>(comparer));
}
@@ -680,7 +808,7 @@ namespace DotNetOpenAuth.Messaging {
/// if their <see cref="IDirectedProtocolMessage.Recipient"/> property is non-null.
/// </remarks>
internal static bool IsRequest(this IDirectedProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
return message.Recipient != null;
}
@@ -698,7 +826,7 @@ namespace DotNetOpenAuth.Messaging {
/// <see cref="IDirectResponseProtocolMessage.OriginatingRequest"/> property is non-null.
/// </remarks>
internal static bool IsDirectResponse(this IDirectResponseProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
return message.OriginatingRequest != null;
}
@@ -707,15 +835,16 @@ namespace DotNetOpenAuth.Messaging {
/// on the user agent when assigned to a variable.
/// </summary>
/// <param name="namesAndValues">The untrusted names and untrusted values to inject into the JSON object.</param>
+ /// <param name="valuesPreEncoded">if set to <c>true</c> the values will NOT be escaped as if it were a pure string.</param>
/// <returns>The Javascript JSON object as a string.</returns>
- internal static string CreateJsonObject(IEnumerable<KeyValuePair<string, string>> namesAndValues) {
+ internal static string CreateJsonObject(IEnumerable<KeyValuePair<string, string>> namesAndValues, bool valuesPreEncoded) {
StringBuilder builder = new StringBuilder();
builder.Append("{ ");
foreach (var pair in namesAndValues) {
builder.Append(MessagingUtilities.GetSafeJavascriptValue(pair.Key));
builder.Append(": ");
- builder.Append(MessagingUtilities.GetSafeJavascriptValue(pair.Value));
+ builder.Append(valuesPreEncoded ? pair.Value : MessagingUtilities.GetSafeJavascriptValue(pair.Value));
builder.Append(",");
}
@@ -760,6 +889,8 @@ namespace DotNetOpenAuth.Messaging {
/// host actually having this configuration element present.
/// </remarks>
internal static string EscapeUriDataStringRfc3986(string value) {
+ Contract.Requires<ArgumentNullException>(value != null);
+
// Start with RFC 2396 escaping by calling the .NET method to do the work.
// This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
// If it does, the escaping we do that follows it will be a no-op since the
@@ -816,9 +947,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="comparison">The comparison method to use.</param>
internal ComparisonHelper(Comparison<T> comparison) {
- if (comparison == null) {
- throw new ArgumentNullException("comparison");
- }
+ Contract.Requires<ArgumentNullException>(comparison != null);
this.comparison = comparison;
}
diff --git a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
new file mode 100644
index 0000000..f9a5988
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
@@ -0,0 +1,206 @@
+//-----------------------------------------------------------------------
+// <copyright file="MultipartPostPart.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Net;
+ using System.Text;
+
+ /// <summary>
+ /// Represents a single part in a HTTP multipart POST request.
+ /// </summary>
+ public class MultipartPostPart : IDisposable {
+ /// <summary>
+ /// The "Content-Disposition" string.
+ /// </summary>
+ private const string ContentDispositionHeader = "Content-Disposition";
+
+ /// <summary>
+ /// The two-character \r\n newline character sequence to use.
+ /// </summary>
+ private const string NewLine = "\r\n";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MultipartPostPart"/> class.
+ /// </summary>
+ /// <param name="contentDisposition">The content disposition of the part.</param>
+ public MultipartPostPart(string contentDisposition) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentDisposition));
+
+ this.ContentDisposition = contentDisposition;
+ this.ContentAttributes = new Dictionary<string, string>();
+ this.PartHeaders = new WebHeaderCollection();
+ }
+
+ /// <summary>
+ /// Gets the content disposition.
+ /// </summary>
+ /// <value>The content disposition.</value>
+ public string ContentDisposition { get; private set; }
+
+ /// <summary>
+ /// Gets the key=value attributes that appear on the same line as the Content-Disposition.
+ /// </summary>
+ /// <value>The content attributes.</value>
+ public IDictionary<string, string> ContentAttributes { get; private set; }
+
+ /// <summary>
+ /// Gets the headers that appear on subsequent lines after the Content-Disposition.
+ /// </summary>
+ public WebHeaderCollection PartHeaders { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the content of the part.
+ /// </summary>
+ public Stream Content { get; set; }
+
+ /// <summary>
+ /// Gets the length of this entire part.
+ /// </summary>
+ /// <remarks>Useful for calculating the ContentLength HTTP header to send before actually serializing the content.</remarks>
+ public long Length {
+ get {
+ ErrorUtilities.VerifyOperation(this.Content != null && this.Content.Length >= 0, MessagingStrings.StreamMustHaveKnownLength);
+
+ long length = 0;
+ length += ContentDispositionHeader.Length;
+ length += ": ".Length;
+ length += this.ContentDisposition.Length;
+ foreach (var pair in this.ContentAttributes) {
+ length += "; ".Length + pair.Key.Length + "=\"".Length + pair.Value.Length + "\"".Length;
+ }
+
+ length += NewLine.Length;
+ foreach (string headerName in this.PartHeaders) {
+ length += headerName.Length;
+ length += ": ".Length;
+ length += this.PartHeaders[headerName].Length;
+ length += NewLine.Length;
+ }
+
+ length += NewLine.Length;
+ length += this.Content.Length;
+
+ return length;
+ }
+ }
+
+ /// <summary>
+ /// Creates a part that represents a simple form field.
+ /// </summary>
+ /// <param name="name">The name of the form field.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>The constructed part.</returns>
+ public static MultipartPostPart CreateFormPart(string name, string value) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
+ Contract.Requires<ArgumentException>(value != null);
+
+ var part = new MultipartPostPart("form-data");
+ part.ContentAttributes["name"] = name;
+ part.Content = new MemoryStream(Encoding.UTF8.GetBytes(value));
+ return part;
+ }
+
+ /// <summary>
+ /// Creates a part that represents a file attachment.
+ /// </summary>
+ /// <param name="name">The name of the form field.</param>
+ /// <param name="filePath">The path to the file to send.</param>
+ /// <param name="contentType">Type of the content in HTTP Content-Type format.</param>
+ /// <returns>The constructed part.</returns>
+ public static MultipartPostPart CreateFormFilePart(string name, string filePath, string contentType) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(filePath));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType));
+
+ string fileName = Path.GetFileName(filePath);
+ return CreateFormFilePart(name, fileName, contentType, File.OpenRead(filePath));
+ }
+
+ /// <summary>
+ /// Creates a part that represents a file attachment.
+ /// </summary>
+ /// <param name="name">The name of the form field.</param>
+ /// <param name="fileName">Name of the file as the server should see it.</param>
+ /// <param name="contentType">Type of the content in HTTP Content-Type format.</param>
+ /// <param name="content">The content of the file.</param>
+ /// <returns>The constructed part.</returns>
+ public static MultipartPostPart CreateFormFilePart(string name, string fileName, string contentType, Stream content) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType));
+ Contract.Requires<ArgumentException>(content != null);
+
+ var part = new MultipartPostPart("file");
+ part.ContentAttributes["name"] = name;
+ part.ContentAttributes["filename"] = fileName;
+ part.PartHeaders[HttpRequestHeader.ContentType] = contentType;
+ if (!contentType.StartsWith("text/", StringComparison.Ordinal)) {
+ part.PartHeaders["Content-Transfer-Encoding"] = "binary";
+ }
+ part.Content = content;
+ return part;
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose() {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Serializes the part to a stream.
+ /// </summary>
+ /// <param name="streamWriter">The stream writer.</param>
+ internal void Serialize(StreamWriter streamWriter) {
+ // VERY IMPORTANT: any changes at all made to this must be kept in sync with the
+ // Length property which calculates exactly how many bytes this method will write.
+ streamWriter.NewLine = NewLine;
+ streamWriter.Write("{0}: {1}", ContentDispositionHeader, this.ContentDisposition);
+ foreach (var pair in this.ContentAttributes) {
+ streamWriter.Write("; {0}=\"{1}\"", pair.Key, pair.Value);
+ }
+
+ streamWriter.WriteLine();
+ foreach (string headerName in this.PartHeaders) {
+ streamWriter.WriteLine("{0}: {1}", headerName, this.PartHeaders[headerName]);
+ }
+
+ streamWriter.WriteLine();
+ streamWriter.Flush();
+ this.Content.CopyTo(streamWriter.BaseStream);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ this.Content.Dispose();
+ }
+ }
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void Invariant() {
+ Contract.Invariant(!string.IsNullOrEmpty(this.ContentDisposition));
+ Contract.Invariant(this.PartHeaders != null);
+ Contract.Invariant(this.ContentAttributes != null);
+ }
+#endif
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
index 2ee7feb..643cf81 100644
--- a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using System.Text;
@@ -15,6 +16,7 @@ namespace DotNetOpenAuth.Messaging {
/// A live network HTTP response
/// </summary>
[DebuggerDisplay("{Status} {ContentType.MediaType}")]
+ [ContractVerification(true)]
internal class NetworkDirectWebResponse : IncomingWebResponse, IDisposable {
/// <summary>
/// The network response object, used to initialize this instance, that still needs
@@ -40,6 +42,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="response">The response.</param>
internal NetworkDirectWebResponse(Uri requestUri, HttpWebResponse response)
: base(requestUri, response) {
+ Contract.Requires<ArgumentNullException>(requestUri != null);
+ Contract.Requires<ArgumentNullException>(response != null);
this.httpWebResponse = response;
this.responseStream = response.GetResponseStream();
}
@@ -82,6 +86,8 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
ErrorUtilities.VerifyOperation(!this.streamReadBegun, "Network stream reading has already begun.");
+ ErrorUtilities.VerifyOperation(this.httpWebResponse != null, "httpWebResponse != null");
+
this.streamReadBegun = true;
var result = new CachedDirectWebResponse(this.RequestUri, this.httpWebResponse, maximumBytesToCache);
this.Dispose();
diff --git a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
index bad582c..f6a930c 100644
--- a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
@@ -30,6 +30,11 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
public class OutgoingWebResponse {
/// <summary>
+ /// The encoder to use for serializing the response body.
+ /// </summary>
+ private static Encoding bodyStringEncoder = new UTF8Encoding(false);
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OutgoingWebResponse"/> class.
/// </summary>
internal OutgoingWebResponse() {
@@ -44,7 +49,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="response">The <see cref="HttpWebResponse"/> to clone.</param>
/// <param name="maximumBytesToRead">The maximum bytes to read from the response stream.</param>
protected internal OutgoingWebResponse(HttpWebResponse response, int maximumBytesToRead) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.Status = response.StatusCode;
this.Headers = response.Headers;
@@ -120,8 +125,7 @@ namespace DotNetOpenAuth.Messaging {
/// Requires a current HttpContext.
/// </remarks>
public virtual void Send() {
- Contract.Requires(HttpContext.Current != null);
- ErrorUtilities.VerifyHttpContext();
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
this.Send(HttpContext.Current);
}
@@ -134,8 +138,7 @@ namespace DotNetOpenAuth.Messaging {
/// Typically this is <see cref="HttpContext.Current"/>.</param>
/// <exception cref="ThreadAbortException">Thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
public virtual void Send(HttpContext context) {
- Contract.Requires(context != null);
- ErrorUtilities.VerifyArgumentNotNull(context, "context");
+ Contract.Requires<ArgumentNullException>(context != null);
context.Response.Clear();
context.Response.StatusCode = (int)this.Status;
@@ -162,8 +165,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="response">The response to set to this message.</param>
public virtual void Send(HttpListenerResponse response) {
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
response.StatusCode = (int)this.Status;
MessagingUtilities.ApplyHeadersToResponse(this.Headers, response);
@@ -186,8 +188,7 @@ namespace DotNetOpenAuth.Messaging {
/// rather than cause a redirect.
/// </remarks>
internal Uri GetDirectUriRequest(Channel channel) {
- Contract.Requires(channel != null);
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
var message = this.OriginalMessage as IDirectedProtocolMessage;
if (message == null) {
@@ -210,10 +211,9 @@ namespace DotNetOpenAuth.Messaging {
return;
}
- Encoding encoding = Encoding.UTF8;
- this.Headers[HttpResponseHeader.ContentEncoding] = encoding.HeaderName;
+ this.Headers[HttpResponseHeader.ContentEncoding] = bodyStringEncoder.HeaderName;
this.ResponseStream = new MemoryStream();
- StreamWriter writer = new StreamWriter(this.ResponseStream, encoding);
+ StreamWriter writer = new StreamWriter(this.ResponseStream, bodyStringEncoder);
writer.Write(body);
writer.Flush();
this.ResponseStream.Seek(0, SeekOrigin.Begin);
diff --git a/src/DotNetOpenAuth/Messaging/OutgoingWebResponseActionResult.cs b/src/DotNetOpenAuth/Messaging/OutgoingWebResponseActionResult.cs
index 2da1ebf..1cfc638 100644
--- a/src/DotNetOpenAuth/Messaging/OutgoingWebResponseActionResult.cs
+++ b/src/DotNetOpenAuth/Messaging/OutgoingWebResponseActionResult.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
+ using System;
using System.Diagnostics.Contracts;
using System.Web.Mvc;
using DotNetOpenAuth.Messaging;
@@ -24,8 +25,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="response">The response.</param>
internal OutgoingWebResponseActionResult(OutgoingWebResponse response) {
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
}
diff --git a/src/DotNetOpenAuth/Messaging/ProtocolException.cs b/src/DotNetOpenAuth/Messaging/ProtocolException.cs
index daf13d7..25f8eee 100644
--- a/src/DotNetOpenAuth/Messaging/ProtocolException.cs
+++ b/src/DotNetOpenAuth/Messaging/ProtocolException.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Security.Permissions;
/// <summary>
@@ -43,7 +44,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="faultedMessage">The message that was the cause of the exception. Must not be null.</param>
protected internal ProtocolException(string message, IProtocolMessage faultedMessage)
: base(message) {
- ErrorUtilities.VerifyArgumentNotNull(faultedMessage, "faultedMessage");
+ Contract.Requires<ArgumentNullException>(faultedMessage != null);
this.FaultedMessage = faultedMessage;
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs
index d2c9596..03534f2 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartEncoder.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -16,6 +17,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <remarks>
/// Implementations of this interface must include a default constructor and must be thread-safe.
/// </remarks>
+ [ContractClass(typeof(IMessagePartEncoderContract))]
public interface IMessagePartEncoder {
/// <summary>
/// Encodes the specified value.
@@ -32,4 +34,45 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
object Decode(string value);
}
+
+ /// <summary>
+ /// Code contract for the <see cref="IMessagePartEncoder"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IMessagePartEncoder))]
+ internal abstract class IMessagePartEncoderContract : IMessagePartEncoder {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IMessagePartEncoderContract"/> class.
+ /// </summary>
+ protected IMessagePartEncoderContract() {
+ }
+
+ #region IMessagePartEncoder Members
+
+ /// <summary>
+ /// Encodes the specified value.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>
+ /// The <paramref name="value"/> in string form, ready for message transport.
+ /// </returns>
+ string IMessagePartEncoder.Encode(object value) {
+ Contract.Requires<ArgumentNullException>(value != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Decodes the specified value.
+ /// </summary>
+ /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
+ /// <returns>
+ /// The deserialized form of the given string.
+ /// </returns>
+ /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
+ object IMessagePartEncoder.Decode(string value) {
+ Contract.Requires<ArgumentNullException>(value != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
index 5cb7877..5493ba6 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
@@ -40,17 +40,9 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messageType">Type of the message.</param>
/// <param name="messageVersion">The message version.</param>
internal MessageDescription(Type messageType, Version messageVersion) {
- Contract.Requires(messageType != null && typeof(IMessage).IsAssignableFrom(messageType));
- Contract.Requires(messageVersion != null);
- ErrorUtilities.VerifyArgumentNotNull(messageType, "messageType");
- ErrorUtilities.VerifyArgumentNotNull(messageVersion, "messageVersion");
- if (!typeof(IMessage).IsAssignableFrom(messageType)) {
- throw new ArgumentException(string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(IMessage),
- messageType));
- }
+ Contract.Requires<ArgumentNullException>(messageType != null);
+ Contract.Requires<ArgumentException>(typeof(IMessage).IsAssignableFrom(messageType));
+ Contract.Requires<ArgumentNullException>(messageVersion != null);
this.messageType = messageType;
this.messageVersion = messageVersion;
@@ -70,10 +62,10 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="message">The message the dictionary should provide access to.</param>
/// <returns>The dictionary accessor to the message</returns>
+ [Pure]
internal MessageDictionary GetDictionary(IMessage message) {
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
Contract.Ensures(Contract.Result<MessageDictionary>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
return new MessageDictionary(message, this);
}
@@ -114,10 +106,19 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Ensures the message parts pass basic validation.
/// </summary>
- /// <param name="parts">The key/value pairs of the serialzied message.</param>
+ /// <param name="parts">The key/value pairs of the serialized message.</param>
internal void EnsureMessagePartsPassBasicValidation(IDictionary<string, string> parts) {
- this.EnsureRequiredMessagePartsArePresent(parts.Keys);
- this.EnsureRequiredProtocolMessagePartsAreNotEmpty(parts);
+ try {
+ this.EnsureRequiredMessagePartsArePresent(parts.Keys);
+ this.EnsureRequiredProtocolMessagePartsAreNotEmpty(parts);
+ } catch (ProtocolException) {
+ Logger.Messaging.ErrorFormat(
+ "Error while performing basic validation of {0} with these message parts:{1}{2}",
+ this.messageType.Name,
+ Environment.NewLine,
+ parts.ToStringDeferred());
+ throw;
+ }
}
/// <summary>
@@ -127,7 +128,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="keys">The names of all parameters included in a message.</param>
/// <exception cref="ProtocolException">Thrown when required parts of a message are not in <paramref name="keys"/></exception>
private void EnsureRequiredMessagePartsArePresent(IEnumerable<string> keys) {
- var missingKeys = (from part in Mapping.Values
+ var missingKeys = (from part in this.Mapping.Values
where part.IsRequired && !keys.Contains(part.Name)
select part.Name).ToArray();
if (missingKeys.Length > 0) {
@@ -146,7 +147,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="partValues">A dictionary of key/value pairs that make up the serialized message.</param>
private void EnsureRequiredProtocolMessagePartsAreNotEmpty(IDictionary<string, string> partValues) {
string value;
- var emptyValuedKeys = (from part in Mapping.Values
+ var emptyValuedKeys = (from part in this.Mapping.Values
where !part.AllowEmpty && partValues.TryGetValue(part.Name, out value) && value != null && value.Length == 0
select part.Name).ToArray();
if (emptyValuedKeys.Length > 0) {
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
index 9af4bdf..ff8b74b 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -33,12 +33,12 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messageType">A type that implements <see cref="IMessage"/>.</param>
/// <param name="messageVersion">The protocol version of the message.</param>
/// <returns>A <see cref="MessageDescription"/> instance.</returns>
+ [Pure]
internal MessageDescription Get(Type messageType, Version messageVersion) {
- Contract.Requires(messageType != null && typeof(IMessage).IsAssignableFrom(messageType));
- Contract.Requires(messageVersion != null);
+ Contract.Requires<ArgumentNullException>(messageType != null);
+ Contract.Requires<ArgumentException>(typeof(IMessage).IsAssignableFrom(messageType));
+ Contract.Requires<ArgumentNullException>(messageVersion != null);
Contract.Ensures(Contract.Result<MessageDescription>() != null);
- ErrorUtilities.VerifyArgumentNotNull(messageType, "messageType");
- ErrorUtilities.VerifyArgumentNotNull(messageVersion, "messageVersion");
MessageTypeAndVersion key = new MessageTypeAndVersion(messageType, messageVersion);
@@ -63,10 +63,10 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <returns>
/// A <see cref="MessageDescription"/> instance.
/// </returns>
+ [Pure]
internal MessageDescription Get(IMessage message) {
- Contract.Requires(message != null);
+ Contract.Requires<ArgumentNullException>(message != null);
Contract.Ensures(Contract.Result<MessageDescription>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
return this.Get(message.GetType(), message.Version);
}
@@ -75,9 +75,9 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="message">The message.</param>
/// <returns>The dictionary.</returns>
+ [Pure]
internal MessageDictionary GetAccessor(IMessage message) {
- Contract.Requires(message != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
return this.Get(message).GetDictionary(message);
}
@@ -102,10 +102,8 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messageType">Type of the message.</param>
/// <param name="messageVersion">The message version.</param>
internal MessageTypeAndVersion(Type messageType, Version messageVersion) {
- Contract.Requires(messageType != null);
- Contract.Requires(messageVersion != null);
- ErrorUtilities.VerifyArgumentNotNull(messageType, "messageType");
- ErrorUtilities.VerifyArgumentNotNull(messageVersion, "messageVersion");
+ Contract.Requires<ArgumentNullException>(messageType != null);
+ Contract.Requires<ArgumentNullException>(messageVersion != null);
this.type = messageType;
this.version = messageVersion;
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index 0b5b6d0..f6eed24 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
/// <summary>
@@ -16,27 +17,27 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// provides access to both well-defined message properties and "extra"
/// name/value pairs that have no properties associated with them.
/// </summary>
+ [ContractVerification(false)]
internal class MessageDictionary : IDictionary<string, string> {
/// <summary>
/// The <see cref="IMessage"/> instance manipulated by this dictionary.
/// </summary>
- private IMessage message;
+ private readonly IMessage message;
/// <summary>
/// The <see cref="MessageDescription"/> instance that describes the message type.
/// </summary>
- private MessageDescription description;
+ private readonly MessageDescription description;
/// <summary>
/// Initializes a new instance of the <see cref="MessageDictionary"/> class.
/// </summary>
/// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
/// <param name="description">The message description.</param>
+ [Pure]
internal MessageDictionary(IMessage message, MessageDescription description) {
- Contract.Requires(message != null);
- Contract.Requires(description != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyArgumentNotNull(description, "description");
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<ArgumentNullException>(description != null);
this.message = message;
this.description = description;
@@ -202,9 +203,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Thrown if <paramref name="value"/> is null.
/// </exception>
public void Add(string key, string value) {
- if (value == null) {
- throw new ArgumentNullException("value");
- }
+ ErrorUtilities.VerifyArgumentNotNull(value, "value");
MessagePart part;
if (this.description.Mapping.TryGetValue(key, out part)) {
@@ -270,6 +269,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Sets a named value in the message.
/// </summary>
/// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void Add(KeyValuePair<string, string> item) {
this.Add(item.Key, item.Value);
}
@@ -371,7 +371,9 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Saves the data in a message to a standard dictionary.
/// </summary>
/// <returns>The generated dictionary.</returns>
+ [Pure]
public IDictionary<string, string> Serialize() {
+ Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
return this.Serializer.Serialize(this);
}
@@ -380,7 +382,21 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="fields">The data to load into the message.</param>
public void Deserialize(IDictionary<string, string> fields) {
+ Contract.Requires<ArgumentNullException>(fields != null);
this.Serializer.Deserialize(fields, this);
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.Message != null);
+ Contract.Invariant(this.Description != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index b02e52c..08e2411 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -9,7 +9,9 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Net.Security;
using System.Reflection;
using System.Xml;
@@ -18,6 +20,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Describes an individual member of a message and assists in its serialization.
/// </summary>
+ [ContractVerification(true)]
[DebuggerDisplay("MessagePart {Name}")]
internal class MessagePart {
/// <summary>
@@ -62,13 +65,39 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "By design.")]
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
static MessagePart() {
- Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
+ Func<string, Uri> safeUri = str => {
+ Contract.Assume(str != null);
+ return new Uri(str);
+ };
+ Func<string, bool> safeBool = str => {
+ Contract.Assume(str != null);
+ return bool.Parse(str);
+ };
+ Func<string, Identifier> safeIdentfier = str => {
+ Contract.Assume(str != null);
+ ErrorUtilities.VerifyFormat(str.Length > 0, MessagingStrings.NonEmptyStringExpected);
+ return Identifier.Parse(str);
+ };
+ Func<byte[], string> safeFromByteArray = bytes => {
+ Contract.Assume(bytes != null);
+ return Convert.ToBase64String(bytes);
+ };
+ Func<string, byte[]> safeToByteArray = str => {
+ Contract.Assume(str != null);
+ return Convert.FromBase64String(str);
+ };
+ Func<string, Realm> safeRealm = str => {
+ Contract.Assume(str != null);
+ return new Realm(str);
+ };
+ Map<Uri>(uri => uri.AbsoluteUri, safeUri);
Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
- Map<byte[]>(bytes => Convert.ToBase64String(bytes), str => Convert.FromBase64String(str));
- Map<Realm>(realm => realm.ToString(), str => new Realm(str));
- Map<Identifier>(id => id.ToString(), str => Identifier.Parse(str));
- Map<bool>(value => value.ToString().ToLowerInvariant(), str => bool.Parse(str));
+ Map<byte[]>(safeFromByteArray, safeToByteArray);
+ Map<Realm>(realm => realm.ToString(), safeRealm);
+ Map<Identifier>(id => id.ToString(), safeIdentfier);
+ Map<bool>(value => value.ToString().ToLowerInvariant(), safeBool);
Map<CultureInfo>(c => c.Name, str => new CultureInfo(str));
+ Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), str => str.Split(',').Select(s => new CultureInfo(s)).ToArray());
}
/// <summary>
@@ -82,27 +111,14 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// The attribute discovered on <paramref name="member"/> that describes the
/// serialization requirements of the message part.
/// </param>
+ [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts requires it.")]
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
- if (member == null) {
- throw new ArgumentNullException("member");
- }
+ Contract.Requires<ArgumentNullException>(member != null);
+ Contract.Requires<ArgumentException>(member is FieldInfo || member is PropertyInfo);
+ Contract.Requires<ArgumentNullException>(attribute != null);
this.field = member as FieldInfo;
this.property = member as PropertyInfo;
- if (this.field == null && this.property == null) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
- member.GetType().Name),
- "member");
- }
-
- if (attribute == null) {
- throw new ArgumentNullException("attribute");
- }
-
this.Name = attribute.Name ?? member.Name;
this.RequiredProtection = attribute.RequiredProtection;
this.IsRequired = attribute.IsRequired;
@@ -110,6 +126,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
+ Contract.Assume(this.memberDeclaredType != null); // CC missing PropertyInfo.PropertyType ensures result != null
if (attribute.Encoder == null) {
if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
this.converter = new ValueMapping(
@@ -167,9 +184,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="message">The message instance containing the member whose value should be set.</param>
/// <param name="value">The string representation of the value to set.</param>
internal void SetValue(IMessage message, string value) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
+ Contract.Requires<ArgumentNullException>(message != null);
try {
if (this.IsConstantValue) {
@@ -271,6 +286,9 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messagePartEncoder">The message part encoder type.</param>
/// <returns>An instance of the desired encoder.</returns>
private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
+ Contract.Requires<ArgumentNullException>(messagePartEncoder != null);
+ Contract.Ensures(Contract.Result<IMessagePartEncoder>() != null);
+
IMessagePartEncoder encoder;
if (!encoders.TryGetValue(messagePartEncoder, out encoder)) {
encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder);
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
index 332274e..1c7631e 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
@@ -6,10 +6,12 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
+ using System.Diagnostics.Contracts;
/// <summary>
/// A pair of conversion functions to map some type to a string and back again.
/// </summary>
+ [ContractVerification(true)]
internal struct ValueMapping {
/// <summary>
/// The mapping function that converts some custom type to a string.
@@ -27,8 +29,8 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="toString">The mapping function that converts some custom type to a string.</param>
/// <param name="toValue">The mapping function that converts a string to some custom type.</param>
internal ValueMapping(Func<object, string> toString, Func<string, object> toValue) {
- ErrorUtilities.VerifyArgumentNotNull(toString, "toString");
- ErrorUtilities.VerifyArgumentNotNull(toValue, "toValue");
+ Contract.Requires<ArgumentNullException>(toString != null);
+ Contract.Requires<ArgumentNullException>(toValue != null);
this.ValueToString = toString;
this.StringToValue = toValue;
@@ -39,7 +41,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="encoder">The encoder.</param>
internal ValueMapping(IMessagePartEncoder encoder) {
- ErrorUtilities.VerifyArgumentNotNull(encoder, "encoder");
+ Contract.Requires<ArgumentNullException>(encoder != null);
var nullEncoder = encoder as IMessagePartNullEncoder;
string nullString = nullEncoder != null ? nullEncoder.EncodedNullValue : null;
diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
index cc991cd..5b17c09 100644
--- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using System.Net.Sockets;
@@ -16,7 +17,7 @@ namespace DotNetOpenAuth.Messaging {
/// The default handler for transmitting <see cref="HttpWebRequest"/> instances
/// and returning the responses.
/// </summary>
- internal class StandardWebRequestHandler : IDirectWebRequestHandler {
+ public class StandardWebRequestHandler : IDirectWebRequestHandler {
/// <summary>
/// The set of options this web request handler supports.
/// </summary>
@@ -36,6 +37,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>
/// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
/// </returns>
+ [Pure]
public bool CanSupport(DirectWebRequestOptions options) {
return (options & ~SupportedOptions) == 0;
}
@@ -76,9 +78,6 @@ namespace DotNetOpenAuth.Messaging {
/// a single exception type for hosts to catch.</para>
/// </remarks>
public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifySupported(this.CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name);
-
return GetRequestStreamCore(request);
}
@@ -118,9 +117,6 @@ namespace DotNetOpenAuth.Messaging {
/// value, if set, shoud be Closed before throwing.</para>
/// </remarks>
public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifySupported(this.CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name);
-
// This request MAY have already been prepared by GetRequestStream, but
// we have no guarantee, so do it just to be safe.
PrepareRequest(request, false);
@@ -229,7 +225,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request.</param>
/// <param name="preparingPost"><c>true</c> if this is a POST request whose headers have not yet been sent out; <c>false</c> otherwise.</param>
private static void PrepareRequest(HttpWebRequest request, bool preparingPost) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
// Be careful to not try to change the HTTP headers that have already gone out.
if (preparingPost || request.Method == "GET") {
diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
index 733b698..3ea1bf2 100644
--- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Net;
@@ -87,7 +88,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="chainedWebRequestHandler">The chained web request handler.</param>
public UntrustedWebRequestHandler(IDirectWebRequestHandler chainedWebRequestHandler) {
- ErrorUtilities.VerifyArgumentNotNull(chainedWebRequestHandler, "chainedWebRequestHandler");
+ Contract.Requires<ArgumentNullException>(chainedWebRequestHandler != null);
this.chainedWebRequestHandler = chainedWebRequestHandler;
if (Debugger.IsAttached) {
@@ -111,7 +112,7 @@ namespace DotNetOpenAuth.Messaging {
}
set {
- ErrorUtilities.VerifyArgumentInRange(value >= 2048, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value >= 2048);
this.maximumBytesToRead = value;
}
}
@@ -126,7 +127,7 @@ namespace DotNetOpenAuth.Messaging {
}
set {
- ErrorUtilities.VerifyArgumentInRange(value >= 0, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value >= 0);
this.maximumRedirections = value;
}
}
@@ -185,6 +186,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>
/// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
/// </returns>
+ [Pure]
public bool CanSupport(DirectWebRequestOptions options) {
// We support whatever our chained handler supports, plus RequireSsl.
return this.chainedWebRequestHandler.CanSupport(options & ~DirectWebRequestOptions.RequireSsl);
@@ -207,7 +209,6 @@ namespace DotNetOpenAuth.Messaging {
/// a single exception type for hosts to catch.</para>
/// </remarks>
public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
this.EnsureAllowableRequestUri(request.RequestUri, (options & DirectWebRequestOptions.RequireSsl) != 0);
this.PrepareRequest(request, true);
@@ -234,8 +235,6 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
// This request MAY have already been prepared by GetRequestStream, but
// we have no guarantee, so do it just to be safe.
this.PrepareRequest(request, false);
@@ -259,6 +258,15 @@ namespace DotNetOpenAuth.Messaging {
Uri redirectUri = new Uri(response.FinalUri, response.Headers[HttpResponseHeader.Location]);
request = request.Clone(redirectUri);
} else {
+ if (response.FinalUri != request.RequestUri) {
+ // Since we don't automatically follow redirects, there's only one scenario where this
+ // can happen: when the server sends a (non-redirecting) Content-Location header in the response.
+ // It's imperative that we do not trust that header though, so coerce the FinalUri to be
+ // what we just requested.
+ Logger.Http.WarnFormat("The response from {0} included an HTTP header indicating it's the same as {1}, but it's not a redirect so we won't trust that.", request.RequestUri, response.FinalUri);
+ response.FinalUri = request.RequestUri;
+ }
+
return response;
}
}
@@ -306,7 +314,7 @@ namespace DotNetOpenAuth.Messaging {
/// <c>true</c> if this is a loopback IP address; <c>false</c> otherwise.
/// </returns>
private static bool IsIPv6Loopback(IPAddress ip) {
- ErrorUtilities.VerifyArgumentNotNull(ip, "ip");
+ Contract.Requires<ArgumentNullException>(ip != null);
byte[] addressBytes = ip.GetAddressBytes();
for (int i = 0; i < addressBytes.Length - 1; i++) {
if (addressBytes[i] != 0) {
@@ -329,9 +337,9 @@ namespace DotNetOpenAuth.Messaging {
/// <c>true</c> if the specified host falls within at least one of the given lists; otherwise, <c>false</c>.
/// </returns>
private static bool IsHostInList(string host, ICollection<string> stringList, ICollection<Regex> regexList) {
- ErrorUtilities.VerifyNonZeroLength(host, "host");
- ErrorUtilities.VerifyArgumentNotNull(stringList, "stringList");
- ErrorUtilities.VerifyArgumentNotNull(regexList, "regexList");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(host));
+ Contract.Requires<ArgumentNullException>(stringList != null);
+ Contract.Requires<ArgumentNullException>(regexList != null);
foreach (string testHost in stringList) {
if (string.Equals(host, testHost, StringComparison.OrdinalIgnoreCase)) {
return true;
@@ -387,7 +395,7 @@ namespace DotNetOpenAuth.Messaging {
/// <c>true</c> if [is URI allowable] [the specified URI]; otherwise, <c>false</c>.
/// </returns>
private bool IsUriAllowable(Uri uri) {
- ErrorUtilities.VerifyArgumentNotNull(uri, "uri");
+ Contract.Requires<ArgumentNullException>(uri != null);
if (!this.allowableSchemes.Contains(uri.Scheme)) {
Logger.Http.WarnFormat("Rejecting URL {0} because it uses a disallowed scheme.", uri);
return false;
@@ -447,7 +455,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request to prepare.</param>
/// <param name="preparingPost"><c>true</c> if this is a POST request whose headers have not yet been sent out; <c>false</c> otherwise.</param>
private void PrepareRequest(HttpWebRequest request, bool preparingPost) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
// Be careful to not try to change the HTTP headers that have already gone out.
if (preparingPost || request.Method == "GET") {
@@ -455,12 +463,14 @@ namespace DotNetOpenAuth.Messaging {
request.ReadWriteTimeout = (int)this.ReadWriteTimeout.TotalMilliseconds;
request.Timeout = (int)this.Timeout.TotalMilliseconds;
request.KeepAlive = false;
-
- // If SSL is required throughout, we cannot allow auto redirects because
- // it may include a pass through an unprotected HTTP request.
- // We have to follow redirects manually.
- request.AllowAutoRedirect = false;
}
+
+ // If SSL is required throughout, we cannot allow auto redirects because
+ // it may include a pass through an unprotected HTTP request.
+ // We have to follow redirects manually.
+ // It also allows us to ignore HttpWebResponse.FinalUri since that can be affected by
+ // the Content-Location header and open security holes.
+ request.AllowAutoRedirect = false;
}
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs
index 42a38fe..53930bc 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs
@@ -31,7 +31,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This method signs the message per OAuth 1.0 section 9.2.
/// </remarks>
protected override string GetSignature(ITamperResistantOAuthMessage message) {
- ErrorUtilities.VerifyOperation(this.Channel != null, "Channel property has not been set.");
string key = GetConsumerAndTokenSecretString(message);
HashAlgorithm hasher = new HMACSHA1(Encoding.ASCII.GetBytes(key));
string baseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message));
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs
index ff004cb..dd28e71 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/ICombinedOpenIdProviderTokenManager.cs
@@ -15,7 +15,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This interface should be implemented by the same class that implements
/// <see cref="ITokenManager"/> in order to enable the OpenID+OAuth extension.
/// </remarks>
- public interface ICombinedOpenIdProviderTokenManager {
+ public interface ICombinedOpenIdProviderTokenManager : IOpenIdOAuthTokenManager, ITokenManager {
/// <summary>
/// Gets the OAuth consumer key for a given OpenID relying party realm.
/// </summary>
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs
index 02ebffb..ea0e90c 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -14,6 +15,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// A token manager for use by a web site in its role as a
/// service provider.
/// </summary>
+ [ContractClass(typeof(IServiceProviderTokenManagerContract))]
public interface IServiceProviderTokenManager : ITokenManager {
/// <summary>
/// Gets the Consumer description for a given a Consumer Key.
@@ -24,6 +26,18 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
IConsumerDescription GetConsumer(string consumerKey);
/// <summary>
+ /// Checks whether a given request token has already been authorized
+ /// by some user for use by the Consumer that requested it.
+ /// </summary>
+ /// <param name="requestToken">The Consumer's request token.</param>
+ /// <returns>
+ /// True if the request token has already been fully authorized by the user
+ /// who owns the relevant protected resources. False if the token has not yet
+ /// been authorized, has expired or does not exist.
+ /// </returns>
+ bool IsRequestTokenAuthorized(string requestToken);
+
+ /// <summary>
/// Gets details on the named request token.
/// </summary>
/// <param name="token">The request token.</param>
@@ -50,5 +64,197 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// log and throw the appropriate error.
/// </remarks>
IServiceProviderAccessToken GetAccessToken(string token);
+
+ /// <summary>
+ /// Persists any changes made to the token.
+ /// </summary>
+ /// <param name="token">The token whose properties have been changed.</param>
+ /// <remarks>
+ /// This library will invoke this method after making a set
+ /// of changes to the token as part of a web request to give the host
+ /// the opportunity to persist those changes to a database.
+ /// Depending on the object persistence framework the host site uses,
+ /// this method MAY not need to do anything (if changes made to the token
+ /// will automatically be saved without any extra handling).
+ /// </remarks>
+ void UpdateToken(IServiceProviderRequestToken token);
+ }
+
+ /// <summary>
+ /// Code contract class for the <see cref="IServiceProviderTokenManager"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IServiceProviderTokenManager))]
+ internal class IServiceProviderTokenManagerContract : IServiceProviderTokenManager {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IServiceProviderTokenManagerContract"/> class.
+ /// </summary>
+ internal IServiceProviderTokenManagerContract() {
+ }
+
+ #region IServiceProviderTokenManager Members
+
+ /// <summary>
+ /// Gets the Consumer description for a given a Consumer Key.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer Key.</param>
+ /// <returns>
+ /// A description of the consumer. Never null.
+ /// </returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the consumer key cannot be found.</exception>
+ IConsumerDescription IServiceProviderTokenManager.GetConsumer(string consumerKey) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(consumerKey));
+ Contract.Ensures(Contract.Result<IConsumerDescription>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Checks whether a given request token has already been authorized
+ /// by some user for use by the Consumer that requested it.
+ /// </summary>
+ /// <param name="requestToken">The Consumer's request token.</param>
+ /// <returns>
+ /// True if the request token has already been fully authorized by the user
+ /// who owns the relevant protected resources. False if the token has not yet
+ /// been authorized, has expired or does not exist.
+ /// </returns>
+ bool IServiceProviderTokenManager.IsRequestTokenAuthorized(string requestToken) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(requestToken));
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets details on the named request token.
+ /// </summary>
+ /// <param name="token">The request token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderRequestToken IServiceProviderTokenManager.GetRequestToken(string token) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(token));
+ Contract.Ensures(Contract.Result<IServiceProviderRequestToken>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets details on the named access token.
+ /// </summary>
+ /// <param name="token">The access token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderAccessToken IServiceProviderTokenManager.GetAccessToken(string token) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(token));
+ Contract.Ensures(Contract.Result<IServiceProviderAccessToken>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Persists any changes made to the token.
+ /// </summary>
+ /// <param name="token">The token whose properties have been changed.</param>
+ /// <remarks>
+ /// This library will invoke this method after making a set
+ /// of changes to the token as part of a web request to give the host
+ /// the opportunity to persist those changes to a database.
+ /// Depending on the object persistence framework the host site uses,
+ /// this method MAY not need to do anything (if changes made to the token
+ /// will automatically be saved without any extra handling).
+ /// </remarks>
+ void IServiceProviderTokenManager.UpdateToken(IServiceProviderRequestToken token) {
+ Contract.Requires<ArgumentNullException>(token != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region ITokenManager Members
+
+ /// <summary>
+ /// Gets the Token Secret given a request or access token.
+ /// </summary>
+ /// <param name="token">The request or access token.</param>
+ /// <returns>
+ /// The secret associated with the given token.
+ /// </returns>
+ /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception>
+ string ITokenManager.GetTokenSecret(string token) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(token));
+ Contract.Ensures(Contract.Result<string>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Stores a newly generated unauthorized request token, secret, and optional
+ /// application-specific parameters for later recall.
+ /// </summary>
+ /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param>
+ /// <param name="response">The response message that includes the unauthorized request token.</param>
+ /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception>
+ /// <remarks>
+ /// Request tokens stored by this method SHOULD NOT associate any user account with this token.
+ /// It usually opens up security holes in your application to do so. Instead, you associate a user
+ /// account with access tokens (not request tokens) in the <see cref="ITokenManager.ExpireRequestTokenAndStoreNewAccessToken"/>
+ /// method.
+ /// </remarks>
+ void ITokenManager.StoreNewRequestToken(DotNetOpenAuth.OAuth.Messages.UnauthorizedTokenRequest request, DotNetOpenAuth.OAuth.Messages.ITokenSecretContainingMessage response) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(response != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Deletes a request token and its associated secret and stores a new access token and secret.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param>
+ /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param>
+ /// <param name="accessToken">The new access token that is being issued to the Consumer.</param>
+ /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param>
+ /// <remarks>
+ /// <para>
+ /// Any scope of granted privileges associated with the request token from the
+ /// original call to <see cref="ITokenManager.StoreNewRequestToken"/> should be carried over
+ /// to the new Access Token.
+ /// </para>
+ /// <para>
+ /// To associate a user account with the new access token,
+ /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be
+ /// useful in an ASP.NET web application within the implementation of this method.
+ /// Alternatively you may store the access token here without associating with a user account,
+ /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or
+ /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access
+ /// token to associate the access token with a user account at that point.
+ /// </para>
+ /// </remarks>
+ void ITokenManager.ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(consumerKey));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(requestToken));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken));
+ Contract.Requires<ArgumentNullException>(accessTokenSecret != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Classifies a token as a request token or an access token.
+ /// </summary>
+ /// <param name="token">The token to classify.</param>
+ /// <returns>
+ /// Request or Access token, or invalid if the token is not recognized.
+ /// </returns>
+ TokenType ITokenManager.GetTokenType(string token) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(token));
+ throw new NotImplementedException();
+ }
+
+ #endregion
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/ITokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/ITokenManager.cs
index 46aa03f..532e4b0 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/ITokenManager.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/ITokenManager.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.OAuth.Messages;
@@ -16,6 +17,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// and recall of tokens and secrets for an individual OAuth consumer
/// or service provider.
/// </summary>
+ [ContractClass(typeof(ITokenManagerContract))]
public interface ITokenManager {
/// <summary>
/// Gets the Token Secret given a request or access token.
@@ -32,19 +34,92 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param>
/// <param name="response">The response message that includes the unauthorized request token.</param>
/// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception>
+ /// <remarks>
+ /// Request tokens stored by this method SHOULD NOT associate any user account with this token.
+ /// It usually opens up security holes in your application to do so. Instead, you associate a user
+ /// account with access tokens (not request tokens) in the <see cref="ExpireRequestTokenAndStoreNewAccessToken"/>
+ /// method.
+ /// </remarks>
void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response);
/// <summary>
- /// Checks whether a given request token has already been authorized
- /// by some user for use by the Consumer that requested it.
+ /// Deletes a request token and its associated secret and stores a new access token and secret.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param>
+ /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param>
+ /// <param name="accessToken">The new access token that is being issued to the Consumer.</param>
+ /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param>
+ /// <remarks>
+ /// <para>
+ /// Any scope of granted privileges associated with the request token from the
+ /// original call to <see cref="StoreNewRequestToken"/> should be carried over
+ /// to the new Access Token.
+ /// </para>
+ /// <para>
+ /// To associate a user account with the new access token,
+ /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be
+ /// useful in an ASP.NET web application within the implementation of this method.
+ /// Alternatively you may store the access token here without associating with a user account,
+ /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or
+ /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access
+ /// token to associate the access token with a user account at that point.
+ /// </para>
+ /// </remarks>
+ void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret);
+
+ /// <summary>
+ /// Classifies a token as a request token or an access token.
+ /// </summary>
+ /// <param name="token">The token to classify.</param>
+ /// <returns>Request or Access token, or invalid if the token is not recognized.</returns>
+ TokenType GetTokenType(string token);
+ }
+
+ /// <summary>
+ /// The code contract class for the <see cref="ITokenManager"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(ITokenManager))]
+ internal class ITokenManagerContract : ITokenManager {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ITokenManagerContract"/> class.
+ /// </summary>
+ internal ITokenManagerContract() {
+ }
+
+ #region ITokenManager Members
+
+ /// <summary>
+ /// Gets the Token Secret given a request or access token.
/// </summary>
- /// <param name="requestToken">The Consumer's request token.</param>
+ /// <param name="token">The request or access token.</param>
/// <returns>
- /// True if the request token has already been fully authorized by the user
- /// who owns the relevant protected resources. False if the token has not yet
- /// been authorized, has expired or does not exist.
+ /// The secret associated with the given token.
/// </returns>
- bool IsRequestTokenAuthorized(string requestToken);
+ /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception>
+ string ITokenManager.GetTokenSecret(string token) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(token));
+ Contract.Ensures(Contract.Result<string>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Stores a newly generated unauthorized request token, secret, and optional
+ /// application-specific parameters for later recall.
+ /// </summary>
+ /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param>
+ /// <param name="response">The response message that includes the unauthorized request token.</param>
+ /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception>
+ /// <remarks>
+ /// Request tokens stored by this method SHOULD NOT associate any user account with this token.
+ /// It usually opens up security holes in your application to do so. Instead, you associate a user
+ /// account with access tokens (not request tokens) in the <see cref="ITokenManager.ExpireRequestTokenAndStoreNewAccessToken"/>
+ /// method.
+ /// </remarks>
+ void ITokenManager.StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(response != null);
+ throw new NotImplementedException();
+ }
/// <summary>
/// Deletes a request token and its associated secret and stores a new access token and secret.
@@ -54,17 +129,41 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="accessToken">The new access token that is being issued to the Consumer.</param>
/// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param>
/// <remarks>
+ /// <para>
/// Any scope of granted privileges associated with the request token from the
- /// original call to <see cref="StoreNewRequestToken"/> should be carried over
+ /// original call to <see cref="ITokenManager.StoreNewRequestToken"/> should be carried over
/// to the new Access Token.
+ /// </para>
+ /// <para>
+ /// To associate a user account with the new access token,
+ /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be
+ /// useful in an ASP.NET web application within the implementation of this method.
+ /// Alternatively you may store the access token here without associating with a user account,
+ /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or
+ /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access
+ /// token to associate the access token with a user account at that point.
+ /// </para>
/// </remarks>
- void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret);
+ void ITokenManager.ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(consumerKey));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(requestToken));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken));
+ Contract.Requires<ArgumentNullException>(accessTokenSecret != null);
+ throw new NotImplementedException();
+ }
/// <summary>
/// Classifies a token as a request token or an access token.
/// </summary>
/// <param name="token">The token to classify.</param>
- /// <returns>Request or Access token, or invalid if the token is not recognized.</returns>
- TokenType GetTokenType(string token);
+ /// <returns>
+ /// Request or Access token, or invalid if the token is not recognized.
+ /// </returns>
+ TokenType ITokenManager.GetTokenType(string token) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(token));
+ throw new NotImplementedException();
+ }
+
+ #endregion
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
index d325825..b0e938f 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
+ using System.Linq;
using System.Net;
using System.Text;
using System.Web;
@@ -64,11 +65,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </param>
internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, IMessageFactory messageTypeProvider)
: base(messageTypeProvider, InitializeBindingElements(signingBindingElement, store, tokenManager)) {
- ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
+ Contract.Requires<ArgumentNullException>(signingBindingElement != null);
+ Contract.Requires<ArgumentException>(signingBindingElement.SignatureCallback == null, OAuthStrings.SigningElementAlreadyAssociatedWithChannel);
this.TokenManager = tokenManager;
- ErrorUtilities.VerifyArgumentNamed(signingBindingElement.SignatureCallback == null, "signingBindingElement", OAuthStrings.SigningElementAlreadyAssociatedWithChannel);
-
signingBindingElement.SignatureCallback = this.SignatureCallback;
}
@@ -87,7 +88,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="message">The message with data to encode.</param>
/// <returns>A dictionary of name-value pairs with their strings encoded.</returns>
- internal static IDictionary<string, string> GetUriEscapedParameters(MessageDictionary message) {
+ internal static IDictionary<string, string> GetUriEscapedParameters(IEnumerable<KeyValuePair<string, string>> message) {
var encodedDictionary = new Dictionary<string, string>();
UriEscapeParameters(message, encodedDictionary);
return encodedDictionary;
@@ -101,7 +102,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="request">The message to attach.</param>
/// <returns>The initialized web request.</returns>
internal HttpWebRequest InitializeRequest(IDirectedProtocolMessage request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
ProcessOutgoingMessage(request);
return this.CreateHttpRequest(request);
@@ -114,8 +115,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="request">The HTTP request to search.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
var fields = new Dictionary<string, string>();
// First search the Authorization header.
@@ -157,8 +156,16 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
}
}
+ MessageReceivingEndpoint recipient;
+ try {
+ recipient = request.GetRecipient();
+ } catch (ArgumentException ex) {
+ Logger.OAuth.WarnFormat("Unrecognized HTTP request: " + ex.ToString());
+ return null;
+ }
+
// Deserialize the message using all the data we've collected.
- var message = (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
+ var message = (IDirectedProtocolMessage)this.Receive(fields, recipient);
// Add receiving HTTP transport information required for signature generation.
var signedMessage = message as ITamperResistantOAuthMessage;
@@ -178,8 +185,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// The deserialized message parts, if found. Null otherwise.
/// </returns>
protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
-
string body = response.GetResponseReader().ReadToEnd();
return HttpUtility.ParseQueryString(body).ToDictionary();
}
@@ -192,21 +197,23 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// The <see cref="HttpRequest"/> prepared to send the request.
/// </returns>
protected override HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNamed(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient);
-
- IDirectedProtocolMessage oauthRequest = request as IDirectedProtocolMessage;
- ErrorUtilities.VerifyArgument(oauthRequest != null, MessagingStrings.UnexpectedType, typeof(IDirectedProtocolMessage), request.GetType());
-
HttpWebRequest httpRequest;
- HttpDeliveryMethods transmissionMethod = oauthRequest.HttpMethods;
+ HttpDeliveryMethods transmissionMethod = request.HttpMethods;
if ((transmissionMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) {
httpRequest = this.InitializeRequestAsAuthHeader(request);
} else if ((transmissionMethod & HttpDeliveryMethods.PostRequest) != 0) {
+ var requestMessageWithBinaryData = request as IMessageWithBinaryData;
+ ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || !requestMessageWithBinaryData.SendAsMultipart, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader);
httpRequest = this.InitializeRequestAsPost(request);
} else if ((transmissionMethod & HttpDeliveryMethods.GetRequest) != 0) {
httpRequest = InitializeRequestAsGet(request);
+ } else if ((transmissionMethod & HttpDeliveryMethods.HeadRequest) != 0) {
+ httpRequest = InitializeRequestAsHead(request);
+ } else if ((transmissionMethod & HttpDeliveryMethods.PutRequest) != 0) {
+ httpRequest = this.InitializeRequestAsPut(request);
+ } else if ((transmissionMethod & HttpDeliveryMethods.DeleteRequest) != 0) {
+ httpRequest = InitializeRequestAsDelete(request);
} else {
throw new NotSupportedException();
}
@@ -223,8 +230,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This method implements spec V1.0 section 5.3.
/// </remarks>
protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
-
var messageAccessor = this.MessageDescriptions.GetAccessor(response);
var fields = messageAccessor.Serialize();
string responseBody = MessagingUtilities.CreateQueryString(fields);
@@ -272,9 +277,9 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="source">The dictionary with names and values to encode.</param>
/// <param name="destination">The dictionary to add the encoded pairs to.</param>
- private static void UriEscapeParameters(IDictionary<string, string> source, IDictionary<string, string> destination) {
- ErrorUtilities.VerifyArgumentNotNull(source, "source");
- ErrorUtilities.VerifyArgumentNotNull(destination, "destination");
+ private static void UriEscapeParameters(IEnumerable<KeyValuePair<string, string>> source, IDictionary<string, string> destination) {
+ Contract.Requires<ArgumentNullException>(source != null);
+ Contract.Requires<ArgumentNullException>(destination != null);
foreach (var pair in source) {
var key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key);
@@ -289,14 +294,13 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="message">The message.</param>
/// <returns>"POST", "GET" or some other similar http verb.</returns>
private static string GetHttpMethod(IDirectedProtocolMessage message) {
- Contract.Requires(message != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
var signedMessage = message as ITamperResistantOAuthMessage;
if (signedMessage != null) {
return signedMessage.HttpMethod;
} else {
- return (message.HttpMethods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
+ return MessagingUtilities.GetHttpVerb(message.HttpMethods);
}
}
@@ -350,12 +354,22 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
if (hasEntity) {
// WARNING: We only set up the request stream for the caller if there is
// extra data. If there isn't any extra data, the caller must do this themselves.
- if (requestMessage.ExtraData.Count > 0) {
- SendParametersInEntity(httpRequest, requestMessage.ExtraData);
+ var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData;
+ if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) {
+ // Include the binary data in the multipart entity, and any standard text extra message data.
+ // The standard declared message parts are included in the authorization header.
+ var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData);
+ multiPartFields.AddRange(requestMessage.ExtraData.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value)));
+ this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields);
} else {
- // We'll assume the content length is zero since the caller may not have
- // anything. They're responsible to change it when the add the payload if they have one.
- httpRequest.ContentLength = 0;
+ ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart);
+ if (requestMessage.ExtraData.Count > 0) {
+ this.SendParametersInEntity(httpRequest, requestMessage.ExtraData);
+ } else {
+ // We'll assume the content length is zero since the caller may not have
+ // anything. They're responsible to change it when the add the payload if they have one.
+ httpRequest.ContentLength = 0;
+ }
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
index e05bb62..327b923 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.Messages;
@@ -38,9 +39,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// UserAuthorizationResponse
/// </remarks>
public virtual IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
MessageBase message = null;
if (fields.ContainsKey("oauth_token")) {
@@ -74,9 +72,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// AuthorizedTokenResponse
/// </remarks>
public virtual IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
MessageBase message = null;
// All response messages have the oauth_token field.
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthHttpMethodBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthHttpMethodBindingElement.cs
index c9df0e8..37fb80b 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthHttpMethodBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthHttpMethodBindingElement.cs
@@ -42,15 +42,13 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
if (oauthMessage != null) {
HttpDeliveryMethods transmissionMethod = oauthMessage.HttpMethods;
- if ((transmissionMethod & HttpDeliveryMethods.PostRequest) != 0) {
- oauthMessage.HttpMethod = "POST";
- } else if ((transmissionMethod & HttpDeliveryMethods.GetRequest) != 0) {
- oauthMessage.HttpMethod = "GET";
- } else {
+ try {
+ oauthMessage.HttpMethod = MessagingUtilities.GetHttpVerb(transmissionMethod);
+ return MessageProtections.None;
+ } catch (ArgumentException ex) {
+ Logger.OAuth.Error("Unrecognized HttpDeliveryMethods value.", ex);
return null;
}
-
- return MessageProtections.None;
} else {
return null;
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs
index bd57012..65bde20 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs
@@ -24,8 +24,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="username">The username.</param>
internal OAuthIdentity(string username) {
- Contract.Requires(!String.IsNullOrEmpty(username));
- ErrorUtilities.VerifyNonZeroLength(username, "username");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(username));
this.Name = username;
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
index a9f363a..025ef09 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
@@ -31,7 +31,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="token">The access token.</param>
internal OAuthPrincipal(IServiceProviderAccessToken token)
: this(token.Username, token.Roles) {
- Contract.Requires(token != null);
+ Contract.Requires<ArgumentNullException>(token != null);
this.AccessToken = token.Token;
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
index abb99d8..5b3c918 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.Messages;
@@ -25,7 +26,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="tokenManager">The token manager instance to use.</param>
public OAuthServiceProviderMessageFactory(IServiceProviderTokenManager tokenManager) {
- ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
this.tokenManager = tokenManager;
}
@@ -50,9 +51,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// AccessProtectedResourceRequest
/// </remarks>
public virtual IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
MessageBase message = null;
Protocol protocol = Protocol.V10; // default to assuming the less-secure 1.0 instead of 1.0a until we prove otherwise.
string token;
@@ -119,9 +117,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// None.
/// </remarks>
public virtual IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
Logger.OAuth.Error("Service Providers are not expected to ever receive responses.");
return null;
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/PlaintextSigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/PlaintextSigningBindingElement.cs
index 9f99066..22e5f20 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/PlaintextSigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/PlaintextSigningBindingElement.cs
@@ -41,7 +41,12 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="message">The message that needs to be signed.</param>
/// <returns>True if this binding element can be used to sign the message. False otherwise.</returns>
protected override bool IsMessageApplicable(ITamperResistantOAuthMessage message) {
- return string.Equals(message.Recipient.Scheme, "https", StringComparison.OrdinalIgnoreCase);
+ if (string.Equals(message.Recipient.Scheme, "https", StringComparison.OrdinalIgnoreCase)) {
+ return true;
+ } else {
+ Logger.Bindings.DebugFormat("The {0} element will not sign this message because the URI scheme is not https.", this.GetType().Name);
+ return false;
+ }
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs
index 4f8b5e5..f7b8370 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs
@@ -33,8 +33,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="signingCertificate">The certificate used to sign outgoing messages.</param>
public RsaSha1SigningBindingElement(X509Certificate2 signingCertificate)
: base(HashAlgorithmName) {
- Contract.Requires(signingCertificate != null);
- ErrorUtilities.VerifyArgumentNotNull(signingCertificate, "signingCertificate");
+ Contract.Requires<ArgumentNullException>(signingCertificate != null);
this.SigningCertificate = signingCertificate;
}
@@ -46,8 +45,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="tokenManager">The token manager.</param>
public RsaSha1SigningBindingElement(IServiceProviderTokenManager tokenManager)
: base(HashAlgorithmName) {
- Contract.Requires(tokenManager != null);
- ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
this.tokenManager = tokenManager;
}
@@ -66,7 +64,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This method signs the message per OAuth 1.0 section 9.3.
/// </remarks>
protected override string GetSignature(ITamperResistantOAuthMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning);
string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message));
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
index 6991818..b45da66 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Text;
using System.Web;
using DotNetOpenAuth.Messaging;
@@ -148,25 +149,35 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This method implements OAuth 1.0 section 9.1.
/// </remarks>
internal static string ConstructSignatureBaseString(ITamperResistantOAuthMessage message, MessageDictionary messageDictionary) {
- Contract.Requires(message != null);
- Contract.Requires(messageDictionary != null);
- Contract.Requires(messageDictionary.Message == message);
-
- if (String.IsNullOrEmpty(message.HttpMethod)) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.ArgumentPropertyMissing,
- typeof(ITamperResistantOAuthMessage).Name,
- "HttpMethod"),
- "message");
- }
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(message.HttpMethod));
+ Contract.Requires<ArgumentNullException>(messageDictionary != null);
+ Contract.Requires<ArgumentException>(messageDictionary.Message == message);
List<string> signatureBaseStringElements = new List<string>(3);
signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant());
- var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary);
+ // For multipart POST messages, only include the message parts that are NOT
+ // in the POST entity (those parts that may appear in an OAuth authorization header).
+ var encodedDictionary = new Dictionary<string, string>();
+ IEnumerable<KeyValuePair<string, string>> partsToInclude = Enumerable.Empty<KeyValuePair<string, string>>();
+ var binaryMessage = message as IMessageWithBinaryData;
+ if (binaryMessage != null && binaryMessage.SendAsMultipart) {
+ HttpDeliveryMethods authHeaderInUseFlags = HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest;
+ ErrorUtilities.VerifyProtocol((binaryMessage.HttpMethods & authHeaderInUseFlags) == authHeaderInUseFlags, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader);
+
+ // Include the declared keys in the signature as those will be signable.
+ // Cache in local variable to avoid recalculating DeclaredKeys in the delegate.
+ ICollection<string> declaredKeys = messageDictionary.DeclaredKeys;
+ partsToInclude = messageDictionary.Where(pair => declaredKeys.Contains(pair.Key));
+ } else {
+ partsToInclude = messageDictionary;
+ }
+
+ foreach (var pair in OAuthChannel.GetUriEscapedParameters(partsToInclude)) {
+ encodedDictionary[pair.Key] = pair.Value;
+ }
// An incoming message will already have included the query and form parameters
// in the message dictionary, but an outgoing message COULD have SOME parameters
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs
index 7019b45..7b369c3 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs
@@ -40,7 +40,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <param name="message">The message to sign.</param>
/// <returns>The signature for the message.</returns>
protected override string GetSignature(ITamperResistantOAuthMessage message) {
- Contract.Requires(this.Channel != null);
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(this.Channel != null);
throw new NotImplementedException();
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs
index f88e7ab..67c5205 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -19,7 +20,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <summary>
/// The various signing binding elements that may be applicable to a message in preferred use order.
/// </summary>
- private ITamperProtectionChannelBindingElement[] signers;
+ private readonly ITamperProtectionChannelBindingElement[] signers;
/// <summary>
/// Initializes a new instance of the <see cref="SigningBindingElementChain"/> class.
@@ -29,19 +30,10 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// in preferred use order.
/// </param>
internal SigningBindingElementChain(ITamperProtectionChannelBindingElement[] signers) {
- if (signers == null) {
- throw new ArgumentNullException("signers");
- }
- if (signers.Length == 0) {
- throw new ArgumentException(MessagingStrings.SequenceContainsNoElements, "signers");
- }
- if (signers.Contains(null)) {
- throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, "signers");
- }
- MessageProtections protection = signers[0].Protection;
- if (signers.Any(element => element.Protection != protection)) {
- throw new ArgumentException(OAuthStrings.SigningElementsMustShareSameProtection, "signers");
- }
+ Contract.Requires<ArgumentNullException>(signers != null);
+ Contract.Requires<ArgumentException>(signers.Length > 0);
+ Contract.Requires<ArgumentException>(!signers.Contains(null), MessagingStrings.SequenceContainsNullElement);
+ Contract.Requires<ArgumentException>(signers.Select(s => s.Protection).Distinct().Count() == 1, OAuthStrings.SigningElementsMustShareSameProtection);
this.signers = signers;
}
@@ -101,6 +93,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </returns>
public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
foreach (IChannelBindingElement signer in this.signers) {
+ ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null.");
MessageProtections? result = signer.ProcessOutgoingMessage(message);
if (result.HasValue) {
return result;
@@ -121,6 +114,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </returns>
public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
foreach (IChannelBindingElement signer in this.signers) {
+ ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null.");
MessageProtections? result = signer.ProcessIncomingMessage(message);
if (result.HasValue) {
return result;
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
index 3e75e7b..f9547c6 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
@@ -29,8 +29,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="tokenManager">The token manager.</param>
internal TokenHandlingBindingElement(IServiceProviderTokenManager tokenManager) {
- Contract.Requires(tokenManager != null);
- ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
this.tokenManager = tokenManager;
}
@@ -68,11 +67,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
-
var userAuthResponse = message as UserAuthorizationResponse;
if (userAuthResponse != null && userAuthResponse.Version >= Protocol.V10a.Version) {
- this.tokenManager.GetRequestToken(userAuthResponse.RequestToken).VerificationCode = userAuthResponse.VerificationCode;
+ var requestToken = this.tokenManager.GetRequestToken(userAuthResponse.RequestToken);
+ requestToken.VerificationCode = userAuthResponse.VerificationCode;
+ this.tokenManager.UpdateToken(requestToken);
return MessageProtections.None;
}
@@ -80,10 +79,14 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
var grantRequestTokenResponse = message as UnauthorizedTokenResponse;
if (grantRequestTokenResponse != null) {
this.tokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse);
- this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).ConsumerVersion = grantRequestTokenResponse.Version;
+
+ // The host may have already set these properties, but just to make sure...
+ var requestToken = this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken);
+ requestToken.ConsumerVersion = grantRequestTokenResponse.Version;
if (grantRequestTokenResponse.RequestMessage.Callback != null) {
- this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).Callback = grantRequestTokenResponse.RequestMessage.Callback;
+ requestToken.Callback = grantRequestTokenResponse.RequestMessage.Callback;
}
+ this.tokenManager.UpdateToken(requestToken);
return MessageProtections.None;
}
@@ -109,8 +112,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
-
var authorizedTokenRequest = message as AuthorizedTokenRequest;
if (authorizedTokenRequest != null) {
if (authorizedTokenRequest.Version >= Protocol.V10a.Version) {
@@ -142,7 +143,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="message">The incoming message carrying the access token.</param>
private void VerifyThrowTokenNotExpired(AccessProtectedResourceRequest message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
try {
IServiceProviderAccessToken token = this.tokenManager.GetAccessToken(message.AccessToken);
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs
index 5aedc9d..287ef01 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -51,8 +52,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// The <paramref name="value"/> in string form, ready for message transport.
/// </returns>
public string Encode(object value) {
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
-
Uri uriValue = (Uri)value;
return uriValue.AbsoluteUri;
}
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 55b40ac..48f54d7 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth {
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+ using System.Linq;
using System.Net;
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
@@ -26,14 +27,16 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
/// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
protected ConsumerBase(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager) {
- ErrorUtilities.VerifyArgumentNotNull(serviceDescription, "serviceDescription");
- ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+ Contract.Requires<ArgumentNullException>(serviceDescription != null);
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
- INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
+ INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge);
this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager);
this.ServiceProvider = serviceDescription;
this.SecuritySettings = DotNetOpenAuthSection.Configuration.OAuth.Consumer.SecuritySettings.CreateSecuritySettings();
+
+ Reporting.RecordFeatureAndDependencyUse(this, serviceDescription, tokenManager, null);
}
/// <summary>
@@ -80,10 +83,8 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="accessToken">The access token that permits access to the protected resource.</param>
/// <returns>The initialized WebRequest object.</returns>
public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
- Contract.Requires(endpoint != null);
- Contract.Requires(!String.IsNullOrEmpty(accessToken));
- ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
- ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
+ Contract.Requires<ArgumentNullException>(endpoint != null);
+ Contract.Requires<ArgumentNullException>(!String.IsNullOrEmpty(accessToken));
return this.PrepareAuthorizedRequest(endpoint, accessToken, EmptyDictionary<string, string>.Instance);
}
@@ -97,12 +98,9 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="extraData">Extra parameters to include in the message. Must not be null, but may be empty.</param>
/// <returns>The initialized WebRequest object.</returns>
public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken, IDictionary<string, string> extraData) {
- Contract.Requires(endpoint != null);
- Contract.Requires(!String.IsNullOrEmpty(accessToken));
- Contract.Requires(extraData != null);
- ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
- ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
- ErrorUtilities.VerifyArgumentNotNull(extraData, "extraData");
+ Contract.Requires<ArgumentNullException>(endpoint != null);
+ Contract.Requires<ArgumentNullException>(!String.IsNullOrEmpty(accessToken));
+ Contract.Requires<ArgumentNullException>(extraData != null);
IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
foreach (var pair in extraData) {
@@ -114,6 +112,27 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Prepares an authorized request that carries an HTTP multi-part POST, allowing for binary data.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <param name="binaryData">Extra parameters to include in the message. Must not be null, but may be empty.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken, IEnumerable<MultipartPostPart> binaryData) {
+ Contract.Requires<ArgumentNullException>(endpoint != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken));
+ Contract.Requires<ArgumentNullException>(binaryData != null);
+
+ AccessProtectedResourceRequest message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ foreach (MultipartPostPart part in binaryData) {
+ message.BinaryData.Add(part);
+ }
+
+ HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
+ return wr;
+ }
+
+ /// <summary>
/// Prepares an HTTP request that has OAuth authorization already attached to it.
/// </summary>
/// <param name="message">The OAuth authorization message to attach to the HTTP request.</param>
@@ -130,8 +149,7 @@ namespace DotNetOpenAuth.OAuth {
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Type of parameter forces the method to apply only to specific scenario.")]
public HttpWebRequest PrepareAuthorizedRequest(AccessProtectedResourceRequest message) {
- Contract.Requires(message != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
return this.OAuthChannel.InitializeRequest(message);
}
@@ -169,10 +187,8 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="accessToken">The access token that permits access to the protected resource.</param>
/// <returns>The initialized WebRequest object.</returns>
protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
- Contract.Requires(endpoint != null);
- Contract.Requires(!String.IsNullOrEmpty(accessToken));
- ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
- ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken");
+ Contract.Requires<ArgumentNullException>(endpoint != null);
+ Contract.Requires<ArgumentNullException>(!String.IsNullOrEmpty(accessToken));
AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint, this.ServiceProvider.Version) {
AccessToken = accessToken,
@@ -233,9 +249,8 @@ namespace DotNetOpenAuth.OAuth {
/// The access token assigned by the Service Provider.
/// </returns>
protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken, string verifier) {
- Contract.Requires(!String.IsNullOrEmpty(requestToken));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(requestToken));
Contract.Ensures(Contract.Result<AuthorizedTokenResponse>() != null);
- ErrorUtilities.VerifyNonZeroLength(requestToken, "requestToken");
var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) {
RequestToken = requestToken,
diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
index b60fda4..f3231f0 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.OAuth.Messages {
using System;
+ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using DotNetOpenAuth.Messaging;
@@ -13,7 +14,12 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// A message attached to a request for protected resources that provides the necessary
/// credentials to be granted access to those resources.
/// </summary>
- public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage {
+ public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage, IMessageWithBinaryData {
+ /// <summary>
+ /// A store for the binary data that is carried in the message.
+ /// </summary>
+ private List<MultipartPostPart> binaryData = new List<MultipartPostPart>();
+
/// <summary>
/// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class.
/// </summary>
@@ -43,5 +49,24 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </remarks>
[MessagePart("oauth_token", IsRequired = true)]
public string AccessToken { get; set; }
+
+ #region IMessageWithBinaryData Members
+
+ /// <summary>
+ /// Gets the parts of the message that carry binary data.
+ /// </summary>
+ /// <value>A list of parts. Never null.</value>
+ public IList<MultipartPostPart> BinaryData {
+ get { return this.binaryData; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message should be sent as multi-part POST.
+ /// </summary>
+ public bool SendAsMultipart {
+ get { return this.HttpMethod == "POST" && this.BinaryData.Count > 0; }
+ }
+
+ #endregion
}
}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
index 944bc5c..1a0ba23 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
@@ -65,8 +65,8 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// <param name="originatingRequest">The request that asked for this direct response.</param>
/// <param name="version">The OAuth version.</param>
protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest, Version version) {
- ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
- ErrorUtilities.VerifyArgumentNotNull(version, "version");
+ Contract.Requires<ArgumentNullException>(originatingRequest != null);
+ Contract.Requires<ArgumentNullException>(version != null);
this.protectionRequired = protectionRequired;
this.transport = MessageTransport.Direct;
@@ -82,8 +82,8 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// <param name="recipient">The URI that a directed message will be delivered to.</param>
/// <param name="version">The OAuth version.</param>
protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient, Version version) {
- ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient");
- ErrorUtilities.VerifyArgumentNotNull(version, "version");
+ Contract.Requires<ArgumentNullException>(recipient != null);
+ Contract.Requires<ArgumentNullException>(version != null);
this.protectionRequired = protectionRequired;
this.transport = transport;
@@ -242,8 +242,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// The string representation of this object.
/// </returns>
internal virtual string ToString(Channel channel) {
- Contract.Requires(channel != null);
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
StringBuilder builder = new StringBuilder();
builder.AppendFormat(CultureInfo.InvariantCulture, "{0} message", GetType().Name);
diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
index 1d8ca21..57ce470 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
@@ -36,7 +36,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
: base(MessageProtections.All, transport, recipient, version) {
ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this;
HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods;
- self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
+ self.HttpMethod = MessagingUtilities.GetHttpVerb(methods);
}
#region ITamperResistantOAuthMessage Members
diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs
index ce09213..0be9f63 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -26,8 +27,8 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </remarks>
protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret)
: this(requestMessage, requestMessage.Version) {
- ErrorUtilities.VerifyArgumentNotNull(requestToken, "requestToken");
- ErrorUtilities.VerifyArgumentNotNull(tokenSecret, "tokenSecret");
+ Contract.Requires<ArgumentNullException>(requestToken != null);
+ Contract.Requires<ArgumentNullException>(tokenSecret != null);
this.RequestToken = requestToken;
this.TokenSecret = tokenSecret;
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
index 3593446..fd74bcb 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.4918
+// Runtime Version:2.0.50727.4927
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -115,29 +115,38 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
- /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface..
+ /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}..
/// </summary>
- internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface {
+ internal static string MinimumConsumerVersionRequirementNotMet {
get {
- return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture);
+ return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture);
}
}
/// <summary>
- /// Looks up a localized string similar to The OpenID Relying Party&apos;s realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given..
+ /// Looks up a localized string similar to Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed..
/// </summary>
- internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch {
+ internal static string MultipartPostMustBeUsedWithAuthHeader {
get {
- return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture);
+ return ResourceManager.GetString("MultipartPostMustBeUsedWithAuthHeader", resourceCulture);
}
}
/// <summary>
- /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}..
+ /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface..
/// </summary>
- internal static string MinimumConsumerVersionRequirementNotMet {
+ internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface {
get {
- return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture);
+ return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The OpenID Relying Party&apos;s realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given..
+ /// </summary>
+ internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch {
+ get {
+ return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture);
}
}
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
index bbeeda9..34b314b 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
@@ -138,6 +138,9 @@
<data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve">
<value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value>
</data>
+ <data name="MultipartPostMustBeUsedWithAuthHeader" xml:space="preserve">
+ <value>Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.</value>
+ </data>
<data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve">
<value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value>
</data>
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.sr.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.sr.resx
new file mode 100644
index 0000000..ef9ce60
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.sr.resx
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="AccessTokenNotAuthorized" xml:space="preserve">
+ <value>Ne može se poslati pristupni token za Consumera za token zahteva '{0}' pre nego što bude autorizovan.</value>
+ </data>
+ <data name="BadAccessTokenInProtectedResourceRequest" xml:space="preserve">
+ <value>Pristupni token '{0}' je neispravan ili je mu je istekao rok važenja.</value>
+ </data>
+ <data name="ConsumerOrTokenSecretNotFound" xml:space="preserve">
+ <value>Greška u traženju šifre za korisnika ili za token.</value>
+ </data>
+ <data name="IncorrectVerifier" xml:space="preserve">
+ <value>oauth_verifier argument je neispravan.</value>
+ </data>
+ <data name="InvalidIncomingMessage" xml:space="preserve">
+ <value>Neispravna OAuth poruka je primljena i odbačena.</value>
+ </data>
+ <data name="MessageNotAllowedExtraParameters" xml:space="preserve">
+ <value>Poruka {0} je sadržala višak podataka koji nije dozvoljen.</value>
+ </data>
+ <data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve">
+ <value>Ovaj provajder OAuth usluge zahteva od OAuth korisnika da da implementira OAuth {0}, ali izgleda da korisnika podržava jedino {1}.</value>
+ </data>
+ <data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve">
+ <value>Korišćenje OpenID+OAuth ekstenzije zahteva da token menadžer koji se koristi implementira {0} interfejs.</value>
+ </data>
+ <data name="OpenIdOAuthRealmConsumerKeyDoNotMatch" xml:space="preserve">
+ <value>OpenID Relying Party domen nije prepoznat kao da pripada OAuth Consumeru identifikovanom po datom korisničkom tokenu.</value>
+ </data>
+ <data name="RequestUrlMustNotHaveOAuthParameters" xml:space="preserve">
+ <value>URL upit zahteva NE SME sadržati bilo kakve OAuth Protocol Parametre.</value>
+ </data>
+ <data name="SigningElementAlreadyAssociatedWithChannel" xml:space="preserve">
+ <value>Potpisujući element je već asociran sa kanalom.</value>
+ </data>
+ <data name="SigningElementsMustShareSameProtection" xml:space="preserve">
+ <value>Svi elementi za potpisivanje moraju nuditi istu zaštitu poruke.</value>
+ </data>
+ <data name="TokenNotFound" xml:space="preserve">
+ <value>Token u poruci nije prepoznat od strane provajdera usluge.</value>
+ </data>
+ <data name="X509CertificateNotProvidedForSigning" xml:space="preserve">
+ <value>RSA-SHA1 potpisujući povezujući element nije podešen sa sertifikatom za potpisivanje.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs
index cd4e486..a524ba7 100644
--- a/src/DotNetOpenAuth/OAuth/Protocol.cs
+++ b/src/DotNetOpenAuth/OAuth/Protocol.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -139,10 +140,9 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="version">The OAuth version to get.</param>
/// <returns>A matching <see cref="Protocol"/> instance.</returns>
internal static Protocol Lookup(Version version) {
- ErrorUtilities.VerifyArgumentNotNull(version, "version");
- Protocol protocol = AllVersions.FirstOrDefault(p => p.Version == version);
- ErrorUtilities.VerifyArgumentInRange(protocol != null, "version");
- return protocol;
+ Contract.Requires<ArgumentNullException>(version != null);
+ Contract.Requires<ArgumentOutOfRangeException>(AllVersions.Any(p => p.Version == version));
+ return AllVersions.First(p => p.Version == version);
}
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
index fe65ed7..829b572 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OAuth {
using System;
using System.Collections.Generic;
+ using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
@@ -36,6 +37,12 @@ namespace DotNetOpenAuth.OAuth {
/// </remarks>
public class ServiceProvider : IDisposable {
/// <summary>
+ /// The name of the key to use in the HttpApplication cache to store the
+ /// instance of <see cref="NonceMemoryStore"/> to use.
+ /// </summary>
+ private const string ApplicationStoreKey = "DotNetOpenAuth.OAuth.ServiceProvider.HttpApplicationStore";
+
+ /// <summary>
/// The length of the verifier code (in raw bytes before base64 encoding) to generate.
/// </summary>
private const int VerifierCodeLength = 5;
@@ -61,7 +68,10 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
/// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param>
public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider)
- : this(serviceDescription, tokenManager, new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge), messageTypeProvider) {
+ : this(serviceDescription, tokenManager, DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.ApplicationStore.CreateInstance(HttpApplicationStore), messageTypeProvider) {
+ Contract.Requires<ArgumentNullException>(serviceDescription != null);
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
+ Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
}
/// <summary>
@@ -82,16 +92,45 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="nonceStore">The nonce store.</param>
/// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param>
public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore, OAuthServiceProviderMessageFactory messageTypeProvider) {
- ErrorUtilities.VerifyArgumentNotNull(serviceDescription, "serviceDescription");
- ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
- ErrorUtilities.VerifyArgumentNotNull(messageTypeProvider, "messageTypeProvider");
+ Contract.Requires<ArgumentNullException>(serviceDescription != null);
+ Contract.Requires<ArgumentNullException>(tokenManager != null);
+ Contract.Requires<ArgumentNullException>(nonceStore != null);
+ Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
var signingElement = serviceDescription.CreateTamperProtectionElement();
this.ServiceDescription = serviceDescription;
this.OAuthChannel = new OAuthChannel(signingElement, nonceStore, tokenManager, messageTypeProvider);
this.TokenGenerator = new StandardTokenGenerator();
this.SecuritySettings = DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.CreateSecuritySettings();
+
+ Reporting.RecordFeatureAndDependencyUse(this, serviceDescription, tokenManager, nonceStore);
+ }
+
+ /// <summary>
+ /// Gets the standard state storage mechanism that uses ASP.NET's
+ /// HttpApplication state dictionary to store associations and nonces.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public static INonceStore HttpApplicationStore {
+ get {
+ Contract.Ensures(Contract.Result<INonceStore>() != null);
+
+ HttpContext context = HttpContext.Current;
+ ErrorUtilities.VerifyOperation(context != null, Strings.StoreRequiredWhenNoHttpContextAvailable, typeof(INonceStore).Name);
+ var store = (INonceStore)context.Application[ApplicationStoreKey];
+ if (store == null) {
+ context.Application.Lock();
+ try {
+ if ((store = (INonceStore)context.Application[ApplicationStoreKey]) == null) {
+ context.Application[ApplicationStoreKey] = store = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge);
+ }
+ } finally {
+ context.Application.UnLock();
+ }
+ }
+
+ return store;
+ }
}
/// <summary>
@@ -132,8 +171,7 @@ namespace DotNetOpenAuth.OAuth {
}
set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.channel = value;
}
}
@@ -148,8 +186,7 @@ namespace DotNetOpenAuth.OAuth {
/// length of the final string.</param>
/// <returns>The verification code.</returns>
public static string CreateVerificationCode(VerificationCodeFormat format, int length) {
- Contract.Requires(length >= 0);
- ErrorUtilities.VerifyArgumentInRange(length >= 0, "length");
+ Contract.Requires<ArgumentOutOfRangeException>(length >= 0);
switch (format) {
case VerificationCodeFormat.IncludedInCallback:
@@ -220,7 +257,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The token request message the Consumer sent that the Service Provider is now responding to.</param>
/// <returns>The response message to send using the <see cref="Channel"/>, after optionally adding extra data to it.</returns>
public UnauthorizedTokenResponse PrepareUnauthorizedTokenMessage(UnauthorizedTokenRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey);
string secret = this.TokenGenerator.GenerateSecret();
@@ -257,11 +294,12 @@ namespace DotNetOpenAuth.OAuth {
/// <summary>
/// Gets the OAuth authorization request included with an OpenID authentication
- /// request.
+ /// request, if there is one.
/// </summary>
/// <param name="openIdRequest">The OpenID authentication request.</param>
/// <returns>
- /// The scope of access the relying party is requesting.
+ /// The scope of access the relying party is requesting, or null if no OAuth request
+ /// is present.
/// </returns>
/// <remarks>
/// <para>Call this method rather than simply extracting the OAuth extension
@@ -269,9 +307,8 @@ namespace DotNetOpenAuth.OAuth {
/// security measures that are required are taken.</para>
/// </remarks>
public AuthorizationRequest ReadAuthorizationRequest(IHostProcessedRequest openIdRequest) {
- Contract.Requires(openIdRequest != null);
- Contract.Requires(this.TokenManager is ICombinedOpenIdProviderTokenManager);
- ErrorUtilities.VerifyArgumentNotNull(openIdRequest, "openIdAuthenticationRequest");
+ Contract.Requires<ArgumentNullException>(openIdRequest != null);
+ Contract.Requires<InvalidOperationException>(this.TokenManager is ICombinedOpenIdProviderTokenManager);
var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager;
ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
@@ -291,24 +328,39 @@ namespace DotNetOpenAuth.OAuth {
return authzRequest;
}
+ /// <summary>
+ /// Attaches the authorization response to an OpenID authentication response.
+ /// </summary>
+ /// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
+ /// <param name="consumerKey">The consumer key. Must be <c>null</c> if and only if <paramref name="scope"/> is null.</param>
+ /// <param name="scope">The approved access scope. Use <c>null</c> to indicate no access was granted. The empty string will be interpreted as some default level of access is granted.</param>
+ [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to take IAuthenticationRequest because that's the only supported use case.")]
+ [Obsolete("Call the overload that doesn't take a consumerKey instead.")]
+ public void AttachAuthorizationResponse(IHostProcessedRequest openIdAuthenticationRequest, string consumerKey, string scope) {
+ Contract.Requires<ArgumentNullException>(openIdAuthenticationRequest != null);
+ Contract.Requires<ArgumentException>((consumerKey == null) == (scope == null));
+ Contract.Requires<InvalidOperationException>(this.TokenManager is ICombinedOpenIdProviderTokenManager);
+ var openidTokenManager = (ICombinedOpenIdProviderTokenManager)this.TokenManager;
+ ErrorUtilities.VerifyArgument(consumerKey == null || consumerKey == openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm), "The consumer key and the realm did not match according to the token manager.");
+
+ this.AttachAuthorizationResponse(openIdAuthenticationRequest, scope);
+ }
+
/// <summary>
/// Attaches the authorization response to an OpenID authentication response.
/// </summary>
/// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
- /// <param name="consumerKey">The consumer key. May and should be <c>null</c> if and only if <paramref name="scope"/> is null.</param>
/// <param name="scope">The approved access scope. Use <c>null</c> to indicate no access was granted. The empty string will be interpreted as some default level of access is granted.</param>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to take IAuthenticationRequest because that's the only supported use case.")]
- public void AttachAuthorizationResponse(IAuthenticationRequest openIdAuthenticationRequest, string consumerKey, string scope) {
- Contract.Requires(openIdAuthenticationRequest != null);
- Contract.Requires((consumerKey == null) == (scope == null));
- Contract.Requires(this.TokenManager is IOpenIdOAuthTokenManager);
- ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
- var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;
- ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
+ public void AttachAuthorizationResponse(IHostProcessedRequest openIdAuthenticationRequest, string scope) {
+ Contract.Requires<ArgumentNullException>(openIdAuthenticationRequest != null);
+ Contract.Requires<InvalidOperationException>(this.TokenManager is ICombinedOpenIdProviderTokenManager);
+ var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager;
IOpenIdMessageExtension response;
if (scope != null) {
// Generate an authorized request token to return to the relying party.
+ string consumerKey = openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm);
var approvedResponse = new AuthorizationApprovedResponse {
RequestToken = this.TokenGenerator.GenerateRequestToken(consumerKey),
Scope = scope,
@@ -333,8 +385,7 @@ namespace DotNetOpenAuth.OAuth {
/// </returns>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Consistent user experience with instance.")]
public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
// It is very important for us to ignore the oauth_callback argument in the
// UserAuthorizationRequest if the Consumer is a 1.0a consumer or else we
@@ -371,10 +422,8 @@ namespace DotNetOpenAuth.OAuth {
/// </returns>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Consistent user experience with instance.")]
public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request, Uri callback) {
- Contract.Requires(request != null);
- Contract.Requires(callback != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(callback, "callback");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(callback != null);
var authorization = new UserAuthorizationResponse(callback, request.Version) {
RequestToken = request.RequestToken,
@@ -417,8 +466,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The Consumer's message requesting an access token.</param>
/// <returns>The HTTP response to actually send to the Consumer.</returns>
public AuthorizedTokenResponse PrepareAccessTokenMessage(AuthorizedTokenRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
ErrorUtilities.VerifyProtocol(this.TokenManager.IsRequestTokenAuthorized(request.RequestToken), OAuthStrings.AccessTokenNotAuthorized, request.RequestToken);
@@ -475,7 +523,7 @@ namespace DotNetOpenAuth.OAuth {
/// </remarks>
/// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception>
public AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestInfo request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
AccessProtectedResourceRequest accessMessage;
if (this.Channel.TryReadFromRequest<AccessProtectedResourceRequest>(request, out accessMessage)) {
@@ -497,8 +545,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The request.</param>
/// <returns>The <see cref="IPrincipal"/> instance that can be used for access control of resources.</returns>
public OAuthPrincipal CreatePrincipal(AccessProtectedResourceRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
IServiceProviderAccessToken accessToken = this.TokenManager.GetAccessToken(request.AccessToken);
return new OAuthPrincipal(accessToken);
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
index 9014762..6205f55 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth {
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.ChannelElements;
@@ -94,6 +95,7 @@ namespace DotNetOpenAuth.OAuth {
/// </summary>
/// <returns>The created signing element.</returns>
internal ITamperProtectionChannelBindingElement CreateTamperProtectionElement() {
+ Contract.Requires(this.TamperProtectionElements != null);
return new SigningBindingElementChain(this.TamperProtectionElements.Select(el => (ITamperProtectionChannelBindingElement)el.Clone()).ToArray());
}
}
diff --git a/src/DotNetOpenAuth/OAuth/WebConsumer.cs b/src/DotNetOpenAuth/OAuth/WebConsumer.cs
index 56d3029..de37b80 100644
--- a/src/DotNetOpenAuth/OAuth/WebConsumer.cs
+++ b/src/DotNetOpenAuth/OAuth/WebConsumer.cs
@@ -79,8 +79,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
/// <param name="scope">The scope of access that is requested of the service provider.</param>
public void AttachAuthorizationRequest(IAuthenticationRequest openIdAuthenticationRequest, string scope) {
- Contract.Requires(openIdAuthenticationRequest != null);
- ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
+ Contract.Requires<ArgumentNullException>(openIdAuthenticationRequest != null);
var authorizationRequest = new AuthorizationRequest {
Consumer = this.ConsumerKey,
@@ -102,9 +101,8 @@ namespace DotNetOpenAuth.OAuth {
/// The token manager instance must implement <see cref="IOpenIdOAuthTokenManager"/>.
/// </remarks>
public AuthorizedTokenResponse ProcessUserAuthorization(IAuthenticationResponse openIdAuthenticationResponse) {
- Contract.Requires(openIdAuthenticationResponse != null);
- Contract.Requires(this.TokenManager is IOpenIdOAuthTokenManager);
- ErrorUtilities.VerifyArgumentNotNull(openIdAuthenticationResponse, "openIdAuthenticationResponse");
+ Contract.Requires<ArgumentNullException>(openIdAuthenticationResponse != null);
+ Contract.Requires<InvalidOperationException>(this.TokenManager is IOpenIdOAuthTokenManager);
var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;
ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
@@ -120,7 +118,8 @@ namespace DotNetOpenAuth.OAuth {
}
// Prepare a message to exchange the request token for an access token.
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) {
+ // We are careful to use a v1.0 message version so that the oauth_verifier is not required.
+ var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.V10.Version) {
RequestToken = positiveAuthorization.RequestToken,
ConsumerKey = this.ConsumerKey,
};
@@ -141,8 +140,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The incoming HTTP request.</param>
/// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
public AuthorizedTokenResponse ProcessUserAuthorization(HttpRequestInfo request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
UserAuthorizationResponse authorizationMessage;
if (this.Channel.TryReadFromRequest<UserAuthorizationResponse>(request, out authorizationMessage)) {
diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs
index ce129bb..62e91ec 100644
--- a/src/DotNetOpenAuth/OpenId/Association.cs
+++ b/src/DotNetOpenAuth/OpenId/Association.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId {
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Security.Cryptography;
using System.Text;
@@ -23,6 +24,8 @@ namespace DotNetOpenAuth.OpenId {
/// (dumb associations).
/// </remarks>
[DebuggerDisplay("Handle = {Handle}, Expires = {Expires}")]
+ [ContractVerification(true)]
+ [ContractClass(typeof(AssociationContract))]
public abstract class Association {
/// <summary>
/// Initializes a new instance of the <see cref="Association"/> class.
@@ -30,10 +33,14 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="handle">The handle.</param>
/// <param name="secret">The secret.</param>
/// <param name="totalLifeLength">How long the association will be useful.</param>
- /// <param name="issued">When this association was originally issued by the Provider.</param>
+ /// <param name="issued">The UTC time of when this association was originally issued by the Provider.</param>
protected Association(string handle, byte[] secret, TimeSpan totalLifeLength, DateTime issued) {
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(handle));
+ Contract.Requires<ArgumentNullException>(secret != null);
+ Contract.Requires<ArgumentOutOfRangeException>(totalLifeLength > TimeSpan.Zero);
+ Contract.Requires<ArgumentException>(issued.Kind == DateTimeKind.Utc);
+ Contract.Requires<ArgumentOutOfRangeException>(issued <= DateTime.UtcNow);
+ Contract.Ensures(this.TotalLifeLength == totalLifeLength);
this.Handle = handle;
this.SecretKey = secret;
@@ -47,7 +54,7 @@ namespace DotNetOpenAuth.OpenId {
public string Handle { get; private set; }
/// <summary>
- /// Gets the time when this <see cref="Association"/> will expire.
+ /// Gets the UTC time when this <see cref="Association"/> will expire.
/// </summary>
public DateTime Expires {
get { return this.Issued + this.TotalLifeLength; }
@@ -76,7 +83,7 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Gets or sets the time that this <see cref="Association"/> was first created.
+ /// Gets or sets the UTC time that this <see cref="Association"/> was first created.
/// </summary>
internal DateTime Issued { get; set; }
@@ -85,7 +92,10 @@ namespace DotNetOpenAuth.OpenId {
/// Never negative (counter runs to zero).
/// </summary>
protected internal long SecondsTillExpiration {
- get { return Math.Max(0, (long)this.TimeTillExpiration.TotalSeconds); }
+ get {
+ Contract.Ensures(Contract.Result<long>() >= 0);
+ return Math.Max(0, (long)this.TimeTillExpiration.TotalSeconds);
+ }
}
/// <summary>
@@ -98,7 +108,10 @@ namespace DotNetOpenAuth.OpenId {
/// Gets the duration a secret key used for signing dumb client requests will be good for.
/// </summary>
protected static TimeSpan DumbSecretLifetime {
- get { return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime; }
+ get {
+ Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
+ return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime;
+ }
}
/// <summary>
@@ -113,7 +126,10 @@ namespace DotNetOpenAuth.OpenId {
/// Associations that are not likely to last the duration of a user login are not worth using at all.
/// </remarks>
private static TimeSpan MinimumUsefulAssociationLifetime {
- get { return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime; }
+ get {
+ Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
+ return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime;
+ }
}
/// <summary>
@@ -130,8 +146,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="handle">
/// The <see cref="Handle"/> property of the previous <see cref="Association"/> instance.
/// </param>
- /// <param name="expires">
- /// The value of the <see cref="Expires"/> property of the previous <see cref="Association"/> instance.
+ /// <param name="expiresUtc">
+ /// The UTC value of the <see cref="Expires"/> property of the previous <see cref="Association"/> instance.
/// </param>
/// <param name="privateData">
/// The byte array returned by a call to <see cref="SerializePrivateData"/> on the previous
@@ -142,15 +158,13 @@ namespace DotNetOpenAuth.OpenId {
/// from a custom association store's
/// <see cref="IAssociationStore&lt;TKey&gt;.GetAssociation(TKey, SecuritySettings)"/> method.
/// </returns>
- public static Association Deserialize(string handle, DateTime expires, byte[] privateData) {
- if (string.IsNullOrEmpty(handle)) {
- throw new ArgumentNullException("handle");
- }
- if (privateData == null) {
- throw new ArgumentNullException("privateData");
- }
- expires = expires.ToUniversalTimeSafe();
- TimeSpan remainingLifeLength = expires - DateTime.UtcNow;
+ public static Association Deserialize(string handle, DateTime expiresUtc, byte[] privateData) {
+ Contract.Requires<ArgumentNullException>(!String.IsNullOrEmpty(handle));
+ Contract.Requires<ArgumentNullException>(privateData != null);
+ Contract.Ensures(Contract.Result<Association>() != null);
+
+ expiresUtc = expiresUtc.ToUniversalTimeSafe();
+ TimeSpan remainingLifeLength = expiresUtc - DateTime.UtcNow;
byte[] secret = privateData; // the whole of privateData is the secret key for now.
// We figure out what derived type to instantiate based on the length of the secret.
try {
@@ -174,12 +188,16 @@ namespace DotNetOpenAuth.OpenId {
/// in this byte array, as they are useful for fast database lookup and are persisted separately.
/// </remarks>
public byte[] SerializePrivateData() {
+ Contract.Ensures(Contract.Result<byte[]>() != null);
+
// We may want to encrypt this secret using the machine.config private key,
// and add data regarding which Association derivative will need to be
// re-instantiated on deserialization.
// For now, we just send out the secret key. We can derive the type from the length later.
byte[] secretKeyCopy = new byte[this.SecretKey.Length];
- this.SecretKey.CopyTo(secretKeyCopy, 0);
+ if (this.SecretKey.Length > 0) {
+ this.SecretKey.CopyTo(secretKeyCopy, 0);
+ }
return secretKeyCopy;
}
@@ -253,6 +271,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="data">The data to sign. This data will not be changed (the signature is the return value).</param>
/// <returns>The calculated signature of the data.</returns>
protected internal byte[] Sign(byte[] data) {
+ Contract.Requires<ArgumentNullException>(data != null);
using (HashAlgorithm hasher = this.CreateHasher()) {
return hasher.ComputeHash(data);
}
@@ -263,5 +282,19 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <returns>The hash algorithm used for message signing.</returns>
protected abstract HashAlgorithm CreateHasher();
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(!string.IsNullOrEmpty(this.Handle));
+ Contract.Invariant(this.TotalLifeLength > TimeSpan.Zero);
+ Contract.Invariant(this.SecretKey != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/OpenId/AssociationContract.cs b/src/DotNetOpenAuth/OpenId/AssociationContract.cs
new file mode 100644
index 0000000..57f4fd9
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/AssociationContract.cs
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssociationContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Security.Cryptography;
+ using System.Text;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Code contract for the <see cref="Association"/> class.
+ /// </summary>
+ [ContractClassFor(typeof(Association))]
+ internal abstract class AssociationContract : Association {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="AssociationContract"/> class from being created.
+ /// </summary>
+ private AssociationContract()
+ : base(null, null, TimeSpan.Zero, DateTime.Now) {
+ }
+
+ /// <summary>
+ /// Gets the length (in bits) of the hash this association creates when signing.
+ /// </summary>
+ public override int HashBitLength {
+ get {
+ Contract.Ensures(Contract.Result<int>() > 0);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// The string to pass as the assoc_type value in the OpenID protocol.
+ /// </summary>
+ /// <param name="protocol">The protocol version of the message that the assoc_type value will be included in.</param>
+ /// <returns>
+ /// The value that should be used for the openid.assoc_type parameter.
+ /// </returns>
+ [Pure]
+ internal override string GetAssociationType(Protocol protocol) {
+ Contract.Requires<ArgumentNullException>(protocol != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Returns the specific hash algorithm used for message signing.
+ /// </summary>
+ /// <returns>
+ /// The hash algorithm used for message signing.
+ /// </returns>
+ [Pure]
+ protected override HashAlgorithm CreateHasher() {
+ Contract.Ensures(Contract.Result<HashAlgorithm>() != null);
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Associations.cs b/src/DotNetOpenAuth/OpenId/Associations.cs
index fe2be42..cf49d1c 100644
--- a/src/DotNetOpenAuth/OpenId/Associations.cs
+++ b/src/DotNetOpenAuth/OpenId/Associations.cs
@@ -9,6 +9,8 @@ namespace DotNetOpenAuth.OpenId {
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
@@ -21,6 +23,7 @@ namespace DotNetOpenAuth.OpenId {
/// can break if the collection is changed by another thread during enumeration.
/// </remarks>
[DebuggerDisplay("Count = {assocs.Count}")]
+ [ContractVerification(true)]
internal class Associations {
/// <summary>
/// The lookup table where keys are the association handles and values are the associations themselves.
@@ -44,6 +47,8 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
public IEnumerable<Association> Best {
get {
+ Contract.Ensures(Contract.Result<IEnumerable<Association>>() != null);
+
lock (this.associations) {
return this.associations.OrderByDescending(assoc => assoc.Issued);
}
@@ -55,11 +60,14 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="association">The association to add to the collection.</param>
public void Set(Association association) {
- ErrorUtilities.VerifyArgumentNotNull(association, "association");
+ Contract.Requires<ArgumentNullException>(association != null);
+ Contract.Ensures(this.Get(association.Handle) == association);
lock (this.associations) {
this.associations.Remove(association.Handle); // just in case one already exists.
this.associations.Add(association);
}
+
+ Contract.Assume(this.Get(association.Handle) == association);
}
/// <summary>
@@ -67,7 +75,10 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="handle">The handle to the required association.</param>
/// <returns>The desired association, or null if none with the given handle could be found.</returns>
+ [Pure]
public Association Get(string handle) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(handle));
+
lock (this.associations) {
if (this.associations.Contains(handle)) {
return this.associations[handle];
@@ -83,6 +94,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="handle">The handle to the required association.</param>
/// <returns>Whether an <see cref="Association"/> with the given handle was in the collection for removal.</returns>
public bool Remove(string handle) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(handle));
lock (this.associations) {
return this.associations.Remove(handle);
}
@@ -99,5 +111,17 @@ namespace DotNetOpenAuth.OpenId {
}
}
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.associations != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
index d7dca9a..580bdfd 100644
--- a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
@@ -23,7 +23,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
/// to the originally requested extension and format.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sreg", Justification = "Abbreviation")]
- public class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior {
+ public sealed class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior {
/// <summary>
/// Initializes static members of the <see cref="AXFetchAsSregTransform"/> class.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
index 4166f19..937ecaf 100644
--- a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
@@ -61,6 +61,42 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
}
/// <summary>
+ /// Looks up a localized string similar to The PAPE request has an incomplete set of authentication policies..
+ /// </summary>
+ internal static string PapeRequestMissingRequiredPolicies {
+ get {
+ return ResourceManager.GetString("PapeRequestMissingRequiredPolicies", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A PAPE response is missing or is missing required policies..
+ /// </summary>
+ internal static string PapeResponseOrRequiredPoliciesMissing {
+ get {
+ return ResourceManager.GetString("PapeResponseOrRequiredPoliciesMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No personally identifiable information should be included in authentication responses when the PAPE authentication policy http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf is present..
+ /// </summary>
+ internal static string PiiIncludedWithNoPiiPolicy {
+ get {
+ return ResourceManager.GetString("PiiIncludedWithNoPiiPolicy", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No personally identifiable information should be requested when the http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf PAPE policy is present..
+ /// </summary>
+ internal static string PiiRequestedWithNoPiiPolicy {
+ get {
+ return ResourceManager.GetString("PiiRequestedWithNoPiiPolicy", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to No PPID provider has been configured..
/// </summary>
internal static string PpidProviderNotGiven {
@@ -68,5 +104,23 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
return ResourceManager.GetString("PpidProviderNotGiven", resourceCulture);
}
}
+
+ /// <summary>
+ /// Looks up a localized string similar to Discovery on the Realm URL MUST be performed before sending a positive assertion..
+ /// </summary>
+ internal static string RealmDiscoveryNotPerformed {
+ get {
+ return ResourceManager.GetString("RealmDiscoveryNotPerformed", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The Realm in an authentication request must be an HTTPS URL..
+ /// </summary>
+ internal static string RealmMustBeHttps {
+ get {
+ return ResourceManager.GetString("RealmMustBeHttps", resourceCulture);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
index 23e3e73..a8bf2d6 100644
--- a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
@@ -117,7 +117,25 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="PapeRequestMissingRequiredPolicies" xml:space="preserve">
+ <value>The PAPE request has an incomplete set of authentication policies.</value>
+ </data>
+ <data name="PapeResponseOrRequiredPoliciesMissing" xml:space="preserve">
+ <value>A PAPE response is missing or is missing required policies.</value>
+ </data>
+ <data name="PiiIncludedWithNoPiiPolicy" xml:space="preserve">
+ <value>No personally identifiable information should be included in authentication responses when the PAPE authentication policy http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf is present.</value>
+ </data>
+ <data name="PiiRequestedWithNoPiiPolicy" xml:space="preserve">
+ <value>No personally identifiable information should be requested when the http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf PAPE policy is present.</value>
+ </data>
<data name="PpidProviderNotGiven" xml:space="preserve">
<value>No PPID provider has been configured.</value>
</data>
+ <data name="RealmDiscoveryNotPerformed" xml:space="preserve">
+ <value>Discovery on the Realm URL MUST be performed before sending a positive assertion.</value>
+ </data>
+ <data name="RealmMustBeHttps" xml:space="preserve">
+ <value>The Realm in an authentication request must be an HTTPS URL.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.sr.resx b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.sr.resx
new file mode 100644
index 0000000..2b1b911
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.sr.resx
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="PpidProviderNotGiven" xml:space="preserve">
+ <value>Nijedan PPID provajder nije konfigurisan.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs b/src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs
new file mode 100644
index 0000000..20c207f
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs
@@ -0,0 +1,285 @@
+//-----------------------------------------------------------------------
+// <copyright file="GsaIcamProfile.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Behaviors {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Provider;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// Implements the Identity, Credential, &amp; Access Management (ICAM) OpenID 2.0 Profile
+ /// for the General Services Administration (GSA).
+ /// </summary>
+ /// <remarks>
+ /// <para>Relying parties that include this profile are always held to the terms required by the profile,
+ /// but Providers are only affected by the special behaviors of the profile when the RP specifically
+ /// indicates that they want to use this profile. </para>
+ /// </remarks>
+ [Serializable]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Icam", Justification = "Acronym")]
+ public sealed class GsaIcamProfile : IRelyingPartyBehavior, IProviderBehavior {
+ /// <summary>
+ /// The maximum time a shared association can live.
+ /// </summary>
+ private static readonly TimeSpan MaximumAssociationLifetime = TimeSpan.FromSeconds(86400);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GsaIcamProfile"/> class.
+ /// </summary>
+ public GsaIcamProfile() {
+ if (DisableSslRequirement) {
+ Logger.OpenId.Warn("GSA level 1 behavior has its RequireSsl requirement disabled.");
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the provider for generating PPID identifiers.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ppid", Justification = "Acronym")]
+ public static IDirectedIdentityIdentifierProvider PpidIdentifierProvider { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether PII is allowed to be requested or received via OpenID.
+ /// </summary>
+ /// <value>The default value is <c>false</c>.</value>
+ public static bool AllowPersonallyIdentifiableInformation { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to ignore the SSL requirement (for testing purposes only).
+ /// </summary>
+ public static bool DisableSslRequirement { get; set; }
+
+ #region IRelyingPartyBehavior Members
+
+ /// <summary>
+ /// Applies a well known set of security requirements.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void IRelyingPartyBehavior.ApplySecuritySettings(RelyingPartySecuritySettings securitySettings) {
+ if (securitySettings.MaximumHashBitLength < 256) {
+ securitySettings.MaximumHashBitLength = 256;
+ }
+
+ securitySettings.RequireSsl = !DisableSslRequirement;
+ securitySettings.RequireDirectedIdentity = true;
+ securitySettings.RequireAssociation = true;
+ securitySettings.RejectDelegatingIdentifiers = true;
+ securitySettings.IgnoreUnsignedExtensions = true;
+ securitySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20;
+ }
+
+ /// <summary>
+ /// Called when an authentication request is about to be sent.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ void IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(RelyingParty.IAuthenticationRequest request) {
+ RelyingParty.AuthenticationRequest requestInternal = (RelyingParty.AuthenticationRequest)request;
+ ErrorUtilities.VerifyProtocol(string.Equals(request.Realm.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) || DisableSslRequirement, BehaviorStrings.RealmMustBeHttps);
+
+ var pape = requestInternal.AppliedExtensions.OfType<PolicyRequest>().SingleOrDefault();
+ if (pape == null) {
+ request.AddExtension(pape = new PolicyRequest());
+ }
+
+ if (!pape.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ pape.PreferredPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier);
+ }
+
+ if (!pape.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) {
+ pape.PreferredPolicies.Add(AuthenticationPolicies.USGovernmentTrustLevel1);
+ }
+
+ if (!AllowPersonallyIdentifiableInformation && !pape.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) {
+ pape.PreferredPolicies.Add(AuthenticationPolicies.NoPersonallyIdentifiableInformation);
+ }
+
+ if (pape.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) {
+ ErrorUtilities.VerifyProtocol(
+ (!requestInternal.AppliedExtensions.OfType<ClaimsRequest>().Any() &&
+ !requestInternal.AppliedExtensions.OfType<FetchRequest>().Any()),
+ BehaviorStrings.PiiIncludedWithNoPiiPolicy);
+ }
+
+ Reporting.RecordEventOccurrence(this, "RP");
+ }
+
+ /// <summary>
+ /// Called when an incoming positive assertion is received.
+ /// </summary>
+ /// <param name="assertion">The positive assertion.</param>
+ void IRelyingPartyBehavior.OnIncomingPositiveAssertion(IAuthenticationResponse assertion) {
+ PolicyResponse pape = assertion.GetExtension<PolicyResponse>();
+ ErrorUtilities.VerifyProtocol(
+ pape != null &&
+ pape.ActualPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1) &&
+ pape.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier),
+ BehaviorStrings.PapeResponseOrRequiredPoliciesMissing);
+
+ ErrorUtilities.VerifyProtocol(AllowPersonallyIdentifiableInformation || pape.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation), BehaviorStrings.PapeResponseOrRequiredPoliciesMissing);
+
+ if (pape.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) {
+ ErrorUtilities.VerifyProtocol(
+ assertion.GetExtension<ClaimsResponse>() == null &&
+ assertion.GetExtension<FetchResponse>() == null,
+ BehaviorStrings.PiiIncludedWithNoPiiPolicy);
+ }
+ }
+
+ #endregion
+
+ #region IProviderBehavior Members
+
+ /// <summary>
+ /// Adapts the default security settings to the requirements of this behavior.
+ /// </summary>
+ /// <param name="securitySettings">The original security settings.</param>
+ void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) {
+ if (securitySettings.MaximumHashBitLength < 256) {
+ securitySettings.MaximumHashBitLength = 256;
+ }
+
+ SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256, MaximumAssociationLifetime, securitySettings);
+ SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, MaximumAssociationLifetime, securitySettings);
+ }
+
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool IProviderBehavior.OnIncomingRequest(IRequest request) {
+ var hostProcessedRequest = request as IHostProcessedRequest;
+ if (hostProcessedRequest != null) {
+ // Only apply our special policies if the RP requested it.
+ var papeRequest = request.GetExtension<PolicyRequest>();
+ if (papeRequest != null) {
+ if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) {
+ // Whenever we see this GSA policy requested, we MUST also see the PPID policy requested.
+ ErrorUtilities.VerifyProtocol(papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier), BehaviorStrings.PapeRequestMissingRequiredPolicies);
+ ErrorUtilities.VerifyProtocol(string.Equals(hostProcessedRequest.Realm.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) || DisableSslRequirement, BehaviorStrings.RealmMustBeHttps);
+
+ // Apply GSA-specific security to this individual request.
+ request.SecuritySettings.RequireSsl = !DisableSslRequirement;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool IProviderBehavior.OnOutgoingResponse(Provider.IAuthenticationRequest request) {
+ bool result = false;
+
+ // Nothing to do for negative assertions.
+ if (!request.IsAuthenticated.Value) {
+ return result;
+ }
+
+ var requestInternal = (Provider.AuthenticationRequest)request;
+ var responseMessage = (IProtocolMessageWithExtensions)requestInternal.Response;
+
+ // Only apply our special policies if the RP requested it.
+ var papeRequest = request.GetExtension<PolicyRequest>();
+ if (papeRequest != null) {
+ var papeResponse = responseMessage.Extensions.OfType<PolicyResponse>().SingleOrDefault();
+ if (papeResponse == null) {
+ request.AddResponseExtension(papeResponse = new PolicyResponse());
+ }
+
+ if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) {
+ result = true;
+ if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) {
+ papeResponse.ActualPolicies.Add(AuthenticationPolicies.USGovernmentTrustLevel1);
+ }
+
+ // The spec requires that the OP perform discovery and if that fails, it must either sternly
+ // warn the user of a potential threat or just abort the authentication.
+ // We can't verify that the OP displayed anything to the user at this level, but we can
+ // at least verify that the OP performed the discovery on the realm and halt things if it didn't.
+ ErrorUtilities.VerifyHost(requestInternal.HasRealmDiscoveryBeenPerformed, BehaviorStrings.RealmDiscoveryNotPerformed);
+ }
+
+ if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ ErrorUtilities.VerifyProtocol(request.ClaimedIdentifier == request.LocalIdentifier, OpenIdStrings.DelegatingIdentifiersNotAllowed);
+
+ // Mask the user's identity with a PPID.
+ ErrorUtilities.VerifyHost(PpidIdentifierProvider != null, BehaviorStrings.PpidProviderNotGiven);
+ Identifier ppidIdentifier = PpidIdentifierProvider.GetIdentifier(request.LocalIdentifier, request.Realm);
+ requestInternal.ResetClaimedAndLocalIdentifiers(ppidIdentifier);
+
+ // Indicate that the RP is receiving a PPID claimed_id
+ if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ papeResponse.ActualPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier);
+ }
+ }
+
+ if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) {
+ ErrorUtilities.VerifyProtocol(
+ !responseMessage.Extensions.OfType<ClaimsResponse>().Any() &&
+ !responseMessage.Extensions.OfType<FetchResponse>().Any(),
+ BehaviorStrings.PiiIncludedWithNoPiiPolicy);
+
+ // If no PII is given in extensions, and the claimed_id is a PPID, then we can state we issue no PII.
+ if (papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) {
+ papeResponse.ActualPolicies.Add(AuthenticationPolicies.NoPersonallyIdentifiableInformation);
+ }
+ }
+ }
+
+ Reporting.RecordEventOccurrence(this, "OP");
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Ensures the maximum association lifetime does not exceed a given limit.
+ /// </summary>
+ /// <param name="associationType">Type of the association.</param>
+ /// <param name="maximumLifetime">The maximum lifetime.</param>
+ /// <param name="securitySettings">The security settings to adjust.</param>
+ private static void SetMaximumAssociationLifetimeToNotExceed(string associationType, TimeSpan maximumLifetime, ProviderSecuritySettings securitySettings) {
+ Contract.Requires(!String.IsNullOrEmpty(associationType));
+ Contract.Requires(maximumLifetime.TotalSeconds > 0);
+ if (!securitySettings.AssociationLifetimes.ContainsKey(associationType) ||
+ securitySettings.AssociationLifetimes[associationType] > maximumLifetime) {
+ securitySettings.AssociationLifetimes[associationType] = maximumLifetime;
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs b/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
index f09e886..a465611 100644
--- a/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Behaviors {
using System;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
@@ -73,8 +74,6 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
/// from handling it; <c>false</c> to allow other behaviors to process this request.
/// </returns>
bool IProviderBehavior.OnOutgoingResponse(IAuthenticationRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
// Nothing to do for negative assertions.
if (!request.IsAuthenticated.Value) {
return false;
@@ -109,6 +108,8 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
papeResponse.ActualPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier);
}
+
+ Reporting.RecordEventOccurrence(this, string.Empty);
}
}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
index de9cda9..b730b1f 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
@@ -17,13 +17,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </summary>
internal class BackwardCompatibilityBindingElement : IChannelBindingElement {
/// <summary>
- /// The name of the callback parameter that stores the Provider Endpoint URL
+ /// The "dnoa.op_endpoint" callback parameter that stores the Provider Endpoint URL
/// to tack onto the return_to URI.
/// </summary>
private const string ProviderEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint";
/// <summary>
- /// The name of the callback parameter that stores the Claimed Identifier
+ /// The "dnoa.claimed_id" callback parameter that stores the Claimed Identifier
/// to tack onto the return_to URI.
/// </summary>
private const string ClaimedIdentifierParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id";
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
index fa6bfa4..c516e8f 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -33,8 +34,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="extensionFactory">The extension factory.</param>
/// <param name="securitySettings">The security settings.</param>
internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory, SecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(extensionFactory, "extensionFactory");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(extensionFactory != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
this.ExtensionFactory = extensionFactory;
this.relyingPartySecuritySettings = securitySettings as RelyingPartySecuritySettings;
@@ -76,10 +77,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// Implementations that provide message protection must honor the
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
+ [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "It doesn't look too bad to me. :)")]
public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyOperation(this.Channel != null, "Channel property has not been set.");
-
var extendableMessage = message as IProtocolMessageWithExtensions;
if (extendableMessage != null) {
Protocol protocol = Protocol.Lookup(message.Version);
@@ -91,6 +90,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
foreach (IExtensionMessage protocolExtension in extendableMessage.Extensions) {
var extension = protocolExtension as IOpenIdMessageExtension;
if (extension != null) {
+ Reporting.RecordFeatureUse(protocolExtension);
+
// Give extensions that require custom serialization a chance to do their work.
var customSerializingExtension = extension as IMessageWithEvents;
if (customSerializingExtension != null) {
@@ -100,7 +101,12 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
// OpenID 2.0 Section 12 forbids two extensions with the same TypeURI in the same message.
ErrorUtilities.VerifyProtocol(!extensionManager.ContainsExtension(extension.TypeUri), OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extension.TypeUri);
- var extensionDictionary = this.Channel.MessageDescriptions.GetAccessor(extension).Serialize();
+ // Ensure that we're sending out a valid extension.
+ var extensionDescription = this.Channel.MessageDescriptions.Get(extension);
+ var extensionDictionary = extensionDescription.GetDictionary(extension).Serialize();
+ extensionDescription.EnsureMessagePartsPassBasicValidation(extensionDictionary);
+
+ // Add the extension to the outgoing message payload.
extensionManager.AddExtensionArguments(extension.TypeUri, extensionDictionary);
} else {
Logger.OpenId.WarnFormat("Unexpected extension type {0} did not implement {1}.", protocolExtension.GetType(), typeof(IOpenIdMessageExtension).Name);
@@ -143,6 +149,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
if (extendableMessage != null) {
// First add the extensions that are signed by the Provider.
foreach (IOpenIdMessageExtension signedExtension in this.GetExtensions(extendableMessage, true, null)) {
+ Reporting.RecordFeatureUse(signedExtension);
signedExtension.IsSignedByRemoteParty = true;
extendableMessage.Extensions.Add(signedExtension);
}
@@ -152,6 +159,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
if (this.relyingPartySecuritySettings == null || !this.relyingPartySecuritySettings.IgnoreUnsignedExtensions) {
Func<string, bool> isNotSigned = typeUri => !extendableMessage.Extensions.Cast<IOpenIdMessageExtension>().Any(ext => ext.TypeUri == typeUri);
foreach (IOpenIdMessageExtension unsignedExtension in this.GetExtensions(extendableMessage, false, isNotSigned)) {
+ Reporting.RecordFeatureUse(unsignedExtension);
unsignedExtension.IsSignedByRemoteParty = false;
extendableMessage.Extensions.Add(unsignedExtension);
}
@@ -193,7 +201,12 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, message, isAtProvider);
if (extension != null) {
try {
- MessageDictionary extensionDictionary = this.Channel.MessageDescriptions.GetAccessor(extension);
+ // Make sure the extension fulfills spec requirements before deserializing it.
+ MessageDescription messageDescription = this.Channel.MessageDescriptions.Get(extension);
+ messageDescription.EnsureMessagePartsPassBasicValidation(extensionData);
+
+ // Deserialize the extension.
+ MessageDictionary extensionDictionary = messageDescription.GetDictionary(extension);
foreach (var pair in extensionData) {
extensionDictionary[pair.Key] = pair.Value;
}
@@ -212,7 +225,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
yield return extension;
}
} else {
- Logger.OpenId.WarnFormat("Extension with type URI '{0}' ignored because it is not a recognized extension.", typeUri);
+ Logger.OpenId.DebugFormat("Extension with type URI '{0}' ignored because it is not a recognized extension.", typeUri);
}
}
}
@@ -226,8 +239,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// A dictionary of message parts, including only signed parts when appropriate.
/// </returns>
private IDictionary<string, string> GetExtensionsDictionary(IProtocolMessage message, bool ignoreUnsigned) {
- Contract.Requires(this.Channel != null);
- ErrorUtilities.VerifyOperation(this.Channel != null, "Channel property has not been set.");
+ Contract.Requires<InvalidOperationException>(this.Channel != null);
IndirectSignedResponse signedResponse = message as IndirectSignedResponse;
if (signedResponse != null && ignoreUnsigned) {
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs
index 03ba0f2..c363c11 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Text;
@@ -99,7 +100,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// be used.
/// </remarks>
public static byte[] GetBytes(IEnumerable<KeyValuePair<string, string>> keysAndValues) {
- ErrorUtilities.VerifyArgumentNotNull(keysAndValues, "keysAndValues");
+ Contract.Requires<ArgumentNullException>(keysAndValues != null);
MemoryStream ms = new MemoryStream();
using (StreamWriter sw = new StreamWriter(ms, textEncoding)) {
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
index 0f71ebc..b25e819 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
@@ -51,7 +51,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="securitySettings">The security settings to apply.</param>
internal OpenIdChannel(IAssociationStore<Uri> associationStore, INonceStore nonceStore, RelyingPartySecuritySettings securitySettings)
: this(associationStore, nonceStore, new OpenIdMessageFactory(), securitySettings, false) {
- Contract.Requires(securitySettings != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
}
/// <summary>
@@ -63,7 +63,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="securitySettings">The security settings.</param>
internal OpenIdChannel(IAssociationStore<AssociationRelyingPartyType> associationStore, INonceStore nonceStore, ProviderSecuritySettings securitySettings)
: this(associationStore, nonceStore, new OpenIdMessageFactory(), securitySettings) {
- Contract.Requires(securitySettings != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
}
/// <summary>
@@ -77,8 +77,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="nonVerifying">A value indicating whether the channel is set up with no functional security binding elements.</param>
private OpenIdChannel(IAssociationStore<Uri> associationStore, INonceStore nonceStore, IMessageFactory messageTypeProvider, RelyingPartySecuritySettings securitySettings, bool nonVerifying) :
this(messageTypeProvider, InitializeBindingElements(associationStore, nonceStore, securitySettings, nonVerifying)) {
- Contract.Requires(messageTypeProvider != null);
- Contract.Requires(securitySettings != null);
+ Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ Contract.Requires<ArgumentException>(!nonVerifying || securitySettings is RelyingPartySecuritySettings);
}
/// <summary>
@@ -91,8 +92,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="securitySettings">The security settings.</param>
private OpenIdChannel(IAssociationStore<AssociationRelyingPartyType> associationStore, INonceStore nonceStore, IMessageFactory messageTypeProvider, ProviderSecuritySettings securitySettings) :
this(messageTypeProvider, InitializeBindingElements(associationStore, nonceStore, securitySettings, false)) {
- Contract.Requires(messageTypeProvider != null);
- Contract.Requires(securitySettings != null);
+ Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
}
/// <summary>
@@ -103,7 +104,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
private OpenIdChannel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements)
: base(messageTypeProvider, bindingElements) {
- Contract.Requires(messageTypeProvider != null);
+ Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
// Customize the binding element order, since we play some tricks for higher
// security and backward compatibility with older OpenID versions.
@@ -207,10 +208,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </returns>
/// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) {
- if (response == null) {
- throw new ArgumentNullException("response");
- }
-
try {
return this.keyValueForm.GetDictionary(response.ResponseStream);
} catch (FormatException ex) {
@@ -253,8 +250,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// This method implements spec V1.0 section 5.3.
/// </remarks>
protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
-
var messageAccessor = this.MessageDescriptions.GetAccessor(response);
var fields = messageAccessor.Serialize();
byte[] keyValueEncoding = KeyValueFormEncoding.GetBytes(fields);
@@ -317,9 +312,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </returns>
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Needed for code contracts.")]
private static IChannelBindingElement[] InitializeBindingElements<T>(IAssociationStore<T> associationStore, INonceStore nonceStore, SecuritySettings securitySettings, bool nonVerifying) {
- Contract.Requires(securitySettings != null);
- Contract.Requires(!nonVerifying || securitySettings is RelyingPartySecuritySettings);
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ Contract.Requires<ArgumentException>(!nonVerifying || securitySettings is RelyingPartySecuritySettings);
var rpSecuritySettings = securitySettings as RelyingPartySecuritySettings;
var opSecuritySettings = securitySettings as ProviderSecuritySettings;
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
index 31a2da5..1e5ea4c 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -30,9 +31,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// deserialize to. Null if the request isn't recognized as a valid protocol message.
/// </returns>
public IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
RequestBase message = null;
// Discern the OpenID version of the message.
@@ -97,9 +95,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// deserialize to. Null if the request isn't recognized as a valid protocol message.
/// </returns>
public IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
-
DirectResponseBase message = null;
// Discern the OpenID version of the message.
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
index 9c4c46d..43d6c03 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
@@ -78,10 +78,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="nonceStore">The nonce store to use.</param>
/// <param name="securitySettings">The security settings of the RP.</param>
internal ReturnToNonceBindingElement(INonceStore nonceStore, RelyingPartySecuritySettings securitySettings) {
- Contract.Requires(nonceStore != null);
- Contract.Requires(securitySettings != null);
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(nonceStore != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
this.nonceStore = nonceStore;
this.securitySettings = securitySettings;
@@ -144,6 +142,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
SignedResponseRequest request = message as SignedResponseRequest;
if (this.UseRequestNonce(request)) {
request.AddReturnToArguments(NonceParameter, CustomNonce.NewNonce().Serialize());
+ request.SignReturnTo = true; // a nonce without a signature is completely pointless
return MessageProtections.ReplayProtection;
}
@@ -171,9 +170,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
IndirectSignedResponse response = message as IndirectSignedResponse;
if (this.UseRequestNonce(response)) {
+ if (!response.ReturnToParametersSignatureValidated) {
+ Logger.OpenId.Error("Incoming message is expected to have a nonce, but the return_to parameter is not signed.");
+ }
+
string nonceValue = response.GetReturnToArgument(NonceParameter);
ErrorUtilities.VerifyProtocol(
- nonceValue != null,
+ nonceValue != null && response.ReturnToParametersSignatureValidated,
this.securitySettings.RejectUnsolicitedAssertions ? OpenIdStrings.UnsolicitedAssertionsNotAllowed : OpenIdStrings.UnsolicitedAssertionsNotAllowedFrom1xOPs);
CustomNonce nonce = CustomNonce.Deserialize(nonceValue);
@@ -184,6 +187,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
IReplayProtectedProtocolMessage replayResponse = response;
if (!this.nonceStore.StoreNonce(replayResponse.NonceContext, nonce.RandomPartAsString, nonce.CreationDateUtc)) {
+ Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", replayResponse.Nonce, replayResponse.UtcCreationDate);
throw new ReplayedMessageException(message);
}
@@ -209,7 +213,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
}
/// <summary>
- /// A special DotNetOpenId-only nonce used by the RP when talking to 1.0 OPs in order
+ /// A special DotNetOpenAuth-only nonce used by the RP when talking to 1.0 OPs in order
/// to protect against replay attacks.
/// </summary>
private class CustomNonce {
@@ -254,7 +258,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="value">The base64-encoded value of the nonce.</param>
/// <returns>The instantiated and initialized nonce.</returns>
internal static CustomNonce Deserialize(string value) {
- ErrorUtilities.VerifyNonZeroLength(value, "value");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
byte[] nonce = Convert.FromBase64String(value);
Contract.Assume(nonce != null);
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
index a760c0d..438c496 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
+ using System.Diagnostics.Contracts;
using System.Security.Cryptography;
using System.Web;
using DotNetOpenAuth.Messaging;
@@ -52,7 +53,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="secretStore">The secret store from which to retrieve the secret used for signing.</param>
/// <param name="securitySettings">The security settings.</param>
internal ReturnToSignatureBindingElement(IAssociationStore<Uri> secretStore, RelyingPartySecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+ Contract.Requires<ArgumentNullException>(secretStore != null);
this.secretManager = new PrivateSecretManager(securitySettings, secretStore);
}
@@ -94,7 +95,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </remarks>
public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
SignedResponseRequest request = message as SignedResponseRequest;
- if (request != null && request.ReturnTo != null) {
+ if (request != null && request.ReturnTo != null && request.SignReturnTo) {
request.AddReturnToArguments(ReturnToSignatureHandleParameterName, this.secretManager.CurrentHandle);
request.AddReturnToArguments(ReturnToSignatureParameterName, this.GetReturnToSignature(request.ReturnTo));
@@ -161,7 +162,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// or other minor changes do not invalidate the signature.
/// </remarks>
private string GetReturnToSignature(Uri returnTo) {
- ErrorUtilities.VerifyArgumentNotNull(returnTo, "returnTo");
+ Contract.Requires<ArgumentNullException>(returnTo != null);
// Assemble the dictionary to sign, taking care to remove the signature itself
// in order to accurately reproduce the original signature (which of course didn't include
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
index 6544d49..3f4a998 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
@@ -55,8 +55,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="associationStore">The association store used to look up the secrets needed for signing.</param>
/// <param name="securitySettings">The security settings.</param>
internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associationStore, ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(associationStore != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
this.opAssociations = associationStore;
this.opSecuritySettings = securitySettings;
@@ -190,7 +190,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <c>true</c> if the relying party is vulnerable; otherwise, <c>false</c>.
/// </returns>
private static bool IsRelyingPartyVulnerableToReplays(SignedResponseRequest request, IndirectSignedResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
// OpenID 2.0 includes replay protection as part of the protocol.
if (response.Version.Major >= 2) {
@@ -250,9 +250,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="association">The association to use to sign the message.</param>
/// <returns>The calculated signature of the method.</returns>
private string GetSignature(ITamperResistantOpenIdMessage signedMessage, Association association) {
- ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
- ErrorUtilities.VerifyNonZeroLength(signedMessage.SignedParameterOrder, "signedMessage.SignedParameterOrder");
- ErrorUtilities.VerifyArgumentNotNull(association, "association");
+ Contract.Requires<ArgumentNullException>(signedMessage != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(signedMessage.SignedParameterOrder));
+ Contract.Requires<ArgumentNullException>(association != null);
// Prepare the parts to sign, taking care to replace an openid.mode value
// of check_authentication with its original id_res so the signature matches.
@@ -285,9 +285,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// the inclusion and order of message parts that will be signed.
/// </returns>
private string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage) {
- Contract.Requires(this.Channel != null);
- ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
- ErrorUtilities.VerifyOperation(this.Channel != null, "Channel property has not been set.");
+ Contract.Requires<InvalidOperationException>(this.Channel != null);
+ Contract.Requires<ArgumentNullException>(signedMessage != null);
Protocol protocol = Protocol.Lookup(signedMessage.Version);
@@ -323,7 +322,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="signedMessage">The message to sign or verify.</param>
/// <returns>The association to use to sign or verify the message.</returns>
private Association GetAssociation(ITamperResistantOpenIdMessage signedMessage) {
- Contract.Requires(signedMessage != null);
+ Contract.Requires<ArgumentNullException>(signedMessage != null);
if (this.IsOnProvider) {
// We're on a Provider to either sign (smart/dumb) or verify a dumb signature.
diff --git a/src/DotNetOpenAuth/OpenId/DiffieHellman/DiffieHellmanManaged.cs b/src/DotNetOpenAuth/OpenId/DiffieHellman/DiffieHellmanManaged.cs
index 2535b0f..75787c3 100644
--- a/src/DotNetOpenAuth/OpenId/DiffieHellman/DiffieHellmanManaged.cs
+++ b/src/DotNetOpenAuth/OpenId/DiffieHellman/DiffieHellmanManaged.cs
@@ -162,7 +162,7 @@ namespace Org.Mentalis.Security.Cryptography {
/// Imports the specified <see cref="DHParameters"/>.
/// </summary>
/// <param name="parameters">The parameters for <see cref="DiffieHellman"/>.</param>
- /// <exception cref="CryptographicException"><paramref name="P"/> or <paramref name="G"/> is a null reference (<b>Nothing</b> in Visual Basic) -or- <paramref name="P"/> is not a prime number.</exception>
+ /// <exception cref="CryptographicException"><see cref="DHParameters.P">parameters.P</see> or <see cref="DHParameters.G">parameters.G</see> is a null reference (<b>Nothing</b> in Visual Basic) -or- <see cref="DHParameters.P">parameters.P</see> is not a prime number.</exception>
public override void ImportParameters(DHParameters parameters) {
if (parameters.P == null)
throw new CryptographicException("Missing P value.");
diff --git a/src/DotNetOpenAuth/OpenId/DiffieHellmanUtilities.cs b/src/DotNetOpenAuth/OpenId/DiffieHellmanUtilities.cs
index e4fea46..249f1f3 100644
--- a/src/DotNetOpenAuth/OpenId/DiffieHellmanUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/DiffieHellmanUtilities.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
@@ -36,8 +37,8 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>The hashing algorithm to use.</returns>
/// <exception cref="ProtocolException">Thrown if no match could be found for the given <paramref name="sessionType"/>.</exception>
public static HashAlgorithm Lookup(Protocol protocol, string sessionType) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyArgumentNotNull(sessionType, "sessionType");
+ Contract.Requires<ArgumentNullException>(protocol != null);
+ Contract.Requires<ArgumentNullException>(sessionType != null);
// We COULD use just First instead of FirstOrDefault, but we want to throw ProtocolException instead of InvalidOperationException.
DHSha match = diffieHellmanSessionTypes.FirstOrDefault(dhsha => String.Equals(dhsha.GetName(protocol), sessionType, StringComparison.Ordinal));
@@ -52,7 +53,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="hashSizeInBits">The hash size (in bits) that the DH session must have.</param>
/// <returns>The value to be used for the openid.session_type parameter, or null if no match was found.</returns>
internal static string GetNameForSize(Protocol protocol, int hashSizeInBits) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
+ Contract.Requires<ArgumentNullException>(protocol != null);
DHSha match = diffieHellmanSessionTypes.FirstOrDefault(dhsha => dhsha.Algorithm.HashSize == hashSizeInBits);
return match != null ? match.GetName(protocol) : null;
}
@@ -72,10 +73,10 @@ namespace DotNetOpenAuth.OpenId {
/// The secret itself if the encrypted version of the secret was given in <paramref name="remotePublicKey"/>.
/// </returns>
internal static byte[] SHAHashXorSecret(HashAlgorithm hasher, DiffieHellman dh, byte[] remotePublicKey, byte[] plainOrEncryptedSecret) {
- ErrorUtilities.VerifyArgumentNotNull(hasher, "hasher");
- ErrorUtilities.VerifyArgumentNotNull(dh, "dh");
- ErrorUtilities.VerifyArgumentNotNull(remotePublicKey, "remotePublicKey");
- ErrorUtilities.VerifyArgumentNotNull(plainOrEncryptedSecret, "plainOrEncryptedSecret");
+ Contract.Requires<ArgumentNullException>(hasher != null);
+ Contract.Requires<ArgumentNullException>(dh != null);
+ Contract.Requires<ArgumentNullException>(remotePublicKey != null);
+ Contract.Requires<ArgumentNullException>(plainOrEncryptedSecret != null);
byte[] sharedBlock = dh.DecryptKeyExchange(remotePublicKey);
byte[] sharedBlockHash = hasher.ComputeHash(EnsurePositive(sharedBlock));
@@ -101,7 +102,7 @@ namespace DotNetOpenAuth.OpenId {
/// This is to be consistent with OpenID spec section 4.2.
/// </remarks>
internal static byte[] EnsurePositive(byte[] inputBytes) {
- ErrorUtilities.VerifyArgumentNotNull(inputBytes, "inputBytes");
+ Contract.Requires<ArgumentNullException>(inputBytes != null);
if (inputBytes.Length == 0) {
throw new ArgumentException(MessagingStrings.UnexpectedEmptyArray, "inputBytes");
}
@@ -127,8 +128,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="algorithm">The hashing algorithm used in this particular Diffie-Hellman session type.</param>
/// <param name="getName">A function that will return the value of the openid.session_type parameter for a given version of OpenID.</param>
public DHSha(HashAlgorithm algorithm, Func<Protocol, string> getName) {
- ErrorUtilities.VerifyArgumentNotNull(algorithm, "algorithm");
- ErrorUtilities.VerifyArgumentNotNull(getName, "getName");
+ Contract.Requires<ArgumentNullException>(algorithm != null);
+ Contract.Requires<ArgumentNullException>(getName != null);
this.GetName = getName;
this.Algorithm = algorithm;
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs b/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
index e16f9a4..0a84266 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -19,7 +20,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <summary>
/// The format of auto-generated aliases.
/// </summary>
- private readonly string aliasFormat = "alias{0}";
+ private const string AliasFormat = "alias{0}";
/// <summary>
/// Tracks extension Type URIs and aliases assigned to them.
@@ -44,7 +45,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="typeUri">The type URI.</param>
/// <returns>The alias assigned to this type URI. Never null.</returns>
public string GetAlias(string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
string alias;
return this.typeUriToAliasMap.TryGetValue(typeUri, out alias) ? alias : this.AssignNewAlias(typeUri);
}
@@ -55,8 +56,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="alias">The alias.</param>
/// <param name="typeUri">The type URI.</param>
public void SetAlias(string alias, string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(alias, "alias");
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(alias));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
this.aliasToTypeUriMap.Add(alias, typeUri);
this.typeUriToAliasMap.Add(typeUri, alias);
}
@@ -67,6 +68,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="typeUris">The type URIs to create aliases for.</param>
/// <param name="preferredTypeUriToAliases">An optional dictionary of URI/alias pairs that suggest preferred aliases to use if available for certain type URIs.</param>
public void AssignAliases(IEnumerable<string> typeUris, IDictionary<string, string> preferredTypeUriToAliases) {
+ Contract.Requires<ArgumentNullException>(typeUris != null);
+
// First go through the actually used type URIs and see which ones have matching preferred aliases.
if (preferredTypeUriToAliases != null) {
foreach (string typeUri in typeUris) {
@@ -100,7 +103,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// </summary>
/// <param name="preferredTypeUriToAliases">A dictionary of type URI keys and alias values.</param>
public void SetPreferredAliasesWhereNotSet(IDictionary<string, string> preferredTypeUriToAliases) {
- ErrorUtilities.VerifyArgumentNotNull(preferredTypeUriToAliases, "preferredTypeUriToAliases");
+ Contract.Requires<ArgumentNullException>(preferredTypeUriToAliases != null);
foreach (var pair in preferredTypeUriToAliases) {
if (this.typeUriToAliasMap.ContainsKey(pair.Key)) {
@@ -125,6 +128,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <returns>The Type URI.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the given alias does not have a matching TypeURI.</exception>
public string ResolveAlias(string alias) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(alias));
string typeUri = this.TryResolveAlias(alias);
if (typeUri == null) {
throw new ArgumentOutOfRangeException("alias");
@@ -138,7 +142,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="alias">The alias.</param>
/// <returns>The Type URI for the given alias, or null if none for that alias exist.</returns>
public string TryResolveAlias(string alias) {
- ErrorUtilities.VerifyNonZeroLength(alias, "alias");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(alias));
string typeUri = null;
this.aliasToTypeUriMap.TryGetValue(alias, out typeUri);
return typeUri;
@@ -150,7 +154,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="alias">The alias in question.</param>
/// <returns>True if the alias has already been assigned. False otherwise.</returns>
public bool IsAliasUsed(string alias) {
- ErrorUtilities.VerifyNonZeroLength(alias, "alias");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(alias));
return this.aliasToTypeUriMap.ContainsKey(alias);
}
@@ -162,7 +166,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <c>true</c> if the given type URI already has an alias assigned; <c>false</c> otherwise.
/// </returns>
public bool IsAliasAssignedTo(string typeUri) {
- ErrorUtilities.VerifyArgumentNotNull(typeUri, "typeUri");
+ Contract.Requires<ArgumentNullException>(typeUri != null);
return this.typeUriToAliasMap.ContainsKey(typeUri);
}
@@ -172,9 +176,9 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="typeUri">The type URI to assign a new alias to.</param>
/// <returns>The newly generated alias.</returns>
private string AssignNewAlias(string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
ErrorUtilities.VerifyInternal(!this.typeUriToAliasMap.ContainsKey(typeUri), "Oops! This type URI already has an alias!");
- string alias = string.Format(CultureInfo.InvariantCulture, this.aliasFormat, this.typeUriToAliasMap.Count + 1);
+ string alias = string.Format(CultureInfo.InvariantCulture, AliasFormat, this.typeUriToAliasMap.Count + 1);
this.typeUriToAliasMap.Add(typeUri, alias);
this.aliasToTypeUriMap.Add(alias, typeUri);
return alias;
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs
index a3f64ab..2b947f7 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs
@@ -21,7 +21,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// <param name="collection">The attribute request collection.</param>
/// <param name="typeUri">The type URI of the required attribute.</param>
public static void AddRequired(this ICollection<AttributeRequest> collection, string typeUri) {
- ErrorUtilities.VerifyArgumentNotNull(collection, "collection");
+ Contract.Requires<ArgumentNullException>(collection != null);
collection.Add(new AttributeRequest(typeUri, true));
}
@@ -31,7 +31,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// <param name="collection">The attribute request collection.</param>
/// <param name="typeUri">The type URI of the requested attribute.</param>
public static void AddOptional(this ICollection<AttributeRequest> collection, string typeUri) {
- ErrorUtilities.VerifyArgumentNotNull(collection, "collection");
+ Contract.Requires<ArgumentNullException>(collection != null);
collection.Add(new AttributeRequest(typeUri, false));
}
@@ -43,8 +43,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// <param name="typeUri">The type URI of the attribute.</param>
/// <param name="values">The attribute values.</param>
public static void Add(this ICollection<AttributeValues> collection, string typeUri, params string[] values) {
- Contract.Requires(collection != null);
- ErrorUtilities.VerifyArgumentNotNull(collection, "collection");
+ Contract.Requires<ArgumentNullException>(collection != null);
collection.Add(new AttributeValues(typeUri, values));
}
@@ -54,8 +53,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// <param name="fields">The dictionary to fill with serialized attributes.</param>
/// <param name="attributes">The attributes.</param>
internal static void SerializeAttributes(IDictionary<string, string> fields, IEnumerable<AttributeValues> attributes) {
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
- ErrorUtilities.VerifyArgumentNotNull(attributes, "attributes");
+ Contract.Requires<ArgumentNullException>(fields != null);
+ Contract.Requires<ArgumentNullException>(attributes != null);
AliasManager aliasManager = new AliasManager();
foreach (var att in attributes) {
@@ -124,7 +123,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// <param name="fields">The data included in the extension message.</param>
/// <returns>The alias manager that provides lookup between aliases and type URIs.</returns>
private static AliasManager ParseAliases(IDictionary<string, string> fields) {
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+ Contract.Requires<ArgumentNullException>(fields != null);
AliasManager aliasManager = new AliasManager();
const string TypePrefix = "type.";
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs
index e508233..358db9b 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeRequest.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
using System;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -34,7 +35,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// </summary>
/// <param name="typeUri">The unique TypeURI for that describes the attribute being sought.</param>
public AttributeRequest(string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
this.TypeUri = typeUri;
}
@@ -82,7 +83,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
}
set {
- ErrorUtilities.VerifyArgumentInRange(value > 0, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value > 0);
this.count = value;
}
}
@@ -97,8 +98,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// the <see cref="FetchResponse"/> object.
/// </returns>
public AttributeValues Respond(params string[] values) {
- ErrorUtilities.VerifyArgumentNotNull(values, "values");
- ErrorUtilities.VerifyArgument(values.Length <= this.Count, OpenIdStrings.AttributeTooManyValues, this.Count, this.TypeUri, values.Length);
+ Contract.Requires<ArgumentNullException>(values != null);
+ Contract.Requires<ArgumentException>(values.Length <= this.Count);
return new AttributeValues(this.TypeUri, values);
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs
index e87e188..6466cda 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AttributeValues.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -22,7 +23,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// <param name="typeUri">The TypeURI that uniquely identifies the attribute.</param>
/// <param name="values">The values for the attribute.</param>
public AttributeValues(string typeUri, params string[] values) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
this.TypeUri = typeUri;
this.Values = (IList<string>)values ?? EmptyList<string>.Instance;
@@ -44,7 +45,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
/// </summary>
/// <param name="typeUri">The TypeURI of the attribute whose values are being provided.</param>
internal AttributeValues(string typeUri) {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
this.TypeUri = typeUri;
this.Values = new List<string>(1);
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
index 5c4e978..0a78df1 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Extensions {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -51,12 +52,19 @@ namespace DotNetOpenAuth.OpenId.Extensions {
private ExtensionArgumentsManager() { }
/// <summary>
+ /// Gets a value indicating whether the extensions are being read (as opposed to written).
+ /// </summary>
+ internal bool ReadMode {
+ get { return this.isReadMode; }
+ }
+
+ /// <summary>
/// Creates a <see cref="ExtensionArgumentsManager"/> instance to process incoming extensions.
/// </summary>
/// <param name="query">The parameters in the OpenID message.</param>
/// <returns>The newly created instance of <see cref="ExtensionArgumentsManager"/>.</returns>
public static ExtensionArgumentsManager CreateIncomingExtensions(IDictionary<string, string> query) {
- ErrorUtilities.VerifyArgumentNotNull(query, "query");
+ Contract.Requires<ArgumentNullException>(query != null);
var mgr = new ExtensionArgumentsManager();
mgr.protocol = Protocol.Detect(query);
mgr.isReadMode = true;
@@ -70,10 +78,12 @@ namespace DotNetOpenAuth.OpenId.Extensions {
}
// For backwards compatibility, add certain aliases if they aren't defined.
- foreach (var pair in typeUriToAliasAffinity) {
- if (!mgr.aliasManager.IsAliasAssignedTo(pair.Key) &&
- !mgr.aliasManager.IsAliasUsed(pair.Value)) {
- mgr.aliasManager.SetAlias(pair.Value, pair.Key);
+ if (mgr.protocol.Version.Major < 2) {
+ foreach (var pair in typeUriToAliasAffinity) {
+ if (!mgr.aliasManager.IsAliasAssignedTo(pair.Key) &&
+ !mgr.aliasManager.IsAliasUsed(pair.Value)) {
+ mgr.aliasManager.SetAlias(pair.Value, pair.Key);
+ }
}
}
@@ -118,6 +128,31 @@ namespace DotNetOpenAuth.OpenId.Extensions {
}
/// <summary>
+ /// Adds query parameters for OpenID extensions to the request directed
+ /// at the OpenID provider.
+ /// </summary>
+ /// <param name="extensionTypeUri">The extension type URI.</param>
+ /// <param name="arguments">The arguments for this extension to add to the message.</param>
+ public void AddExtensionArguments(string extensionTypeUri, IDictionary<string, string> arguments) {
+ Contract.Requires<InvalidOperationException>(!this.ReadMode);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(extensionTypeUri));
+ Contract.Requires<ArgumentNullException>(arguments != null);
+ if (arguments.Count == 0) {
+ return;
+ }
+
+ IDictionary<string, string> extensionArgs;
+ if (!this.extensions.TryGetValue(extensionTypeUri, out extensionArgs)) {
+ this.extensions.Add(extensionTypeUri, extensionArgs = new Dictionary<string, string>(arguments.Count));
+ }
+
+ ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
+ foreach (var pair in arguments) {
+ extensionArgs.Add(pair.Key, pair.Value);
+ }
+ }
+
+ /// <summary>
/// Gets the actual arguments to add to a querystring or other response,
/// where type URI, alias, and actual key/values are all defined.
/// </summary>
@@ -126,10 +161,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// This should be <c>true</c> for all but direct response messages.
/// </param>
/// <returns>A dictionary of key=value pairs to add to the message to carry the extension.</returns>
- public IDictionary<string, string> GetArgumentsToSend(bool includeOpenIdPrefix) {
- if (this.isReadMode) {
- throw new InvalidOperationException();
- }
+ internal IDictionary<string, string> GetArgumentsToSend(bool includeOpenIdPrefix) {
+ Contract.Requires<InvalidOperationException>(!this.ReadMode);
Dictionary<string, string> args = new Dictionary<string, string>();
foreach (var typeUriAndExtension in this.extensions) {
string typeUri = typeUriAndExtension.Key;
@@ -155,44 +188,15 @@ namespace DotNetOpenAuth.OpenId.Extensions {
}
/// <summary>
- /// Adds query parameters for OpenID extensions to the request directed
- /// at the OpenID provider.
- /// </summary>
- /// <param name="extensionTypeUri">The extension type URI.</param>
- /// <param name="arguments">The arguments for this extension to add to the message.</param>
- public void AddExtensionArguments(string extensionTypeUri, IDictionary<string, string> arguments) {
- if (this.isReadMode) {
- throw new InvalidOperationException();
- }
- ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
- ErrorUtilities.VerifyArgumentNotNull(arguments, "arguments");
- if (arguments.Count == 0) {
- return;
- }
-
- IDictionary<string, string> extensionArgs;
- if (!this.extensions.TryGetValue(extensionTypeUri, out extensionArgs)) {
- this.extensions.Add(extensionTypeUri, extensionArgs = new Dictionary<string, string>(arguments.Count));
- }
-
- ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
- foreach (var pair in arguments) {
- extensionArgs.Add(pair.Key, pair.Value);
- }
- }
-
- /// <summary>
/// Gets the fields carried by a given OpenId extension.
/// </summary>
/// <param name="extensionTypeUri">The type URI of the extension whose fields are being queried for.</param>
/// <returns>
/// The fields included in the given extension, or null if the extension is not present.
/// </returns>
- public IDictionary<string, string> GetExtensionArguments(string extensionTypeUri) {
- ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
- if (!this.isReadMode) {
- throw new InvalidOperationException();
- }
+ internal IDictionary<string, string> GetExtensionArguments(string extensionTypeUri) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(extensionTypeUri));
+ Contract.Requires<InvalidOperationException>(this.ReadMode);
IDictionary<string, string> extensionArgs;
this.extensions.TryGetValue(extensionTypeUri, out extensionArgs);
@@ -204,7 +208,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// </summary>
/// <param name="extensionTypeUri">The extension Type URI in question.</param>
/// <returns><c>true</c> if this extension is present; <c>false</c> otherwise.</returns>
- public bool ContainsExtension(string extensionTypeUri) {
+ internal bool ContainsExtension(string extensionTypeUri) {
return this.extensions.ContainsKey(extensionTypeUri);
}
@@ -212,7 +216,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// Gets the type URIs of all discovered extensions in the message.
/// </summary>
/// <returns>A sequence of the type URIs.</returns>
- public IEnumerable<string> GetExtensionTypeUris() {
+ internal IEnumerable<string> GetExtensionTypeUris() {
return this.extensions.Keys;
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
index 36358a7..9e7ccd2 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
@@ -35,25 +35,24 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <remarks>
/// <para>If discovery on the user-supplied identifier yields hints regarding which
/// extensions and attribute formats the Provider supports, this method MAY ignore the
- /// <paramref name="attributeFormat"/> argument and accomodate the Provider to minimize
+ /// <paramref name="attributeFormats"/> argument and accomodate the Provider to minimize
/// the size of the request.</para>
/// <para>If the request does not carry an sreg extension, the method logs a warning but
/// otherwise quietly returns doing nothing.</para>
/// </remarks>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sreg", Justification = "Abbreviation")]
public static void SpreadSregToAX(this RelyingParty.IAuthenticationRequest request, AXAttributeFormats attributeFormats) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
var req = (RelyingParty.AuthenticationRequest)request;
var sreg = req.AppliedExtensions.OfType<ClaimsRequest>().SingleOrDefault();
if (sreg == null) {
- Logger.OpenId.Warn("No Simple Registration (ClaimsRequest) extension present in the request to spread to AX.");
+ Logger.OpenId.Debug("No Simple Registration (ClaimsRequest) extension present in the request to spread to AX.");
return;
}
- if (req.Provider.IsExtensionSupported<ClaimsRequest>()) {
- Logger.OpenId.Info("Skipping generation of AX request because the Identifier advertises the Provider supports the Sreg extension.");
+ if (req.DiscoveryResult.IsExtensionSupported<ClaimsRequest>()) {
+ Logger.OpenId.Debug("Skipping generation of AX request because the Identifier advertises the Provider supports the Sreg extension.");
return;
}
@@ -66,11 +65,11 @@ namespace DotNetOpenAuth.OpenId.Extensions {
// Try to use just one AX Type URI format if we can figure out which type the OP accepts.
AXAttributeFormats detectedFormat;
if (TryDetectOPAttributeFormat(request, out detectedFormat)) {
- Logger.OpenId.Info("Detected OP support for AX but not for Sreg. Removing Sreg extension request and using AX instead.");
+ Logger.OpenId.Debug("Detected OP support for AX but not for Sreg. Removing Sreg extension request and using AX instead.");
attributeFormats = detectedFormat;
req.Extensions.Remove(sreg);
} else {
- Logger.OpenId.Info("Could not determine whether OP supported Sreg or AX. Using both extensions.");
+ Logger.OpenId.Debug("Could not determine whether OP supported Sreg or AX. Using both extensions.");
}
foreach (AXAttributeFormats format in ForEachFormat(attributeFormats)) {
@@ -99,8 +98,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// Never <c>null</c>.</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sreg", Justification = "Abbreviation")]
public static ClaimsResponse UnifyExtensionsAsSreg(this RelyingParty.IAuthenticationResponse response, bool allowUnsigned) {
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
var resp = (RelyingParty.IAuthenticationResponse)response;
var sreg = allowUnsigned ? resp.GetUntrustedExtension<ClaimsResponse>() : resp.GetExtension<ClaimsResponse>();
@@ -140,8 +138,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// or a fabricated one based on the Attribute Exchange extension if found,
/// or <c>null</c> if no attribute extension request is found.</returns>
internal static ClaimsRequest UnifyExtensionsAsSreg(this Provider.IHostProcessedRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
var req = (Provider.AuthenticationRequest)request;
var sreg = req.GetExtension<ClaimsRequest>();
@@ -258,8 +255,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="typeUri">The type URI of the attribute in axschema.org format.</param>
/// <returns>The demand level for the attribute.</returns>
private static DemandLevel GetDemandLevelFor(FetchRequest ax, string typeUri) {
- Contract.Requires(ax != null);
- Contract.Requires(!String.IsNullOrEmpty(typeUri));
+ Contract.Requires<ArgumentNullException>(ax != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
foreach (AXAttributeFormats format in ForEachFormat(AXAttributeFormats.All)) {
string typeUriInFormat = TransformAXFormat(typeUri, format);
@@ -278,9 +275,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="attributeFormat">The attribute formats the RP will try if this discovery fails.</param>
/// <returns>The AX format(s) to use based on the Provider's advertised AX support.</returns>
private static bool TryDetectOPAttributeFormat(RelyingParty.IAuthenticationRequest request, out AXAttributeFormats attributeFormat) {
- Contract.Requires(request != null);
- var provider = (RelyingParty.ServiceEndpoint)request.Provider;
- attributeFormat = DetectAXFormat(provider.ProviderDescription.Capabilities);
+ Contract.Requires<ArgumentNullException>(request != null);
+ attributeFormat = DetectAXFormat(request.DiscoveryResult.Capabilities);
return attributeFormat != AXAttributeFormats.None;
}
@@ -290,7 +286,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="typeURIs">The type URIs to scan for recognized formats.</param>
/// <returns>The first AX type URI format recognized in the list.</returns>
private static AXAttributeFormats DetectAXFormat(IEnumerable<string> typeURIs) {
- Contract.Requires(typeURIs != null);
+ Contract.Requires<ArgumentNullException>(typeURIs != null);
if (typeURIs.Any(uri => uri.StartsWith("http://axschema.org/", StringComparison.Ordinal))) {
return AXAttributeFormats.AXSchemaOrg;
@@ -314,7 +310,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="targetFormat">The target format. Only one flag should be set.</param>
/// <returns>The AX attribute type URI in the target format.</returns>
private static string TransformAXFormat(string axSchemaOrgFormatTypeUri, AXAttributeFormats targetFormat) {
- Contract.Requires(!String.IsNullOrEmpty(axSchemaOrgFormatTypeUri));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(axSchemaOrgFormatTypeUri));
switch (targetFormat) {
case AXAttributeFormats.AXSchemaOrg:
@@ -355,8 +351,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <param name="axSchemaOrgFormatAttribute">The attribute in axschema.org format.</param>
/// <param name="demandLevel">The demand level.</param>
private static void FetchAttribute(FetchRequest ax, AXAttributeFormats format, string axSchemaOrgFormatAttribute, DemandLevel demandLevel) {
- Contract.Requires(ax != null);
- Contract.Requires(!String.IsNullOrEmpty(axSchemaOrgFormatAttribute));
+ Contract.Requires<ArgumentNullException>(ax != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(axSchemaOrgFormatAttribute));
string typeUri = TransformAXFormat(axSchemaOrgFormatAttribute, format);
if (!ax.Attributes.Contains(typeUri)) {
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
index 4392cd5..99c7a2e 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
@@ -43,6 +43,23 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
public const string PrivatePersonalIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier";
/// <summary>
+ /// Indicates that the OP MUST only respond with a positive assertion if the requirements demonstrated
+ /// by the OP to obtain certification by a Federally adopted Trust Framework Provider have been met.
+ /// </summary>
+ /// <remarks>
+ /// Notwithstanding the RP may request this authentication policy, the RP MUST still
+ /// verify that this policy appears in the positive assertion response rather than assume the OP
+ /// recognized and complied with the request.
+ /// </remarks>
+ public const string USGovernmentTrustLevel1 = "http://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdf";
+
+ /// <summary>
+ /// Indicates that the OP MUST not include any OpenID Attribute Exchange or Simple Registration
+ /// information regarding the user in the assertion.
+ /// </summary>
+ public const string NoPersonallyIdentifiableInformation = "http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf";
+
+ /// <summary>
/// Used in a PAPE response to indicate that no PAPE authentication policies could be satisfied.
/// </summary>
/// <remarks>
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs
index 0a3147a..3031aad 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs
@@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
/// <remarks>
/// <para>One using this enum should review the following publication for details
/// before asserting or interpreting what these levels signify, notwithstanding
- /// the brief summaries attached to each level in DotNetOpenId documentation.
+ /// the brief summaries attached to each level in DotNetOpenAuth documentation.
/// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf</para>
/// <para>
/// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PapeUtilities.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PapeUtilities.cs
index 0f365ac..eeaea31 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PapeUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PapeUtilities.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;
@@ -45,7 +46,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
/// <returns>The concatenated string of elements.</returns>
/// <exception cref="FormatException">Thrown if any element in the sequence includes a space.</exception>
internal static string ConcatenateListOfElements(IEnumerable<string> values) {
- ErrorUtilities.VerifyArgumentNotNull(values, "values");
+ Contract.Requires<ArgumentNullException>(values != null);
StringBuilder valuesList = new StringBuilder();
foreach (string value in values.Distinct()) {
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs
index b476cf7..246ec07 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
@@ -82,12 +83,10 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
}
set {
+ Contract.Requires<ArgumentException>(!value.HasValue || value.Value.Kind != DateTimeKind.Unspecified, OpenIdStrings.UnspecifiedDateTimeKindNotAllowed);
+
// Make sure that whatever is set here, it becomes UTC time.
if (value.HasValue) {
- if (value.Value.Kind == DateTimeKind.Unspecified) {
- throw new ArgumentException(OpenIdStrings.UnspecifiedDateTimeKindNotAllowed, "value");
- }
-
// Convert to UTC and cut to the second, since the protocol only allows for
// that level of precision.
this.authenticationTimeUtc = OpenIdUtilities.CutToSecond(value.Value.ToUniversalTimeSafe());
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
index 10622bf..f775492 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -58,7 +59,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
/// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param>
internal ClaimsRequest(string typeUri)
: this() {
- ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
this.typeUriDeserializedFrom = typeUri;
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
index a58c754..7db6d45 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Net.Mail;
using System.Text;
@@ -69,7 +70,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
/// </param>
internal ClaimsResponse(string typeUriToUse)
: base(new Version(1, 0), typeUriToUse, EmptyList<string>.Instance) {
- ErrorUtilities.VerifyNonZeroLength(typeUriToUse, "typeUriToUse");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUriToUse));
}
/// <summary>
@@ -121,11 +122,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
}
set {
+ ErrorUtilities.VerifyArgument(value == null || birthDateValidator.IsMatch(value), OpenIdStrings.SregInvalidBirthdate);
if (value != null) {
- if (!birthDateValidator.IsMatch(value)) {
- throw new ArgumentException(OpenIdStrings.SregInvalidBirthdate, "value");
- }
-
// Update the BirthDate property, if possible.
// Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors.
// Some valid sreg dob values like "2000-00-00" will not work as a DateTime struct,
@@ -316,7 +314,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
sreg[Constants.language] = this.Language;
sreg[Constants.timezone] = this.TimeZone;
- return MessagingUtilities.CreateJsonObject(sreg);
+ return MessagingUtilities.CreateJsonObject(sreg, false);
}
#endregion
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs
index 963b301..1cc920a 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIConstants.cs
@@ -24,5 +24,11 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// specifying the user's preferred language through the UI extension.
/// </summary>
internal const string LangPrefSupported = "http://specs.openid.net/extensions/ui/1.0/lang-pref";
+
+ /// <summary>
+ /// The Type URI that appears in the XRDS document when the OP supports the RP
+ /// specifying the icon for the OP to display during authentication through the UI extension.
+ /// </summary>
+ internal const string IconSupported = "http://specs.openid.net/extensions/ui/1.0/icon";
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
index d67d932..76a1c9b 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
@@ -8,11 +8,14 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.Provider;
using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Xrds;
/// <summary>
/// OpenID User Interface extension 1.0 request message.
@@ -25,8 +28,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <see cref="UIModes.Popup"/>. </para>
/// <para>An RP may determine whether an arbitrary OP supports this extension (and thereby determine
/// whether to use a standard full window redirect or a popup) via the
- /// <see cref="IProviderEndpoint.IsExtensionSupported"/> method on the <see cref="IAuthenticationRequest.Provider"/>
- /// object.</para>
+ /// <see cref="IdentifierDiscoveryResult.IsExtensionSupported&lt;T&gt;()"/> method.</para>
/// </remarks>
public sealed class UIRequest : IOpenIdMessageExtension, IMessageWithEvents {
/// <summary>
@@ -46,6 +48,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
private static readonly string[] additionalTypeUris = new string[] {
UIConstants.LangPrefSupported,
UIConstants.PopupSupported,
+ UIConstants.IconSupported,
};
/// <summary>
@@ -57,18 +60,19 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// Initializes a new instance of the <see cref="UIRequest"/> class.
/// </summary>
public UIRequest() {
- this.LanguagePreference = CultureInfo.CurrentUICulture;
+ this.LanguagePreference = new[] { CultureInfo.CurrentUICulture };
}
/// <summary>
- /// Gets or sets the user's preferred language.
+ /// Gets or sets the list of user's preferred languages, sorted in decreasing preferred order.
/// </summary>
/// <value>The default is the <see cref="CultureInfo.CurrentUICulture"/> of the thread that created this instance.</value>
/// <remarks>
- /// The user's preferred language, reusing the Language Tag format used by the [Language Preference Attribute] (axschema.org, “Language Preference Attribute,” .) for [OpenID Attribute Exchange] (Hardt, D., Bufu, J., and J. Hoyt, “OpenID Attribute Exchange 1.0,” .) and defined in [RFC4646] (Phillips, A. and M. Davis, “Tags for Identifying Languages,” .). For example "en-US" represents the English language as spoken in the United States, and "fr-CA" represents the French language spoken in Canada.
+ /// The user's preferred languages as a [BCP 47] language priority list, represented as a comma-separated list of BCP 47 basic language ranges in descending priority order. For instance, the value "fr-CA,fr-FR,en-CA" represents the preference for French spoken in Canada, French spoken in France, followed by English spoken in Canada.
/// </remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design.")]
[MessagePart("lang", AllowEmpty = false)]
- public CultureInfo LanguagePreference { get; set; }
+ public CultureInfo[] LanguagePreference { get; set; }
/// <summary>
/// Gets the style of UI that the RP is hosting the OP's authentication page in.
@@ -87,7 +91,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <remarks>
/// By default, the Provider displays the relying party's favicon.ico.
/// </remarks>
- [MessagePart("popup", AllowEmpty = false, IsRequired = false)]
+ [MessagePart("icon", AllowEmpty = false, IsRequired = false)]
public bool? Icon { get; set; }
#region IOpenIdMessageExtension Members
@@ -126,7 +130,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
#endregion
- #region IMessage Members
+ #region IMessage Properties
/// <summary>
/// Gets the version of the protocol or extension this message is prepared to implement.
@@ -149,6 +153,72 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
get { return this.extraData; }
}
+ #endregion
+
+ /// <summary>
+ /// Gets the URL of the RP icon for the OP to display.
+ /// </summary>
+ /// <param name="realm">The realm of the RP where the authentication request originated.</param>
+ /// <param name="webRequestHandler">The web request handler to use for discovery.
+ /// Usually available via <see cref="Channel.WebRequestHandler">OpenIdProvider.Channel.WebRequestHandler</see>.</param>
+ /// <returns>
+ /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order.
+ /// </returns>
+ /// <value>The icon URL.</value>
+ /// <remarks>
+ /// This property is automatically set for the OP with the result of RP discovery.
+ /// RPs should set this value by including an entry such as this in their XRDS document.
+ /// <example>
+ /// &lt;Service xmlns="xri://$xrd*($v*2.0)"&gt;
+ /// &lt;Type&gt;http://specs.openid.net/extensions/ui/icon&lt;/Type&gt;
+ /// &lt;URI&gt;http://consumer.example.com/images/image.jpg&lt;/URI&gt;
+ /// &lt;/Service&gt;
+ /// </example>
+ /// </remarks>
+ public static IEnumerable<Uri> GetRelyingPartyIconUrls(Realm realm, IDirectWebRequestHandler webRequestHandler) {
+ Contract.Requires(realm != null);
+ Contract.Requires(webRequestHandler != null);
+ ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
+ ErrorUtilities.VerifyArgumentNotNull(webRequestHandler, "webRequestHandler");
+
+ XrdsDocument xrds = realm.Discover(webRequestHandler, false);
+ if (xrds == null) {
+ return Enumerable.Empty<Uri>();
+ } else {
+ return xrds.FindRelyingPartyIcons();
+ }
+ }
+
+ /// <summary>
+ /// Gets the URL of the RP icon for the OP to display.
+ /// </summary>
+ /// <param name="realm">The realm of the RP where the authentication request originated.</param>
+ /// <param name="provider">The Provider instance used to obtain the authentication request.</param>
+ /// <returns>
+ /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order.
+ /// </returns>
+ /// <value>The icon URL.</value>
+ /// <remarks>
+ /// This property is automatically set for the OP with the result of RP discovery.
+ /// RPs should set this value by including an entry such as this in their XRDS document.
+ /// <example>
+ /// &lt;Service xmlns="xri://$xrd*($v*2.0)"&gt;
+ /// &lt;Type&gt;http://specs.openid.net/extensions/ui/icon&lt;/Type&gt;
+ /// &lt;URI&gt;http://consumer.example.com/images/image.jpg&lt;/URI&gt;
+ /// &lt;/Service&gt;
+ /// </example>
+ /// </remarks>
+ public static IEnumerable<Uri> GetRelyingPartyIconUrls(Realm realm, OpenIdProvider provider) {
+ Contract.Requires(realm != null);
+ Contract.Requires(provider != null);
+ ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
+ ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+
+ return GetRelyingPartyIconUrls(realm, provider.Channel.WebRequestHandler);
+ }
+
+ #region IMessage methods
+
/// <summary>
/// Checks the message state for conformity to the protocol specification
/// and throws an exception if the message is invalid.
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs
index 088404b..cee6882 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIUtilities.cs
@@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <summary>
/// The required width of the popup window the relying party creates for the provider.
/// </summary>
- public const int PopupWidth = 450;
+ public const int PopupWidth = 500; // UI extension calls for 450px, but Yahoo needs 500px
/// <summary>
/// The required height of the popup window the relying party creates for the provider.
@@ -34,9 +34,9 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <param name="windowName">The name to assign to the popup window.</param>
/// <returns>A string starting with 'window.open' and forming just that one method call.</returns>
internal static string GetWindowPopupScript(OpenIdRelyingParty relyingParty, IAuthenticationRequest request, string windowName) {
- Contract.Requires(relyingParty != null);
- Contract.Requires(request != null);
- Contract.Requires(!string.IsNullOrEmpty(windowName));
+ Contract.Requires<ArgumentNullException>(relyingParty != null);
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(windowName));
Uri popupUrl = request.RedirectingResponse.GetDirectUriRequest(relyingParty.Channel);
diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
index 4c31100..edc08ee 100644
--- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
+++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
@@ -19,6 +20,7 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// An association that uses the HMAC-SHA family of algorithms for message signing.
/// </summary>
+ [ContractVerification(true)]
internal class HmacShaAssociation : Association {
/// <summary>
/// The default lifetime of a shared association when no lifetime is given
@@ -66,9 +68,11 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">The time duration the association will be good for.</param>
private HmacShaAssociation(HmacSha typeIdentity, string handle, byte[] secret, TimeSpan totalLifeLength)
: base(handle, secret, totalLifeLength, DateTime.UtcNow) {
- ErrorUtilities.VerifyArgumentNotNull(typeIdentity, "typeIdentity");
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires<ArgumentNullException>(typeIdentity != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(handle));
+ Contract.Requires<ArgumentNullException>(secret != null);
+ Contract.Requires<ArgumentOutOfRangeException>(totalLifeLength > TimeSpan.Zero);
+ Contract.Ensures(this.TotalLifeLength == totalLifeLength);
ErrorUtilities.VerifyProtocol(secret.Length == typeIdentity.SecretLength, OpenIdStrings.AssociationSecretAndTypeLengthMismatch, secret.Length, typeIdentity.GetAssociationType(Protocol.Default));
this.typeIdentity = typeIdentity;
@@ -94,9 +98,10 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">How long the association will be good for.</param>
/// <returns>The newly created association.</returns>
public static HmacShaAssociation Create(Protocol protocol, string associationType, string handle, byte[] secret, TimeSpan totalLifeLength) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires<ArgumentNullException>(protocol != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(associationType));
+ Contract.Requires<ArgumentNullException>(secret != null);
+ Contract.Ensures(Contract.Result<HmacShaAssociation>() != null);
HmacSha match = hmacShaAssociationTypes.FirstOrDefault(sha => String.Equals(sha.GetAssociationType(protocol), associationType, StringComparison.Ordinal));
ErrorUtilities.VerifyProtocol(match != null, OpenIdStrings.NoAssociationTypeFoundByName, associationType);
@@ -111,8 +116,9 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">Total lifetime.</param>
/// <returns>The newly created association.</returns>
public static HmacShaAssociation Create(string handle, byte[] secret, TimeSpan totalLifeLength) {
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(handle));
+ Contract.Requires<ArgumentNullException>(secret != null);
+ Contract.Ensures(Contract.Result<HmacShaAssociation>() != null);
HmacSha shaType = hmacShaAssociationTypes.FirstOrDefault(sha => sha.SecretLength == secret.Length);
ErrorUtilities.VerifyProtocol(shaType != null, OpenIdStrings.NoAssociationTypeFoundByLength, secret.Length);
@@ -136,7 +142,7 @@ namespace DotNetOpenAuth.OpenId {
/// Creates a new association of a given type.
/// </summary>
/// <param name="protocol">The protocol.</param>
- /// <param name="associationType">Type of the association.</param>
+ /// <param name="associationType">Type of the association (i.e. HMAC-SHA1 or HMAC-SHA256)</param>
/// <param name="associationUse">A value indicating whether the new association will be used privately by the Provider for "dumb mode" authentication
/// or shared with the Relying Party for "smart mode" authentication.</param>
/// <param name="securitySettings">The security settings of the Provider.</param>
@@ -145,9 +151,10 @@ namespace DotNetOpenAuth.OpenId {
/// The new association is NOT automatically put into an association store. This must be done by the caller.
/// </remarks>
internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse, ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(protocol != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(associationType));
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ Contract.Ensures(Contract.Result<HmacShaAssociation>() != null);
int secretLength = GetSecretLength(protocol, associationType);
@@ -173,6 +180,9 @@ namespace DotNetOpenAuth.OpenId {
lifetime = DumbSecretLifetime;
}
+ Contract.Assert(protocol != null); // All the way up to the method call, the condition holds, yet we get a Requires failure next
+ Contract.Assert(secret != null);
+ Contract.Assert(!String.IsNullOrEmpty(associationType));
return Create(protocol, associationType, handle, secret, lifetime);
}
@@ -191,8 +201,8 @@ namespace DotNetOpenAuth.OpenId {
/// True if a qualifying association could be found; false otherwise.
/// </returns>
internal static bool TryFindBestAssociation(Protocol protocol, bool highSecurityIsBetter, SecuritySettings securityRequirements, bool requireMatchingDHSessionType, out string associationType, out string sessionType) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
+ Contract.Requires<ArgumentNullException>(protocol != null);
+ Contract.Requires<ArgumentNullException>(securityRequirements != null);
associationType = null;
sessionType = null;
@@ -232,9 +242,9 @@ namespace DotNetOpenAuth.OpenId {
/// <c>true</c> if the named association and session types are compatible; otherwise, <c>false</c>.
/// </returns>
internal static bool IsDHSessionCompatible(Protocol protocol, string associationType, string sessionType) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(sessionType, "sessionType");
+ Contract.Requires<ArgumentNullException>(protocol != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(associationType));
+ Contract.Requires<ArgumentNullException>(sessionType != null);
// All association types can work when no DH session is used at all.
if (string.Equals(sessionType, protocol.Args.SessionType.NoEncryption, StringComparison.Ordinal)) {
@@ -254,6 +264,7 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// The value that should be used for the openid.assoc_type parameter.
/// </returns>
+ [Pure]
internal override string GetAssociationType(Protocol protocol) {
return this.typeIdentity.GetAssociationType(protocol);
}
@@ -264,8 +275,11 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// The hash algorithm used for message signing.
/// </returns>
+ [Pure]
protected override HashAlgorithm CreateHasher() {
- return this.typeIdentity.CreateHasher(SecretKey);
+ var result = this.typeIdentity.CreateHasher(SecretKey);
+ Contract.Assume(result != null);
+ return result;
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs
new file mode 100644
index 0000000..e96f362
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs
@@ -0,0 +1,504 @@
+//-----------------------------------------------------------------------
+// <copyright file="HostMetaDiscoveryService.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Security;
+ using System.Security.Cryptography;
+ using System.Security.Cryptography.X509Certificates;
+ using System.Security.Permissions;
+ using System.Text;
+ using System.Text.RegularExpressions;
+ using System.Xml;
+ using System.Xml.XPath;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Xrds;
+ using DotNetOpenAuth.Yadis;
+
+ /// <summary>
+ /// The discovery service to support host-meta based discovery, such as Google Apps for Domains.
+ /// </summary>
+ /// <remarks>
+ /// The spec for this discovery mechanism can be found at:
+ /// http://groups.google.com/group/google-federated-login-api/web/openid-discovery-for-hosted-domains
+ /// and the XMLDSig spec referenced in that spec can be found at:
+ /// http://wiki.oasis-open.org/xri/XrdOne/XmlDsigProfile
+ /// </remarks>
+ public class HostMetaDiscoveryService : IIdentifierDiscoveryService {
+ /// <summary>
+ /// The URI template for discovery host-meta on domains hosted by
+ /// Google Apps for Domains.
+ /// </summary>
+ private static readonly HostMetaProxy GoogleHostedHostMeta = new HostMetaProxy("https://www.google.com/accounts/o8/.well-known/host-meta?hd={0}", "hosted-id.google.com");
+
+ /// <summary>
+ /// Path to the well-known location of the host-meta document at a domain.
+ /// </summary>
+ private const string LocalHostMetaPath = "/.well-known/host-meta";
+
+ /// <summary>
+ /// The pattern within a host-meta file to look for to obtain the URI to the XRDS document.
+ /// </summary>
+ private static readonly Regex HostMetaLink = new Regex(@"^Link: <(?<location>.+?)>; rel=""describedby http://reltype.google.com/openid/xrd-op""; type=""application/xrds\+xml""$");
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostMetaDiscoveryService"/> class.
+ /// </summary>
+ public HostMetaDiscoveryService() {
+ this.TrustedHostMetaProxies = new List<HostMetaProxy>();
+ }
+
+ /// <summary>
+ /// Gets the set of URI templates to use to contact host-meta hosting proxies
+ /// for domain discovery.
+ /// </summary>
+ public IList<HostMetaProxy> TrustedHostMetaProxies { get; private set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to trust Google to host domains' host-meta documents.
+ /// </summary>
+ /// <remarks>
+ /// This property is just a convenient mechanism for checking or changing the set of
+ /// trusted host-meta proxies in the <see cref="TrustedHostMetaProxies"/> property.
+ /// </remarks>
+ public bool UseGoogleHostedHostMeta {
+ get {
+ return this.TrustedHostMetaProxies.Contains(GoogleHostedHostMeta);
+ }
+
+ set {
+ if (value != this.UseGoogleHostedHostMeta) {
+ if (value) {
+ this.TrustedHostMetaProxies.Add(GoogleHostedHostMeta);
+ } else {
+ this.TrustedHostMetaProxies.Remove(GoogleHostedHostMeta);
+ }
+ }
+ }
+ }
+
+ #region IIdentifierDiscoveryService Members
+
+ /// <summary>
+ /// Performs discovery on the specified identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
+ /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
+ /// <returns>
+ /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
+ /// </returns>
+ public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) {
+ abortDiscoveryChain = false;
+
+ // Google Apps are always URIs -- not XRIs.
+ var uriIdentifier = identifier as UriIdentifier;
+ if (uriIdentifier == null) {
+ return Enumerable.Empty<IdentifierDiscoveryResult>();
+ }
+
+ var results = new List<IdentifierDiscoveryResult>();
+ string signingHost;
+ var response = GetXrdsResponse(uriIdentifier, requestHandler, out signingHost);
+
+ if (response != null) {
+ try {
+ var document = new XrdsDocument(XmlReader.Create(response.ResponseStream));
+ ValidateXmlDSig(document, uriIdentifier, response, signingHost);
+ var xrds = GetXrdElements(document, uriIdentifier.Uri.Host);
+
+ // Look for claimed identifier template URIs for an additional XRDS document.
+ results.AddRange(GetExternalServices(xrds, uriIdentifier, requestHandler));
+
+ // If we couldn't find any claimed identifiers, look for OP identifiers.
+ // Normally this would be the opposite (OP Identifiers take precedence over
+ // claimed identifiers, but for Google Apps, XRDS' always have OP Identifiers
+ // mixed in, which the OpenID spec mandate should eclipse Claimed Identifiers,
+ // which would break positive assertion checks).
+ if (results.Count == 0) {
+ results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier));
+ }
+
+ abortDiscoveryChain = true;
+ } catch (XmlException ex) {
+ Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.FinalUri, ex);
+ }
+ }
+
+ return results;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the XRD elements that have a given CanonicalID.
+ /// </summary>
+ /// <param name="document">The XRDS document.</param>
+ /// <param name="canonicalId">The CanonicalID to match on.</param>
+ /// <returns>A sequence of XRD elements.</returns>
+ private static IEnumerable<XrdElement> GetXrdElements(XrdsDocument document, string canonicalId) {
+ // filter to include only those XRD elements describing the host whose host-meta pointed us to this document.
+ return document.XrdElements.Where(xrd => string.Equals(xrd.CanonicalID, canonicalId, StringComparison.Ordinal));
+ }
+
+ /// <summary>
+ /// Gets the described-by services in XRD elements.
+ /// </summary>
+ /// <param name="xrds">The XRDs to search.</param>
+ /// <returns>A sequence of services.</returns>
+ private static IEnumerable<ServiceElement> GetDescribedByServices(IEnumerable<XrdElement> xrds) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null);
+
+ var describedBy = from xrd in xrds
+ from service in xrd.SearchForServiceTypeUris(p => "http://www.iana.org/assignments/relation/describedby")
+ select service;
+ return describedBy;
+ }
+
+ /// <summary>
+ /// Gets the services for an identifier that are described by an external XRDS document.
+ /// </summary>
+ /// <param name="xrds">The XRD elements to search for described-by services.</param>
+ /// <param name="identifier">The identifier under discovery.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <returns>The discovered services.</returns>
+ private static IEnumerable<IdentifierDiscoveryResult> GetExternalServices(IEnumerable<XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
+
+ var results = new List<IdentifierDiscoveryResult>();
+ foreach (var serviceElement in GetDescribedByServices(xrds)) {
+ var templateNode = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver);
+ var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver);
+ if (templateNode != null) {
+ Uri externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri)));
+ string nextAuthority = nextAuthorityNode != null ? nextAuthorityNode.Value.Trim() : identifier.Uri.Host;
+ try {
+ var externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation);
+ XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream));
+ ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority);
+ results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier));
+ } catch (ProtocolException ex) {
+ Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
+ } catch (XmlException ex) {
+ Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
+ }
+ }
+ }
+
+ return results;
+ }
+
+ /// <summary>
+ /// Validates the XML digital signature on an XRDS document.
+ /// </summary>
+ /// <param name="document">The XRDS document whose signature should be validated.</param>
+ /// <param name="identifier">The identifier under discovery.</param>
+ /// <param name="response">The response.</param>
+ /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
+ /// <exception cref="ProtocolException">Thrown if the XRDS document has an invalid or a missing signature.</exception>
+ private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost) {
+ Contract.Requires<ArgumentNullException>(document != null);
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(response != null);
+
+ var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver);
+ ErrorUtilities.VerifyProtocol(signatureNode != null, "Missing Signature element.");
+ var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver);
+ ErrorUtilities.VerifyProtocol(signedInfoNode != null, "Missing SignedInfo element.");
+ ErrorUtilities.VerifyProtocol(
+ signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null,
+ "Unrecognized or missing canonicalization method.");
+ ErrorUtilities.VerifyProtocol(
+ signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null,
+ "Unrecognized or missing signature method.");
+ var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver);
+ ErrorUtilities.VerifyProtocol(certNodes.Count > 0, "Missing X509Certificate element.");
+ var certs = certNodes.Cast<XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList();
+
+ // Verify that we trust the signer of the certificates.
+ // Start by trying to validate just the certificate used to sign the XRDS document,
+ // since we can do that with partial trust.
+ if (!certs[0].Verify()) {
+ // We couldn't verify just the signing certificate, so try to verify the whole certificate chain.
+ try {
+ VerifyCertChain(certs);
+ } catch (SecurityException) {
+ Logger.Yadis.Warn("Signing certificate verification failed and we have insufficient code access security permissions to perform certificate chain validation.");
+ ErrorUtilities.ThrowProtocol(OpenIdStrings.X509CertificateNotTrusted);
+ }
+ }
+
+ // Verify that the certificate is issued to the host on whom we are performing discovery.
+ string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false);
+ ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), "X.509 signing certificate issued to {0}, but a certificate for {1} was expected.", hostName, signingHost);
+
+ // Verify the signature itself
+ byte[] signature = Convert.FromBase64String(response.Headers["Signature"]);
+ var provider = (RSACryptoServiceProvider)certs.First().PublicKey.Key;
+ byte[] data = new byte[response.ResponseStream.Length];
+ response.ResponseStream.Seek(0, SeekOrigin.Begin);
+ response.ResponseStream.Read(data, 0, data.Length);
+ ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), "Invalid XmlDSig signature on XRDS document.");
+ }
+
+ /// <summary>
+ /// Verifies the cert chain.
+ /// </summary>
+ /// <param name="certs">The certs.</param>
+ /// <remarks>
+ /// This must be in a method of its own because there is a LinkDemand on the <see cref="X509Chain.Build"/>
+ /// method. By being in a method of its own, the caller of this method may catch a
+ /// <see cref="SecurityException"/> that is thrown if we're not running with full trust and execute
+ /// an alternative plan.
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the certificate chain is invalid or unverifiable.</exception>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "By design")]
+ private static void VerifyCertChain(List<X509Certificate2> certs) {
+ var chain = new X509Chain();
+ foreach (var cert in certs) {
+ chain.Build(cert);
+ }
+
+ if (chain.ChainStatus.Length > 0) {
+ ErrorUtilities.ThrowProtocol(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ OpenIdStrings.X509CertificateNotTrusted + " {0}",
+ string.Join(", ", chain.ChainStatus.Select(status => status.StatusInformation).ToArray())));
+ }
+ }
+
+ /// <summary>
+ /// Gets the XRDS HTTP response for a given identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param>
+ /// <returns>
+ /// A HTTP response carrying an XRDS document.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
+ private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Requires<ArgumentNullException>(xrdsLocation != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+
+ var request = (HttpWebRequest)WebRequest.Create(xrdsLocation);
+ request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
+ request.Accept = ContentTypes.Xrds;
+ var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None;
+ var response = requestHandler.GetResponse(request, options);
+ if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) {
+ Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType);
+ }
+
+ return response;
+ }
+
+ /// <summary>
+ /// Gets the XRDS HTTP response for a given identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
+ /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns>
+ /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
+ private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost);
+ if (xrdsLocation == null) {
+ return null;
+ }
+
+ var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation);
+
+ return response;
+ }
+
+ /// <summary>
+ /// Gets the location of the XRDS document that describes a given identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier under discovery.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
+ /// <returns>An absolute URI, or <c>null</c> if one could not be determined.</returns>
+ private Uri GetXrdsLocation(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ var hostMetaResponse = this.GetHostMeta(identifier, requestHandler, out signingHost);
+ if (hostMetaResponse == null) {
+ return null;
+ }
+
+ using (var sr = hostMetaResponse.GetResponseReader()) {
+ string line = sr.ReadLine();
+ Match m = HostMetaLink.Match(line);
+ if (m.Success) {
+ Uri location = new Uri(m.Groups["location"].Value);
+ Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.FinalUri);
+ return location;
+ }
+ }
+
+ Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.FinalUri);
+ return null;
+ }
+
+ /// <summary>
+ /// Gets the host-meta for a given identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
+ /// <returns>
+ /// The host-meta response, or <c>null</c> if no host-meta document could be obtained.
+ /// </returns>
+ private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) {
+ var hostMetaLocation = hostMetaProxy.GetProxy(identifier);
+ var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation);
+ request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
+ var options = DirectWebRequestOptions.AcceptAllHttpResponses;
+ if (identifier.IsDiscoverySecureEndToEnd) {
+ options |= DirectWebRequestOptions.RequireSsl;
+ }
+ var response = requestHandler.GetResponse(request, options);
+ if (response.Status == HttpStatusCode.OK) {
+ Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation);
+ signingHost = hostMetaProxy.GetSigningHost(identifier);
+ return response;
+ } else {
+ Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation);
+ }
+ }
+
+ signingHost = null;
+ return null;
+ }
+
+ /// <summary>
+ /// Gets the URIs authorized to host host-meta documents on behalf of a given domain.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>A sequence of URIs that MAY provide the host-meta for a given identifier.</returns>
+ private IEnumerable<HostMetaProxy> GetHostMetaLocations(UriIdentifier identifier) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+
+ // First try the proxies, as they are considered more "secure" than the local
+ // host-meta for a domain since the domain may be defaced.
+ IEnumerable<HostMetaProxy> result = this.TrustedHostMetaProxies;
+
+ // Finally, look for the local host-meta.
+ UriBuilder localHostMetaBuilder = new UriBuilder();
+ localHostMetaBuilder.Scheme = identifier.IsDiscoverySecureEndToEnd || identifier.Uri.IsTransportSecure() ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
+ localHostMetaBuilder.Host = identifier.Uri.Host;
+ localHostMetaBuilder.Path = LocalHostMetaPath;
+ result = result.Concat(new[] { new HostMetaProxy(localHostMetaBuilder.Uri.AbsoluteUri, identifier.Uri.Host) });
+
+ return result;
+ }
+
+ /// <summary>
+ /// A description of a web server that hosts host-meta documents.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "By design")]
+ public class HostMetaProxy {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostMetaProxy"/> class.
+ /// </summary>
+ /// <param name="proxyFormat">The proxy formatting string.</param>
+ /// <param name="signingHostFormat">The signing host formatting string.</param>
+ public HostMetaProxy(string proxyFormat, string signingHostFormat) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(proxyFormat));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(signingHostFormat));
+ this.ProxyFormat = proxyFormat;
+ this.SigningHostFormat = signingHostFormat;
+ }
+
+ /// <summary>
+ /// Gets the URL of the host-meta proxy.
+ /// </summary>
+ /// <value>The absolute proxy URL, which may include {0} to be replaced with the host of the identifier to be discovered.</value>
+ public string ProxyFormat { get; private set; }
+
+ /// <summary>
+ /// Gets the formatting string to determine the expected host name on the certificate
+ /// that is expected to be used to sign the XRDS document.
+ /// </summary>
+ /// <value>
+ /// Either a string literal, or a formatting string where these placeholders may exist:
+ /// {0} the host on the identifier discovery was originally performed on;
+ /// {1} the host on this proxy.
+ /// </value>
+ public string SigningHostFormat { get; private set; }
+
+ /// <summary>
+ /// Gets the absolute proxy URI.
+ /// </summary>
+ /// <param name="identifier">The identifier being discovered.</param>
+ /// <returns>The an absolute URI.</returns>
+ public virtual Uri GetProxy(UriIdentifier identifier) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ return new Uri(string.Format(CultureInfo.InvariantCulture, this.ProxyFormat, Uri.EscapeDataString(identifier.Uri.Host)));
+ }
+
+ /// <summary>
+ /// Gets the signing host URI.
+ /// </summary>
+ /// <param name="identifier">The identifier being discovered.</param>
+ /// <returns>A host name.</returns>
+ public virtual string GetSigningHost(UriIdentifier identifier) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ return string.Format(CultureInfo.InvariantCulture, this.SigningHostFormat, identifier.Uri.Host, this.GetProxy(identifier).Host);
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
+ /// </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) {
+ var other = obj as HostMetaProxy;
+ if (other == null) {
+ return false;
+ }
+
+ return this.ProxyFormat == other.ProxyFormat && this.SigningHostFormat == other.SigningHostFormat;
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a particular type.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ return this.ProxyFormat.GetHashCode();
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
index 2376b0d..71d8652 100644
--- a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
@@ -5,6 +5,9 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Diagnostics.Contracts;
+
/// <summary>
/// An enumeration that can specify how a given <see cref="Association"/> is used.
/// </summary>
@@ -33,6 +36,7 @@ namespace DotNetOpenAuth.OpenId {
/// <see cref="System.Uri"/> for consumers (to distinguish associations across servers) or
/// <see cref="AssociationRelyingPartyType"/> for providers (to distinguish dumb and smart client associations).
/// </typeparam>
+ ////[ContractClass(typeof(IAssociationStoreContract<>))]
public interface IAssociationStore<TKey> {
/// <summary>
/// Saves an <see cref="Association"/> for later recall.
@@ -92,4 +96,99 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
void ClearExpiredAssociations();
}
+
+ // For some odd reason, having this next class causes our test project to fail to build with this error:
+ // Error 42 Method 'StoreAssociation' in type 'DotNetOpenAuth.OpenId.IAssociationStoreContract_Accessor`1' from assembly 'DotNetOpenAuth_Accessor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. DotNetOpenAuth.Test
+ /////// <summary>
+ /////// Code Contract for the <see cref="IAssociationStore&lt;TKey&gt;"/> class.
+ /////// </summary>
+ /////// <typeparam name="TKey">The type of the key.</typeparam>
+ ////[ContractClassFor(typeof(IAssociationStore<>))]
+ ////internal abstract class IAssociationStoreContract<TKey> : IAssociationStore<TKey> {
+ //// #region IAssociationStore<TKey> Members
+
+ //// /// <summary>
+ //// /// Saves an <see cref="Association"/> for later recall.
+ //// /// </summary>
+ //// /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for providers).</param>
+ //// /// <param name="association">The association to store.</param>
+ //// /// <remarks>
+ //// /// TODO: what should implementations do on association handle conflict?
+ //// /// </remarks>
+ //// void IAssociationStore<TKey>.StoreAssociation(TKey distinguishingFactor, Association association) {
+ //// Contract.Requires<ArgumentNullException>(distinguishingFactor != null);
+ //// Contract.Requires<ArgumentNullException>(association != null);
+ //// throw new NotImplementedException();
+ //// }
+
+ //// /// <summary>
+ //// /// Gets the best association (the one with the longest remaining life) for a given key.
+ //// /// </summary>
+ //// /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param>
+ //// /// <param name="securityRequirements">The security requirements that the returned association must meet.</param>
+ //// /// <returns>
+ //// /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key.
+ //// /// </returns>
+ //// /// <remarks>
+ //// /// In the event that multiple associations exist for the given
+ //// /// <paramref name="distinguishingFactor"/>, it is important for the
+ //// /// implementation for this method to use the <paramref name="securityRequirements"/>
+ //// /// to pick the best (highest grade or longest living as the host's policy may dictate)
+ //// /// association that fits the security requirements.
+ //// /// Associations that are returned that do not meet the security requirements will be
+ //// /// ignored and a new association created.
+ //// /// </remarks>
+ //// Association IAssociationStore<TKey>.GetAssociation(TKey distinguishingFactor, SecuritySettings securityRequirements) {
+ //// Contract.Requires<ArgumentNullException>(distinguishingFactor != null);
+ //// Contract.Requires<ArgumentNullException>(securityRequirements != null);
+ //// throw new NotImplementedException();
+ //// }
+
+ //// /// <summary>
+ //// /// Gets the association for a given key and handle.
+ //// /// </summary>
+ //// /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param>
+ //// /// <param name="handle">The handle of the specific association that must be recalled.</param>
+ //// /// <returns>
+ //// /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key and handle.
+ //// /// </returns>
+ //// Association IAssociationStore<TKey>.GetAssociation(TKey distinguishingFactor, string handle) {
+ //// Contract.Requires<ArgumentNullException>(distinguishingFactor != null);
+ //// Contract.Requires(!String.IsNullOrEmpty(handle));
+ //// throw new NotImplementedException();
+ //// }
+
+ //// /// <summary>
+ //// /// Removes a specified handle that may exist in the store.
+ //// /// </summary>
+ //// /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param>
+ //// /// <param name="handle">The handle of the specific association that must be deleted.</param>
+ //// /// <returns>
+ //// /// True if the association existed in this store previous to this call.
+ //// /// </returns>
+ //// /// <remarks>
+ //// /// No exception should be thrown if the association does not exist in the store
+ //// /// before this call.
+ //// /// </remarks>
+ //// bool IAssociationStore<TKey>.RemoveAssociation(TKey distinguishingFactor, string handle) {
+ //// Contract.Requires<ArgumentNullException>(distinguishingFactor != null);
+ //// Contract.Requires(!String.IsNullOrEmpty(handle));
+ //// throw new NotImplementedException();
+ //// }
+
+ //// /// <summary>
+ //// /// Clears all expired associations from the store.
+ //// /// </summary>
+ //// /// <remarks>
+ //// /// If another algorithm is in place to periodically clear out expired associations,
+ //// /// this method call may be ignored.
+ //// /// This should be done frequently enough to avoid a memory leak, but sparingly enough
+ //// /// to not be a performance drain.
+ //// /// </remarks>
+ //// void IAssociationStore<TKey>.ClearExpiredAssociations() {
+ //// throw new NotImplementedException();
+ //// }
+
+ //// #endregion
+ ////}
}
diff --git a/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs
new file mode 100644
index 0000000..eb2bf98
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="IIdentifierDiscoveryService.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// A module that provides discovery services for OpenID identifiers.
+ /// </summary>
+ [ContractClass(typeof(IIdentifierDiscoveryServiceContract))]
+ public interface IIdentifierDiscoveryService {
+ /// <summary>
+ /// Performs discovery on the specified identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
+ /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
+ /// <returns>
+ /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
+ /// </returns>
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By design")]
+ [Pure]
+ IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain);
+ }
+
+ /// <summary>
+ /// Code contract for the <see cref="IIdentifierDiscoveryService"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IIdentifierDiscoveryService))]
+ internal class IIdentifierDiscoveryServiceContract : IIdentifierDiscoveryService {
+ #region IDiscoveryService Members
+
+ /// <summary>
+ /// Performs discovery on the specified identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
+ /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
+ /// <returns>
+ /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
+ /// </returns>
+ IEnumerable<IdentifierDiscoveryResult> IIdentifierDiscoveryService.Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Identifier.cs b/src/DotNetOpenAuth/OpenId/Identifier.cs
index 6e71b0a..548a673 100644
--- a/src/DotNetOpenAuth/OpenId/Identifier.cs
+++ b/src/DotNetOpenAuth/OpenId/Identifier.cs
@@ -24,15 +24,21 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// Initializes a new instance of the <see cref="Identifier"/> class.
/// </summary>
- /// <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>
- protected Identifier(bool isDiscoverySecureEndToEnd) {
+ /// <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>
+ public string OriginalString { get; private set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether <see cref="Identifier"/> instances are considered equal
/// based solely on their string reprsentations.
/// </summary>
@@ -61,7 +67,7 @@ namespace DotNetOpenAuth.OpenId {
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "Our named alternate is Parse.")]
[DebuggerStepThrough]
public static implicit operator Identifier(string identifier) {
- Contract.Requires(identifier == null || identifier.Length > 0);
+ Contract.Requires<ArgumentException>(identifier == null || identifier.Length > 0);
if (identifier == null) {
return null;
}
@@ -106,8 +112,7 @@ namespace DotNetOpenAuth.OpenId {
/// <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((identifier != null && identifier.Length > 0) || !string.IsNullOrEmpty(identifier));
- ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(identifier));
if (XriIdentifier.IsValidXri(identifier)) {
return new XriIdentifier(identifier);
} else {
@@ -147,7 +152,7 @@ namespace DotNetOpenAuth.OpenId {
/// </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(!string.IsNullOrEmpty(identifier));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(identifier));
return XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier);
}
@@ -202,21 +207,13 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Performs discovery on the Identifier.
- /// </summary>
- /// <param name="requestHandler">The web request handler to use for discovery.</param>
- /// <returns>
- /// An initialized structure containing the discovered provider endpoint information.
- /// </returns>
- internal abstract IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler);
-
- /// <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>
diff --git a/src/DotNetOpenAuth/OpenId/IdentifierContract.cs b/src/DotNetOpenAuth/OpenId/IdentifierContract.cs
index 1758f06..4af18e1 100644
--- a/src/DotNetOpenAuth/OpenId/IdentifierContract.cs
+++ b/src/DotNetOpenAuth/OpenId/IdentifierContract.cs
@@ -20,20 +20,7 @@ namespace DotNetOpenAuth.OpenId {
/// Prevents a default instance of the IdentifierContract class from being created.
/// </summary>
private IdentifierContract()
- : base(false) {
- }
-
- /// <summary>
- /// Performs discovery on the Identifier.
- /// </summary>
- /// <param name="requestHandler">The web request handler to use for discovery.</param>
- /// <returns>
- /// An initialized structure containing the discovered provider endpoint information.
- /// </returns>
- internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) {
- Contract.Requires(requestHandler != null);
- Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null);
- throw new NotImplementedException();
+ : base(null, false) {
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs b/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs
index 88264f5..3190920 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs
@@ -1,30 +1,33 @@
//-----------------------------------------------------------------------
-// <copyright file="ServiceEndpoint.cs" company="Andrew Arnott">
+// <copyright file="IdentifierDiscoveryResult.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.OpenId.RelyingParty {
+namespace DotNetOpenAuth.OpenId {
using System;
+ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
/// Represents a single OP endpoint from discovery on some OpenID Identifier.
/// </summary>
[DebuggerDisplay("ClaimedIdentifier: {ClaimedIdentifier}, ProviderEndpoint: {ProviderEndpoint}, OpenId: {Protocol.Version}")]
- internal class ServiceEndpoint : IXrdsProviderEndpoint {
+ public sealed class IdentifierDiscoveryResult : IProviderEndpoint {
/// <summary>
- /// The i-name identifier the user actually typed in
- /// or the url identifier with the scheme stripped off.
+ /// Backing field for the <see cref="Protocol"/> property.
/// </summary>
- private string friendlyIdentifierForDisplay;
+ private Protocol protocol;
/// <summary>
/// Backing field for the <see cref="ClaimedIdentifier"/> property.
@@ -32,23 +35,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private Identifier claimedIdentifier;
/// <summary>
- /// The OpenID protocol version used at the identity Provider.
- /// </summary>
- private Protocol protocol;
-
- /// <summary>
- /// The @priority given in the XRDS document for this specific OP endpoint.
- /// </summary>
- private int? uriPriority;
-
- /// <summary>
- /// The @priority given in the XRDS document for this service
- /// (which may consist of several endpoints).
+ /// Backing field for the <see cref="FriendlyIdentifierForDisplay"/> property.
/// </summary>
- private int? servicePriority;
+ private string friendlyIdentifierForDisplay;
/// <summary>
- /// Initializes a new instance of the <see cref="ServiceEndpoint"/> class.
+ /// Initializes a new instance of the <see cref="IdentifierDiscoveryResult"/> class.
/// </summary>
/// <param name="providerEndpoint">The provider endpoint.</param>
/// <param name="claimedIdentifier">The Claimed Identifier.</param>
@@ -56,47 +48,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="providerLocalIdentifier">The Provider Local Identifier.</param>
/// <param name="servicePriority">The service priority.</param>
/// <param name="uriPriority">The URI priority.</param>
- private ServiceEndpoint(ProviderEndpointDescription providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, int? servicePriority, int? uriPriority) {
- ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- this.ProviderDescription = providerEndpoint;
+ private IdentifierDiscoveryResult(ProviderEndpointDescription providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, int? servicePriority, int? uriPriority) {
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
+ Contract.Requires<ArgumentNullException>(claimedIdentifier != null);
+ this.ProviderEndpoint = providerEndpoint.Uri;
+ this.Capabilities = new ReadOnlyCollection<string>(providerEndpoint.Capabilities);
+ this.Version = providerEndpoint.Version;
this.ClaimedIdentifier = claimedIdentifier;
- this.UserSuppliedIdentifier = userSuppliedIdentifier;
this.ProviderLocalIdentifier = providerLocalIdentifier ?? claimedIdentifier;
- this.servicePriority = servicePriority;
- this.uriPriority = uriPriority;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServiceEndpoint"/> class.
- /// </summary>
- /// <param name="providerEndpoint">The provider endpoint.</param>
- /// <param name="claimedIdentifier">The Claimed Identifier.</param>
- /// <param name="userSuppliedIdentifier">The User-supplied Identifier.</param>
- /// <param name="providerLocalIdentifier">The Provider Local Identifier.</param>
- /// <param name="protocol">The protocol.</param>
- /// <remarks>
- /// Used for deserializing <see cref="ServiceEndpoint"/> from authentication responses.
- /// </remarks>
- private ServiceEndpoint(Uri providerEndpoint, Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, Protocol protocol) {
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(providerLocalIdentifier, "providerLocalIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
-
- this.ClaimedIdentifier = claimedIdentifier;
this.UserSuppliedIdentifier = userSuppliedIdentifier;
- this.ProviderDescription = new ProviderEndpointDescription(providerEndpoint, protocol.Version);
- this.ProviderLocalIdentifier = providerLocalIdentifier ?? claimedIdentifier;
- this.protocol = protocol;
+ this.ServicePriority = servicePriority;
+ this.ProviderEndpointPriority = uriPriority;
}
/// <summary>
- /// Gets the URL that the OpenID Provider receives authentication requests at.
+ /// Gets the detected version of OpenID implemented by the Provider.
/// </summary>
- Uri IProviderEndpoint.Uri {
- get { return this.ProviderDescription.Endpoint; }
- }
+ public Version Version { get; private set; }
/// <summary>
/// Gets the Identifier that was presented by the end user to the Relying Party,
@@ -109,14 +77,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public Identifier UserSuppliedIdentifier { get; private set; }
/// <summary>
- /// Gets or sets the Identifier that the end user claims to own.
+ /// Gets the Identifier that the end user claims to control.
/// </summary>
public Identifier ClaimedIdentifier {
get {
return this.claimedIdentifier;
}
- set {
+ internal set {
// Take care to reparse the incoming identifier to make sure it's
// not a derived type that will override expected behavior.
// Elsewhere in this class, we count on the fact that this property
@@ -133,8 +101,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public Identifier ProviderLocalIdentifier { get; private set; }
/// <summary>
- /// Gets the value for the <see cref="IAuthenticationResponse.FriendlyIdentifierForDisplay"/> property.
+ /// Gets a more user-friendly (but NON-secure!) string to display to the user as his identifier.
/// </summary>
+ /// <returns>A human-readable, abbreviated (but not secure) identifier the user MAY recognize as his own.</returns>
public string FriendlyIdentifierForDisplay {
get {
if (this.friendlyIdentifierForDisplay == null) {
@@ -148,8 +117,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
} else if (uri != null) {
if (uri != this.Protocol.ClaimedIdentifierForOPIdentifier) {
- string displayUri = uri.Uri.Authority + uri.Uri.PathAndQuery;
- displayUri = displayUri.TrimEnd('/');
+ string displayUri = uri.Uri.Host;
+
+ // We typically want to display the path, because that will often have the username in it.
+ // As Google Apps for Domains and the like become more popular, a standard /openid path
+ // will often appear, which is not helpful to identifying the user so we'll avoid including
+ // that path if it's present.
+ if (!string.Equals(uri.Uri.AbsolutePath, "/openid", StringComparison.OrdinalIgnoreCase)) {
+ displayUri += uri.Uri.AbsolutePath.TrimEnd('/');
+ }
// Multi-byte unicode characters get encoded by the Uri class for transit.
// Since this is for display purposes, we want to reverse this and display a readable
@@ -161,57 +137,45 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.friendlyIdentifierForDisplay = this.ClaimedIdentifier;
}
}
+
return this.friendlyIdentifierForDisplay;
}
}
/// <summary>
- /// Gets the list of services available at this OP Endpoint for the
- /// claimed Identifier. May be null.
+ /// Gets the provider endpoint.
/// </summary>
- public ReadOnlyCollection<string> ProviderSupportedServiceTypeUris {
- get { return this.ProviderDescription.Capabilities; }
- }
+ public Uri ProviderEndpoint { get; private set; }
/// <summary>
- /// Gets the OpenID protocol used by the Provider.
+ /// Gets the @priority given in the XRDS document for this specific OP endpoint.
/// </summary>
- public Protocol Protocol {
- get {
- if (this.protocol == null) {
- this.protocol = Protocol.Lookup(this.ProviderDescription.ProtocolVersion);
- }
-
- return this.protocol;
- }
- }
-
- #region IXrdsProviderEndpoint Members
+ public int? ProviderEndpointPriority { get; private set; }
/// <summary>
- /// Gets the priority associated with this service that may have been given
- /// in the XRDS document.
+ /// Gets the @priority given in the XRDS document for this service
+ /// (which may consist of several endpoints).
/// </summary>
- int? IXrdsProviderEndpoint.ServicePriority {
- get { return this.servicePriority; }
- }
+ public int? ServicePriority { get; private set; }
/// <summary>
- /// Gets the priority associated with the service endpoint URL.
+ /// Gets the collection of service type URIs found in the XRDS document describing this Provider.
/// </summary>
- int? IXrdsProviderEndpoint.UriPriority {
- get { return this.uriPriority; }
- }
+ /// <value>Should never be null, but may be empty.</value>
+ public ReadOnlyCollection<string> Capabilities { get; private set; }
- #endregion
+ #region IProviderEndpoint Members
/// <summary>
- /// Gets the detected version of OpenID implemented by the Provider.
+ /// Gets the URL that the OpenID Provider receives authentication requests at.
/// </summary>
- Version IProviderEndpoint.Version {
- get { return this.ProviderDescription.ProtocolVersion; }
+ /// <value>This value MUST be an absolute HTTP or HTTPS URL.</value>
+ Uri IProviderEndpoint.Uri {
+ get { return this.ProviderEndpoint; }
}
+ #endregion
+
/// <summary>
/// Gets an XRDS sorting routine that uses the XRDS Service/@Priority
/// attribute to determine order.
@@ -219,7 +183,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <remarks>
/// Endpoints lacking any priority value are sorted to the end of the list.
/// </remarks>
- internal static Comparison<IXrdsProviderEndpoint> EndpointOrder {
+ internal static Comparison<IdentifierDiscoveryResult> EndpointOrder {
get {
// Sort first by service type (OpenID 2.0, 1.1, 1.0),
// then by Service/@priority, then by Service/Uri/@priority
@@ -233,11 +197,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
if (result != 0) {
return result;
}
- if (se1.UriPriority.HasValue && se2.UriPriority.HasValue) {
- return se1.UriPriority.Value.CompareTo(se2.UriPriority.Value);
- } else if (se1.UriPriority.HasValue) {
+ if (se1.ProviderEndpointPriority.HasValue && se2.ProviderEndpointPriority.HasValue) {
+ return se1.ProviderEndpointPriority.Value.CompareTo(se2.ProviderEndpointPriority.Value);
+ } else if (se1.ProviderEndpointPriority.HasValue) {
return -1;
- } else if (se2.UriPriority.HasValue) {
+ } else if (se2.ProviderEndpointPriority.HasValue) {
return 1;
} else {
return 0;
@@ -249,11 +213,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return 1;
} else {
// neither service defines a priority, so base ordering by uri priority.
- if (se1.UriPriority.HasValue && se2.UriPriority.HasValue) {
- return se1.UriPriority.Value.CompareTo(se2.UriPriority.Value);
- } else if (se1.UriPriority.HasValue) {
+ if (se1.ProviderEndpointPriority.HasValue && se2.ProviderEndpointPriority.HasValue) {
+ return se1.ProviderEndpointPriority.Value.CompareTo(se2.ProviderEndpointPriority.Value);
+ } else if (se1.ProviderEndpointPriority.HasValue) {
return -1;
- } else if (se2.UriPriority.HasValue) {
+ } else if (se2.ProviderEndpointPriority.HasValue) {
return 1;
} else {
return 0;
@@ -265,35 +229,25 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Gets the URL which accepts OpenID Authentication protocol messages.
+ /// Gets the protocol used by the OpenID Provider.
/// </summary>
- /// <remarks>
- /// Obtained by performing discovery on the User-Supplied Identifier.
- /// This value MUST be an absolute HTTP or HTTPS URL.
- /// </remarks>
- internal Uri ProviderEndpoint {
- get { return this.ProviderDescription.Endpoint; }
- }
+ internal Protocol Protocol {
+ get {
+ if (this.protocol == null) {
+ this.protocol = Protocol.Lookup(this.Version);
+ }
- /// <summary>
- /// Gets a value indicating whether the <see cref="ProviderEndpoint"/> is using an encrypted channel.
- /// </summary>
- internal bool IsSecure {
- get { return string.Equals(this.ProviderEndpoint.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); }
+ return this.protocol;
+ }
}
/// <summary>
- /// Gets the provider description.
- /// </summary>
- internal ProviderEndpointDescription ProviderDescription { get; private set; }
-
- /// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="se1">The first service endpoint.</param>
/// <param name="se2">The second service endpoint.</param>
/// <returns>The result of the operator.</returns>
- public static bool operator ==(ServiceEndpoint se1, ServiceEndpoint se2) {
+ public static bool operator ==(IdentifierDiscoveryResult se1, IdentifierDiscoveryResult se2) {
return se1.EqualsNullSafe(se2);
}
@@ -303,44 +257,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="se1">The first service endpoint.</param>
/// <param name="se2">The second service endpoint.</param>
/// <returns>The result of the operator.</returns>
- public static bool operator !=(ServiceEndpoint se1, ServiceEndpoint se2) {
+ public static bool operator !=(IdentifierDiscoveryResult se1, IdentifierDiscoveryResult se2) {
return !(se1 == se2);
}
/// <summary>
- /// Checks for the presence of a given Type URI in an XRDS service.
- /// </summary>
- /// <param name="typeUri">The type URI to check for.</param>
- /// <returns>
- /// <c>true</c> if the service type uri is present; <c>false</c> otherwise.
- /// </returns>
- public bool IsTypeUriPresent(string typeUri) {
- return this.ProviderDescription.IsExtensionSupported(typeUri);
- }
-
- /// <summary>
- /// Determines whether a given extension is supported by this endpoint.
- /// </summary>
- /// <typeparam name="T">The type of extension to check support for on this endpoint.</typeparam>
- /// <returns>
- /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
- /// </returns>
- public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() {
- return this.ProviderDescription.IsExtensionSupported<T>();
- }
-
- /// <summary>
- /// Determines whether a given extension is supported by this endpoint.
- /// </summary>
- /// <param name="extensionType">The type of extension to check support for on this endpoint.</param>
- /// <returns>
- /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
- /// </returns>
- public bool IsExtensionSupported(Type extensionType) {
- return this.ProviderDescription.IsExtensionSupported(extensionType);
- }
-
- /// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
@@ -351,7 +272,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The <paramref name="obj"/> parameter is null.
/// </exception>
public override bool Equals(object obj) {
- ServiceEndpoint other = obj as ServiceEndpoint;
+ var other = obj as IdentifierDiscoveryResult;
if (other == null) {
return false;
}
@@ -387,57 +308,95 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
StringBuilder builder = new StringBuilder();
builder.AppendLine("ClaimedIdentifier: " + this.ClaimedIdentifier);
builder.AppendLine("ProviderLocalIdentifier: " + this.ProviderLocalIdentifier);
- builder.AppendLine("ProviderEndpoint: " + this.ProviderEndpoint.AbsoluteUri);
- builder.AppendLine("OpenID version: " + this.Protocol.Version);
+ builder.AppendLine("ProviderEndpoint: " + this.ProviderEndpoint);
+ builder.AppendLine("OpenID version: " + this.Version);
builder.AppendLine("Service Type URIs:");
- if (this.ProviderSupportedServiceTypeUris != null) {
- foreach (string serviceTypeUri in this.ProviderSupportedServiceTypeUris) {
- builder.Append("\t");
- builder.AppendLine(serviceTypeUri);
- }
- } else {
- builder.AppendLine("\t(unavailable)");
+ foreach (string serviceTypeUri in this.Capabilities) {
+ builder.Append("\t");
+ builder.AppendLine(serviceTypeUri);
}
builder.Length -= Environment.NewLine.Length; // trim last newline
return builder.ToString();
}
/// <summary>
- /// Reads previously discovered information about an endpoint
- /// from a solicited authentication assertion for validation.
+ /// Checks whether the OpenId Identifier claims support for a given extension.
/// </summary>
- /// <param name="reader">The reader from which to deserialize the <see cref="ServiceEndpoint"/>.</param>
+ /// <typeparam name="T">The extension whose support is being queried.</typeparam>
/// <returns>
- /// A <see cref="ServiceEndpoint"/> object that has everything
- /// except the <see cref="ProviderSupportedServiceTypeUris"/>
- /// deserialized.
+ /// True if support for the extension is advertised. False otherwise.
/// </returns>
- internal static ServiceEndpoint Deserialize(TextReader reader) {
- var claimedIdentifier = Identifier.Parse(reader.ReadLine());
- var providerLocalIdentifier = Identifier.Parse(reader.ReadLine());
- string userSuppliedIdentifier = reader.ReadLine();
- if (userSuppliedIdentifier.Length == 0) {
- userSuppliedIdentifier = null;
+ /// <remarks>
+ /// Note that a true or false return value is no guarantee of a Provider's
+ /// support for or lack of support for an extension. The return value is
+ /// determined by how the authenticating user filled out his/her XRDS document only.
+ /// The only way to be sure of support for a given extension is to include
+ /// the extension in the request and see if a response comes back for that extension.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "No parameter at all.")]
+ public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() {
+ T extension = new T();
+ return this.IsExtensionSupported(extension);
+ }
+
+ /// <summary>
+ /// Checks whether the OpenId Identifier claims support for a given extension.
+ /// </summary>
+ /// <param name="extensionType">The extension whose support is being queried.</param>
+ /// <returns>
+ /// True if support for the extension is advertised. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Note that a true or false return value is no guarantee of a Provider's
+ /// support for or lack of support for an extension. The return value is
+ /// determined by how the authenticating user filled out his/her XRDS document only.
+ /// The only way to be sure of support for a given extension is to include
+ /// the extension in the request and see if a response comes back for that extension.
+ /// </remarks>
+ public bool IsExtensionSupported(Type extensionType) {
+ var extension = (IOpenIdMessageExtension)Activator.CreateInstance(extensionType);
+ return this.IsExtensionSupported(extension);
+ }
+
+ /// <summary>
+ /// Determines whether a given extension is supported by this endpoint.
+ /// </summary>
+ /// <param name="extension">An instance of the extension to check support for.</param>
+ /// <returns>
+ /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
+ /// </returns>
+ public bool IsExtensionSupported(IOpenIdMessageExtension extension) {
+ Contract.Requires<ArgumentNullException>(extension != null);
+
+ // Consider the primary case.
+ if (this.IsTypeUriPresent(extension.TypeUri)) {
+ return true;
+ }
+
+ // Consider the secondary cases.
+ if (extension.AdditionalSupportedTypeUris != null) {
+ if (extension.AdditionalSupportedTypeUris.Any(typeUri => this.IsTypeUriPresent(typeUri))) {
+ return true;
+ }
}
- var providerEndpoint = new Uri(reader.ReadLine());
- var protocol = Protocol.FindBestVersion(p => p.Version, new[] { new Version(reader.ReadLine()) });
- return new ServiceEndpoint(providerEndpoint, claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, protocol);
+
+ return false;
}
/// <summary>
- /// Creates a <see cref="ServiceEndpoint"/> instance to represent some OP Identifier.
+ /// Creates a <see cref="IdentifierDiscoveryResult"/> instance to represent some OP Identifier.
/// </summary>
- /// <param name="providerIdentifier">The provider identifier.</param>
+ /// <param name="providerIdentifier">The provider identifier (actually the user-supplied identifier).</param>
/// <param name="providerEndpoint">The provider endpoint.</param>
/// <param name="servicePriority">The service priority.</param>
/// <param name="uriPriority">The URI priority.</param>
- /// <returns>The created <see cref="ServiceEndpoint"/> instance</returns>
- internal static ServiceEndpoint CreateForProviderIdentifier(Identifier providerIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) {
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
+ /// <returns>The created <see cref="IdentifierDiscoveryResult"/> instance</returns>
+ internal static IdentifierDiscoveryResult CreateForProviderIdentifier(Identifier providerIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) {
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
- Protocol protocol = Protocol.Detect(providerEndpoint.Capabilities);
+ Protocol protocol = Protocol.Lookup(providerEndpoint.Version);
- return new ServiceEndpoint(
+ return new IdentifierDiscoveryResult(
providerEndpoint,
protocol.ClaimedIdentifierForOPIdentifier,
providerIdentifier,
@@ -447,20 +406,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Creates a <see cref="ServiceEndpoint"/> instance to represent some Claimed Identifier.
+ /// Creates a <see cref="IdentifierDiscoveryResult"/> instance to represent some Claimed Identifier.
/// </summary>
/// <param name="claimedIdentifier">The claimed identifier.</param>
/// <param name="providerLocalIdentifier">The provider local identifier.</param>
/// <param name="providerEndpoint">The provider endpoint.</param>
/// <param name="servicePriority">The service priority.</param>
/// <param name="uriPriority">The URI priority.</param>
- /// <returns>The created <see cref="ServiceEndpoint"/> instance</returns>
- internal static ServiceEndpoint CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) {
+ /// <returns>The created <see cref="IdentifierDiscoveryResult"/> instance</returns>
+ internal static IdentifierDiscoveryResult CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) {
return CreateForClaimedIdentifier(claimedIdentifier, null, providerLocalIdentifier, providerEndpoint, servicePriority, uriPriority);
}
/// <summary>
- /// Creates a <see cref="ServiceEndpoint"/> instance to represent some Claimed Identifier.
+ /// Creates a <see cref="IdentifierDiscoveryResult"/> instance to represent some Claimed Identifier.
/// </summary>
/// <param name="claimedIdentifier">The claimed identifier.</param>
/// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
@@ -468,24 +427,30 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="providerEndpoint">The provider endpoint.</param>
/// <param name="servicePriority">The service priority.</param>
/// <param name="uriPriority">The URI priority.</param>
- /// <returns>The created <see cref="ServiceEndpoint"/> instance</returns>
- internal static ServiceEndpoint CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) {
- return new ServiceEndpoint(providerEndpoint, claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, servicePriority, uriPriority);
+ /// <returns>The created <see cref="IdentifierDiscoveryResult"/> instance</returns>
+ internal static IdentifierDiscoveryResult CreateForClaimedIdentifier(Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier, ProviderEndpointDescription providerEndpoint, int? servicePriority, int? uriPriority) {
+ return new IdentifierDiscoveryResult(providerEndpoint, claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, servicePriority, uriPriority);
}
/// <summary>
- /// Saves the discovered information about this endpoint
- /// for later comparison to validate assertions.
+ /// Determines whether a given type URI is present on the specified provider endpoint.
/// </summary>
- /// <param name="writer">The writer to use for serializing out the fields.</param>
- internal void Serialize(TextWriter writer) {
- writer.WriteLine(this.ClaimedIdentifier);
- writer.WriteLine(this.ProviderLocalIdentifier);
- writer.WriteLine(this.UserSuppliedIdentifier);
- writer.WriteLine(this.ProviderEndpoint);
- writer.WriteLine(this.Protocol.Version);
-
- // No reason to serialize priority. We only needed priority to decide whether to use this endpoint.
+ /// <param name="typeUri">The type URI.</param>
+ /// <returns>
+ /// <c>true</c> if the type URI is present on the specified provider endpoint; otherwise, <c>false</c>.
+ /// </returns>
+ internal bool IsTypeUriPresent(string typeUri) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(typeUri));
+ return this.Capabilities.Contains(typeUri);
+ }
+
+ /// <summary>
+ /// Sets the Capabilities property (this method is a test hook.)
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <remarks>The publicize.exe tool should work for the unit tests, but for some reason it fails on the build server.</remarks>
+ internal void SetCapabilitiesForTestHook(ReadOnlyCollection<string> value) {
+ this.Capabilities = value;
}
/// <summary>
@@ -494,22 +459,39 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="endpoint">The endpoint to prioritize.</param>
/// <returns>An arbitary integer, which may be used for sorting against other returned values from this method.</returns>
- private static double GetEndpointPrecedenceOrderByServiceType(IXrdsProviderEndpoint endpoint) {
+ private static double GetEndpointPrecedenceOrderByServiceType(IdentifierDiscoveryResult endpoint) {
// The numbers returned from this method only need to compare against other numbers
// from this method, which makes them arbitrary but relational to only others here.
- if (endpoint.IsTypeUriPresent(Protocol.V20.OPIdentifierServiceTypeURI)) {
+ if (endpoint.Capabilities.Contains(Protocol.V20.OPIdentifierServiceTypeURI)) {
return 0;
}
- if (endpoint.IsTypeUriPresent(Protocol.V20.ClaimedIdentifierServiceTypeURI)) {
+ if (endpoint.Capabilities.Contains(Protocol.V20.ClaimedIdentifierServiceTypeURI)) {
return 1;
}
- if (endpoint.IsTypeUriPresent(Protocol.V11.ClaimedIdentifierServiceTypeURI)) {
+ if (endpoint.Capabilities.Contains(Protocol.V11.ClaimedIdentifierServiceTypeURI)) {
return 2;
}
- if (endpoint.IsTypeUriPresent(Protocol.V10.ClaimedIdentifierServiceTypeURI)) {
+ if (endpoint.Capabilities.Contains(Protocol.V10.ClaimedIdentifierServiceTypeURI)) {
return 3;
}
return 10;
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.ProviderEndpoint != null);
+ Contract.Invariant(this.ClaimedIdentifier != null);
+ Contract.Invariant(this.ProviderLocalIdentifier != null);
+ Contract.Invariant(this.Capabilities != null);
+ Contract.Invariant(this.Version != null);
+ Contract.Invariant(this.Protocol != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs b/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs
index a5926d9..6319c02 100644
--- a/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs
+++ b/src/DotNetOpenAuth/OpenId/Interop/AuthenticationResponseShim.cs
@@ -30,8 +30,7 @@ namespace DotNetOpenAuth.OpenId.Interop {
/// </summary>
/// <param name="response">The response.</param>
internal AuthenticationResponseShim(IAuthenticationResponse response) {
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
var claimsResponse = this.response.GetExtension<ClaimsResponse>();
diff --git a/src/DotNetOpenAuth/OpenId/Interop/ClaimsResponseShim.cs b/src/DotNetOpenAuth/OpenId/Interop/ClaimsResponseShim.cs
index ef5e79d..689777b 100644
--- a/src/DotNetOpenAuth/OpenId/Interop/ClaimsResponseShim.cs
+++ b/src/DotNetOpenAuth/OpenId/Interop/ClaimsResponseShim.cs
@@ -31,8 +31,7 @@ namespace DotNetOpenAuth.OpenId.Interop {
/// <param name="response">The Simple Registration response to wrap.</param>
internal ClaimsResponseShim(ClaimsResponse response)
{
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
}
diff --git a/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs b/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
index 41c4e21..fc0f32e 100644
--- a/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
+++ b/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
@@ -16,8 +16,8 @@ namespace DotNetOpenAuth.OpenId.Interop {
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
- /// The COM interface describing the DotNetOpenId functionality available to
- /// COM client relying parties.
+ /// The COM interface describing the DotNetOpenAuth functionality available to
+ /// COM client OpenID relying parties.
/// </summary>
[Guid("56BD3DB0-EE0D-4191-ADFC-1F3705CD2636")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
@@ -100,6 +100,13 @@ namespace DotNetOpenAuth.OpenId.Interop {
}
/// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdRelyingPartyShim"/> class.
+ /// </summary>
+ public OpenIdRelyingPartyShim() {
+ Reporting.RecordFeatureUse(this);
+ }
+
+ /// <summary>
/// Creates an authentication request to verify that a user controls
/// some given Identifier.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs
index 99e413d..ccfb1b8 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.OpenId.Messages {
using System;
+ using System.Diagnostics.Contracts;
using System.Security.Cryptography;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Reflection;
@@ -51,7 +52,6 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// The resulting association is <i>not</i> added to the association store and must be done by the caller.
/// </remarks>
protected override Association CreateAssociationAtRelyingParty(AssociateRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
var diffieHellmanRequest = request as AssociateDiffieHellmanRequest;
ErrorUtilities.VerifyArgument(diffieHellmanRequest != null, "request");
@@ -73,10 +73,8 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// but the resulting association is <i>not</i> added to the association store and must be done by the caller.
/// </remarks>
protected override Association CreateAssociationAtProvider(AssociateRequest request, ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
var diffieHellmanRequest = request as AssociateDiffieHellmanRequest;
- ErrorUtilities.VerifyArgument(diffieHellmanRequest != null, "request");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ ErrorUtilities.VerifyInternal(diffieHellmanRequest != null, "Expected a DH request type.");
this.SessionType = this.SessionType ?? request.SessionType;
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
index 6a5c0a8..3fd9424 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -75,16 +76,16 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// Null if no association could be created that meet the security requirements
/// and the provider OpenID version.
/// </returns>
- internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider) {
- ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ internal static AssociateRequest Create(SecuritySettings securityRequirements, IProviderEndpoint provider) {
+ Contract.Requires<ArgumentNullException>(securityRequirements != null);
+ Contract.Requires<ArgumentNullException>(provider != null);
// Apply our knowledge of the endpoint's transport, OpenID version, and
// security requirements to decide the best association.
- bool unencryptedAllowed = provider.Endpoint.IsTransportSecure();
+ bool unencryptedAllowed = provider.Uri.IsTransportSecure();
bool useDiffieHellman = !unencryptedAllowed;
string associationType, sessionType;
- if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), true, securityRequirements, useDiffieHellman, out associationType, out sessionType)) {
+ if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.Version), true, securityRequirements, useDiffieHellman, out associationType, out sessionType)) {
// There are no associations that meet all requirements.
Logger.OpenId.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced.");
return null;
@@ -105,19 +106,19 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// Null if no association could be created that meet the security requirements
/// and the provider OpenID version.
/// </returns>
- internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider, string associationType, string sessionType) {
- ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(sessionType, "sessionType");
+ internal static AssociateRequest Create(SecuritySettings securityRequirements, IProviderEndpoint provider, string associationType, string sessionType) {
+ Contract.Requires<ArgumentNullException>(securityRequirements != null);
+ Contract.Requires<ArgumentNullException>(provider != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(associationType));
+ Contract.Requires<ArgumentNullException>(sessionType != null);
- bool unencryptedAllowed = provider.Endpoint.IsTransportSecure();
+ bool unencryptedAllowed = provider.Uri.IsTransportSecure();
if (unencryptedAllowed) {
- var associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint);
+ var associateRequest = new AssociateUnencryptedRequest(provider.Version, provider.Uri);
associateRequest.AssociationType = associationType;
return associateRequest;
} else {
- var associateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint);
+ var associateRequest = new AssociateDiffieHellmanRequest(provider.Version, provider.Uri);
associateRequest.AssociationType = associationType;
associateRequest.SessionType = sessionType;
associateRequest.InitializeRequest();
@@ -140,8 +141,8 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
/// </remarks>
internal IProtocolMessage CreateResponse(IAssociationStore<AssociationRelyingPartyType> associationStore, ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(associationStore != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
IProtocolMessage response;
if (securitySettings.IsAssociationInPermittedRange(Protocol, this.AssociationType) &&
@@ -184,7 +185,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="securitySettings">The security settings that apply to this Provider.</param>
/// <returns>The response to send to the Relying Party.</returns>
private AssociateUnsuccessfulResponse CreateUnsuccessfulResponse(ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
var unsuccessfulResponse = new AssociateUnsuccessfulResponse(this.Version, this);
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs
index d2ca70a..137cd60 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponse.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -20,6 +21,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// Association response messages are described in OpenID 2.0 section 8.2. This type covers section 8.2.1.
/// </remarks>
[DebuggerDisplay("OpenID {Version} associate response {AssociationHandle} {AssociationType} {SessionType}")]
+ [ContractClass(typeof(AssociateSuccessfulResponseContract))]
internal abstract class AssociateSuccessfulResponse : DirectResponseBase {
/// <summary>
/// A flag indicating whether an association has already been created.
@@ -106,7 +108,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// quite different operations in either scenario.</para>
/// </remarks>
internal Association CreateAssociation(AssociateRequest request, ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
ErrorUtilities.VerifyInternal(!this.associationCreated, "The association has already been created.");
Association association;
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponseContract.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponseContract.cs
new file mode 100644
index 0000000..dd37da6
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateSuccessfulResponseContract.cs
@@ -0,0 +1,29 @@
+// <auto-generated />
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Provider;
+
+ [ContractClassFor(typeof(AssociateSuccessfulResponse))]
+ internal abstract class AssociateSuccessfulResponseContract : AssociateSuccessfulResponse {
+ protected AssociateSuccessfulResponseContract() : base(null, null) {
+ }
+
+ protected override Association CreateAssociationAtProvider(AssociateRequest request, ProviderSecuritySettings securitySettings) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ throw new NotImplementedException();
+ }
+
+ protected override Association CreateAssociationAtRelyingParty(AssociateRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
index a10f073..5306c54 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
@@ -41,8 +41,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="channel">The channel. This is used only within the constructor and is not stored in a field.</param>
internal CheckAuthenticationRequest(IndirectSignedResponse message, Channel channel)
: base(message.Version, message.ProviderEndpoint, GetProtocolConstant(message.Version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
- Contract.Requires(channel != null);
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
// Copy all message parts from the id_res message into this one,
// except for the openid.mode parameter.
diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs
index c34d2b4..61825e8 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -36,7 +37,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="provider">The OpenID Provider that is preparing to send this response.</param>
internal CheckAuthenticationResponse(CheckAuthenticationRequest request, OpenIdProvider provider)
: base(request.Version, request) {
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ Contract.Requires<ArgumentNullException>(provider != null);
// The channel's binding elements have already set the request's IsValid property
// appropriately. We just copy it into the response message.
diff --git a/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
index dd9c927..e7619bc 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -53,7 +54,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="responseVersion">The OpenID version of the response message.</param>
/// <param name="originatingRequest">The originating request. May be null in case the request is unrecognizable and this is an error response.</param>
protected DirectResponseBase(Version responseVersion, IDirectedProtocolMessage originatingRequest) {
- ErrorUtilities.VerifyArgumentNotNull(responseVersion, "responseVersion");
+ Contract.Requires<ArgumentNullException>(responseVersion != null);
this.Version = responseVersion;
this.originatingRequest = originatingRequest;
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs
index 95080e6..57233ac 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -19,6 +20,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// [<see cref="SerializableAttribute"/>] to allow serializing state servers
/// to cache messages, particularly responses.
/// </remarks>
+ [ContractClass(typeof(IOpenIdMessageExtensionContract))]
public interface IOpenIdMessageExtension : IExtensionMessage {
/// <summary>
/// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements.
@@ -51,4 +53,110 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </value>
bool IsSignedByRemoteParty { get; set; }
}
+
+ /// <summary>
+ /// Code contract class for the IOpenIdMessageExtension interface.
+ /// </summary>
+ [ContractClassFor(typeof(IOpenIdMessageExtension))]
+ internal class IOpenIdMessageExtensionContract : IOpenIdMessageExtension {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="IOpenIdMessageExtensionContract"/> class from being created.
+ /// </summary>
+ private IOpenIdMessageExtensionContract() {
+ }
+
+ #region IOpenIdMessageExtension Members
+
+ /// <summary>
+ /// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements.
+ /// </summary>
+ string IOpenIdMessageExtension.TypeUri {
+ get {
+ Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets the additional TypeURIs that are supported by this extension, in preferred order.
+ /// May be empty if none other than <see cref="IOpenIdMessageExtension.TypeUri"/> is supported, but
+ /// should not be null.
+ /// </summary>
+ /// <remarks>
+ /// Useful for reading in messages with an older version of an extension.
+ /// The value in the <see cref="IOpenIdMessageExtension.TypeUri"/> property is always checked before
+ /// trying this list.
+ /// If you do support multiple versions of an extension using this method,
+ /// consider adding a CreateResponse method to your request extension class
+ /// so that the response can have the context it needs to remain compatible
+ /// given the version of the extension in the request message.
+ /// The <see cref="Extensions.SimpleRegistration.ClaimsRequest.CreateResponse"/> for an example.
+ /// </remarks>
+ IEnumerable<string> IOpenIdMessageExtension.AdditionalSupportedTypeUris {
+ get {
+ Contract.Ensures(Contract.Result<IEnumerable<string>>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this extension was
+ /// signed by the sender.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is signed by the sender; otherwise, <c>false</c>.
+ /// </value>
+ bool IOpenIdMessageExtension.IsSignedByRemoteParty {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IMessage Members
+
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ Version IMessage.Version {
+ get {
+ Contract.Ensures(Contract.Result<Version>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> IMessage.ExtraData {
+ get {
+ Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void IMessage.EnsureValidMessage() {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
index 1147790..d53b9d0 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -23,7 +24,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="mode">The value of the openid.mode parameter.</param>
protected IndirectResponseBase(SignedResponseRequest request, string mode)
: base(GetVersion(request), GetReturnTo(request), mode, MessageTransport.Indirect) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
this.OriginatingRequest = request;
}
@@ -56,7 +57,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// instead of a <see cref="NullReferenceException"/>.
/// </remarks>
internal static Version GetVersion(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
return message.Version;
}
@@ -70,7 +71,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// instead of a <see cref="NullReferenceException"/>.
/// </remarks>
private static Uri GetReturnTo(SignedResponseRequest message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
ErrorUtilities.VerifyProtocol(message.ReturnTo != null, OpenIdStrings.ReturnToRequiredForResponse);
return message.ReturnTo;
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
index 9462d21..2f02974 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
@@ -62,7 +62,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </param>
internal IndirectSignedResponse(SignedResponseRequest request)
: base(request, Protocol.Lookup(GetVersion(request)).Args.Mode.id_res) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
this.ReturnTo = request.ReturnTo;
this.ProviderEndpoint = request.Recipient.StripQueryArgumentsWithPrefix(Protocol.openid.Prefix);
@@ -77,8 +77,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="channel">The channel. This is used only within the constructor and is not stored in a field.</param>
internal IndirectSignedResponse(CheckAuthenticationRequest previouslySignedMessage, Channel channel)
: base(GetVersion(previouslySignedMessage), previouslySignedMessage.ReturnTo, Protocol.Lookup(GetVersion(previouslySignedMessage)).Args.Mode.id_res) {
- Contract.Requires(channel != null);
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
// Copy all message parts from the check_authentication message into this one,
// except for the openid.mode parameter.
@@ -332,7 +331,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// cannot verify the private signature made by the relying party.
/// </remarks>
internal string GetReturnToArgument(string key) {
- ErrorUtilities.VerifyNonZeroLength(key, "key");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(key));
ErrorUtilities.VerifyInternal(this.ReturnTo != null, "ReturnTo was expected to be required but is null.");
string value;
@@ -358,8 +357,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// A dictionary of the signed message parts.
/// </returns>
internal IDictionary<string, string> GetSignedMessageParts(Channel channel) {
- Contract.Requires(channel != null);
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
ITamperResistantOpenIdMessage signedSelf = this;
if (signedSelf.SignedParameterOrder == null) {
diff --git a/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
index 99a7c5a..52ff884 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -114,8 +115,8 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="channel">The channel to use to simulate construction of the message.</param>
/// <returns>The value to use for the user_setup_url parameter.</returns>
private static Uri ConstructUserSetupUrl(CheckIdRequest immediateRequest, Channel channel) {
- ErrorUtilities.VerifyArgumentNotNull(immediateRequest, "immediateRequest");
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(immediateRequest != null);
+ Contract.Requires<ArgumentNullException>(channel != null);
ErrorUtilities.VerifyInternal(immediateRequest.Immediate, "Only immediate requests should be sent here.");
var setupRequest = new CheckIdRequest(immediateRequest.Version, immediateRequest.Recipient, AuthenticationRequestMode.Setup);
@@ -132,7 +133,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="request">The request that we're responding to.</param>
/// <returns>The value of the openid.mode parameter to use.</returns>
private static string GetMode(SignedResponseRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
Protocol protocol = Protocol.Lookup(request.Version);
return request.Immediate ? protocol.Args.Mode.setup_needed : protocol.Args.Mode.cancel;
diff --git a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
index 7189583..8e4cb9d 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -31,14 +32,14 @@ namespace DotNetOpenAuth.OpenId.Messages {
#pragma warning restore 0414
/// <summary>
- /// Backing store for the <see cref="Incoming"/> property.
+ /// Backing store for the <see cref="ExtraData"/> property.
/// </summary>
- private bool incoming;
+ private readonly Dictionary<string, string> extraData = new Dictionary<string, string>();
/// <summary>
- /// Backing store for the <see cref="ExtraData"/> property.
+ /// Backing store for the <see cref="Incoming"/> property.
/// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
+ private bool incoming;
/// <summary>
/// Initializes a new instance of the <see cref="RequestBase"/> class.
@@ -48,12 +49,8 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <param name="mode">The value for the openid.mode parameter.</param>
/// <param name="transport">A value indicating whether the message will be transmitted directly or indirectly.</param>
protected RequestBase(Version version, Uri providerEndpoint, string mode, MessageTransport transport) {
- if (providerEndpoint == null) {
- throw new ArgumentNullException("providerEndpoint");
- }
- if (String.IsNullOrEmpty(mode)) {
- throw new ArgumentNullException("mode");
- }
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(mode));
this.Recipient = providerEndpoint;
this.Mode = mode;
@@ -181,7 +178,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// instead of a <see cref="NullReferenceException"/>.
/// </remarks>
protected static string GetProtocolConstant(Version protocolVersion, Func<Protocol, string> mode) {
- ErrorUtilities.VerifyArgumentNotNull(protocolVersion, "protocolVersion");
+ Contract.Requires<ArgumentNullException>(protocolVersion != null);
return mode(Protocol.Lookup(protocolVersion));
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs
index 1096468..7eb5407 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -107,6 +108,11 @@ namespace DotNetOpenAuth.OpenId.Messages {
internal Realm Realm { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether the return_to value should be signed.
+ /// </summary>
+ internal bool SignReturnTo { get; set; }
+
+ /// <summary>
/// Checks the message state for conformity to the protocol specification
/// and throws an exception if the message is invalid.
/// </summary>
@@ -139,7 +145,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// when and if a positive assertion comes back from the Provider.
/// </remarks>
internal void AddReturnToArguments(IEnumerable<KeyValuePair<string, string>> keysValues) {
- ErrorUtilities.VerifyArgumentNotNull(keysValues, "keysValues");
+ Contract.Requires<ArgumentNullException>(keysValues != null);
ErrorUtilities.VerifyOperation(this.ReturnTo != null, OpenIdStrings.ReturnToRequiredForOperation);
UriBuilder returnToBuilder = new UriBuilder(this.ReturnTo);
returnToBuilder.AppendAndReplaceQueryArgs(keysValues);
@@ -170,7 +176,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </param>
/// <returns>checkid_immediate or checkid_setup</returns>
private static string GetMode(Version version, AuthenticationRequestMode mode) {
- ErrorUtilities.VerifyArgumentNotNull(version, "version");
+ Contract.Requires<ArgumentNullException>(version != null);
Protocol protocol = Protocol.Lookup(version);
return mode == AuthenticationRequestMode.Immediate ? protocol.Args.Mode.checkid_immediate : protocol.Args.Mode.checkid_setup;
diff --git a/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs b/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs
index 2c8e865..1a6e7e9 100644
--- a/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId {
+ using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
@@ -28,9 +29,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="wrappedIdentifier">The ordinary Identifier whose discovery is being masked.</param>
/// <param name="claimSsl">Whether this Identifier should claim to be SSL-secure, although no discovery will never generate service endpoints anyway.</param>
internal NoDiscoveryIdentifier(Identifier wrappedIdentifier, bool claimSsl)
- : base(claimSsl) {
- Contract.Requires(wrappedIdentifier != null);
- ErrorUtilities.VerifyArgumentNotNull(wrappedIdentifier, "wrappedIdentifier");
+ : base(wrappedIdentifier.OriginalString, claimSsl) {
+ Contract.Requires<ArgumentNullException>(wrappedIdentifier != null);
this.wrappedIdentifier = wrappedIdentifier;
}
@@ -70,17 +70,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Performs discovery on the Identifier.
- /// </summary>
- /// <param name="requestHandler">The web request handler to use for discovery.</param>
- /// <returns>
- /// An initialized structure containing the discovered provider endpoint information.
- /// </returns>
- internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) {
- return Enumerable.Empty<ServiceEndpoint>();
- }
-
- /// <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.
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
index d03ced5..adfe4ee 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
@@ -286,6 +286,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The HTML head tag must include runat=&quot;server&quot;..
+ /// </summary>
+ internal static string HeadTagMustIncludeRunatServer {
+ get {
+ return ResourceManager.GetString("HeadTagMustIncludeRunatServer", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to ClaimedIdentifier and LocalIdentifier must be the same when IsIdentifierSelect is true..
/// </summary>
internal static string IdentifierSelectRequiresMatchingIdentifiers {
@@ -358,7 +367,7 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Looks up a localized string similar to Not a recognized XRI format: &apos;{0}&apos;..
+ /// Looks up a localized string similar to Not a recognized XRI format..
/// </summary>
internal static string InvalidXri {
get {
@@ -515,6 +524,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The {0} property must be set first..
+ /// </summary>
+ internal static string PropertyNotSet {
+ get {
+ return ResourceManager.GetString("PropertyNotSet", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to This property value is not supported by this control..
/// </summary>
internal static string PropertyValueNotSupported {
@@ -614,15 +632,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Looks up a localized string similar to No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}..
- /// </summary>
- internal static string StoreRequiredWhenNoHttpContextAvailable {
- get {
- return ResourceManager.GetString("StoreRequiredWhenNoHttpContextAvailable", resourceCulture);
- }
- }
-
- /// <summary>
/// Looks up a localized string similar to The type must implement {0}..
/// </summary>
internal static string TypeMustImplementX {
@@ -713,6 +722,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The X.509 certificate used to sign this document is not trusted..
+ /// </summary>
+ internal static string X509CertificateNotTrusted {
+ get {
+ return ResourceManager.GetString("X509CertificateNotTrusted", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to XRI support has been disabled at this site..
/// </summary>
internal static string XriResolutionDisabled {
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
index dd17fb8..ae68fe6 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
@@ -178,7 +178,7 @@
<value>The value '{0}' is not a valid URI.</value>
</data>
<data name="InvalidXri" xml:space="preserve">
- <value>Not a recognized XRI format: '{0}'.</value>
+ <value>Not a recognized XRI format.</value>
</data>
<data name="IssuedAssertionFailsIdentifierDiscovery" xml:space="preserve">
<value>The OpenID Provider issued an assertion for an Identifier whose discovery information did not match.
@@ -244,9 +244,6 @@ Discovered endpoint info:
<data name="XriResolutionFailed" xml:space="preserve">
<value>XRI resolution failed.</value>
</data>
- <data name="StoreRequiredWhenNoHttpContextAvailable" xml:space="preserve">
- <value>No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}.</value>
- </data>
<data name="AttributeAlreadyAdded" xml:space="preserve">
<value>An attribute with type URI '{0}' has already been added.</value>
</data>
@@ -343,4 +340,13 @@ Discovered endpoint info:
<data name="PositiveAssertionFromNonWhitelistedProvider" xml:space="preserve">
<value>An positive OpenID assertion was received from OP endpoint {0} that is not on this relying party's whitelist.</value>
</data>
-</root>
+ <data name="HeadTagMustIncludeRunatServer" xml:space="preserve">
+ <value>The HTML head tag must include runat="server".</value>
+ </data>
+ <data name="PropertyNotSet" xml:space="preserve">
+ <value>The {0} property must be set first.</value>
+ </data>
+ <data name="X509CertificateNotTrusted" xml:space="preserve">
+ <value>The X.509 certificate used to sign this document is not trusted.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.sr.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.sr.resx
new file mode 100644
index 0000000..0df62c0
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.sr.resx
@@ -0,0 +1,340 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="AssociationSecretAndTypeLengthMismatch" xml:space="preserve">
+ <value>Dužina deljene tajne ({0}) ne slaže se sa dužinom zahtevanom od povezujućeg tipa ('{1}').</value>
+ </data>
+ <data name="AssociationSecretHashLengthMismatch" xml:space="preserve">
+ <value>Dužina šifrovane deljene tajne ({0}) ne slaže se sa dužinom hashing algoritma ({1}).</value>
+ </data>
+ <data name="AssociationStoreRequiresNonceStore" xml:space="preserve">
+ <value>Ako je dato povezujuće skladište, jedinstveni identifikator skladišta takođe mora biti prisutan.</value>
+ </data>
+ <data name="BadAssociationPrivateData" xml:space="preserve">
+ <value>Dostavljeni privatni podaci ne slažu se ni sa jednim poznatim Association tipom. Dužina im je možda prekratka ili su podaci neispravni.</value>
+ </data>
+ <data name="CallbackArgumentsRequireSecretStore" xml:space="preserve">
+ <value>Callback argumenti su podržani jedino kada je {0} dustupan za {1}.</value>
+ </data>
+ <data name="CallDeserializeBeforeCreateResponse" xml:space="preserve">
+ <value>Simple Registration zahtev može generisati odgovor jedino na prijemnoj strani.</value>
+ </data>
+ <data name="ClaimedIdAndLocalIdMustBothPresentOrAbsent" xml:space="preserve">
+ <value>openid.claimed_id i openid.identity parametri moraju istovremeno biti prisutni ili nedostajati.</value>
+ </data>
+ <data name="ClaimedIdentifierCannotBeSetOnDelegatedAuthentication" xml:space="preserve">
+ <value>ClaimedIdentifier svojstvo ne može se podesiti kada IsDelegatedIdentifier ima vrednost true da bi se izbeglo narušavanje OpenID URL delegiranja.</value>
+ </data>
+ <data name="ClaimedIdentifierMustBeSetFirst" xml:space="preserve">
+ <value>ClaimedIdentifier svojstvo se najpre mora podesiti.</value>
+ </data>
+ <data name="DiffieHellmanRequiredPropertiesNotSet" xml:space="preserve">
+ <value>Sledeća svojstva moraju biti podešena pre nego što Diffie-Hellmanov algoritam može generisati javni ključ: {0}</value>
+ </data>
+ <data name="ExplicitHttpUriSuppliedWithSslRequirement" xml:space="preserve">
+ <value>URI nije SSL a svojstvo requireSslDiscovery je podešeno na true.</value>
+ </data>
+ <data name="ExtensionAlreadyAddedWithSameTypeURI" xml:space="preserve">
+ <value>Prostor deljenja proširenja '{0}' je već dodat. Samo jedno proširenje po prostoru je dozvoljeno u datom zahtevu.</value>
+ </data>
+ <data name="ExtensionLookupSupportUnavailable" xml:space="preserve">
+ <value>Ne može se tražiti podrška za proširenja na rehidriranom ServiceEndpoint.</value>
+ </data>
+ <data name="FragmentNotAllowedOnXRIs" xml:space="preserve">
+ <value>Segmenti fragmenta se ne primenjuju na XRI identifikatore.</value>
+ </data>
+ <data name="IdentifierSelectRequiresMatchingIdentifiers" xml:space="preserve">
+ <value>ClaimedIdentifier i LocalIdentifier moraju biti isti kada IsIdentifierSelect ima vrednost true.</value>
+ </data>
+ <data name="IndirectErrorFormattedMessage" xml:space="preserve">
+ <value>{0} (Kontakt: {1}, Referenca: {2})</value>
+ </data>
+ <data name="InvalidCharacterInKeyValueFormInput" xml:space="preserve">
+ <value>Ne može se enkodovati '{0}' jer sadrži nevalidan znak za Key-Value Form enkodiranje. (linija {1}: '{2}')</value>
+ </data>
+ <data name="InvalidKeyValueFormCharacterMissing" xml:space="preserve">
+ <value>Ne može se dekodovati Key-Value Form jer je pronađena linija bez '{0}' znaka. (linija {1}: '{2}')</value>
+ </data>
+ <data name="InvalidScheme" xml:space="preserve">
+ <value>Šema mora biti http ili https a bila je '{0}'.</value>
+ </data>
+ <data name="InvalidUri" xml:space="preserve">
+ <value>Vrednost '{0}' nije validan URI.</value>
+ </data>
+ <data name="InvalidXri" xml:space="preserve">
+ <value>Nije prepoznat XRI format: '{0}'.</value>
+ </data>
+ <data name="IssuedAssertionFailsIdentifierDiscovery" xml:space="preserve">
+ <value>OpenID Provider dao je iskaz za Identifier čije se informacije o pronalaženju ne slažu.
+Informacije o krajnoj tački iskaza:
+{0}
+Informacije o otkrivenoj krajnjoj tački:
+{1}</value>
+ </data>
+ <data name="KeysListAndDictionaryDoNotMatch" xml:space="preserve">
+ <value>Lista ključeva se ne slaže sa ponuđenim rečnikom.</value>
+ </data>
+ <data name="MatchingArgumentsExpected" xml:space="preserve">
+ <value>Parametri '{0}' i '{1}' moraju oba biti ili ne smeju oba biti '{2}'.</value>
+ </data>
+ <data name="NoAssociationTypeFoundByLength" xml:space="preserve">
+ <value>Ni jedan prepoznati tip asociranja se ne uklapa sa zahtevanom dužinom {0}.</value>
+ </data>
+ <data name="NoAssociationTypeFoundByName" xml:space="preserve">
+ <value>Ni jedan prepoznati tip asociranja se ne uklapa sa zahtevanim imenom '{0}'.</value>
+ </data>
+ <data name="NoEncryptionSessionRequiresHttps" xml:space="preserve">
+ <value>Osim ako se ne koristi enkripcija transportnog sloja, "no-encryption" NE SME biti korišćen.</value>
+ </data>
+ <data name="NoSessionTypeFound" xml:space="preserve">
+ <value>Diffie-Hellman sesija tipa '{0}' nije pronađena za OpenID {1}.</value>
+ </data>
+ <data name="OpenIdEndpointNotFound" xml:space="preserve">
+ <value>Nijedna OpenID krajnja tačka nije pronađena.</value>
+ </data>
+ <data name="OperationOnlyValidForSetupRequiredState" xml:space="preserve">
+ <value>Ova operacija je jedino dozvoljena kada je IAuthenticationResponse.State == AuthenticationStatus.SetupRequired.</value>
+ </data>
+ <data name="ProviderVersionUnrecognized" xml:space="preserve">
+ <value>Nije moguće utvrditi verziju OpenID protokola implementiranog od strane Provider-a na krajnjoj tački '{0}'.</value>
+ </data>
+ <data name="RealmCausedRedirectUponDiscovery" xml:space="preserve">
+ <value>HTTP zahtev ka URL-u domena ({0}) rezultovao je redirekcijom, koja nije dozvoljena u togu pronalaženja Relying Party.</value>
+ </data>
+ <data name="ReturnToNotUnderRealm" xml:space="preserve">
+ <value>return_to '{0}' nije unutar domena '{1}'.</value>
+ </data>
+ <data name="ReturnToParamDoesNotMatchRequestUrl" xml:space="preserve">
+ <value>{0} parametar ({1}) se ne slaže sa trenutnim URL ({2}) sa kojim je zahtev napravljen.</value>
+ </data>
+ <data name="ReturnToRequiredForResponse" xml:space="preserve">
+ <value>openid.return_to parametar je neophodan u poruci zahteva da bi se konstruisao odgovor, ali ovaj parametar nedostaje.</value>
+ </data>
+ <data name="SignatureDoesNotIncludeMandatoryParts" xml:space="preserve">
+ <value>Sledeći parametri nisu uključeni u potpis a moraju da budu: {0}</value>
+ </data>
+ <data name="SregInvalidBirthdate" xml:space="preserve">
+ <value>Neispravna vrednost za datum rođenja. Mora biti u formi gggg-MM-dd.</value>
+ </data>
+ <data name="TypeMustImplementX" xml:space="preserve">
+ <value>Tip mora implementirati {0}.</value>
+ </data>
+ <data name="UnsolicitedAssertionsNotAllowedFrom1xOPs" xml:space="preserve">
+ <value>Nezahtevani iskazi nisu dozvoljeni od strane 1.0 OpenID Providers.</value>
+ </data>
+ <data name="UserSetupUrlRequiredInImmediateNegativeResponse" xml:space="preserve">
+ <value>openid.user_setup_url parametar je neophodan prilikom slanja negativnih poruka sa iskazima prilikom odgovaranja na zahteve u trenutnom modu.</value>
+ </data>
+ <data name="XriResolutionFailed" xml:space="preserve">
+ <value>XRI razrešivanje neuspešno.</value>
+ </data>
+ <data name="StoreRequiredWhenNoHttpContextAvailable" xml:space="preserve">
+ <value>Tekući HttpContext nije detektovan, tako da {0} instanca mora biti eksplicitno postavljena ili specificirana u .config fajlu. Pozvati preklopljeni konstruktor koji uzima parametar {0}.</value>
+ </data>
+ <data name="AttributeAlreadyAdded" xml:space="preserve">
+ <value>Atribut sa URI tipom '{0}' je već dodat.</value>
+ </data>
+ <data name="AttributeTooManyValues" xml:space="preserve">
+ <value>Samo {0} vrednosti za atribut '{1}' su zahtevane, ali {2} su ponuđene.</value>
+ </data>
+ <data name="UnspecifiedDateTimeKindNotAllowed" xml:space="preserve">
+ <value>Prosleđivanje objekta tipa DateTime čije svojstvo Kind ima vrednost Unspecified nije dozvoljeno.</value>
+ </data>
+ <data name="AssociationOrSessionTypeUnrecognizedOrNotSupported" xml:space="preserve">
+ <value>Zahtevani tip asocijacije '{0}' sa sesijom tipa '{1}' nije prepoznat ili nije podržan od strane ovog Provider-a zbog bezbedonosnih zahteva.</value>
+ </data>
+ <data name="IncompatibleAssociationAndSessionTypes" xml:space="preserve">
+ <value>Provider je zahtevao asocijaciju tipa '{0}' i sesiju tipa '{1}', koje nisu međusobno kompatibilne.</value>
+ </data>
+ <data name="CreateRequestAlreadyCalled" xml:space="preserve">
+ <value>Zahtev za autentifikacijom je već kreiran korišćenjem CreateRequest().</value>
+ </data>
+ <data name="OpenIdTextBoxEmpty" xml:space="preserve">
+ <value>Nijedan OpenID url nije ponuđen.</value>
+ </data>
+ <data name="ClientScriptExtensionPropertyNameCollision" xml:space="preserve">
+ <value>Ekstenzija sa svojstvom ovog imena ('{0}') je već registrovana.</value>
+ </data>
+ <data name="ClientScriptExtensionTypeCollision" xml:space="preserve">
+ <value>Ekstenzija '{0}' je već registrovana.</value>
+ </data>
+ <data name="UnexpectedHttpStatusCode" xml:space="preserve">
+ <value>Neočekivani HTTP statusni kod {0} {1} primljen u direktnom odgovoru.</value>
+ </data>
+ <data name="NotSupportedByAuthenticationSnapshot" xml:space="preserve">
+ <value>Ova operacija nije podržana od strane serijalizovanih odgovora za autentifikaciju. Pokušati ovu operaciju iz LoggedIn handler-a događaja.</value>
+ </data>
+ <data name="NoRelyingPartyEndpointDiscovered" xml:space="preserve">
+ <value>Nijedan XRDS dokument koji sadrži informaciju o OpenID Relying Party krajnjoj tački nije pronadjen u {0}.</value>
+ </data>
+ <data name="AbsoluteUriRequired" xml:space="preserve">
+ <value>Absolutni URI je zahtevan za ovu vrednost.</value>
+ </data>
+ <data name="UnsolicitedAssertionForUnrelatedClaimedIdentifier" xml:space="preserve">
+ <value>Nezahtevani iskaz ne može biti poslat za navedeni identifikator {0} jer ovo nije autorizovani Provider za taj identifikator.</value>
+ </data>
+ <data name="MaximumAuthenticationTimeExpired" xml:space="preserve">
+ <value>Maksimalno dozvoljeno vreme za kompletiranje autentifikacije je isteklo. Molimo pokušajte ponovo.</value>
+ </data>
+ <data name="PrivateRPSecretNotFound" xml:space="preserve">
+ <value>Ne može se pronaći tajna za potpisivanje od strane handle-a '{0}'.</value>
+ </data>
+ <data name="ResponseNotReady" xml:space="preserve">
+ <value>Odgovor nije spreman. Koristiti najpre IsResponseReady za proveru da li je odgovor spreman.</value>
+ </data>
+ <data name="UnsupportedChannelConfiguration" xml:space="preserve">
+ <value>Ovo svojstvo nije dostupno zbog nepoznate konfiguracije kanala.</value>
+ </data>
+ <data name="IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent" xml:space="preserve">
+ <value>openid.identity i openid.claimed_id parametri moraju istovremeno biti prisutna ili istovremeno odsutna u poruci.</value>
+ </data>
+ <data name="ReturnToRequiredForOperation" xml:space="preserve">
+ <value>Svojstvo ReturnTo ne sme biti null da bi se podržala ova operacija.</value>
+ </data>
+ <data name="UnsolicitedAssertionRejectionRequiresNonceStore" xml:space="preserve">
+ <value>Odbijanje nezahtevanih iskaza zahteva skladište jedinstvenih identifikatora i skladište asocijacija.</value>
+ </data>
+ <data name="UnsolicitedAssertionsNotAllowed" xml:space="preserve">
+ <value>Nezahtevani iskazi nisu dozvoljeni od strane ovog Relying Party.</value>
+ </data>
+ <data name="DelegatingIdentifiersNotAllowed" xml:space="preserve">
+ <value>Samo OpenID-jevi izdati direktno od strane njihovog OpenID Provider-a su ovde dozvoljeni.</value>
+ </data>
+ <data name="XriResolutionDisabled" xml:space="preserve">
+ <value>XRI podrška je onemogućena na ovom sajtu.</value>
+ </data>
+ <data name="AssociationStoreRequired" xml:space="preserve">
+ <value>Skladište asocijacija nije dato a zahtevano je za trenutnu konfiguraciju.</value>
+ </data>
+ <data name="UnexpectedEnumPropertyValue" xml:space="preserve">
+ <value>Svojstvo {0} je imalo neočekivanu vrednost {1}.</value>
+ </data>
+ <data name="NoIdentifierSet" xml:space="preserve">
+ <value>Ni jedan identifikator nije podešen.</value>
+ </data>
+ <data name="PropertyValueNotSupported" xml:space="preserve">
+ <value>Ova vrednost svojstva nije podržana od strane ove kontrole.</value>
+ </data>
+ <data name="ArgumentIsPpidIdentifier" xml:space="preserve">
+ <value>Ovo je već PPID Identifier.</value>
+ </data>
+ <data name="RequireSslNotSatisfiedByAssertedClaimedId" xml:space="preserve">
+ <value>Žao nam je. Ovaj sajt jedino prihvata OpenID-jeve koji su HTTPS-bezbedni, a {0} nije bezbedni Identifier.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
index 3cee968..04eb23c 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
@@ -16,13 +16,14 @@ namespace DotNetOpenAuth.OpenId {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.ChannelElements;
using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Messages;
using DotNetOpenAuth.OpenId.Provider;
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
/// A set of utilities especially useful to OpenID.
/// </summary>
- internal static class OpenIdUtilities {
+ public static class OpenIdUtilities {
/// <summary>
/// The prefix to designate this library's proprietary parameters added to the protocol.
/// </summary>
@@ -34,7 +35,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="message">The message.</param>
/// <returns>The OpenID protocol instance.</returns>
internal static Protocol GetProtocol(this IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Requires<ArgumentNullException>(message != null);
return Protocol.Lookup(message.Version);
}
@@ -103,10 +104,8 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>The fully-qualified realm.</returns>
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "DotNetOpenAuth.OpenId.Realm", Justification = "Using ctor for validation.")]
internal static UriBuilder GetResolvedRealm(Page page, string realm, HttpRequestInfo requestContext) {
- Contract.Requires(page != null);
- Contract.Requires(requestContext != null);
- ErrorUtilities.VerifyArgumentNotNull(page, "page");
- ErrorUtilities.VerifyArgumentNotNull(requestContext, "requestContext");
+ Contract.Requires<ArgumentNullException>(page != null);
+ Contract.Requires<ArgumentNullException>(requestContext != null);
// Allow for *. realm notation, as well as ASP.NET ~/ shortcuts.
@@ -148,8 +147,7 @@ namespace DotNetOpenAuth.OpenId {
/// can plug in.
/// </remarks>
internal static IList<IOpenIdExtensionFactory> GetExtensionFactories(this Channel channel) {
- Contract.Requires(channel != null);
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+ Contract.Requires<ArgumentNullException>(channel != null);
var extensionsBindingElement = channel.BindingElements.OfType<ExtensionsBindingElement>().SingleOrDefault();
ErrorUtilities.VerifyOperation(extensionsBindingElement != null, OpenIdStrings.UnsupportedChannelConfiguration);
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs b/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs
index 2433df2..00468ed 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdXrdsHelper.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId {
+ using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
@@ -26,12 +27,31 @@ namespace DotNetOpenAuth.OpenId {
/// or for Provider's to perform RP discovery/verification as part of authentication.
/// </remarks>
internal static IEnumerable<RelyingPartyEndpointDescription> FindRelyingPartyReceivingEndpoints(this XrdsDocument xrds) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Ensures(Contract.Result<IEnumerable<RelyingPartyEndpointDescription>>() != null);
+
return from service in xrds.FindReturnToServices()
from uri in service.UriElements
select new RelyingPartyEndpointDescription(uri.Uri, service.TypeElementUris);
}
/// <summary>
+ /// Finds the icons the relying party wants an OP to display as part of authentication,
+ /// per the UI extension spec.
+ /// </summary>
+ /// <param name="xrds">The XrdsDocument to search.</param>
+ /// <returns>A sequence of the icon URLs in preferred order.</returns>
+ internal static IEnumerable<Uri> FindRelyingPartyIcons(this XrdsDocument xrds) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Ensures(Contract.Result<IEnumerable<Uri>>() != null);
+
+ return from xrd in xrds.XrdElements
+ from service in xrd.OpenIdRelyingPartyIcons
+ from uri in service.UriElements
+ select uri.Uri;
+ }
+
+ /// <summary>
/// Creates the service endpoints described in this document, useful for requesting
/// authentication of one of the OpenID Providers that result from it.
/// </summary>
@@ -41,15 +61,16 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>.
/// </returns>
- internal static IEnumerable<ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) {
- var endpoints = new List<ServiceEndpoint>();
- endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(claimedIdentifier));
-
- // If any OP Identifier service elements were found, we must not proceed
- // to return any Claimed Identifier services.
- if (endpoints.Count == 0) {
- endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));
- }
+ internal static IEnumerable<IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable<XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Requires<ArgumentNullException>(claimedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
+
+ var endpoints = new List<IdentifierDiscoveryResult>();
+ endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));
+ endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));
+
Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
Logger.Yadis.Debug(endpoints.ToStringDeferred(true));
return endpoints;
@@ -62,18 +83,14 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
/// <param name="userSuppliedIdentifier">The user-supplied i-name that was used to discover this XRDS document.</param>
/// <returns>A sequence of OpenID Providers that can assert ownership of the canonical ID given in this document.</returns>
- internal static IEnumerable<ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, XriIdentifier userSuppliedIdentifier) {
- Contract.Requires(xrds != null);
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null);
- var endpoints = new List<ServiceEndpoint>();
- endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));
+ internal static IEnumerable<IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable<XrdElement> xrds, XriIdentifier userSuppliedIdentifier) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
- // If any OP Identifier service elements were found, we must not proceed
- // to return any Claimed Identifier services.
- if (endpoints.Count == 0) {
- endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(userSuppliedIdentifier));
- }
+ var endpoints = new List<IdentifierDiscoveryResult>();
+ endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));
+ endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(userSuppliedIdentifier));
Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
Logger.Yadis.Debug(endpoints.ToStringDeferred(true));
return endpoints;
@@ -83,17 +100,17 @@ namespace DotNetOpenAuth.OpenId {
/// Generates OpenID Providers that can authenticate using directed identity.
/// </summary>
/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
- /// <param name="opIdentifier">The OP Identifier entered (and resolved) by the user.</param>
+ /// <param name="opIdentifier">The OP Identifier entered (and resolved) by the user. Essentially the user-supplied identifier.</param>
/// <returns>A sequence of the providers that can offer directed identity services.</returns>
- private static IEnumerable<ServiceEndpoint> GenerateOPIdentifierServiceEndpoints(this XrdsDocument xrds, Identifier opIdentifier) {
- Contract.Requires(xrds != null);
- Contract.Requires(opIdentifier != null);
- Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null);
+ private static IEnumerable<IdentifierDiscoveryResult> GenerateOPIdentifierServiceEndpoints(this IEnumerable<XrdElement> xrds, Identifier opIdentifier) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Requires<ArgumentNullException>(opIdentifier != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
return from service in xrds.FindOPIdentifierServices()
from uri in service.UriElements
let protocol = Protocol.FindBestVersion(p => p.OPIdentifierServiceTypeURI, service.TypeElementUris)
let providerDescription = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris)
- select ServiceEndpoint.CreateForProviderIdentifier(opIdentifier, providerDescription, service.Priority, uri.Priority);
+ select IdentifierDiscoveryResult.CreateForProviderIdentifier(opIdentifier, providerDescription, service.Priority, uri.Priority);
}
/// <summary>
@@ -106,12 +123,16 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// A sequence of the providers that can assert ownership of the given identifier.
/// </returns>
- private static IEnumerable<ServiceEndpoint> GenerateClaimedIdentifierServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) {
+ private static IEnumerable<IdentifierDiscoveryResult> GenerateClaimedIdentifierServiceEndpoints(this IEnumerable<XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Requires<ArgumentNullException>(claimedIdentifier != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
+
return from service in xrds.FindClaimedIdentifierServices()
from uri in service.UriElements
where uri.Uri != null
let providerEndpoint = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris)
- select ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority);
+ select IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority);
}
/// <summary>
@@ -121,7 +142,12 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
/// <param name="userSuppliedIdentifier">The i-name supplied by the user.</param>
/// <returns>A sequence of the providers that can assert ownership of the given identifier.</returns>
- private static IEnumerable<ServiceEndpoint> GenerateClaimedIdentifierServiceEndpoints(this XrdsDocument xrds, XriIdentifier userSuppliedIdentifier) {
+ private static IEnumerable<IdentifierDiscoveryResult> GenerateClaimedIdentifierServiceEndpoints(this IEnumerable<XrdElement> xrds, XriIdentifier userSuppliedIdentifier) {
+ // Cannot use code contracts because this method uses yield return.
+ ////Contract.Requires<ArgumentNullException>(xrds != null);
+ ////Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
+ ErrorUtilities.VerifyArgumentNotNull(xrds, "xrds");
+
foreach (var service in xrds.FindClaimedIdentifierServices()) {
foreach (var uri in service.UriElements) {
// spec section 7.3.2.3 on Claimed Id -> CanonicalID substitution
@@ -134,7 +160,7 @@ namespace DotNetOpenAuth.OpenId {
// In the case of XRI names, the ClaimedId is actually the CanonicalID.
var claimedIdentifier = new XriIdentifier(service.Xrd.CanonicalID);
var providerEndpoint = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris);
- yield return ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority);
+ yield return IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority);
}
}
}
@@ -144,8 +170,11 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
/// <returns>A sequence of service elements.</returns>
- private static IEnumerable<ServiceElement> FindOPIdentifierServices(this XrdsDocument xrds) {
- return from xrd in xrds.XrdElements
+ private static IEnumerable<ServiceElement> FindOPIdentifierServices(this IEnumerable<XrdElement> xrds) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null);
+
+ return from xrd in xrds
from service in xrd.OpenIdProviderIdentifierServices
select service;
}
@@ -156,8 +185,11 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
/// <returns>A sequence of the services offered.</returns>
- private static IEnumerable<ServiceElement> FindClaimedIdentifierServices(this XrdsDocument xrds) {
- return from xrd in xrds.XrdElements
+ private static IEnumerable<ServiceElement> FindClaimedIdentifierServices(this IEnumerable<XrdElement> xrds) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null);
+
+ return from xrd in xrds
from service in xrd.OpenIdClaimedIdentifierServices
select service;
}
@@ -169,6 +201,9 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
/// <returns>A sequence of service elements.</returns>
private static IEnumerable<ServiceElement> FindReturnToServices(this XrdsDocument xrds) {
+ Contract.Requires<ArgumentNullException>(xrds != null);
+ Contract.Ensures(Contract.Result<IEnumerable<ServiceElement>>() != null);
+
return from xrd in xrds.XrdElements
from service in xrd.OpenIdRelyingPartyReturnToServices
select service;
diff --git a/src/DotNetOpenAuth/OpenId/Protocol.cs b/src/DotNetOpenAuth/OpenId/Protocol.cs
index 7b8a2f1..5aacfd2 100644
--- a/src/DotNetOpenAuth/OpenId/Protocol.cs
+++ b/src/DotNetOpenAuth/OpenId/Protocol.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId {
using DotNetOpenAuth.Messaging;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Diagnostics;
/// <summary>
@@ -36,7 +37,7 @@ namespace DotNetOpenAuth.OpenId {
/// constants to each version used in the protocol.
/// </summary>
[DebuggerDisplay("OpenID {Version}")]
- internal class Protocol {
+ internal sealed class Protocol {
/// <summary>
/// The value of the openid.ns parameter in the OpenID 2.0 specification.
/// </summary>
@@ -156,7 +157,7 @@ namespace DotNetOpenAuth.OpenId {
/// of an incoming OpenID <i>indirect</i> message or <i>direct request</i>.
/// </summary>
internal static Protocol Detect(IDictionary<string, string> query) {
- if (query == null) throw new ArgumentNullException("query");
+ Contract.Requires<ArgumentNullException>(query != null);
return query.ContainsKey(V20.openid.ns) ? V20 : V11;
}
/// <summary>
@@ -164,7 +165,7 @@ namespace DotNetOpenAuth.OpenId {
/// of an incoming OpenID <i>direct</i> response message.
/// </summary>
internal static Protocol DetectFromDirectResponse(IDictionary<string, string> query) {
- if (query == null) throw new ArgumentNullException("query");
+ Contract.Requires<ArgumentNullException>(query != null);
return query.ContainsKey(V20.openidnp.ns) ? V20 : V11;
}
/// <summary>
@@ -172,7 +173,7 @@ namespace DotNetOpenAuth.OpenId {
/// of XRDS Service Type URIs included for some service.
/// </summary>
internal static Protocol Detect(IEnumerable<string> serviceTypeURIs) {
- if (serviceTypeURIs == null) throw new ArgumentNullException("serviceTypeURIs");
+ Contract.Requires<ArgumentNullException>(serviceTypeURIs != null);
return FindBestVersion(p => p.OPIdentifierServiceTypeURI, serviceTypeURIs) ??
FindBestVersion(p => p.ClaimedIdentifierServiceTypeURI, serviceTypeURIs) ??
FindBestVersion(p => p.RPReturnToTypeURI, serviceTypeURIs);
@@ -253,7 +254,7 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
public QueryArguments Args = new QueryArguments();
- internal class QueryParameters {
+ internal sealed class QueryParameters {
/// <summary>
/// The value "openid."
/// </summary>
@@ -319,18 +320,31 @@ namespace DotNetOpenAuth.OpenId {
public string dh_server_public = "dh_server_public";
public string enc_mac_key = "enc_mac_key";
public string mac_key = "mac_key";
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(!string.IsNullOrEmpty(this.Prefix));
+ }
+#endif
}
- internal class QueryArguments {
+
+ internal sealed class QueryArguments {
public ErrorCodes ErrorCode = new ErrorCodes();
public SessionTypes SessionType = new SessionTypes();
public SignatureAlgorithms SignatureAlgorithm = new SignatureAlgorithms();
public Modes Mode = new Modes();
public IsValidValues IsValid = new IsValidValues();
- internal class ErrorCodes {
+ internal sealed class ErrorCodes {
public string UnsupportedType = "unsupported-type";
}
- internal class SessionTypes {
+ internal sealed class SessionTypes {
/// <summary>
/// A preference order list of all supported session types.
/// </summary>
@@ -352,7 +366,7 @@ namespace DotNetOpenAuth.OpenId {
}
}
}
- internal class SignatureAlgorithms {
+ internal sealed class SignatureAlgorithms {
/// <summary>
/// A preference order list of signature algorithms we support.
/// </summary>
@@ -372,7 +386,7 @@ namespace DotNetOpenAuth.OpenId {
}
}
}
- internal class Modes {
+ internal sealed class Modes {
public string cancel = "cancel";
public string error = "error";
public string id_res = "id_res";
@@ -382,7 +396,7 @@ namespace DotNetOpenAuth.OpenId {
public string associate = "associate";
public string setup_needed = "id_res"; // V2 overrides this
}
- internal class IsValidValues {
+ internal sealed class IsValidValues {
public string True = "true";
public string False = "false";
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
index b53e1f6..24f84d6 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
@@ -30,13 +30,27 @@ namespace DotNetOpenAuth.OpenId.Provider {
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts require it.")]
internal AnonymousRequest(OpenIdProvider provider, SignedResponseRequest request)
: base(provider, request) {
- Contract.Requires(provider != null);
- Contract.Requires(!(request is CheckIdRequest), "Instantiate " + typeof(AuthenticationRequest).Name + " to handle this kind of message.");
- ErrorUtilities.VerifyInternal(!(request is CheckIdRequest), "Instantiate {0} to handle this kind of message.", typeof(AuthenticationRequest).Name);
+ Contract.Requires<ArgumentNullException>(provider != null);
+ Contract.Requires<ArgumentException>(!(request is CheckIdRequest), "Instantiate " + typeof(AuthenticationRequest).Name + " to handle this kind of message.");
this.positiveResponse = new IndirectSignedResponse(request);
}
+ #region HostProcessedRequest members
+
+ /// <summary>
+ /// Gets or sets the provider endpoint.
+ /// </summary>
+ /// <value>
+ /// The default value is the URL that the request came in on from the relying party.
+ /// </value>
+ public override Uri ProviderEndpoint {
+ get { return this.positiveResponse.ProviderEndpoint; }
+ set { this.positiveResponse.ProviderEndpoint = value; }
+ }
+
+ #endregion
+
#region IAnonymousRequest Members
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs
index cdd5311..9ffaa55 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequestEventArgs.cs
@@ -18,8 +18,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="request">The incoming OpenID request.</param>
internal AnonymousRequestEventArgs(IAnonymousRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
this.Request = request;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
index 56e73da..d22f858 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
@@ -29,8 +29,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="request">The incoming authentication request message.</param>
internal AuthenticationRequest(OpenIdProvider provider, CheckIdRequest request)
: base(provider, request) {
- Contract.Requires(provider != null);
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ Contract.Requires<ArgumentNullException>(provider != null);
this.positiveResponse = new PositiveAssertionResponse(request);
@@ -46,8 +45,25 @@ namespace DotNetOpenAuth.OpenId.Provider {
// If the openid.claimed_id is present, and if it's different than the openid.identity argument, then
// the RP has discovered a claimed identifier that has delegated authentication to this Provider.
this.IsDelegatedIdentifier = this.ClaimedIdentifier != null && this.ClaimedIdentifier != this.LocalIdentifier;
+
+ Reporting.RecordEventOccurrence("AuthenticationRequest.IsDelegatedIdentifier", this.IsDelegatedIdentifier.ToString());
}
+ #region HostProcessedRequest members
+
+ /// <summary>
+ /// Gets or sets the provider endpoint.
+ /// </summary>
+ /// <value>
+ /// The default value is the URL that the request came in on from the relying party.
+ /// </value>
+ public override Uri ProviderEndpoint {
+ get { return this.positiveResponse.ProviderEndpoint; }
+ set { this.positiveResponse.ProviderEndpoint = value; }
+ }
+
+ #endregion
+
/// <summary>
/// Gets a value indicating whether the response is ready to be created and sent.
/// </summary>
@@ -135,11 +151,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
set {
// Keep LocalIdentifier and ClaimedIdentifier in sync for directed identity.
if (this.IsDirectedIdentity) {
- ErrorUtilities.VerifyOperation(!(this.LocalIdentifier != null && this.LocalIdentifier != value), OpenIdStrings.IdentifierSelectRequiresMatchingIdentifiers);
this.positiveResponse.LocalIdentifier = value;
}
- ErrorUtilities.VerifyOperation(!this.IsDelegatedIdentifier, OpenIdStrings.ClaimedIdentifierCannotBeSetOnDelegatedAuthentication);
this.positiveResponse.ClaimedIdentifier = value;
}
}
@@ -192,9 +206,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// request before the <see cref="ClaimedIdentifier"/> property is set.
/// </exception>
public void SetClaimedIdentifierFragment(string fragment) {
- ErrorUtilities.VerifyOperation(!(this.IsDirectedIdentity && this.ClaimedIdentifier == null), OpenIdStrings.ClaimedIdentifierMustBeSetFirst);
- ErrorUtilities.VerifyOperation(!(this.ClaimedIdentifier is XriIdentifier), OpenIdStrings.FragmentNotAllowedOnXRIs);
-
UriBuilder builder = new UriBuilder(this.ClaimedIdentifier);
builder.Fragment = fragment;
this.positiveResponse.ClaimedIdentifier = builder.Uri;
@@ -205,8 +216,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="identifier">The value to set to the <see cref="ClaimedIdentifier"/> and <see cref="LocalIdentifier"/> properties.</param>
internal void ResetClaimedAndLocalIdentifiers(Identifier identifier) {
- Contract.Requires(identifier != null);
- ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
+ Contract.Requires<ArgumentNullException>(identifier != null);
this.positiveResponse.ClaimedIdentifier = identifier;
this.positiveResponse.LocalIdentifier = identifier;
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
index d1d310e..e5988dd 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -31,7 +32,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="securitySettings">The security settings.</param>
internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response, ProviderSecuritySettings securitySettings)
: base(request, securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
}
@@ -44,7 +45,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="securitySettings">The security settings.</param>
internal AutoResponsiveRequest(IProtocolMessage response, ProviderSecuritySettings securitySettings)
: base(IndirectResponseBase.GetVersion(response), securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
index 6e61376..ec0c58a 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
@@ -36,9 +36,10 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="request">The incoming request message.</param>
protected HostProcessedRequest(OpenIdProvider provider, SignedResponseRequest request)
: base(request, provider.SecuritySettings) {
- Contract.Requires(provider != null);
+ Contract.Requires<ArgumentNullException>(provider != null);
this.negativeResponse = new NegativeAssertionResponse(request, provider.Channel);
+ Reporting.RecordEventOccurrence(this, request.Realm);
}
#region IHostProcessedRequest Properties
@@ -66,6 +67,14 @@ namespace DotNetOpenAuth.OpenId.Provider {
get { return this.RequestMessage.Realm; }
}
+ /// <summary>
+ /// Gets or sets the provider endpoint.
+ /// </summary>
+ /// <value>
+ /// The default value is the URL that the request came in on from the relying party.
+ /// </value>
+ public abstract Uri ProviderEndpoint { get; set; }
+
#endregion
/// <summary>
@@ -105,8 +114,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// See OpenID Authentication 2.0 spec section 9.2.1.
/// </remarks>
public RelyingPartyDiscoveryResult IsReturnUrlDiscoverable(OpenIdProvider provider) {
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
-
if (!this.realmDiscoveryResult.HasValue) {
this.realmDiscoveryResult = this.IsReturnUrlDiscoverableCore(provider);
}
@@ -121,16 +128,17 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
/// <returns>Result of realm discovery.</returns>
private RelyingPartyDiscoveryResult IsReturnUrlDiscoverableCore(OpenIdProvider provider) {
- Contract.Requires(provider != null);
+ Contract.Requires<ArgumentNullException>(provider != null);
ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now.");
+
try {
if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) {
Logger.OpenId.WarnFormat("RP discovery failed because RequireSsl is true and RP discovery would begin at insecure URL {0}.", this.Realm);
return RelyingPartyDiscoveryResult.NoServiceDocument;
}
- var returnToEndpoints = this.Realm.Discover(provider.Channel.WebRequestHandler, false);
+ var returnToEndpoints = this.Realm.DiscoverReturnToEndpoints(provider.Channel.WebRequestHandler, false);
if (returnToEndpoints == null) {
return RelyingPartyDiscoveryResult.NoServiceDocument;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs
index bb837b5..077dcf1 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IAuthenticationRequest.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -15,6 +16,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// This interface provides the details of the request and allows setting
/// the response.
/// </summary>
+ [ContractClass(typeof(IAuthenticationRequestContract))]
public interface IAuthenticationRequest : IHostProcessedRequest {
/// <summary>
/// Gets a value indicating whether the Provider should help the user
@@ -93,4 +95,263 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </exception>
void SetClaimedIdentifierFragment(string fragment);
}
+
+ /// <summary>
+ /// Code contract class for the <see cref="IAuthenticationRequest"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IAuthenticationRequest))]
+ internal abstract class IAuthenticationRequestContract : IAuthenticationRequest {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IAuthenticationRequestContract"/> class.
+ /// </summary>
+ protected IAuthenticationRequestContract() {
+ }
+
+ #region IAuthenticationRequest Properties
+
+ /// <summary>
+ /// Gets a value indicating whether the Provider should help the user
+ /// select a Claimed Identifier to send back to the relying party.
+ /// </summary>
+ bool IAuthenticationRequest.IsDirectedIdentity {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the requesting Relying Party is using a delegated URL.
+ /// </summary>
+ /// <remarks>
+ /// When delegated identifiers are used, the <see cref="IAuthenticationRequest.ClaimedIdentifier"/> should not
+ /// be changed at the Provider during authentication.
+ /// Delegation is only detectable on requests originating from OpenID 2.0 relying parties.
+ /// A relying party implementing only OpenID 1.x may use delegation and this property will
+ /// return false anyway.
+ /// </remarks>
+ bool IAuthenticationRequest.IsDelegatedIdentifier {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the Local Identifier to this OpenID Provider of the user attempting
+ /// to authenticate. Check <see cref="IAuthenticationRequest.IsDirectedIdentity"/> to see if
+ /// this value is valid.
+ /// </summary>
+ /// <remarks>
+ /// This may or may not be the same as the Claimed Identifier that the user agent
+ /// originally supplied to the relying party. The Claimed Identifier
+ /// endpoint may be delegating authentication to this provider using
+ /// this provider's local id, which is what this property contains.
+ /// Use this identifier when looking up this user in the provider's user account
+ /// list.
+ /// </remarks>
+ Identifier IAuthenticationRequest.LocalIdentifier {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the identifier that the user agent is claiming at the relying party site.
+ /// Check <see cref="IAuthenticationRequest.IsDirectedIdentity"/> to see if this value is valid.
+ /// </summary>
+ /// <remarks>
+ /// <para>This property can only be set if <see cref="IAuthenticationRequest.IsDelegatedIdentifier"/> is
+ /// false, to prevent breaking URL delegation.</para>
+ /// <para>This will not be the same as this provider's local identifier for the user
+ /// if the user has set up his/her own identity page that points to this
+ /// provider for authentication.</para>
+ /// <para>The provider may use this identifier for displaying to the user when
+ /// asking for the user's permission to authenticate to the relying party.</para>
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown from the setter
+ /// if <see cref="IAuthenticationRequest.IsDelegatedIdentifier"/> is true.</exception>
+ Identifier IAuthenticationRequest.ClaimedIdentifier {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ IAuthenticationRequest req = this;
+ Contract.Requires<InvalidOperationException>(!req.IsDelegatedIdentifier, OpenIdStrings.ClaimedIdentifierCannotBeSetOnDelegatedAuthentication);
+ Contract.Requires<InvalidOperationException>(!req.IsDirectedIdentity || !(req.LocalIdentifier != null && req.LocalIdentifier != value), OpenIdStrings.IdentifierSelectRequiresMatchingIdentifiers);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the provider has determined that the
+ /// <see cref="IAuthenticationRequest.ClaimedIdentifier"/> belongs to the currently logged in user
+ /// and wishes to share this information with the consumer.
+ /// </summary>
+ bool? IAuthenticationRequest.IsAuthenticated {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ #region IHostProcessedRequest Properties
+
+ /// <summary>
+ /// Gets the version of OpenID being used by the relying party that sent the request.
+ /// </summary>
+ ProtocolVersion IHostProcessedRequest.RelyingPartyVersion {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the URL the consumer site claims to use as its 'base' address.
+ /// </summary>
+ Realm IHostProcessedRequest.Realm {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the consumer demands an immediate response.
+ /// If false, the consumer is willing to wait for the identity provider
+ /// to authenticate the user.
+ /// </summary>
+ bool IHostProcessedRequest.Immediate {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the provider endpoint claimed in the positive assertion.
+ /// </summary>
+ /// <value>
+ /// The default value is the URL that the request came in on from the relying party.
+ /// This value MUST match the value for the OP Endpoint in the discovery results for the
+ /// claimed identifier being asserted in a positive response.
+ /// </value>
+ Uri IHostProcessedRequest.ProviderEndpoint {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ #region IRequest Properties
+
+ /// <summary>
+ /// Gets a value indicating whether the response is ready to be sent to the user agent.
+ /// </summary>
+ /// <remarks>
+ /// This property returns false if there are properties that must be set on this
+ /// request instance before the response can be sent.
+ /// </remarks>
+ bool IRequest.IsResponseReady {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>
+ /// Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.
+ /// </value>
+ ProviderSecuritySettings IRequest.SecuritySettings {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ #region IAuthenticationRequest Methods
+
+ /// <summary>
+ /// Adds an optional fragment (#fragment) portion to the ClaimedIdentifier.
+ /// Useful for identifier recycling.
+ /// </summary>
+ /// <param name="fragment">Should not include the # prefix character as that will be added internally.
+ /// May be null or the empty string to clear a previously set fragment.</param>
+ /// <remarks>
+ /// <para>Unlike the <see cref="IAuthenticationRequest.ClaimedIdentifier"/> property, which can only be set if
+ /// using directed identity, this method can be called on any URI claimed identifier.</para>
+ /// <para>Because XRI claimed identifiers (the canonical IDs) are never recycled,
+ /// this method should<i>not</i> be called for XRIs.</para>
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when this method is called on an XRI, or on a directed identity
+ /// request before the <see cref="IAuthenticationRequest.ClaimedIdentifier"/> property is set.
+ /// </exception>
+ void IAuthenticationRequest.SetClaimedIdentifierFragment(string fragment) {
+ Contract.Requires<InvalidOperationException>(!(((IAuthenticationRequest)this).IsDirectedIdentity && ((IAuthenticationRequest)this).ClaimedIdentifier == null), OpenIdStrings.ClaimedIdentifierMustBeSetFirst);
+ Contract.Requires<InvalidOperationException>(!(((IAuthenticationRequest)this).ClaimedIdentifier is XriIdentifier), OpenIdStrings.FragmentNotAllowedOnXRIs);
+
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region IHostProcessedRequest Methods
+
+ /// <summary>
+ /// Attempts to perform relying party discovery of the return URL claimed by the Relying Party.
+ /// </summary>
+ /// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
+ /// <returns>
+ /// The details of how successful the relying party discovery was.
+ /// </returns>
+ /// <remarks>
+ /// <para>Return URL verification is only attempted if this method is called.</para>
+ /// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para>
+ /// </remarks>
+ RelyingPartyDiscoveryResult IHostProcessedRequest.IsReturnUrlDiscoverable(OpenIdProvider provider) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region IRequest Methods
+
+ /// <summary>
+ /// Adds an extension to the response to send to the relying party.
+ /// </summary>
+ /// <param name="extension">The extension to add to the response message.</param>
+ void IRequest.AddResponseExtension(DotNetOpenAuth.OpenId.Messages.IOpenIdMessageExtension extension) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets an extension sent from the relying party.
+ /// </summary>
+ /// <typeparam name="T">The type of the extension.</typeparam>
+ /// <returns>
+ /// An instance of the extension initialized with values passed in with the request.
+ /// </returns>
+ T IRequest.GetExtension<T>() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets an extension sent from the relying party.
+ /// </summary>
+ /// <param name="extensionType">The type of the extension.</param>
+ /// <returns>
+ /// An instance of the extension initialized with values passed in with the request.
+ /// </returns>
+ DotNetOpenAuth.OpenId.Messages.IOpenIdMessageExtension IRequest.GetExtension(Type extensionType) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs
index 00a3267..985bb54 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs
@@ -37,6 +37,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <returns>
/// <c>true</c> if the given identifier is the valid, unique identifier for some uesr (and NOT a PPID); otherwise, <c>false</c>.
/// </returns>
+ [Pure]
bool IsUserLocalIdentifier(Identifier identifier);
}
@@ -58,9 +59,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// openid.claimed_id and openid.local_id parameters. Must not be null.
/// </returns>
Uri IDirectedIdentityIdentifierProvider.GetIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) {
- Contract.Requires(localIdentifier != null);
- Contract.Requires(relyingPartyRealm != null);
-
+ Contract.Requires<ArgumentNullException>(localIdentifier != null);
+ Contract.Requires<ArgumentNullException>(relyingPartyRealm != null);
+ Contract.Requires<ArgumentException>(((IDirectedIdentityIdentifierProvider)this).IsUserLocalIdentifier(localIdentifier), OpenIdStrings.ArgumentIsPpidIdentifier);
throw new NotImplementedException();
}
@@ -72,7 +73,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <c>true</c> if the given identifier is the valid, unique identifier for some uesr (and NOT a PPID); otherwise, <c>false</c>.
/// </returns>
bool IDirectedIdentityIdentifierProvider.IsUserLocalIdentifier(Identifier identifier) {
- Contract.Requires(identifier != null);
+ Contract.Requires<ArgumentNullException>(identifier != null);
+
throw new NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
index 5256fdd..be809bd 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
/// <summary>
/// Interface exposing incoming messages to the OpenID Provider that
@@ -33,6 +34,16 @@ namespace DotNetOpenAuth.OpenId.Provider {
bool Immediate { get; }
/// <summary>
+ /// Gets or sets the provider endpoint claimed in the positive assertion.
+ /// </summary>
+ /// <value>
+ /// The default value is the URL that the request came in on from the relying party.
+ /// This value MUST match the value for the OP Endpoint in the discovery results for the
+ /// claimed identifier being asserted in a positive response.
+ /// </value>
+ Uri ProviderEndpoint { get; set; }
+
+ /// <summary>
/// Attempts to perform relying party discovery of the return URL claimed by the Relying Party.
/// </summary>
/// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
@@ -47,10 +58,16 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
/// <summary>
- /// Contract class for the <see cref="IHostProcessedRequest"/> type.
+ /// Code contract for the <see cref="IHostProcessedRequest"/> type.
/// </summary>
[ContractClassFor(typeof(IHostProcessedRequest))]
internal abstract class IHostProcessedRequestContract : IHostProcessedRequest {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IHostProcessedRequestContract"/> class.
+ /// </summary>
+ protected IHostProcessedRequestContract() {
+ }
+
#region IHostProcessedRequest Properties
/// <summary>
@@ -76,6 +93,24 @@ namespace DotNetOpenAuth.OpenId.Provider {
get { throw new System.NotImplementedException(); }
}
+ /// <summary>
+ /// Gets or sets the provider endpoint.
+ /// </summary>
+ /// <value>
+ /// The default value is the URL that the request came in on from the relying party.
+ /// </value>
+ Uri IHostProcessedRequest.ProviderEndpoint {
+ get {
+ Contract.Ensures(Contract.Result<Uri>() != null);
+ throw new NotImplementedException();
+ }
+
+ set {
+ Contract.Requires(value != null);
+ throw new NotImplementedException();
+ }
+ }
+
#endregion
#region IRequest Members
@@ -148,7 +183,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para>
/// </remarks>
RelyingPartyDiscoveryResult IHostProcessedRequest.IsReturnUrlDiscoverable(OpenIdProvider provider) {
- Contract.Requires(provider != null);
+ Contract.Requires<ArgumentNullException>(provider != null);
throw new System.NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
index 48d40d4..01b4ac8 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
@@ -5,16 +5,15 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.OpenId.ChannelElements;
/// <summary>
/// Applies a custom security policy to certain OpenID security settings and behaviors.
/// </summary>
- /// <remarks>
- /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
- /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
- /// </remarks>
- internal interface IProviderBehavior {
+ [ContractClass(typeof(IProviderBehaviorContract))]
+ public interface IProviderBehavior {
/// <summary>
/// Applies a well known set of security requirements to a default set of security settings.
/// </summary>
@@ -51,4 +50,65 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </returns>
bool OnOutgoingResponse(IAuthenticationRequest request);
}
+
+ /// <summary>
+ /// Code contract for the <see cref="IProviderBehavior"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IProviderBehavior))]
+ internal abstract class IProviderBehaviorContract : IProviderBehavior {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IProviderBehaviorContract"/> class.
+ /// </summary>
+ protected IProviderBehaviorContract() {
+ }
+
+ #region IProviderBehavior Members
+
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) {
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool IProviderBehavior.OnIncomingRequest(IRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool IProviderBehavior.OnOutgoingResponse(IAuthenticationRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ throw new System.NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
index 2ca96ca..0fcdc28 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
@@ -64,7 +64,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Code contract for the <see cref="IRequest"/> interface.
/// </summary>
[ContractClassFor(typeof(IRequest))]
- internal class IRequestContract : IRequest {
+ internal abstract class IRequestContract : IRequest {
/// <summary>
/// Prevents a default instance of the <see cref="IRequestContract"/> class from being created.
/// </summary>
@@ -87,7 +87,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
/// </summary>
- /// <value></value>
/// <remarks>
/// This property returns false if there are properties that must be set on this
/// request instance before the response can be sent.
@@ -101,7 +100,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="extension">The extension to add to the response message.</param>
void IRequest.AddResponseExtension(IOpenIdMessageExtension extension) {
- Contract.Requires(extension != null);
+ Contract.Requires<ArgumentNullException>(extension != null);
throw new NotImplementedException();
}
@@ -124,7 +123,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// An instance of the extension initialized with values passed in with the request.
/// </returns>
IOpenIdMessageExtension IRequest.GetExtension(Type extensionType) {
- Contract.Requires(extensionType != null);
+ Contract.Requires<ArgumentNullException>(extensionType != null);
throw new NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index 6890acb..10c69a3 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -20,10 +20,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OpenId.ChannelElements;
using DotNetOpenAuth.OpenId.Messages;
+ using RP = DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
/// Offers services for a web page that is acting as an OpenID identity server.
/// </summary>
+ [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "By design")]
[ContractVerification(true)]
public sealed class OpenIdProvider : IDisposable {
/// <summary>
@@ -43,6 +45,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
private ProviderSecuritySettings securitySettings;
/// <summary>
+ /// The relying party used to perform discovery on identifiers being sent in
+ /// unsolicited positive assertions.
+ /// </summary>
+ private RP.OpenIdRelyingParty relyingParty;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdProvider"/> class.
/// </summary>
public OpenIdProvider()
@@ -58,7 +66,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="applicationStore">The application store to use. Cannot be null.</param>
public OpenIdProvider(IProviderApplicationStore applicationStore)
: this(applicationStore, applicationStore) {
- Contract.Requires(applicationStore != null);
+ Contract.Requires<ArgumentNullException>(applicationStore != null);
Contract.Ensures(this.AssociationStore == applicationStore);
Contract.Ensures(this.SecuritySettings != null);
Contract.Ensures(this.Channel != null);
@@ -70,13 +78,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="associationStore">The association store to use. Cannot be null.</param>
/// <param name="nonceStore">The nonce store to use. Cannot be null.</param>
private OpenIdProvider(IAssociationStore<AssociationRelyingPartyType> associationStore, INonceStore nonceStore) {
- Contract.Requires(associationStore != null);
- Contract.Requires(nonceStore != null);
+ Contract.Requires<ArgumentNullException>(associationStore != null);
+ Contract.Requires<ArgumentNullException>(nonceStore != null);
Contract.Ensures(this.AssociationStore == associationStore);
Contract.Ensures(this.SecuritySettings != null);
Contract.Ensures(this.Channel != null);
- ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
this.AssociationStore = associationStore;
this.SecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.Provider.SecuritySettings.CreateSecuritySettings();
@@ -86,6 +92,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
this.Channel = new OpenIdChannel(this.AssociationStore, nonceStore, this.SecuritySettings);
+
+ Reporting.RecordFeatureAndDependencyUse(this, associationStore, nonceStore);
}
/// <summary>
@@ -95,8 +103,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static IProviderApplicationStore HttpApplicationStore {
get {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
Contract.Ensures(Contract.Result<IProviderApplicationStore>() != null);
- ErrorUtilities.VerifyHttpContext();
HttpContext context = HttpContext.Current;
var store = (IProviderApplicationStore)context.Application[ApplicationStoreKey];
if (store == null) {
@@ -130,8 +138,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
internal set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.securitySettings = value;
}
}
@@ -152,11 +159,22 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Gets a list of custom behaviors to apply to OpenID actions.
/// </summary>
- internal ICollection<IProviderBehavior> Behaviors {
+ /// <remarks>
+ /// Adding behaviors can impact the security settings of the <see cref="OpenIdProvider"/>
+ /// in ways that subsequently removing the behaviors will not reverse.
+ /// </remarks>
+ public ICollection<IProviderBehavior> Behaviors {
get { return this.behaviors; }
}
/// <summary>
+ /// Gets the list of services that can perform discovery on identifiers given to this relying party.
+ /// </summary>
+ internal IList<IIdentifierDiscoveryService> DiscoveryServices {
+ get { return this.RelyingParty.DiscoveryServices; }
+ }
+
+ /// <summary>
/// Gets the association store.
/// </summary>
internal IAssociationStore<AssociationRelyingPartyType> AssociationStore { get; private set; }
@@ -170,6 +188,25 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
/// <summary>
+ /// Gets the relying party used for discovery of identifiers sent in unsolicited assertions.
+ /// </summary>
+ private RP.OpenIdRelyingParty RelyingParty {
+ get {
+ if (this.relyingParty == null) {
+ lock (this) {
+ if (this.relyingParty == null) {
+ // we just need an RP that's capable of discovery, so stateless mode is fine.
+ this.relyingParty = new RP.OpenIdRelyingParty(null);
+ }
+ }
+ }
+
+ this.relyingParty.Channel.WebRequestHandler = this.WebRequestHandler;
+ return this.relyingParty;
+ }
+ }
+
+ /// <summary>
/// Gets the incoming OpenID request if there is one, or null if none was detected.
/// </summary>
/// <returns>The request that the hosting Provider should possibly process and then transmit the response for.</returns>
@@ -201,8 +238,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <exception cref="ProtocolException">Thrown if the incoming message is recognized
/// but deviates from the protocol specification irrecoverably.</exception>
public IRequest GetRequest(HttpRequestInfo httpRequestInfo) {
- Contract.Requires(httpRequestInfo != null);
- ErrorUtilities.VerifyArgumentNotNull(httpRequestInfo, "httpRequestInfo");
+ Contract.Requires<ArgumentNullException>(httpRequestInfo != null);
IDirectedProtocolMessage incomingMessage = null;
try {
@@ -281,10 +317,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <exception cref="InvalidOperationException">Thrown if <see cref="IRequest.IsResponseReady"/> is <c>false</c>.</exception>
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code Contract requires that we cast early.")]
public void SendResponse(IRequest request) {
- Contract.Requires(HttpContext.Current != null);
- Contract.Requires(request != null);
- Contract.Requires(((Request)request).IsResponseReady);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.CurrentHttpContextRequired);
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(((Request)request).IsResponseReady);
this.ApplyBehaviorsToResponse(request);
Request requestInternal = (Request)request;
@@ -299,9 +334,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <exception cref="InvalidOperationException">Thrown if <see cref="IRequest.IsResponseReady"/> is <c>false</c>.</exception>
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code Contract requires that we cast early.")]
public OutgoingWebResponse PrepareResponse(IRequest request) {
- Contract.Requires(request != null);
- Contract.Requires(((Request)request).IsResponseReady);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(((Request)request).IsResponseReady);
this.ApplyBehaviorsToResponse(request);
Request requestInternal = (Request)request;
@@ -314,7 +348,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// web site and log him/her in immediately in one uninterrupted step.
/// </summary>
/// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
- /// <param name="relyingParty">The URL of the Relying Party web site.
+ /// <param name="relyingPartyRealm">The URL of the Relying Party web site.
/// This will typically be the home page, but may be a longer URL if
/// that Relying Party considers the scope of its realm to be more specific.
/// The URL provided here must allow discovery of the Relying Party's
@@ -323,15 +357,15 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="localIdentifier">The Identifier you know your user by internally. This will typically
/// be the same as <paramref name="claimedIdentifier"/>.</param>
/// <param name="extensions">The extensions.</param>
- public void SendUnsolicitedAssertion(Uri providerEndpoint, Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) {
- Contract.Requires(HttpContext.Current != null);
- Contract.Requires(providerEndpoint != null);
- Contract.Requires(providerEndpoint.IsAbsoluteUri);
- Contract.Requires(relyingParty != null);
- Contract.Requires(claimedIdentifier != null);
- Contract.Requires(localIdentifier != null);
-
- this.PrepareUnsolicitedAssertion(providerEndpoint, relyingParty, claimedIdentifier, localIdentifier, extensions).Send();
+ public void SendUnsolicitedAssertion(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
+ Contract.Requires<ArgumentException>(providerEndpoint.IsAbsoluteUri);
+ Contract.Requires<ArgumentNullException>(relyingPartyRealm != null);
+ Contract.Requires<ArgumentNullException>(claimedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(localIdentifier != null);
+
+ this.PrepareUnsolicitedAssertion(providerEndpoint, relyingPartyRealm, claimedIdentifier, localIdentifier, extensions).Send();
}
/// <summary>
@@ -340,7 +374,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// web site and log him/her in immediately in one uninterrupted step.
/// </summary>
/// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
- /// <param name="relyingParty">The URL of the Relying Party web site.
+ /// <param name="relyingPartyRealm">The URL of the Relying Party web site.
/// This will typically be the home page, but may be a longer URL if
/// that Relying Party considers the scope of its realm to be more specific.
/// The URL provided here must allow discovery of the Relying Party's
@@ -353,41 +387,43 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// A <see cref="OutgoingWebResponse"/> object describing the HTTP response to send
/// the user agent to allow the redirect with assertion to happen.
/// </returns>
- public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) {
- Contract.Requires(providerEndpoint != null);
- Contract.Requires(providerEndpoint.IsAbsoluteUri);
- Contract.Requires(relyingParty != null);
- Contract.Requires(claimedIdentifier != null);
- Contract.Requires(localIdentifier != null);
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
- ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(localIdentifier, "localIdentifier");
- ErrorUtilities.VerifyArgumentNamed(providerEndpoint.IsAbsoluteUri, "providerEndpoint", OpenIdStrings.AbsoluteUriRequired);
+ public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) {
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
+ Contract.Requires<ArgumentException>(providerEndpoint.IsAbsoluteUri);
+ Contract.Requires<ArgumentNullException>(relyingPartyRealm != null);
+ Contract.Requires<ArgumentNullException>(claimedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(localIdentifier != null);
+ Contract.Requires<InvalidOperationException>(this.Channel.WebRequestHandler != null);
// Although the RP should do their due diligence to make sure that this OP
// is authorized to send an assertion for the given claimed identifier,
// do due diligence by performing our own discovery on the claimed identifier
// and make sure that it is tied to this OP and OP local identifier.
- var serviceEndpoint = DotNetOpenAuth.OpenId.RelyingParty.ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
- var discoveredEndpoints = claimedIdentifier.Discover(this.WebRequestHandler);
- if (!discoveredEndpoints.Contains(serviceEndpoint)) {
- Logger.OpenId.DebugFormat(
- "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
- claimedIdentifier,
- Environment.NewLine,
- serviceEndpoint,
- discoveredEndpoints.ToStringDeferred(true));
- ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
+ if (this.SecuritySettings.UnsolicitedAssertionVerification != ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.NeverVerify) {
+ var serviceEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
+ var discoveredEndpoints = this.RelyingParty.Discover(claimedIdentifier);
+ if (!discoveredEndpoints.Contains(serviceEndpoint)) {
+ Logger.OpenId.WarnFormat(
+ "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
+ claimedIdentifier,
+ Environment.NewLine,
+ serviceEndpoint,
+ discoveredEndpoints.ToStringDeferred(true));
+
+ // Only FAIL if the setting is set for it.
+ if (this.securitySettings.UnsolicitedAssertionVerification == ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.RequireSuccess) {
+ ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
+ }
+ }
}
Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier);
RelyingPartyEndpointDescription returnToEndpoint = null;
- var returnToEndpoints = relyingParty.Discover(this.WebRequestHandler, true);
+ var returnToEndpoints = relyingPartyRealm.DiscoverReturnToEndpoints(this.WebRequestHandler, true);
if (returnToEndpoints != null) {
returnToEndpoint = returnToEndpoints.FirstOrDefault();
}
- ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingParty);
+ ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingPartyRealm);
var positiveAssertion = new PositiveAssertionResponse(returnToEndpoint) {
ProviderEndpoint = providerEndpoint,
@@ -401,6 +437,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
}
+ Reporting.RecordEventOccurrence(this, "PrepareUnsolicitedAssertion");
return this.Channel.PrepareResponse(positiveAssertion);
}
@@ -425,6 +462,10 @@ namespace DotNetOpenAuth.OpenId.Provider {
if (channel != null) {
channel.Dispose();
}
+
+ if (this.relyingParty != null) {
+ this.relyingParty.Dispose();
+ }
}
}
@@ -456,10 +497,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Either the <see cref="IRequest"/> to return to the host site or null to indicate no response could be reasonably created and that the caller should rethrow the exception.
/// </returns>
private IRequest GetErrorResponse(ProtocolException ex, HttpRequestInfo httpRequestInfo, IDirectedProtocolMessage incomingMessage) {
- Contract.Requires(ex != null);
- Contract.Requires(httpRequestInfo != null);
- ErrorUtilities.VerifyArgumentNotNull(ex, "ex");
- ErrorUtilities.VerifyArgumentNotNull(httpRequestInfo, "httpRequestInfo");
+ Contract.Requires<ArgumentNullException>(ex != null);
+ Contract.Requires<ArgumentNullException>(httpRequestInfo != null);
Logger.OpenId.Error("An exception was generated while processing an incoming OpenID request.", ex);
IErrorMessage errorMessage;
@@ -490,7 +529,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
return null;
}
- errorMessage.ErrorMessage = ex.GetAllMessages();
+ errorMessage.ErrorMessage = ex.ToStringDescriptive();
// Allow host to log this error and issue a ticket #.
// We tear off the field to a local var for thread safety.
@@ -515,6 +554,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
private void OnBehaviorsChanged(object sender, NotifyCollectionChangedEventArgs e) {
foreach (IProviderBehavior profile in e.NewItems) {
profile.ApplySecuritySettings(this.SecuritySettings);
+ Reporting.RecordFeatureUse(profile);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
index 399a84f..46e172c 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
@@ -35,8 +35,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="baseIdentifier">The base URI on which to append the anonymous part.</param>
protected PrivatePersonalIdentifierProviderBase(Uri baseIdentifier) {
- Contract.Requires(baseIdentifier != null);
- ErrorUtilities.VerifyArgumentNotNull(baseIdentifier, "baseIdentifier");
+ Contract.Requires<ArgumentNullException>(baseIdentifier != null);
this.Hasher = HashAlgorithm.Create(HashAlgorithmName);
this.Encoder = Encoding.UTF8;
@@ -103,8 +102,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
set {
- Contract.Requires(value > 0);
- ErrorUtilities.VerifyArgumentInRange(value > 0, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value > 0);
this.newSaltLength = value;
}
}
@@ -122,10 +120,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// openid.claimed_id and openid.local_id parameters. Must not be null.
/// </returns>
public Uri GetIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) {
- ErrorUtilities.VerifyArgumentNotNull(localIdentifier, "localIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(relyingPartyRealm, "relyingPartyRealm");
- ErrorUtilities.VerifyArgumentNamed(this.IsUserLocalIdentifier(localIdentifier), "localIdentifier", OpenIdStrings.ArgumentIsPpidIdentifier);
-
byte[] salt = this.GetHashSaltForLocalIdentifier(localIdentifier);
string valueToHash = localIdentifier + "#";
switch (this.PairwiseUnique) {
@@ -187,8 +181,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <returns>The full PPID Identifier.</returns>
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "NOT equivalent overload. The recommended one breaks on relative URIs.")]
protected virtual Uri AppendIdentifiers(string uriHash) {
- Contract.Requires(!String.IsNullOrEmpty(uriHash));
- ErrorUtilities.VerifyNonZeroLength(uriHash, "uriHash");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(uriHash));
if (string.IsNullOrEmpty(this.BaseIdentifier.Query)) {
// The uriHash will appear on the path itself.
@@ -220,7 +213,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
- protected void ObjectInvariant() {
+ private void ObjectInvariant() {
Contract.Invariant(this.Hasher != null);
Contract.Invariant(this.Encoder != null);
Contract.Invariant(this.BaseIdentifier != null);
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
index 49d18e0..f778b76 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
using System.ComponentModel;
+ using System.Diagnostics.Contracts;
using System.Text;
using System.Web;
using System.Web.UI;
@@ -67,7 +68,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
set {
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
provider = value;
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
index 876e412..d5fa4a9 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
+ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using DotNetOpenAuth.Messaging;
@@ -28,6 +29,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
internal const bool SignOutgoingExtensionsDefault = true;
/// <summary>
+ /// The default value for the <see cref="UnsolicitedAssertionVerification"/> property.
+ /// </summary>
+ internal const UnsolicitedAssertionVerificationLevel UnsolicitedAssertionVerificationDefault = UnsolicitedAssertionVerificationLevel.RequireSuccess;
+
+ /// <summary>
/// The subset of association types and their customized lifetimes.
/// </summary>
private IDictionary<string, TimeSpan> associationLifetimes = new Dictionary<string, TimeSpan>();
@@ -39,6 +45,38 @@ namespace DotNetOpenAuth.OpenId.Provider {
: base(true) {
this.SignOutgoingExtensions = SignOutgoingExtensionsDefault;
this.ProtectDownlevelReplayAttacks = ProtectDownlevelReplayAttacksDefault;
+ this.UnsolicitedAssertionVerification = UnsolicitedAssertionVerificationDefault;
+ }
+
+ /// <summary>
+ /// The behavior a Provider takes when verifying that it is authoritative for an
+ /// identifier it is about to send an unsolicited assertion for.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "By design")]
+ public enum UnsolicitedAssertionVerificationLevel {
+ /// <summary>
+ /// Always verify that the Provider is authoritative for an identifier before
+ /// sending an unsolicited assertion for it and fail if it is not.
+ /// </summary>
+ RequireSuccess,
+
+ /// <summary>
+ /// Always check that the Provider is authoritative for an identifier before
+ /// sending an unsolicited assertion for it, but only log failures, and proceed
+ /// to send the unsolicited assertion.
+ /// </summary>
+ LogWarningOnFailure,
+
+ /// <summary>
+ /// Never verify that the Provider is authoritative for an identifier before
+ /// sending an unsolicited assertion for it.
+ /// </summary>
+ /// <remarks>
+ /// This setting is useful for web servers that refuse to allow a Provider to
+ /// introspectively perform an HTTP GET on itself, when sending unsolicited assertions
+ /// for identifiers that the OP controls.
+ /// </remarks>
+ NeverVerify,
}
/// <summary>
@@ -57,6 +95,13 @@ namespace DotNetOpenAuth.OpenId.Provider {
public bool RequireSsl { get; set; }
/// <summary>
+ /// Gets or sets the level of verification a Provider performs on an identifier before
+ /// sending an unsolicited assertion for it.
+ /// </summary>
+ /// <value>The default value is <see cref="UnsolicitedAssertionVerificationLevel.RequireSuccess"/>.</value>
+ public UnsolicitedAssertionVerificationLevel UnsolicitedAssertionVerification { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether OpenID 1.x relying parties that may not be
/// protecting their users from replay attacks are protected from
/// replay attacks by this provider.
@@ -101,6 +146,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
securitySettings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks;
securitySettings.RequireSsl = this.RequireSsl;
securitySettings.SignOutgoingExtensions = this.SignOutgoingExtensions;
+ securitySettings.UnsolicitedAssertionVerification = this.UnsolicitedAssertionVerification;
return securitySettings;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/Request.cs b/src/DotNetOpenAuth/OpenId/Provider/Request.cs
index 43697b2..1c3eb86 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/Request.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/Request.cs
@@ -54,10 +54,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="request">The incoming request message.</param>
/// <param name="securitySettings">The security settings from the channel.</param>
protected Request(IDirectedProtocolMessage request, ProviderSecuritySettings securitySettings) {
- Contract.Requires(request != null);
- Contract.Requires(securitySettings != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
this.request = request;
this.SecuritySettings = securitySettings;
@@ -71,10 +69,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <param name="version">The version.</param>
/// <param name="securitySettings">The security settings.</param>
protected Request(Version version, ProviderSecuritySettings securitySettings) {
- Contract.Requires(version != null);
- Contract.Requires(securitySettings != null);
- ErrorUtilities.VerifyArgumentNotNull(version, "version");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(version != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
this.protocolVersion = version;
this.SecuritySettings = securitySettings;
@@ -103,10 +99,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <exception cref="InvalidOperationException">Thrown if <see cref="IsResponseReady"/> is <c>false</c>.</exception>
internal IProtocolMessage Response {
get {
- Contract.Requires(this.IsResponseReady);
+ Contract.Requires<InvalidOperationException>(this.IsResponseReady, OpenIdStrings.ResponseNotReady);
Contract.Ensures(Contract.Result<IProtocolMessage>() != null);
- ErrorUtilities.VerifyOperation(this.IsResponseReady, OpenIdStrings.ResponseNotReady);
if (this.responseExtensions.Count > 0) {
var extensibleResponse = this.ResponseMessage as IProtocolMessageWithExtensions;
ErrorUtilities.VerifyOperation(extensibleResponse != null, MessagingStrings.MessageNotExtensible, this.ResponseMessage.GetType().Name);
@@ -161,8 +156,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="extension">The extension to add to the response message.</param>
public void AddResponseExtension(IOpenIdMessageExtension extension) {
- ErrorUtilities.VerifyArgumentNotNull(extension, "extension");
-
// Because the derived AuthenticationRequest class can swap out
// one response message for another (auth vs. no-auth), and because
// some response messages support extensions while others don't,
@@ -194,7 +187,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// An instance of the extension initialized with values passed in with the request.
/// </returns>
public IOpenIdMessageExtension GetExtension(Type extensionType) {
- ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
if (this.extensibleMessage != null) {
return this.extensibleMessage.Extensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).SingleOrDefault();
} else {
diff --git a/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs b/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
index b94b37d..dee140e 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
@@ -39,7 +39,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
protected override IProtocolMessage ResponseMessage {
get {
- Contract.Requires(this.IsResponseReady);
+ Contract.Requires<InvalidOperationException>(this.IsResponseReady);
Contract.Ensures(Contract.Result<IProtocolMessage>() != null);
throw new NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
index 7085e72..f33a655 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
@@ -122,7 +122,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
/// <param name="nonce">A series of random characters.</param>
- /// <param name="timestamp">The timestamp that together with the nonce string make it unique.
+ /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
/// <returns>
/// True if the nonce+timestamp (combination) was not previously in the database.
@@ -135,8 +135,8 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string context, string nonce, DateTime timestamp) {
- return this.nonceStore.StoreNonce(context, nonce, timestamp);
+ public bool StoreNonce(string context, string nonce, DateTime timestampUtc) {
+ return this.nonceStore.StoreNonce(context, nonce, timestampUtc);
}
#endregion
diff --git a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
index 0751f51..bb4a9ba 100644
--- a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
+++ b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
@@ -8,6 +8,8 @@ namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
@@ -19,19 +21,19 @@ namespace DotNetOpenAuth.OpenId {
/// <remarks>
/// This is an immutable type.
/// </remarks>
- [Serializable]
- internal class ProviderEndpointDescription : IProviderEndpoint {
+ internal sealed class ProviderEndpointDescription : IProviderEndpoint {
/// <summary>
/// Initializes a new instance of the <see cref="ProviderEndpointDescription"/> class.
/// </summary>
/// <param name="providerEndpoint">The OpenID Provider endpoint URL.</param>
/// <param name="openIdVersion">The OpenID version supported by this particular endpoint.</param>
internal ProviderEndpointDescription(Uri providerEndpoint, Version openIdVersion) {
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- ErrorUtilities.VerifyArgumentNotNull(openIdVersion, "version");
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
+ Contract.Requires<ArgumentNullException>(openIdVersion != null);
- this.Endpoint = providerEndpoint;
- this.ProtocolVersion = openIdVersion;
+ this.Uri = providerEndpoint;
+ this.Version = openIdVersion;
+ this.Capabilities = new ReadOnlyCollection<string>(EmptyList<string>.Instance);
}
/// <summary>
@@ -40,45 +42,27 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="providerEndpoint">The URI the provider listens on for OpenID requests.</param>
/// <param name="serviceTypeURIs">The set of services offered by this endpoint.</param>
internal ProviderEndpointDescription(Uri providerEndpoint, IEnumerable<string> serviceTypeURIs) {
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- ErrorUtilities.VerifyArgumentNotNull(serviceTypeURIs, "serviceTypeURIs");
+ Contract.Requires<ArgumentNullException>(providerEndpoint != null);
+ Contract.Requires<ArgumentNullException>(serviceTypeURIs != null);
- this.Endpoint = providerEndpoint;
+ this.Uri = providerEndpoint;
this.Capabilities = new ReadOnlyCollection<string>(serviceTypeURIs.ToList());
Protocol opIdentifierProtocol = Protocol.FindBestVersion(p => p.OPIdentifierServiceTypeURI, serviceTypeURIs);
Protocol claimedIdentifierProviderVersion = Protocol.FindBestVersion(p => p.ClaimedIdentifierServiceTypeURI, serviceTypeURIs);
if (opIdentifierProtocol != null) {
- this.ProtocolVersion = opIdentifierProtocol.Version;
+ this.Version = opIdentifierProtocol.Version;
} else if (claimedIdentifierProviderVersion != null) {
- this.ProtocolVersion = claimedIdentifierProviderVersion.Version;
+ this.Version = claimedIdentifierProviderVersion.Version;
+ } else {
+ ErrorUtilities.ThrowProtocol(OpenIdStrings.ProviderVersionUnrecognized, this.Uri);
}
-
- ErrorUtilities.VerifyProtocol(this.ProtocolVersion != null, OpenIdStrings.ProviderVersionUnrecognized, this.Endpoint);
}
- #region IProviderEndpoint Properties
-
- /// <summary>
- /// Gets the detected version of OpenID implemented by the Provider.
- /// </summary>
- Version IProviderEndpoint.Version {
- get { return this.ProtocolVersion; }
- }
-
- /// <summary>
- /// Gets the URL that the OpenID Provider receives authentication requests at.
- /// </summary>
- Uri IProviderEndpoint.Uri {
- get { return this.Endpoint; }
- }
-
- #endregion
-
/// <summary>
/// Gets the URL that the OpenID Provider listens for incoming OpenID messages on.
/// </summary>
- internal Uri Endpoint { get; private set; }
+ public Uri Uri { get; private set; }
/// <summary>
/// Gets the OpenID protocol version this endpoint supports.
@@ -87,14 +71,14 @@ namespace DotNetOpenAuth.OpenId {
/// If an endpoint supports multiple versions, each version must be represented
/// by its own <see cref="ProviderEndpointDescription"/> object.
/// </remarks>
- internal Version ProtocolVersion { get; private set; }
+ public Version Version { get; private set; }
/// <summary>
/// Gets the collection of service type URIs found in the XRDS document describing this Provider.
/// </summary>
internal ReadOnlyCollection<string> Capabilities { get; private set; }
- #region IProviderEndpoint Methods
+ #region IProviderEndpoint Members
/// <summary>
/// Checks whether the OpenId Identifier claims support for a given extension.
@@ -110,9 +94,8 @@ namespace DotNetOpenAuth.OpenId {
/// The only way to be sure of support for a given extension is to include
/// the extension in the request and see if a response comes back for that extension.
/// </remarks>
- public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() {
- T extension = new T();
- return this.IsExtensionSupported(extension);
+ bool IProviderEndpoint.IsExtensionSupported<T>() {
+ throw new NotImplementedException();
}
/// <summary>
@@ -129,51 +112,22 @@ namespace DotNetOpenAuth.OpenId {
/// The only way to be sure of support for a given extension is to include
/// the extension in the request and see if a response comes back for that extension.
/// </remarks>
- public bool IsExtensionSupported(Type extensionType) {
- ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
- ErrorUtilities.VerifyArgument(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType), OpenIdStrings.TypeMustImplementX, typeof(IOpenIdMessageExtension).FullName);
- var extension = (IOpenIdMessageExtension)Activator.CreateInstance(extensionType);
- return this.IsExtensionSupported(extension);
+ bool IProviderEndpoint.IsExtensionSupported(Type extensionType) {
+ throw new NotImplementedException();
}
#endregion
+#if CONTRACTS_FULL
/// <summary>
- /// Determines whether some extension is supported by the Provider.
+ /// Verifies conditions that should be true for any valid state of this object.
/// </summary>
- /// <param name="extensionUri">The extension URI.</param>
- /// <returns>
- /// <c>true</c> if the extension is supported; otherwise, <c>false</c>.
- /// </returns>
- protected internal bool IsExtensionSupported(string extensionUri) {
- ErrorUtilities.VerifyNonZeroLength(extensionUri, "extensionUri");
- ErrorUtilities.VerifyOperation(this.Capabilities != null, OpenIdStrings.ExtensionLookupSupportUnavailable);
- return this.Capabilities.Contains(extensionUri);
- }
-
- /// <summary>
- /// Determines whether a given extension is supported by this endpoint.
- /// </summary>
- /// <param name="extension">An instance of the extension to check support for.</param>
- /// <returns>
- /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
- /// </returns>
- protected internal bool IsExtensionSupported(IOpenIdMessageExtension extension) {
- ErrorUtilities.VerifyArgumentNotNull(extension, "extension");
-
- // Consider the primary case.
- if (this.IsExtensionSupported(extension.TypeUri)) {
- return true;
- }
-
- // Consider the secondary cases.
- if (extension.AdditionalSupportedTypeUris != null) {
- if (extension.AdditionalSupportedTypeUris.Any(typeUri => this.IsExtensionSupported(typeUri))) {
- return true;
- }
- }
-
- return false;
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.Capabilities != null);
}
+#endif
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Realm.cs b/src/DotNetOpenAuth/OpenId/Realm.cs
index 2f0dcb3..818718a 100644
--- a/src/DotNetOpenAuth/OpenId/Realm.cs
+++ b/src/DotNetOpenAuth/OpenId/Realm.cs
@@ -13,6 +13,7 @@ namespace DotNetOpenAuth.OpenId {
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
+ using System.Web;
using System.Xml;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Xrds;
@@ -62,7 +63,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="realmUrl">The realm URL to use in the new instance.</param>
[SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "Not all realms are valid URLs (because of wildcards).")]
public Realm(string realmUrl) {
- ErrorUtilities.VerifyArgumentNotNull(realmUrl, "realmUrl"); // not non-zero check so we throw UriFormatException later
+ Contract.Requires<ArgumentNullException>(realmUrl != null); // not non-zero check so we throw UriFormatException later
this.DomainWildcard = Regex.IsMatch(realmUrl, WildcardDetectionPattern);
this.uri = new Uri(Regex.Replace(realmUrl, WildcardDetectionPattern, m => m.Groups[1].Value));
if (!this.uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) &&
@@ -77,7 +78,7 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="realmUrl">The realm URL of the Relying Party.</param>
public Realm(Uri realmUrl) {
- ErrorUtilities.VerifyArgumentNotNull(realmUrl, "realmUrl");
+ Contract.Requires<ArgumentNullException>(realmUrl != null);
this.uri = realmUrl;
if (!this.uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) &&
!this.uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) {
@@ -98,6 +99,38 @@ namespace DotNetOpenAuth.OpenId {
: this(SafeUriBuilderToString(realmUriBuilder)) { }
/// <summary>
+ /// Gets the suggested realm to use for the calling web application.
+ /// </summary>
+ /// <value>A realm that matches this applications root URL.</value>
+ /// <remarks>
+ /// <para>For most circumstances the Realm generated by this property is sufficient.
+ /// However a wildcard Realm, such as "http://*.microsoft.com/" may at times be more
+ /// desirable than "http://www.microsoft.com/" in order to allow identifier
+ /// correlation across related web sites for directed identity Providers.</para>
+ /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para>
+ /// </remarks>
+ public static Realm AutoDetect {
+ get {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ HttpRequestInfo requestInfo = new HttpRequestInfo(HttpContext.Current.Request);
+ UriBuilder realmUrl = new UriBuilder(requestInfo.UrlBeforeRewriting);
+ realmUrl.Path = HttpContext.Current.Request.ApplicationPath;
+ realmUrl.Query = null;
+ realmUrl.Fragment = null;
+
+ // For RP discovery, the realm url MUST NOT redirect. To prevent this for
+ // virtual directory hosted apps, we need to make sure that the realm path ends
+ // in a slash (since our calculation above guarantees it doesn't end in a specific
+ // page like default.aspx).
+ if (!realmUrl.Path.EndsWith("/", StringComparison.Ordinal)) {
+ realmUrl.Path += "/";
+ }
+
+ return realmUrl.Uri;
+ }
+ }
+
+ /// <summary>
/// Gets a value indicating whether a '*.' prefix to the hostname is
/// used in the realm to allow subdomains or hosts to be added to the URL.
/// </summary>
@@ -381,7 +414,26 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// The details of the endpoints if found; or <c>null</c> if no service document was discovered.
/// </returns>
- internal virtual IEnumerable<RelyingPartyEndpointDescription> Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
+ internal virtual IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
+ XrdsDocument xrds = this.Discover(requestHandler, allowRedirects);
+ if (xrds != null) {
+ return xrds.FindRelyingPartyReceivingEndpoints();
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Searches for an XRDS document at the realm URL.
+ /// </summary>
+ /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param>
+ /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm.
+ /// This may be true when creating an unsolicited assertion, but must be
+ /// false when performing return URL verification per 2.0 spec section 9.2.1.</param>
+ /// <returns>
+ /// The XRDS document if found; or <c>null</c> if no service document was discovered.
+ /// </returns>
+ internal virtual XrdsDocument Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) {
// Attempt YADIS discovery
DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this.UriWithWildcardChangedToWww, false);
if (yadisResult != null) {
@@ -389,8 +441,7 @@ namespace DotNetOpenAuth.OpenId {
ErrorUtilities.VerifyProtocol(allowRedirects || yadisResult.NormalizedUri == yadisResult.RequestUri, OpenIdStrings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri);
if (yadisResult.IsXrds) {
try {
- XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
- return xrds.FindRelyingPartyReceivingEndpoints();
+ return new XrdsDocument(yadisResult.ResponseText);
} catch (XmlException ex) {
throw ErrorUtilities.Wrap(ex, XrdsStrings.InvalidXRDSDocument);
}
@@ -413,7 +464,7 @@ namespace DotNetOpenAuth.OpenId {
/// when we should be throwing an <see cref="ArgumentNullException"/>.
/// </remarks>
private static string SafeUriBuilderToString(UriBuilder realmUriBuilder) {
- ErrorUtilities.VerifyArgumentNotNull(realmUriBuilder, "realmUriBuilder");
+ Contract.Requires<ArgumentNullException>(realmUriBuilder != null);
// Note: we MUST use ToString. Uri property throws if wildcard is present.
// Note that Uri.ToString() should generally be avoided, but UriBuilder.ToString()
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
index 85c0096..7d30a3f 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Text;
@@ -40,8 +41,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="associationStore">The association store. May be null for dumb mode relying parties.</param>
/// <param name="securitySettings">The security settings.</param>
internal AssociationManager(Channel channel, IAssociationStore<Uri> associationStore, RelyingPartySecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires<ArgumentNullException>(channel != null);
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
this.channel = channel;
this.associationStore = associationStore;
@@ -58,7 +59,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.channel = value;
}
}
@@ -72,7 +73,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.securitySettings = value;
}
}
@@ -94,15 +95,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="provider">The provider to create an association with.</param>
/// <returns>The association if one exists and has useful life remaining. Otherwise <c>null</c>.</returns>
- internal Association GetExistingAssociation(ProviderEndpointDescription provider) {
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ internal Association GetExistingAssociation(IProviderEndpoint provider) {
+ Contract.Requires<ArgumentNullException>(provider != null);
// If the RP has no application store for associations, there's no point in creating one.
if (this.associationStore == null) {
return null;
}
- Association association = this.associationStore.GetAssociation(provider.Endpoint, this.SecuritySettings);
+ Association association = this.associationStore.GetAssociation(provider.Uri, this.SecuritySettings);
// If the returned association does not fulfill security requirements, ignore it.
if (association != null && !this.SecuritySettings.IsAssociationInPermittedRange(association)) {
@@ -122,7 +123,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="provider">The provider to get an association for.</param>
/// <returns>The existing or new association; <c>null</c> if none existed and one could not be created.</returns>
- internal Association GetOrCreateAssociation(ProviderEndpointDescription provider) {
+ internal Association GetOrCreateAssociation(IProviderEndpoint provider) {
return this.GetExistingAssociation(provider) ?? this.CreateNewAssociation(provider);
}
@@ -139,8 +140,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// association store.
/// Any new association is automatically added to the <see cref="associationStore"/>.
/// </remarks>
- private Association CreateNewAssociation(ProviderEndpointDescription provider) {
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ private Association CreateNewAssociation(IProviderEndpoint provider) {
+ Contract.Requires<ArgumentNullException>(provider != null);
// If there is no association store, there is no point in creating an association.
if (this.associationStore == null) {
@@ -163,8 +164,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The newly created association, or null if no association can be created with
/// the given Provider given the current security settings.
/// </returns>
- private Association CreateNewAssociation(ProviderEndpointDescription provider, AssociateRequest associateRequest, int retriesRemaining) {
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ private Association CreateNewAssociation(IProviderEndpoint provider, AssociateRequest associateRequest, int retriesRemaining) {
+ Contract.Requires<ArgumentNullException>(provider != null);
if (associateRequest == null || retriesRemaining < 0) {
// this can happen if security requirements and protocol conflict
@@ -178,7 +179,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse;
if (associateSuccessfulResponse != null) {
Association association = associateSuccessfulResponse.CreateAssociation(associateRequest, null);
- this.associationStore.StoreAssociation(provider.Endpoint, association);
+ this.associationStore.StoreAssociation(provider.Uri, association);
return association;
} else if (associateUnsuccessfulResponse != null) {
if (string.IsNullOrEmpty(associateUnsuccessfulResponse.AssociationType)) {
@@ -186,7 +187,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return null;
}
- if (!this.securitySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.ProtocolVersion), associateUnsuccessfulResponse.AssociationType)) {
+ if (!this.securitySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.Version), associateUnsuccessfulResponse.AssociationType)) {
Logger.OpenId.DebugFormat("Provider rejected an association request and suggested '{0}' as an association to try, which this Relying Party does not support. Giving up.", associateUnsuccessfulResponse.AssociationType);
return null;
}
@@ -197,7 +198,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
// Make sure the Provider isn't suggesting an incompatible pair of association/session types.
- Protocol protocol = Protocol.Lookup(provider.ProtocolVersion);
+ Protocol protocol = Protocol.Lookup(provider.Version);
ErrorUtilities.VerifyProtocol(
HmacShaAssociation.IsDHSessionCompatible(protocol, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType),
OpenIdStrings.IncompatibleAssociationAndSessionTypes,
@@ -219,7 +220,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// Since having associations with OPs is not totally critical, we'll log and eat
// the exception so that auth may continue in dumb mode.
- Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Endpoint, ex);
+ Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Uri, ex);
return null;
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index 9912d0b..e90c1d3 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -32,12 +32,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private readonly OpenIdRelyingParty RelyingParty;
/// <summary>
- /// The endpoint that describes the particular OpenID Identifier and Provider that
- /// will be used to create the authentication request.
- /// </summary>
- private readonly ServiceEndpoint endpoint;
-
- /// <summary>
/// How an association may or should be created or used in the formulation of the
/// authentication request.
/// </summary>
@@ -55,19 +49,29 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private Dictionary<string, string> returnToArgs = new Dictionary<string, string>();
/// <summary>
+ /// A value indicating whether the return_to callback arguments must be signed.
+ /// </summary>
+ /// <remarks>
+ /// This field defaults to false, but is set to true as soon as the first callback argument
+ /// is added that indicates it must be signed. At which point, all arguments are signed
+ /// even if individual ones did not need to be.
+ /// </remarks>
+ private bool returnToArgsMustBeSigned;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="AuthenticationRequest"/> class.
/// </summary>
- /// <param name="endpoint">The endpoint that describes the OpenID Identifier and Provider that will complete the authentication.</param>
+ /// <param name="discoveryResult">The endpoint that describes the OpenID Identifier and Provider that will complete the authentication.</param>
/// <param name="realm">The realm, or root URL, of the host web site.</param>
/// <param name="returnToUrl">The base return_to URL that the Provider should return the user to to complete authentication. This should not include callback parameters as these should be added using the <see cref="AddCallbackArguments(string, string)"/> method.</param>
/// <param name="relyingParty">The relying party that created this instance.</param>
- private AuthenticationRequest(ServiceEndpoint endpoint, Realm realm, Uri returnToUrl, OpenIdRelyingParty relyingParty) {
- ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint");
- ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
- ErrorUtilities.VerifyArgumentNotNull(returnToUrl, "returnToUrl");
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
+ private AuthenticationRequest(IdentifierDiscoveryResult discoveryResult, Realm realm, Uri returnToUrl, OpenIdRelyingParty relyingParty) {
+ Contract.Requires<ArgumentNullException>(discoveryResult != null);
+ Contract.Requires<ArgumentNullException>(realm != null);
+ Contract.Requires<ArgumentNullException>(returnToUrl != null);
+ Contract.Requires<ArgumentNullException>(relyingParty != null);
- this.endpoint = endpoint;
+ this.DiscoveryResult = discoveryResult;
this.RelyingParty = relyingParty;
this.Realm = realm;
this.ReturnToUrl = returnToUrl;
@@ -127,7 +131,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// property for a null value.
/// </remarks>
public Identifier ClaimedIdentifier {
- get { return this.IsDirectedIdentity ? null : this.endpoint.ClaimedIdentifier; }
+ get { return this.IsDirectedIdentity ? null : this.DiscoveryResult.ClaimedIdentifier; }
}
/// <summary>
@@ -135,7 +139,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// determine and send the ClaimedIdentifier after authentication.
/// </summary>
public bool IsDirectedIdentity {
- get { return this.endpoint.ClaimedIdentifier == this.endpoint.Protocol.ClaimedIdentifierForOPIdentifier; }
+ get { return this.DiscoveryResult.ClaimedIdentifier == this.DiscoveryResult.Protocol.ClaimedIdentifierForOPIdentifier; }
}
/// <summary>
@@ -154,9 +158,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// location.
/// </summary>
public IProviderEndpoint Provider {
- get { return this.endpoint; }
+ get { return this.DiscoveryResult; }
}
+ /// <summary>
+ /// Gets the discovery result leading to the formulation of this request.
+ /// </summary>
+ /// <value>The discovery result.</value>
+ public IdentifierDiscoveryResult DiscoveryResult { get; private set; }
+
#endregion
/// <summary>
@@ -189,18 +199,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="arguments">The arguments to add to the request's return_to URI.</param>
/// <remarks>
- /// <para>Note that these values are NOT protected against tampering in transit. No
- /// security-sensitive data should be stored using this method.</para>
+ /// <para>Note that these values are NOT protected against eavesdropping in transit. No
+ /// privacy-sensitive data should be stored using this method.</para>
/// <para>The values stored here can be retrieved using
- /// <see cref="IAuthenticationResponse.GetCallbackArguments"/>.</para>
+ /// <see cref="IAuthenticationResponse.GetCallbackArguments"/>, which will only return the value
+ /// if it hasn't been tampered with in transit.</para>
/// <para>Since the data set here is sent in the querystring of the request and some
/// servers place limits on the size of a request URL, this data should be kept relatively
/// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
/// </remarks>
public void AddCallbackArguments(IDictionary<string, string> arguments) {
- ErrorUtilities.VerifyArgumentNotNull(arguments, "arguments");
ErrorUtilities.VerifyOperation(this.RelyingParty.CanSignCallbackArguments, OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name);
+ this.returnToArgsMustBeSigned = true;
foreach (var pair in arguments) {
ErrorUtilities.VerifyArgument(!string.IsNullOrEmpty(pair.Key), MessagingStrings.UnexpectedNullOrEmptyKey);
ErrorUtilities.VerifyArgument(pair.Value != null, MessagingStrings.UnexpectedNullValue, pair.Key);
@@ -215,6 +226,28 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="key">The parameter name.</param>
/// <param name="value">The value of the argument.</param>
/// <remarks>
+ /// <para>Note that these values are NOT protected against eavesdropping in transit. No
+ /// privacy-sensitive data should be stored using this method.</para>
+ /// <para>The value stored here can be retrieved using
+ /// <see cref="IAuthenticationResponse.GetCallbackArgument"/>, which will only return the value
+ /// if it hasn't been tampered with in transit.</para>
+ /// <para>Since the data set here is sent in the querystring of the request and some
+ /// servers place limits on the size of a request URL, this data should be kept relatively
+ /// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
+ /// </remarks>
+ public void AddCallbackArguments(string key, string value) {
+ ErrorUtilities.VerifyOperation(this.RelyingParty.CanSignCallbackArguments, OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name);
+
+ this.returnToArgsMustBeSigned = true;
+ this.returnToArgs.Add(key, value);
+ }
+
+ /// <summary>
+ /// Makes a key/value pair available when the authentication is completed.
+ /// </summary>
+ /// <param name="key">The parameter name.</param>
+ /// <param name="value">The value of the argument. Must not be null.</param>
+ /// <remarks>
/// <para>Note that these values are NOT protected against tampering in transit. No
/// security-sensitive data should be stored using this method.</para>
/// <para>The value stored here can be retrieved using
@@ -223,12 +256,30 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// servers place limits on the size of a request URL, this data should be kept relatively
/// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
/// </remarks>
- public void AddCallbackArguments(string key, string value) {
- ErrorUtilities.VerifyNonZeroLength(key, "key");
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ public void SetCallbackArgument(string key, string value) {
ErrorUtilities.VerifyOperation(this.RelyingParty.CanSignCallbackArguments, OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name);
- this.returnToArgs.Add(key, value);
+ this.returnToArgsMustBeSigned = true;
+ this.returnToArgs[key] = value;
+ }
+
+ /// <summary>
+ /// Makes a key/value pair available when the authentication is completed without
+ /// requiring a return_to signature to protect against tampering of the callback argument.
+ /// </summary>
+ /// <param name="key">The parameter name.</param>
+ /// <param name="value">The value of the argument. Must not be null.</param>
+ /// <remarks>
+ /// <para>Note that these values are NOT protected against eavesdropping or tampering in transit. No
+ /// security-sensitive data should be stored using this method. </para>
+ /// <para>The value stored here can be retrieved using
+ /// <see cref="IAuthenticationResponse.GetCallbackArgument"/>.</para>
+ /// <para>Since the data set here is sent in the querystring of the request and some
+ /// servers place limits on the size of a request URL, this data should be kept relatively
+ /// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
+ /// </remarks>
+ public void SetUntrustedCallbackArgument(string key, string value) {
+ this.returnToArgs[key] = value;
}
/// <summary>
@@ -236,7 +287,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="extension">The initialized extension to add to the request.</param>
public void AddExtension(IOpenIdMessageExtension extension) {
- ErrorUtilities.VerifyArgumentNotNull(extension, "extension");
this.extensions.Add(extension);
}
@@ -267,16 +317,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Never null, but may be empty.
/// </returns>
internal static IEnumerable<AuthenticationRequest> Create(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded) {
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Requires(relyingParty != null);
- Contract.Requires(realm != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(relyingParty != null);
+ Contract.Requires<ArgumentNullException>(realm != null);
Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);
- // We have a long data validation and preparation process
- ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
- ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
-
// Normalize the portion of the return_to path that correlates to the realm for capitalization.
// (so that if a web app base path is /MyApp/, but the URL of this request happens to be
// /myapp/login.aspx, we bump up the return_to Url to use /MyApp/ so it matches the realm.
@@ -312,12 +357,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
ErrorUtilities.VerifyProtocol(realm.Contains(returnToUrl), OpenIdStrings.ReturnToNotUnderRealm, returnToUrl, realm);
// Perform discovery right now (not deferred).
- IEnumerable<ServiceEndpoint> serviceEndpoints;
+ IEnumerable<IdentifierDiscoveryResult> serviceEndpoints;
try {
- serviceEndpoints = userSuppliedIdentifier.Discover(relyingParty.WebRequestHandler);
+ var results = relyingParty.Discover(userSuppliedIdentifier).CacheGeneratedResults();
+
+ // If any OP Identifier service elements were found, we must not proceed
+ // to use any Claimed Identifier services, per OpenID 2.0 sections 7.3.2.2 and 11.2.
+ // For a discussion on this topic, see
+ // http://groups.google.com/group/dotnetopenid/browse_thread/thread/4b5a8c6b2210f387/5e25910e4d2252c8
+ // Usually the Discover method we called will automatically filter this for us, but
+ // just to be sure, we'll do it here as well since the RP may be configured to allow
+ // these dual identifiers for assertion verification purposes.
+ var opIdentifiers = results.Where(result => result.ClaimedIdentifier == result.Protocol.ClaimedIdentifierForOPIdentifier).CacheGeneratedResults();
+ var claimedIdentifiers = results.Where(result => result.ClaimedIdentifier != result.Protocol.ClaimedIdentifierForOPIdentifier);
+ serviceEndpoints = opIdentifiers.Any() ? opIdentifiers : claimedIdentifiers;
} catch (ProtocolException ex) {
Logger.Yadis.ErrorFormat("Error while performing discovery on: \"{0}\": {1}", userSuppliedIdentifier, ex);
- serviceEndpoints = EmptyList<ServiceEndpoint>.Instance;
+ serviceEndpoints = Enumerable.Empty<IdentifierDiscoveryResult>();
}
// Filter disallowed endpoints.
@@ -328,6 +384,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Creates an instance of <see cref="AuthenticationRequest"/> FOR TESTING PURPOSES ONLY.
+ /// </summary>
+ /// <param name="discoveryResult">The discovery result.</param>
+ /// <param name="realm">The realm.</param>
+ /// <param name="returnTo">The return to.</param>
+ /// <param name="rp">The relying party.</param>
+ /// <returns>The instantiated <see cref="AuthenticationRequest"/>.</returns>
+ internal static AuthenticationRequest CreateForTest(IdentifierDiscoveryResult discoveryResult, Realm realm, Uri returnTo, OpenIdRelyingParty rp) {
+ return new AuthenticationRequest(discoveryResult, realm, returnTo, rp);
+ }
+
+ /// <summary>
/// Performs deferred request generation for the <see cref="Create"/> method.
/// </summary>
/// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
@@ -344,26 +412,27 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// All data validation and cleansing steps must have ALREADY taken place
/// before calling this method.
/// </remarks>
- private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<ServiceEndpoint> serviceEndpoints, bool createNewAssociationsAsNeeded) {
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Requires(relyingParty != null);
- Contract.Requires(realm != null);
- Contract.Requires(serviceEndpoints != null);
- Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);
+ private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<IdentifierDiscoveryResult> serviceEndpoints, bool createNewAssociationsAsNeeded) {
+ // DO NOT USE CODE CONTRACTS IN THIS METHOD, since it uses yield return
+ ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
+ ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
+ ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
+ ErrorUtilities.VerifyArgumentNotNull(serviceEndpoints, "serviceEndpoints");
+ ////Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);
// If shared associations are required, then we had better have an association store.
ErrorUtilities.VerifyOperation(!relyingParty.SecuritySettings.RequireAssociation || relyingParty.AssociationManager.HasAssociationStore, OpenIdStrings.AssociationStoreRequired);
Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier);
- IEnumerable<ServiceEndpoint> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);
+ IEnumerable<IdentifierDiscoveryResult> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);
// Maintain a list of endpoints that we could not form an association with.
// We'll fallback to generating requests to these if the ones we CAN create
// an association with run out.
- var failedAssociationEndpoints = new List<ServiceEndpoint>(0);
+ var failedAssociationEndpoints = new List<IdentifierDiscoveryResult>(0);
foreach (var endpoint in endpoints) {
- Logger.OpenId.InfoFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);
+ Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);
// The strategy here is to prefer endpoints with whom we can create associations.
Association association = null;
@@ -371,7 +440,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// In some scenarios (like the AJAX control wanting ALL auth requests possible),
// we don't want to create associations with every Provider. But we'll use
// associations where they are already formed from previous authentications.
- association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint.ProviderDescription) : relyingParty.AssociationManager.GetExistingAssociation(endpoint.ProviderDescription);
+ association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint) : relyingParty.AssociationManager.GetExistingAssociation(endpoint);
if (association == null && createNewAssociationsAsNeeded) {
Logger.OpenId.WarnFormat("Failed to create association with {0}. Skipping to next endpoint.", endpoint.ProviderEndpoint);
@@ -393,10 +462,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
if (relyingParty.SecuritySettings.RequireAssociation) {
Logger.OpenId.Warn("Associations could not be formed with some Providers. Security settings require shared associations for authentication requests so these will be skipped.");
} else {
- Logger.OpenId.WarnFormat("Now generating requests for Provider endpoints that failed initial association attempts.");
+ Logger.OpenId.Debug("Now generating requests for Provider endpoints that failed initial association attempts.");
foreach (var endpoint in failedAssociationEndpoints) {
- Logger.OpenId.WarnFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);
+ Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0} at endpoint: {1}", userSuppliedIdentifier, endpoint.ProviderEndpoint.AbsoluteUri);
// Create the auth request, but prevent it from attempting to create an association
// because we've already tried. Let's not have it waste time trying again.
@@ -414,17 +483,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="endpoints">The endpoints.</param>
/// <param name="relyingParty">The relying party.</param>
/// <returns>A filtered and sorted list of endpoints; may be empty if the input was empty or the filter removed all endpoints.</returns>
- private static List<ServiceEndpoint> FilterAndSortEndpoints(IEnumerable<ServiceEndpoint> endpoints, OpenIdRelyingParty relyingParty) {
- ErrorUtilities.VerifyArgumentNotNull(endpoints, "endpoints");
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
+ private static List<IdentifierDiscoveryResult> FilterAndSortEndpoints(IEnumerable<IdentifierDiscoveryResult> endpoints, OpenIdRelyingParty relyingParty) {
+ Contract.Requires<ArgumentNullException>(endpoints != null);
+ Contract.Requires<ArgumentNullException>(relyingParty != null);
// Construct the endpoints filters based on criteria given by the host web site.
- EndpointSelector versionFilter = ep => ((ServiceEndpoint)ep).Protocol.Version >= Protocol.Lookup(relyingParty.SecuritySettings.MinimumRequiredOpenIdVersion).Version;
+ EndpointSelector versionFilter = ep => ep.Version >= Protocol.Lookup(relyingParty.SecuritySettings.MinimumRequiredOpenIdVersion).Version;
EndpointSelector hostingSiteFilter = relyingParty.EndpointFilter ?? (ep => true);
bool anyFilteredOut = false;
- var filteredEndpoints = new List<IXrdsProviderEndpoint>();
- foreach (ServiceEndpoint endpoint in endpoints) {
+ var filteredEndpoints = new List<IdentifierDiscoveryResult>();
+ foreach (var endpoint in endpoints) {
if (versionFilter(endpoint) && hostingSiteFilter(endpoint)) {
filteredEndpoints.Add(endpoint);
} else {
@@ -433,10 +502,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
// Sort endpoints so that the first one in the list is the most preferred one.
- filteredEndpoints.Sort(relyingParty.EndpointOrder);
+ filteredEndpoints.OrderBy(ep => ep, relyingParty.EndpointOrder);
- List<ServiceEndpoint> endpointList = new List<ServiceEndpoint>(filteredEndpoints.Count);
- foreach (ServiceEndpoint endpoint in filteredEndpoints) {
+ var endpointList = new List<IdentifierDiscoveryResult>(filteredEndpoints.Count);
+ foreach (var endpoint in filteredEndpoints) {
endpointList.Add(endpoint);
}
@@ -465,19 +534,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
SignedResponseRequest request;
if (!this.IsExtensionOnly) {
- CheckIdRequest authRequest = new CheckIdRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode);
- authRequest.ClaimedIdentifier = this.endpoint.ClaimedIdentifier;
- authRequest.LocalIdentifier = this.endpoint.ProviderLocalIdentifier;
+ CheckIdRequest authRequest = new CheckIdRequest(this.DiscoveryResult.Version, this.DiscoveryResult.ProviderEndpoint, this.Mode);
+ authRequest.ClaimedIdentifier = this.DiscoveryResult.ClaimedIdentifier;
+ authRequest.LocalIdentifier = this.DiscoveryResult.ProviderLocalIdentifier;
request = authRequest;
} else {
- request = new SignedResponseRequest(this.endpoint.Protocol.Version, this.endpoint.ProviderEndpoint, this.Mode);
+ request = new SignedResponseRequest(this.DiscoveryResult.Version, this.DiscoveryResult.ProviderEndpoint, this.Mode);
}
request.Realm = this.Realm;
request.ReturnTo = this.ReturnToUrl;
request.AssociationHandle = association != null ? association.Handle : null;
+ request.SignReturnTo = this.returnToArgsMustBeSigned;
request.AddReturnToArguments(this.returnToArgs);
- if (this.endpoint.UserSuppliedIdentifier != null) {
- request.AddReturnToArguments(UserSuppliedIdentifierParameterName, this.endpoint.UserSuppliedIdentifier);
+ if (this.DiscoveryResult.UserSuppliedIdentifier != null) {
+ request.AddReturnToArguments(UserSuppliedIdentifierParameterName, this.DiscoveryResult.UserSuppliedIdentifier.OriginalString);
}
foreach (IOpenIdMessageExtension extension in this.extensions) {
request.Extensions.Add(extension);
@@ -494,7 +564,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Association association = null;
switch (this.associationPreference) {
case AssociationPreference.IfPossible:
- association = this.RelyingParty.AssociationManager.GetOrCreateAssociation(this.endpoint.ProviderDescription);
+ association = this.RelyingParty.AssociationManager.GetOrCreateAssociation(this.DiscoveryResult);
if (association == null) {
// Avoid trying to create the association again if the redirecting response
// is generated again.
@@ -502,7 +572,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
break;
case AssociationPreference.IfAlreadyEstablished:
- association = this.RelyingParty.AssociationManager.GetExistingAssociation(this.endpoint.ProviderDescription);
+ association = this.RelyingParty.AssociationManager.GetExistingAssociation(this.DiscoveryResult);
break;
case AssociationPreference.Never:
break;
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/Controls.cd b/src/DotNetOpenAuth/OpenId/RelyingParty/Controls.cd
new file mode 100644
index 0000000..f96db36
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/Controls.cd
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram MajorVersion="1" MinorVersion="1">
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyAjaxControlBase">
+ <Position X="0.5" Y="9.75" Width="3" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <TypeIdentifier>
+ <HashCode>BARAAAAAAAAAAACQAAAAAAAEAAAgAAAAAQAFAAAAAFk=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdMobileTextBox">
+ <Position X="8.5" Y="5.25" Width="2.5" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <TypeIdentifier>
+ <HashCode>AI0JADgFQRQQDAIw4lAYSEIWCAMZhMVlELAASQIAgSI=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdMobileTextBox.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdLogin">
+ <Position X="6.25" Y="1.25" Width="1.75" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <NestedTypes>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdLogin.InPlaceControl" Collapsed="true">
+ <TypeIdentifier>
+ <NewMemberFileName>OpenId\RelyingParty\OpenIdLogin.cs</NewMemberFileName>
+ </TypeIdentifier>
+ </Class>
+ </NestedTypes>
+ <TypeIdentifier>
+ <HashCode>gIMgADAIAQEQIJAYOQBSADiQBgiIECk0jQCggdAp4BQ=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdLogin.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox">
+ <Position X="3.75" Y="10" Width="2.25" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <TypeIdentifier>
+ <HashCode>ACBEEbABZzOzAKCYJNOEwM3uSIR5AAOkUFANCQ7DsVs=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdAjaxTextBox.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdButton">
+ <Position X="8.75" Y="1" Width="1.75" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <InheritanceLine Type="DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyControlBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="2.875" Y="0.5" />
+ <Point X="7.194" Y="0.5" />
+ <Point X="7.194" Y="1" />
+ <Point X="8.75" Y="1" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>BAAEQAAAAAAAAAACAAAgAAAAAIAAAACQABAECABAAAA=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdButton.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdTextBox">
+ <Position X="3.5" Y="1.25" Width="2.25" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <TypeIdentifier>
+ <HashCode>AIEVQjgBIxYITIARcAAACEc2CIAIlER1CBAQSQoEpCg=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdTextBox.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyControlBase">
+ <Position X="0.5" Y="0.5" Width="2.5" />
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <NestedTypes>
+ <Enum Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyControlBase.LoginSiteNotification" Collapsed="true">
+ <TypeIdentifier>
+ <NewMemberFileName>OpenId\RelyingParty\OpenIdRelyingPartyControlBase.cs</NewMemberFileName>
+ </TypeIdentifier>
+ </Enum>
+ <Enum Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyControlBase.LoginPersistence" Collapsed="true">
+ <TypeIdentifier>
+ <NewMemberFileName>OpenId\RelyingParty\OpenIdRelyingPartyControlBase.cs</NewMemberFileName>
+ </TypeIdentifier>
+ </Enum>
+ <Class Name="DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyControlBase.DuplicateRequestedHostsComparer" Collapsed="true">
+ <TypeIdentifier>
+ <NewMemberFileName>OpenId\RelyingParty\OpenIdRelyingPartyControlBase.cs</NewMemberFileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ </NestedTypes>
+ <TypeIdentifier>
+ <HashCode>BA0AAsAAQCAwQAJAoFAWwADSAgE5EIEEEbAGSAwAgfI=</HashCode>
+ <FileName>OpenId\RelyingParty\OpenIdRelyingPartyControlBase.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Font Name="Segoe UI" Size="9" />
+</ClassDiagram> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
index d94af14..682e3ff 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using System.Web;
@@ -27,9 +28,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="exception">The exception that resulted in the failed authentication.</param>
internal FailedAuthenticationResponse(Exception exception) {
- ErrorUtilities.VerifyArgumentNotNull(exception, "exception");
+ Contract.Requires<ArgumentNullException>(exception != null);
this.Exception = exception;
+
+ string category = string.Empty;
+ if (Reporting.Enabled) {
+ var pe = exception as ProtocolException;
+ if (pe != null) {
+ var responseMessage = pe.FaultedMessage as IndirectSignedResponse;
+ if (responseMessage != null && responseMessage.ProviderEndpoint != null) { // check "required" parts because this is a failure after all
+ category = responseMessage.ProviderEndpoint.AbsoluteUri;
+ }
+ }
+
+ Reporting.RecordEventOccurrence(this, category);
+ }
}
#region IAuthenticationResponse Members
@@ -128,7 +142,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <para>Note that these values are NOT protected against tampering in transit.</para>
/// </remarks>
public IDictionary<string, string> GetCallbackArguments() {
- return new Dictionary<string, string>();
+ return EmptyDictionary<string, string>.Instance;
+ }
+
+ /// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public IDictionary<string, string> GetUntrustedCallbackArguments() {
+ return EmptyDictionary<string, string>.Instance;
}
/// <summary>
@@ -150,6 +180,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public string GetUntrustedCallbackArgument(string key) {
+ return null;
+ }
+
+ /// <summary>
/// Tries to get an OpenID extension that may be present in the response.
/// </summary>
/// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
index 8414031..65db0bd 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
@@ -7,6 +7,8 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
@@ -16,6 +18,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// requests that may be queried/modified in specific ways before being
/// routed to the OpenID Provider.
/// </summary>
+ [ContractClass(typeof(IAuthenticationRequestContract))]
public interface IAuthenticationRequest {
/// <summary>
/// Gets or sets the mode the Provider should use during authentication.
@@ -94,14 +97,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
IProviderEndpoint Provider { get; }
/// <summary>
+ /// Gets the discovery result leading to the formulation of this request.
+ /// </summary>
+ /// <value>The discovery result.</value>
+ IdentifierDiscoveryResult DiscoveryResult { get; }
+
+ /// <summary>
/// Makes a dictionary of key/value pairs available when the authentication is completed.
/// </summary>
/// <param name="arguments">The arguments to add to the request's return_to URI. Values must not be null.</param>
/// <remarks>
- /// <para>Note that these values are NOT protected against tampering in transit. No
- /// security-sensitive data should be stored using this method.</para>
+ /// <para>Note that these values are NOT protected against eavesdropping in transit. No
+ /// privacy-sensitive data should be stored using this method.</para>
/// <para>The values stored here can be retrieved using
- /// <see cref="IAuthenticationResponse.GetCallbackArguments"/>.</para>
+ /// <see cref="IAuthenticationResponse.GetCallbackArgument"/>, which will only return the value
+ /// if it can be verified as untampered with in transit.</para>
/// <para>Since the data set here is sent in the querystring of the request and some
/// servers place limits on the size of a request URL, this data should be kept relatively
/// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
@@ -114,7 +124,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="key">The parameter name.</param>
/// <param name="value">The value of the argument. Must not be null.</param>
/// <remarks>
- /// <para>Note that these values are NOT protected against tampering in transit. No
+ /// <para>Note that these values are NOT protected against eavesdropping in transit. No
+ /// privacy-sensitive data should be stored using this method.</para>
+ /// <para>The value stored here can be retrieved using
+ /// <see cref="IAuthenticationResponse.GetCallbackArgument"/>, which will only return the value
+ /// if it can be verified as untampered with in transit.</para>
+ /// <para>Since the data set here is sent in the querystring of the request and some
+ /// servers place limits on the size of a request URL, this data should be kept relatively
+ /// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
+ /// </remarks>
+ void AddCallbackArguments(string key, string value);
+
+ /// <summary>
+ /// Makes a key/value pair available when the authentication is completed.
+ /// </summary>
+ /// <param name="key">The parameter name.</param>
+ /// <param name="value">The value of the argument. Must not be null.</param>
+ /// <remarks>
+ /// <para>Note that these values are NOT protected against eavesdropping in transit. No
/// security-sensitive data should be stored using this method.</para>
/// <para>The value stored here can be retrieved using
/// <see cref="IAuthenticationResponse.GetCallbackArgument"/>.</para>
@@ -122,7 +149,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// servers place limits on the size of a request URL, this data should be kept relatively
/// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
/// </remarks>
- void AddCallbackArguments(string key, string value);
+ void SetCallbackArgument(string key, string value);
+
+ /// <summary>
+ /// Makes a key/value pair available when the authentication is completed without
+ /// requiring a return_to signature to protect against tampering of the callback argument.
+ /// </summary>
+ /// <param name="key">The parameter name.</param>
+ /// <param name="value">The value of the argument. Must not be null.</param>
+ /// <remarks>
+ /// <para>Note that these values are NOT protected against eavesdropping or tampering in transit. No
+ /// security-sensitive data should be stored using this method. </para>
+ /// <para>The value stored here can be retrieved using
+ /// <see cref="IAuthenticationResponse.GetCallbackArgument"/>.</para>
+ /// <para>Since the data set here is sent in the querystring of the request and some
+ /// servers place limits on the size of a request URL, this data should be kept relatively
+ /// small to ensure successful authentication. About 1.5KB is about all that should be stored.</para>
+ /// </remarks>
+ void SetUntrustedCallbackArgument(string key, string value);
/// <summary>
/// Adds an OpenID extension to the request directed at the OpenID provider.
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs
new file mode 100644
index 0000000..cd36cc7
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequestContract.cs
@@ -0,0 +1,111 @@
+// <auto-generated />
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ [ContractClassFor(typeof(IAuthenticationRequest))]
+ internal abstract class IAuthenticationRequestContract : IAuthenticationRequest {
+ #region IAuthenticationRequest Members
+
+ AuthenticationRequestMode IAuthenticationRequest.Mode {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ OutgoingWebResponse IAuthenticationRequest.RedirectingResponse {
+ get { throw new NotImplementedException(); }
+ }
+
+ Uri IAuthenticationRequest.ReturnToUrl {
+ get { throw new NotImplementedException(); }
+ }
+
+ Realm IAuthenticationRequest.Realm {
+ get {
+ Contract.Ensures(Contract.Result<Realm>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ Identifier IAuthenticationRequest.ClaimedIdentifier {
+ get {
+ throw new NotImplementedException();
+ }
+ }
+
+ bool IAuthenticationRequest.IsDirectedIdentity {
+ get { throw new NotImplementedException(); }
+ }
+
+ bool IAuthenticationRequest.IsExtensionOnly {
+ get {
+ throw new NotImplementedException();
+ }
+
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ IProviderEndpoint IAuthenticationRequest.Provider {
+ get {
+ Contract.Ensures(Contract.Result<IProviderEndpoint>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ IdentifierDiscoveryResult IAuthenticationRequest.DiscoveryResult {
+ get {
+ Contract.Ensures(Contract.Result<IdentifierDiscoveryResult>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ void IAuthenticationRequest.AddCallbackArguments(IDictionary<string, string> arguments) {
+ Contract.Requires<ArgumentNullException>(arguments != null);
+ Contract.Requires<ArgumentException>(arguments.Keys.All(k => !String.IsNullOrEmpty(k)));
+ Contract.Requires<ArgumentException>(arguments.Values.All(v => v != null));
+ throw new NotImplementedException();
+ }
+
+ void IAuthenticationRequest.AddCallbackArguments(string key, string value) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(key));
+ Contract.Requires<ArgumentNullException>(value != null);
+ throw new NotImplementedException();
+ }
+
+ void IAuthenticationRequest.SetCallbackArgument(string key, string value) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(key));
+ Contract.Requires<ArgumentNullException>(value != null);
+ throw new NotImplementedException();
+ }
+
+ void IAuthenticationRequest.AddExtension(IOpenIdMessageExtension extension) {
+ Contract.Requires<ArgumentNullException>(extension != null);
+ throw new NotImplementedException();
+ }
+
+ void IAuthenticationRequest.RedirectToProvider() {
+ throw new NotImplementedException();
+ }
+
+ void IAuthenticationRequest.SetUntrustedCallbackArgument(string key, string value) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(key));
+ Contract.Requires<ArgumentNullException>(value != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
index cc94de0..a24220f 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
@@ -8,6 +8,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
using System.Text;
using System.Web;
using DotNetOpenAuth.OpenId.Extensions;
@@ -24,6 +26,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// assertions. This interface does not offer a way to discern between
/// solicited and unsolicited assertions as they should be treated equally.
/// </remarks>
+ [ContractClass(typeof(IAuthenticationResponseContract))]
public interface IAuthenticationResponse {
/// <summary>
/// Gets the Identifier that the end user claims to own. For use with user database storage and lookup.
@@ -104,27 +107,60 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The value of the argument, or null if the named parameter could not be found.
/// </returns>
/// <remarks>
- /// <para>This may return any argument on the querystring that came with the authentication response,
- /// which may include parameters not explicitly added using
- /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.</para>
- /// <para>Note that these values are NOT protected against tampering in transit.</para>
+ /// Callback parameters are only available if they are complete and untampered with
+ /// since the original request message (as proven by a signature).
+ /// If the relying party is operating in stateless mode <c>null</c> is always
+ /// returned since the callback arguments could not be signed to protect against
+ /// tampering.
/// </remarks>
string GetCallbackArgument(string key);
/// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ string GetUntrustedCallbackArgument(string key);
+
+ /// <summary>
/// Gets all the callback arguments that were previously added using
/// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
/// of the return_to URL.
/// </summary>
/// <returns>A name-value dictionary. Never null.</returns>
/// <remarks>
- /// <para>This MAY return any argument on the querystring that came with the authentication response,
- /// which may include parameters not explicitly added using
- /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.</para>
- /// <para>Note that these values are NOT protected against tampering in transit.</para>
+ /// Callback parameters are only available if they are complete and untampered with
+ /// since the original request message (as proven by a signature).
+ /// If the relying party is operating in stateless mode an empty dictionary is always
+ /// returned since the callback arguments could not be signed to protect against
+ /// tampering.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Historically an expensive operation.")]
+ IDictionary<string, string> GetCallbackArguments();
+
+ /// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Historically an expensive operation.")]
- IDictionary<string, string> GetCallbackArguments(); // TODO: change this to a property, and return a cached ReadOnlyDictionary
+ IDictionary<string, string> GetUntrustedCallbackArguments();
/// <summary>
/// Tries to get an OpenID extension that may be present in the response.
@@ -218,4 +254,279 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
IOpenIdMessageExtension GetUntrustedExtension(Type extensionType);
}
+
+ /// <summary>
+ /// Code contract for the <see cref="IAuthenticationResponse"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IAuthenticationResponse))]
+ internal abstract class IAuthenticationResponseContract : IAuthenticationResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IAuthenticationResponseContract"/> class.
+ /// </summary>
+ protected IAuthenticationResponseContract() {
+ }
+
+ #region IAuthenticationResponse Members
+
+ /// <summary>
+ /// Gets the Identifier that the end user claims to own. For use with user database storage and lookup.
+ /// May be null for some failed authentications (i.e. failed directed identity authentications).
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// <para>
+ /// This is the secure identifier that should be used for database storage and lookup.
+ /// It is not always friendly (i.e. =Arnott becomes =!9B72.7DD1.50A9.5CCD), but it protects
+ /// user identities against spoofing and other attacks.
+ /// </para>
+ /// <para>
+ /// For user-friendly identifiers to display, use the
+ /// <see cref="IAuthenticationResponse.FriendlyIdentifierForDisplay"/> property.
+ /// </para>
+ /// </remarks>
+ Identifier IAuthenticationResponse.ClaimedIdentifier {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets a user-friendly OpenID Identifier for display purposes ONLY.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// <para>
+ /// This <i>should</i> be put through <see cref="HttpUtility.HtmlEncode(string)"/> before
+ /// sending to a browser to secure against javascript injection attacks.
+ /// </para>
+ /// <para>
+ /// This property retains some aspects of the user-supplied identifier that get lost
+ /// in the <see cref="IAuthenticationResponse.ClaimedIdentifier"/>. For example, XRIs used as user-supplied
+ /// identifiers (i.e. =Arnott) become unfriendly unique strings (i.e. =!9B72.7DD1.50A9.5CCD).
+ /// For display purposes, such as text on a web page that says "You're logged in as ...",
+ /// this property serves to provide the =Arnott string, or whatever else is the most friendly
+ /// string close to what the user originally typed in.
+ /// </para>
+ /// <para>
+ /// If the user-supplied identifier is a URI, this property will be the URI after all
+ /// redirects, and with the protocol and fragment trimmed off.
+ /// If the user-supplied identifier is an XRI, this property will be the original XRI.
+ /// If the user-supplied identifier is an OpenID Provider identifier (i.e. yahoo.com),
+ /// this property will be the Claimed Identifier, with the protocol stripped if it is a URI.
+ /// </para>
+ /// <para>
+ /// It is <b>very</b> important that this property <i>never</i> be used for database storage
+ /// or lookup to avoid identity spoofing and other security risks. For database storage
+ /// and lookup please use the <see cref="IAuthenticationResponse.ClaimedIdentifier"/> property.
+ /// </para>
+ /// </remarks>
+ string IAuthenticationResponse.FriendlyIdentifierForDisplay {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the detailed success or failure status of the authentication attempt.
+ /// </summary>
+ /// <value></value>
+ AuthenticationStatus IAuthenticationResponse.Status {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets information about the OpenId Provider, as advertised by the
+ /// OpenID discovery documents found at the <see cref="IAuthenticationResponse.ClaimedIdentifier"/>
+ /// location, if available.
+ /// </summary>
+ /// <value>
+ /// The Provider endpoint that issued the positive assertion;
+ /// or <c>null</c> if information about the Provider is unavailable.
+ /// </value>
+ IProviderEndpoint IAuthenticationResponse.Provider {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the details regarding a failed authentication attempt, if available.
+ /// This will be set if and only if <see cref="IAuthenticationResponse.Status"/> is <see cref="AuthenticationStatus.Failed"/>.
+ /// </summary>
+ /// <value></value>
+ Exception IAuthenticationResponse.Exception {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// <para>This may return any argument on the querystring that came with the authentication response,
+ /// which may include parameters not explicitly added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.</para>
+ /// <para>Note that these values are NOT protected against tampering in transit.</para>
+ /// </remarks>
+ string IAuthenticationResponse.GetCallbackArgument(string key) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(key));
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// <para>This MAY return any argument on the querystring that came with the authentication response,
+ /// which may include parameters not explicitly added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.</para>
+ /// <para>Note that these values are NOT protected against tampering in transit.</para>
+ /// </remarks>
+ IDictionary<string, string> IAuthenticationResponse.GetCallbackArguments() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="IAuthenticationResponse.GetUntrustedExtension&lt;T&gt;"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ T IAuthenticationResponse.GetExtension<T>() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned only if the Provider signed them.
+ /// Relying parties that do not care if the values were modified in
+ /// transit should use the <see cref="IAuthenticationResponse.GetUntrustedExtension"/> method
+ /// in order to allow the Provider to not sign the extension. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ IOpenIdMessageExtension IAuthenticationResponse.GetExtension(Type extensionType) {
+ Contract.Requires<ArgumentNullException>(extensionType != null);
+ Contract.Requires<ArgumentException>(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType));
+ ////ErrorUtilities.VerifyArgument(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType), string.Format(CultureInfo.CurrentCulture, OpenIdStrings.TypeMustImplementX, typeof(IOpenIdMessageExtension).FullName));
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="IAuthenticationResponse.GetExtension&lt;T&gt;"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ T IAuthenticationResponse.GetUntrustedExtension<T>() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Tries to get an OpenID extension that may be present in the response, without
+ /// requiring it to be signed by the Provider.
+ /// </summary>
+ /// <param name="extensionType">Type of the extension to look for in the response.</param>
+ /// <returns>
+ /// The extension, if it is found. Null otherwise.
+ /// </returns>
+ /// <remarks>
+ /// <para>Extensions are returned whether they are signed or not.
+ /// Use the <see cref="IAuthenticationResponse.GetExtension"/> method to retrieve
+ /// extension responses only if they are signed by the Provider to
+ /// protect against tampering. </para>
+ /// <para>Unsigned extensions are completely unreliable and should be
+ /// used only to prefill user forms since the user or any other third
+ /// party may have tampered with the data carried by the extension.</para>
+ /// <para>Signed extensions are only reliable if the relying party
+ /// trusts the OpenID Provider that signed them. Signing does not mean
+ /// the relying party can trust the values -- it only means that the values
+ /// have not been tampered with since the Provider sent the message.</para>
+ /// </remarks>
+ IOpenIdMessageExtension IAuthenticationResponse.GetUntrustedExtension(Type extensionType) {
+ Contract.Requires<ArgumentNullException>(extensionType != null);
+ Contract.Requires<ArgumentException>(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType));
+ ////ErrorUtilities.VerifyArgument(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType), string.Format(CultureInfo.CurrentCulture, OpenIdStrings.TypeMustImplementX, typeof(IOpenIdMessageExtension).FullName));
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ string IAuthenticationResponse.GetUntrustedCallbackArgument(string key) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(key));
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ IDictionary<string, string> IAuthenticationResponse.GetUntrustedCallbackArguments() {
+ Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs
index ed9d65a..5d8918d 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IProviderEndpoint.cs
@@ -6,7 +6,10 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
+ using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
@@ -18,6 +21,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Because information provided by this interface is suppplied by a
/// user's individually published documents, it may be incomplete or inaccurate.
/// </remarks>
+ [ContractClass(typeof(IProviderEndpointContract))]
public interface IProviderEndpoint {
/// <summary>
/// Gets the detected version of OpenID implemented by the Provider.
@@ -27,6 +31,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets the URL that the OpenID Provider receives authentication requests at.
/// </summary>
+ /// <value>
+ /// This value MUST be an absolute HTTP or HTTPS URL.
+ /// </value>
Uri Uri { get; }
/// <summary>
@@ -42,6 +49,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// the extension in the request and see if a response comes back for that extension.
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "No parameter at all.")]
+ [Obsolete("Use IAuthenticationRequest.DiscoveryResult.IsExtensionSupported instead.")]
bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new();
/// <summary>
@@ -56,6 +64,81 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The only way to be sure of support for a given extension is to include
/// the extension in the request and see if a response comes back for that extension.
/// </remarks>
+ [Obsolete("Use IAuthenticationRequest.DiscoveryResult.IsExtensionSupported instead.")]
bool IsExtensionSupported(Type extensionType);
}
+
+ /// <summary>
+ /// Code contract for the <see cref="IProviderEndpoint"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IProviderEndpoint))]
+ internal abstract class IProviderEndpointContract : IProviderEndpoint {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="IProviderEndpointContract"/> class from being created.
+ /// </summary>
+ private IProviderEndpointContract() {
+ }
+
+ #region IProviderEndpoint Members
+
+ /// <summary>
+ /// Gets the detected version of OpenID implemented by the Provider.
+ /// </summary>
+ Version IProviderEndpoint.Version {
+ get {
+ Contract.Ensures(Contract.Result<Version>() != null);
+ throw new System.NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets the URL that the OpenID Provider receives authentication requests at.
+ /// </summary>
+ Uri IProviderEndpoint.Uri {
+ get {
+ Contract.Ensures(Contract.Result<Uri>() != null);
+ throw new System.NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Checks whether the OpenId Identifier claims support for a given extension.
+ /// </summary>
+ /// <typeparam name="T">The extension whose support is being queried.</typeparam>
+ /// <returns>
+ /// True if support for the extension is advertised. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Note that a true or false return value is no guarantee of a Provider's
+ /// support for or lack of support for an extension. The return value is
+ /// determined by how the authenticating user filled out his/her XRDS document only.
+ /// The only way to be sure of support for a given extension is to include
+ /// the extension in the request and see if a response comes back for that extension.
+ /// </remarks>
+ bool IProviderEndpoint.IsExtensionSupported<T>() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Checks whether the OpenId Identifier claims support for a given extension.
+ /// </summary>
+ /// <param name="extensionType">The extension whose support is being queried.</param>
+ /// <returns>
+ /// True if support for the extension is advertised. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Note that a true or false return value is no guarantee of a Provider's
+ /// support for or lack of support for an extension. The return value is
+ /// determined by how the authenticating user filled out his/her XRDS document only.
+ /// The only way to be sure of support for a given extension is to include
+ /// the extension in the request and see if a response comes back for that extension.
+ /// </remarks>
+ bool IProviderEndpoint.IsExtensionSupported(Type extensionType) {
+ Contract.Requires<ArgumentNullException>(extensionType != null);
+ Contract.Requires<ArgumentException>(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType));
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
index e7c38db..300a15f 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
@@ -5,14 +5,14 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Diagnostics.Contracts;
+
/// <summary>
/// Applies a custom security policy to certain OpenID security settings and behaviors.
/// </summary>
- /// <remarks>
- /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
- /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
- /// </remarks>
- internal interface IRelyingPartyBehavior {
+ [ContractClass(typeof(IRelyingPartyBehaviorContract))]
+ public interface IRelyingPartyBehavior {
/// <summary>
/// Applies a well known set of security requirements to a default set of security settings.
/// </summary>
@@ -40,4 +40,53 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="assertion">The positive assertion.</param>
void OnIncomingPositiveAssertion(IAuthenticationResponse assertion);
}
+
+ /// <summary>
+ /// Contract class for the <see cref="IRelyingPartyBehavior"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IRelyingPartyBehavior))]
+ internal class IRelyingPartyBehaviorContract : IRelyingPartyBehavior {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IRelyingPartyBehaviorContract"/> class.
+ /// </summary>
+ protected IRelyingPartyBehaviorContract() {
+ }
+
+ #region IRelyingPartyBehavior Members
+
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void IRelyingPartyBehavior.ApplySecuritySettings(RelyingPartySecuritySettings securitySettings) {
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ }
+
+ /// <summary>
+ /// Called when an authentication request is about to be sent.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <remarks>
+ /// Implementations should be prepared to be called multiple times on the same outgoing message
+ /// without malfunctioning.
+ /// </remarks>
+ void IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(IAuthenticationRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ }
+
+ /// <summary>
+ /// Called when an incoming positive assertion is received.
+ /// </summary>
+ /// <param name="assertion">The positive assertion.</param>
+ void IRelyingPartyBehavior.OnIncomingPositiveAssertion(IAuthenticationResponse assertion) {
+ Contract.Requires<ArgumentNullException>(assertion != null);
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/ISetupRequiredAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/ISetupRequiredAuthenticationResponse.cs
index e4f220f..cfbccef 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/ISetupRequiredAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/ISetupRequiredAuthenticationResponse.cs
@@ -5,11 +5,15 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Diagnostics.Contracts;
+
/// <summary>
/// An interface to expose useful properties and functionality for handling
/// authentication responses that are returned from Immediate authentication
/// requests that require a subsequent request to be made in non-immediate mode.
/// </summary>
+ [ContractClass(typeof(ISetupRequiredAuthenticationResponseContract))]
public interface ISetupRequiredAuthenticationResponse {
/// <summary>
/// Gets the <see cref="Identifier"/> to pass to <see cref="OpenIdRelyingParty.CreateRequest(Identifier)"/>
@@ -17,4 +21,31 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
Identifier UserSuppliedIdentifier { get; }
}
+
+ /// <summary>
+ /// Code contract class for the <see cref="ISetupRequiredAuthenticationResponse"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(ISetupRequiredAuthenticationResponse))]
+ internal abstract class ISetupRequiredAuthenticationResponseContract : ISetupRequiredAuthenticationResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ISetupRequiredAuthenticationResponseContract"/> class.
+ /// </summary>
+ protected ISetupRequiredAuthenticationResponseContract() {
+ }
+
+ #region ISetupRequiredAuthenticationResponse Members
+
+ /// <summary>
+ /// Gets the <see cref="Identifier"/> to pass to <see cref="OpenIdRelyingParty.CreateRequest(Identifier)"/>
+ /// in a subsequent authentication attempt.
+ /// </summary>
+ Identifier ISetupRequiredAuthenticationResponse.UserSuppliedIdentifier {
+ get {
+ Contract.Requires<InvalidOperationException>(((IAuthenticationResponse)this).Status == AuthenticationStatus.SetupRequired, OpenIdStrings.OperationOnlyValidForSetupRequiredState);
+ throw new System.NotImplementedException();
+ }
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs
deleted file mode 100644
index ebd8518..0000000
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IXrdsProviderEndpoint.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IXrdsProviderEndpoint.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.RelyingParty {
- using System.Diagnostics.CodeAnalysis;
-
- /// <summary>
- /// An <see cref="IProviderEndpoint"/> interface with additional members for use
- /// in sorting for most preferred endpoint.
- /// </summary>
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Xrds is an acronym.")]
- public interface IXrdsProviderEndpoint : IProviderEndpoint {
- /// <summary>
- /// Gets the priority associated with this service that may have been given
- /// in the XRDS document.
- /// </summary>
- int? ServicePriority { get; }
-
- /// <summary>
- /// Gets the priority associated with the service endpoint URL.
- /// </summary>
- /// <remarks>
- /// When sorting by priority, this property should be considered second after
- /// <see cref="ServicePriority"/>.
- /// </remarks>
- int? UriPriority { get; }
-
- /// <summary>
- /// Checks for the presence of a given Type URI in an XRDS service.
- /// </summary>
- /// <param name="typeUri">The type URI to check for.</param>
- /// <returns><c>true</c> if the service type uri is present; <c>false</c> otherwise.</returns>
- bool IsTypeUriPresent(string typeUri);
- }
-}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
index e66ac28..869a342 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
@@ -27,8 +28,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The negative assertion response received by the Relying Party.</param>
internal NegativeAuthenticationResponse(NegativeAssertionResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
+
+ Reporting.RecordEventOccurrence(this, string.Empty);
}
#region IAuthenticationResponse Properties
@@ -128,8 +131,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <value></value>
public Identifier UserSuppliedIdentifier {
get {
- ErrorUtilities.VerifyOperation(this.Status == AuthenticationStatus.SetupRequired, OpenIdStrings.OperationOnlyValidForSetupRequiredState);
-
string userSuppliedIdentifier;
this.response.ExtraData.TryGetValue(AuthenticationRequest.UserSuppliedIdentifierParameterName, out userSuppliedIdentifier);
return userSuppliedIdentifier;
@@ -159,6 +160,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public string GetUntrustedCallbackArgument(string key) {
+ return null;
+ }
+
+ /// <summary>
/// Gets all the callback arguments that were previously added using
/// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
/// of the return_to URL.
@@ -175,6 +194,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public IDictionary<string, string> GetUntrustedCallbackArguments() {
+ return EmptyDictionary<string, string>.Instance;
+ }
+
+ /// <summary>
/// Tries to get an OpenID extension that may be present in the response.
/// </summary>
/// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
index 6a4413f..097d065 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
@@ -5,7 +5,7 @@
//-----------------------------------------------------------------------
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedScriptResourceName, "text/javascript")]
-[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedDotNetOpenIdLogoResourceName, "image/gif")]
+[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName, "text/css")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName, "image/gif")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName, "image/png")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName, "image/png")]
@@ -19,18 +19,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Drawing.Design;
using System.Globalization;
- using System.Linq;
using System.Text;
- using System.Text.RegularExpressions;
- using System.Web;
using System.Web.UI;
- using System.Web.UI.WebControls;
+ using System.Web.UI.HtmlControls;
using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using DotNetOpenAuth.OpenId.Extensions;
- using DotNetOpenAuth.OpenId.Extensions.UI;
/// <summary>
/// An ASP.NET control that provides a minimal text box that is OpenID-aware and uses AJAX for
@@ -38,16 +33,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
[DefaultProperty("Text"), ValidationProperty("Text")]
[ToolboxData("<{0}:OpenIdAjaxTextBox runat=\"server\" />")]
- public sealed class OpenIdAjaxTextBox : WebControl, ICallbackEventHandler {
+ public class OpenIdAjaxTextBox : OpenIdRelyingPartyAjaxControlBase, IEditableTextControl, ITextControl, IPostBackDataHandler {
/// <summary>
/// The name of the manifest stream containing the OpenIdAjaxTextBox.js file.
/// </summary>
internal const string EmbeddedScriptResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdAjaxTextBox.js";
/// <summary>
- /// The name of the manifest stream containing the dotnetopenid_16x16.gif file.
+ /// The name of the manifest stream containing the OpenIdAjaxTextBox.css file.
/// </summary>
- internal const string EmbeddedDotNetOpenIdLogoResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.dotnetopenid_16x16.gif";
+ internal const string EmbeddedStylesheetResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdAjaxTextBox.css";
/// <summary>
/// The name of the manifest stream containing the spinner.gif file.
@@ -64,47 +59,47 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
internal const string EmbeddedLoginFailureResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.login_failure.png";
- #region Property viewstate keys
-
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="Columns"/> property.
+ /// The default value for the <see cref="DownloadYahooUILibrary"/> property.
/// </summary>
- private const string ColumnsViewStateKey = "Columns";
+ internal const bool DownloadYahooUILibraryDefault = true;
+
+ #region Property viewstate keys
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="OnClientAssertionReceived"/> property.
+ /// The viewstate key to use for storing the value of the <see cref="AutoPostBack"/> property.
/// </summary>
- private const string OnClientAssertionReceivedViewStateKey = "OnClientAssertionReceived";
+ private const string AutoPostBackViewStateKey = "AutoPostback";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="AuthenticationResponse"/> property.
+ /// The viewstate key to use for the <see cref="Text"/> property.
/// </summary>
- private const string AuthenticationResponseViewStateKey = "AuthenticationResponse";
+ private const string TextViewStateKey = "Text";
/// <summary>
- /// The viewstate key to use for storing the value of the a successful authentication.
+ /// The viewstate key to use for storing the value of the <see cref="Columns"/> property.
/// </summary>
- private const string AuthDataViewStateKey = "AuthData";
+ private const string ColumnsViewStateKey = "Columns";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="AuthenticatedAsToolTip"/> property.
+ /// The viewstate key to use for the <see cref="CssClass"/> property.
/// </summary>
- private const string AuthenticatedAsToolTipViewStateKey = "AuthenticatedAsToolTip";
+ private const string CssClassViewStateKey = "CssClass";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="AuthenticationSucceededToolTip"/> property.
+ /// The viewstate key to use for storing the value of the <see cref="OnClientAssertionReceived"/> property.
/// </summary>
- private const string AuthenticationSucceededToolTipViewStateKey = "AuthenticationSucceededToolTip";
+ private const string OnClientAssertionReceivedViewStateKey = "OnClientAssertionReceived";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="ReturnToUrl"/> property.
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticatedAsToolTip"/> property.
/// </summary>
- private const string ReturnToUrlViewStateKey = "ReturnToUrl";
+ private const string AuthenticatedAsToolTipViewStateKey = "AuthenticatedAsToolTip";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="RealmUrl"/> property.
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticationSucceededToolTip"/> property.
/// </summary>
- private const string RealmUrlViewStateKey = "RealmUrl";
+ private const string AuthenticationSucceededToolTipViewStateKey = "AuthenticationSucceededToolTip";
/// <summary>
/// The viewstate key to use for storing the value of the <see cref="LogOnInProgressMessage"/> property.
@@ -142,6 +137,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string LogOnToolTipViewStateKey = "LoginToolTip";
/// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="LogOnPostBackToolTip"/> property.
+ /// </summary>
+ private const string LogOnPostBackToolTipViewStateKey = "LoginPostBackToolTip";
+
+ /// <summary>
/// The viewstate key to use for storing the value of the <see cref="Name"/> property.
/// </summary>
private const string NameViewStateKey = "Name";
@@ -152,14 +152,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string TimeoutViewStateKey = "Timeout";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="Text"/> property.
+ /// The viewstate key to use for storing the value of the <see cref="TabIndex"/> property.
/// </summary>
- private const string TextViewStateKey = "Text";
+ private const string TabIndexViewStateKey = "TabIndex";
/// <summary>
- /// The viewstate key to use for storing the value of the <see cref="TabIndex"/> property.
+ /// The viewstate key to use for the <see cref="Enabled"/> property.
/// </summary>
- private const string TabIndexViewStateKey = "TabIndex";
+ private const string EnabledViewStateKey = "Enabled";
/// <summary>
/// The viewstate key to use for storing the value of the <see cref="RetryToolTip"/> property.
@@ -171,24 +171,34 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
private const string RetryTextViewStateKey = "RetryText";
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="DownloadYahooUILibrary"/> property.
+ /// </summary>
+ private const string DownloadYahooUILibraryViewStateKey = "DownloadYahooUILibrary";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="ShowLogOnPostBackButton"/> property.
+ /// </summary>
+ private const string ShowLogOnPostBackButtonViewStateKey = "ShowLogOnPostBackButton";
+
#endregion
#region Property defaults
/// <summary>
- /// The default value for the <see cref="Columns"/> property.
+ /// The default value for the <see cref="AutoPostBack"/> property.
/// </summary>
- private const int ColumnsDefault = 40;
+ private const bool AutoPostBackDefault = false;
/// <summary>
- /// The default value for the <see cref="ReturnToUrl"/> property.
+ /// The default value for the <see cref="Columns"/> property.
/// </summary>
- private const string ReturnToUrlDefault = "";
+ private const int ColumnsDefault = 40;
/// <summary>
- /// The default value for the <see cref="RealmUrl"/> property.
+ /// The default value for the <see cref="CssClass"/> property.
/// </summary>
- private const string RealmUrlDefault = "~/";
+ private const string CssClassDefault = "openid";
/// <summary>
/// The default value for the <see cref="LogOnInProgressMessage"/> property.
@@ -251,71 +261,46 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string LogOnToolTipDefault = "Click here to log in using a pop-up window.";
/// <summary>
- /// The default value for the <see cref="RetryText"/> property.
+ /// The default value for the <see cref="LogOnPostBackToolTip"/> property.
/// </summary>
- private const string RetryTextDefault = "RETRY";
-
- #endregion
+ private const string LogOnPostBackToolTipDefault = "Click here to log in immediately.";
/// <summary>
- /// Backing field for the <see cref="RelyingParty"/> property.
+ /// The default value for the <see cref="RetryText"/> property.
/// </summary>
- private OpenIdRelyingParty relyingParty;
+ private const string RetryTextDefault = "RETRY";
/// <summary>
- /// Backing field for the <see cref="RelyingPartyNonVerifying"/> property.
+ /// The default value for the <see cref="ShowLogOnPostBackButton"/> property.
/// </summary>
- private OpenIdRelyingParty relyingPartyNonVerifying;
+ private const bool ShowLogOnPostBackButtonDefault = false;
- /// <summary>
- /// Tracks whether the text box should receive input focus when the page is rendered.
- /// </summary>
- private bool focusCalled;
+ #endregion
/// <summary>
- /// The authentication response that just came in.
+ /// The path where the YUI control library should be downloaded from for HTTP pages.
/// </summary>
- private IAuthenticationResponse authenticationResponse;
+ private const string YuiLoaderHttp = "http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js";
/// <summary>
- /// A dictionary of extension response types and the javascript member
- /// name to map them to on the user agent.
+ /// The path where the YUI control library should be downloaded from for HTTPS pages.
/// </summary>
- private Dictionary<Type, string> clientScriptExtensions = new Dictionary<Type, string>();
+ private const string YuiLoaderHttps = "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js";
/// <summary>
- /// Stores the result of an AJAX discovery request while it is waiting
- /// to be picked up by ASP.NET on the way down to the user agent.
+ /// Initializes a new instance of the <see cref="OpenIdAjaxTextBox"/> class.
/// </summary>
- private string discoveryResult;
+ public OpenIdAjaxTextBox() {
+ this.HookFormSubmit = true;
+ }
#region Events
/// <summary>
- /// Fired when the user has typed in their identifier, discovery was successful
- /// and a login attempt is about to begin.
+ /// Fired when the content of the text changes between posts to the server.
/// </summary>
- [Description("Fired when the user has typed in their identifier, discovery was successful and a login attempt is about to begin.")]
- public event EventHandler<OpenIdEventArgs> LoggingIn;
-
- /// <summary>
- /// Fired when a Provider sends back a positive assertion to this control,
- /// but the authentication has not yet been verified.
- /// </summary>
- /// <remarks>
- /// <b>No security critical decisions should be made within event handlers
- /// for this event</b> as the authenticity of the assertion has not been
- /// verified yet. All security related code should go in the event handler
- /// for the <see cref="LoggedIn"/> event.
- /// </remarks>
- [Description("Fired when a Provider sends back a positive assertion to this control, but the authentication has not yet been verified.")]
- public event EventHandler<OpenIdEventArgs> UnconfirmedPositiveAssertion;
-
- /// <summary>
- /// Fired when authentication has completed successfully.
- /// </summary>
- [Description("Fired when authentication has completed successfully.")]
- public event EventHandler<OpenIdEventArgs> LoggedIn;
+ [Description("Occurs when the content of the text changes between posts to the server."), Category(BehaviorCategory)]
+ public event EventHandler TextChanged;
/// <summary>
/// Gets or sets the client-side script that executes when an authentication
@@ -331,10 +316,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// the authentication has not been verified and may have been spoofed.
/// No security-sensitive operations should take place in this javascript code.
/// The authentication is verified on the server by the time the
- /// <see cref="LoggedIn"/> server-side event fires.</para>
+ /// <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/> server-side event fires.</para>
/// </remarks>
[Description("Gets or sets the client-side script that executes when an authentication assertion is received (but before it is verified).")]
- [Bindable(true), DefaultValue(""), Category("Behavior")]
+ [Bindable(true), DefaultValue(""), Category(BehaviorCategory)]
public string OnClientAssertionReceived {
get { return this.ViewState[OnClientAssertionReceivedViewStateKey] as string; }
set { this.ViewState[OnClientAssertionReceivedViewStateKey] = value; }
@@ -345,54 +330,51 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#region Properties
/// <summary>
- /// Gets the completed authentication response.
+ /// Gets or sets the value in the text field, completely unprocessed or normalized.
/// </summary>
- public IAuthenticationResponse AuthenticationResponse {
+ [Bindable(true), DefaultValue(""), Category(AppearanceCategory)]
+ [Description("The content of the text box.")]
+ public string Text {
get {
- if (this.authenticationResponse == null) {
- // We will either validate a new response and return a live AuthenticationResponse
- // or we will try to deserialize a previous IAuthenticationResponse (snapshot)
- // from viewstate and return that.
- IAuthenticationResponse viewstateResponse = this.ViewState[AuthenticationResponseViewStateKey] as IAuthenticationResponse;
- string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
- string formAuthData = this.Page.Request.Form[this.OpenIdAuthDataFormKey];
-
- // First see if there is fresh auth data to be processed into a response.
- if (!string.IsNullOrEmpty(formAuthData) && !string.Equals(viewstateAuthData, formAuthData, StringComparison.Ordinal)) {
- this.ViewState[AuthDataViewStateKey] = formAuthData;
-
- Uri authUri = new Uri(formAuthData);
- HttpRequestInfo clientResponseInfo = new HttpRequestInfo {
- UrlBeforeRewriting = authUri,
- };
-
- this.authenticationResponse = this.RelyingParty.GetResponse(clientResponseInfo);
-
- // Save out the authentication response to viewstate so we can find it on
- // a subsequent postback.
- this.ViewState[AuthenticationResponseViewStateKey] = new AuthenticationResponseSnapshot(this.authenticationResponse);
- } else {
- this.authenticationResponse = viewstateResponse;
- }
+ return this.Identifier != null ? this.Identifier.OriginalString : (this.ViewState[TextViewStateKey] as string ?? string.Empty);
+ }
+
+ set {
+ // Try to store it as a validated identifier,
+ // but failing that at least store the text.
+ Identifier id;
+ if (Identifier.TryParse(value, out id)) {
+ this.Identifier = id;
+ } else {
+ // Be sure to set the viewstate AFTER setting the Identifier,
+ // since setting the Identifier clears the viewstate in OnIdentifierChanged.
+ this.Identifier = null;
+ this.ViewState[TextViewStateKey] = value;
}
- return this.authenticationResponse;
}
}
/// <summary>
- /// Gets or sets the value in the text field, completely unprocessed or normalized.
+ /// Gets or sets a value indicating whether a postback is made to fire the
+ /// <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/> event as soon as authentication has completed
+ /// successfully.
/// </summary>
- [Bindable(true), DefaultValue(""), Category("Appearance")]
- [Description("The value in the text field, completely unprocessed or normalized.")]
- public string Text {
- get { return (string)(this.ViewState[TextViewStateKey] ?? string.Empty); }
- set { this.ViewState[TextViewStateKey] = value ?? string.Empty; }
+ /// <value>
+ /// <c>true</c> if a postback should be made automatically upon authentication;
+ /// otherwise, <c>false</c> to delay the <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/>
+ /// event from firing at the server until a postback is made by some other control.
+ /// </value>
+ [Bindable(true), Category(BehaviorCategory), DefaultValue(AutoPostBackDefault)]
+ [Description("Whether the LoggedIn event fires on the server as soon as authentication completes successfully.")]
+ public bool AutoPostBack {
+ get { return (bool)(this.ViewState[AutoPostBackViewStateKey] ?? AutoPostBackDefault); }
+ set { this.ViewState[AutoPostBackViewStateKey] = value; }
}
/// <summary>
/// Gets or sets the width of the text box in characters.
/// </summary>
- [Bindable(true), Category("Appearance"), DefaultValue(ColumnsDefault)]
+ [Bindable(true), Category(AppearanceCategory), DefaultValue(ColumnsDefault)]
[Description("The width of the text box in characters.")]
public int Columns {
get {
@@ -400,22 +382,44 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyArgumentInRange(value >= 0, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value >= 0);
this.ViewState[ColumnsViewStateKey] = value;
}
}
/// <summary>
+ /// Gets or sets the CSS class assigned to the text box.
+ /// </summary>
+ [Bindable(true), DefaultValue(CssClassDefault), Category(AppearanceCategory)]
+ [Description("The CSS class assigned to the text box.")]
+ public string CssClass {
+ get { return (string)this.ViewState[CssClassViewStateKey]; }
+ set { this.ViewState[CssClassViewStateKey] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the tab index of the text box control. Use 0 to omit an explicit tabindex.
/// </summary>
- [Bindable(true), Category("Behavior"), DefaultValue(TabIndexDefault)]
+ [Bindable(true), Category(BehaviorCategory), DefaultValue(TabIndexDefault)]
[Description("The tab index of the text box control. Use 0 to omit an explicit tabindex.")]
- public override short TabIndex {
+ public virtual short TabIndex {
get { return (short)(this.ViewState[TabIndexViewStateKey] ?? TabIndexDefault); }
set { this.ViewState[TabIndexViewStateKey] = value; }
}
/// <summary>
+ /// Gets or sets a value indicating whether this <see cref="OpenIdTextBox"/> is enabled
+ /// in the browser for editing and will respond to incoming OpenID messages.
+ /// </summary>
+ /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
+ [Bindable(true), DefaultValue(true), Category(BehaviorCategory)]
+ [Description("Whether the control is editable in the browser and will respond to OpenID messages.")]
+ public bool Enabled {
+ get { return (bool)(this.ViewState[EnabledViewStateKey] ?? true); }
+ set { this.ViewState[EnabledViewStateKey] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the HTML name to assign to the text field.
/// </summary>
[Bindable(true), DefaultValue(NameDefault), Category("Misc")]
@@ -426,7 +430,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyNonZeroLength(value, "value");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
this.ViewState[NameViewStateKey] = value ?? string.Empty;
}
}
@@ -434,7 +438,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.
/// </summary>
- [Browsable(true), DefaultValue(typeof(TimeSpan), "00:00:01"), Category("Behavior")]
+ [Browsable(true), DefaultValue(typeof(TimeSpan), "00:00:08"), Category(BehaviorCategory)]
[Description("The time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.")]
public TimeSpan Timeout {
get {
@@ -442,7 +446,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyArgumentInRange(value.TotalMilliseconds > 0, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value.TotalMilliseconds > 0);
this.ViewState[TimeoutViewStateKey] = value;
}
}
@@ -450,7 +454,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the maximum number of OpenID Providers to simultaneously try to authenticate with.
/// </summary>
- [Browsable(true), DefaultValue(ThrottleDefault), Category("Behavior")]
+ [Browsable(true), DefaultValue(ThrottleDefault), Category(BehaviorCategory)]
[Description("The maximum number of OpenID Providers to simultaneously try to authenticate with.")]
public int Throttle {
get {
@@ -458,7 +462,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyArgumentInRange(value > 0, "value");
+ Contract.Requires<ArgumentOutOfRangeException>(value > 0);
this.ViewState[ThrottleViewStateKey] = value;
}
}
@@ -466,7 +470,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
/// </summary>
- [Bindable(true), DefaultValue(LogOnTextDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(LogOnTextDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
public string LogOnText {
get {
@@ -474,7 +478,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyNonZeroLength(value, "value");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
this.ViewState[LogOnTextViewStateKey] = value ?? string.Empty;
}
}
@@ -482,7 +486,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the rool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
/// </summary>
- [Bindable(true), DefaultValue(LogOnToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(LogOnToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
public string LogOnToolTip {
get { return (string)(this.ViewState[LogOnToolTipViewStateKey] ?? LogOnToolTipDefault); }
@@ -490,9 +494,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets or sets the rool tip text that appears on the LOG IN button when clicking the button will result in an immediate postback.
+ /// </summary>
+ [Bindable(true), DefaultValue(LogOnPostBackToolTipDefault), Localizable(true), Category(AppearanceCategory)]
+ [Description("The tool tip text that appears on the LOG IN button when clicking the button will result in an immediate postback.")]
+ public string LogOnPostBackToolTip {
+ get { return (string)(this.ViewState[LogOnPostBackToolTipViewStateKey] ?? LogOnPostBackToolTipDefault); }
+ set { this.ViewState[LogOnPostBackToolTipViewStateKey] = value ?? string.Empty; }
+ }
+
+ /// <summary>
/// Gets or sets the text that appears on the RETRY button in cases where authentication times out.
/// </summary>
- [Bindable(true), DefaultValue(RetryTextDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(RetryTextDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The text that appears on the RETRY button in cases where authentication times out.")]
public string RetryText {
get {
@@ -500,7 +514,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- ErrorUtilities.VerifyNonZeroLength(value, "value");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
this.ViewState[RetryTextViewStateKey] = value ?? string.Empty;
}
}
@@ -508,7 +522,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears on the RETRY button in cases where authentication times out.
/// </summary>
- [Bindable(true), DefaultValue(RetryToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(RetryToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the RETRY button in cases where authentication times out.")]
public string RetryToolTip {
get { return (string)(this.ViewState[RetryToolTipViewStateKey] ?? RetryToolTipDefault); }
@@ -518,7 +532,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears when authentication succeeds.
/// </summary>
- [Bindable(true), DefaultValue(AuthenticationSucceededToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(AuthenticationSucceededToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears when authentication succeeds.")]
public string AuthenticationSucceededToolTip {
get { return (string)(this.ViewState[AuthenticationSucceededToolTipViewStateKey] ?? AuthenticationSucceededToolTipDefault); }
@@ -528,7 +542,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears on the green checkmark when authentication succeeds.
/// </summary>
- [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the green checkmark when authentication succeeds.")]
public string AuthenticatedAsToolTip {
get { return (string)(this.ViewState[AuthenticatedAsToolTipViewStateKey] ?? AuthenticatedAsToolTipDefault); }
@@ -538,7 +552,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears when authentication fails.
/// </summary>
- [Bindable(true), DefaultValue(AuthenticationFailedToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(AuthenticationFailedToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears when authentication fails.")]
public string AuthenticationFailedToolTip {
get { return (string)(this.ViewState[AuthenticationFailedToolTipViewStateKey] ?? AuthenticationFailedToolTipDefault); }
@@ -548,7 +562,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the tool tip text that appears over the text box when it is discovering and authenticating.
/// </summary>
- [Bindable(true), DefaultValue(BusyToolTipDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(BusyToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears over the text box when it is discovering and authenticating.")]
public string BusyToolTip {
get { return (string)(this.ViewState[BusyToolTipViewStateKey] ?? BusyToolTipDefault); }
@@ -558,7 +572,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the message that is displayed if a postback is about to occur before the identifier has been supplied.
/// </summary>
- [Bindable(true), DefaultValue(IdentifierRequiredMessageDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(IdentifierRequiredMessageDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The message that is displayed if a postback is about to occur before the identifier has been supplied.")]
public string IdentifierRequiredMessage {
get { return (string)(this.ViewState[IdentifierRequiredMessageViewStateKey] ?? IdentifierRequiredMessageDefault); }
@@ -568,7 +582,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets or sets the message that is displayed if a postback is attempted while login is in process.
/// </summary>
- [Bindable(true), DefaultValue(LogOnInProgressMessageDefault), Localizable(true), Category("Appearance")]
+ [Bindable(true), DefaultValue(LogOnInProgressMessageDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The message that is displayed if a postback is attempted while login is in process.")]
public string LogOnInProgressMessage {
get { return (string)(this.ViewState[LogOnInProgressMessageViewStateKey] ?? LogOnInProgressMessageDefault); }
@@ -576,222 +590,51 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Gets or sets the OpenID <see cref="Realm"/> of the relying party web site.
+ /// Gets or sets a value indicating whether the Yahoo! User Interface Library (YUI)
+ /// will be downloaded in order to provide a login split button.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "DotNetOpenAuth.OpenId.Realm", Justification = "Using ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [Bindable(true)]
- [Category("Behavior")]
- [DefaultValue(RealmUrlDefault)]
- [Description("The OpenID Realm of the relying party web site.")]
- [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
- public string RealmUrl {
- get {
- return (string)(this.ViewState[RealmUrlViewStateKey] ?? RealmUrlDefault);
- }
-
- set {
- if (Page != null && !DesignMode) {
- // Validate new value by trying to construct a Realm object based on it.
- new Realm(OpenIdUtilities.GetResolvedRealm(this.Page, value, this.RelyingParty.Channel.GetRequestFromContext())); // throws an exception on failure.
- } else {
- // We can't fully test it, but it should start with either ~/ or a protocol.
- if (Regex.IsMatch(value, @"^https?://")) {
- new Uri(value.Replace("*.", "")); // make sure it's fully-qualified, but ignore wildcards
- } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
- // this is valid too
- } else {
- throw new UriFormatException();
- }
- }
- this.ViewState[RealmUrlViewStateKey] = value;
- }
+ /// <value>
+ /// <c>true</c> to use a split button; otherwise, <c>false</c> to use a standard HTML button
+ /// or a split button by downloading the YUI library yourself on the hosting web page.
+ /// </value>
+ /// <remarks>
+ /// The split button brings in about 180KB of YUI javascript dependencies.
+ /// </remarks>
+ [Bindable(true), DefaultValue(DownloadYahooUILibraryDefault), Category(BehaviorCategory)]
+ [Description("Whether a split button will be used for the \"log in\" when the user provides an identifier that delegates to more than one Provider.")]
+ public bool DownloadYahooUILibrary {
+ get { return (bool)(this.ViewState[DownloadYahooUILibraryViewStateKey] ?? DownloadYahooUILibraryDefault); }
+ set { this.ViewState[DownloadYahooUILibraryViewStateKey] = value; }
}
/// <summary>
- /// Gets or sets the OpenID ReturnTo of the relying party web site.
+ /// Gets or sets a value indicating whether the "Log in" button will be shown
+ /// to initiate a postback containing the positive assertion.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid on form designer only supports primitive types.")]
- [Bindable(true)]
- [Category("Behavior")]
- [DefaultValue(ReturnToUrlDefault)]
- [Description("The OpenID ReturnTo of the relying party web site.")]
- [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
- public string ReturnToUrl {
- get {
- return (string)(this.ViewState[ReturnToUrlViewStateKey] ?? ReturnToUrlDefault);
- }
-
- set {
- if (Page != null && !DesignMode) {
- // Validate new value by trying to construct a Uri based on it.
- new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, Page.ResolveUrl(value)); // throws an exception on failure.
- } else {
- // We can't fully test it, but it should start with either ~/ or a protocol.
- if (Regex.IsMatch(value, @"^https?://")) {
- new Uri(value); // make sure it's fully-qualified, but ignore wildcards
- } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
- // this is valid too
- } else {
- throw new UriFormatException();
- }
- }
- this.ViewState[ReturnToUrlViewStateKey] = value;
- }
+ [Bindable(true), DefaultValue(ShowLogOnPostBackButtonDefault), Category(AppearanceCategory)]
+ [Description("Whether the log in button will be shown to initiate a postback containing the positive assertion.")]
+ public bool ShowLogOnPostBackButton {
+ get { return (bool)(this.ViewState[ShowLogOnPostBackButtonViewStateKey] ?? ShowLogOnPostBackButtonDefault); }
+ set { this.ViewState[ShowLogOnPostBackButtonViewStateKey] = value; }
}
#endregion
- #region Properties to hide
-
/// <summary>
- /// Gets or sets the foreground color (typically the color of the text) of the Web server control.
+ /// Gets or sets a value indicating whether the ajax text box should hook the form's submit event for special behavior.
/// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the foreground color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color ForeColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
+ internal bool HookFormSubmit { get; set; }
/// <summary>
- /// Gets or sets the background color of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the background color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>, which indicates that this property is not set.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color BackColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border color of the Web control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the border color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>, which indicates that this property is not set.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color BorderColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border width of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the border width of a Web server control. The default value is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>, which indicates that this property is not set.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The specified border width is a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit BorderWidth {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border style of the Web server control.
- /// </summary>
- /// <returns>
- /// One of the <see cref="T:System.Web.UI.WebControls.BorderStyle"/> enumeration values. The default is NotSet.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override BorderStyle BorderStyle {
- get { return BorderStyle.None; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets the font properties associated with the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.FontInfo"/> that represents the font properties of the Web server control.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override FontInfo Font {
- get { return null; }
- }
-
- /// <summary>
- /// Gets or sets the height of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the height of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The height was set to a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit Height {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the width of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the width of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The width of the Web server control was set to a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit Width {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the text displayed when the mouse pointer hovers over the Web server control.
- /// </summary>
- /// <returns>
- /// The text displayed when the mouse pointer hovers over the Web server control. The default is <see cref="F:System.String.Empty"/>.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override string ToolTip {
- get { return string.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the skin to apply to the control.
- /// </summary>
- /// <returns>
- /// The name of the skin to apply to the control. The default is <see cref="F:System.String.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The skin specified in the <see cref="P:System.Web.UI.WebControls.WebControl.SkinID"/> property does not exist in the theme.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override string SkinID {
- get { return string.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether themes apply to this control.
+ /// Gets the name of the open id auth data form key.
/// </summary>
- /// <returns>true to use themes; otherwise, false. The default is false.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override bool EnableTheming {
- get { return false; }
- set { throw new NotSupportedException(); }
+ /// <value>
+ /// A concatenation of <see cref="Name"/> and <c>"_openidAuthData"</c>.
+ /// </value>
+ protected override string OpenIdAuthDataFormKey {
+ get { return this.Name + "_openidAuthData"; }
}
- #endregion
-
/// <summary>
/// Gets the default value for the <see cref="Timeout"/> property.
/// </summary>
@@ -807,171 +650,45 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
}
- /// <summary>
- /// Gets the relying party to use when verification of incoming messages is needed.
- /// </summary>
- private OpenIdRelyingParty RelyingParty {
- get {
- if (this.relyingParty == null) {
- this.relyingParty = CreateRelyingParty(true);
- }
- return this.relyingParty;
- }
- }
+ #region IPostBackDataHandler Members
/// <summary>
- /// Gets the relying party to use when verification of incoming messages is NOT wanted.
+ /// When implemented by a class, processes postback data for an ASP.NET server control.
/// </summary>
- private OpenIdRelyingParty RelyingPartyNonVerifying {
- get {
- if (this.relyingPartyNonVerifying == null) {
- this.relyingPartyNonVerifying = CreateRelyingParty(false);
- }
- return this.relyingPartyNonVerifying;
- }
- }
-
- /// <summary>
- /// Gets the name of the open id auth data form key.
- /// </summary>
- /// <value>A concatenation of <see cref="Name"/> and <c>"_openidAuthData"</c>.</value>
- private string OpenIdAuthDataFormKey {
- get { return this.Name + "_openidAuthData"; }
- }
-
- /// <summary>
- /// Places focus on the text box when the page is rendered on the browser.
- /// </summary>
- public override void Focus() {
- // we don't emit the code to focus the control immediately, in case the control
- // is never rendered to the page because its Visible property is false or that
- // of any of its parent containers.
- this.focusCalled = true;
- }
-
- /// <summary>
- /// Allows an OpenID extension to read data out of an unverified positive authentication assertion
- /// and send it down to the client browser so that Javascript running on the page can perform
- /// some preprocessing on the extension data.
- /// </summary>
- /// <typeparam name="T">The extension <i>response</i> type that will read data from the assertion.</typeparam>
- /// <param name="propertyName">The property name on the openid_identifier input box object that will be used to store the extension data. For example: sreg</param>
- /// <remarks>
- /// This method should be called from the <see cref="UnconfirmedPositiveAssertion"/> event handler.
- /// </remarks>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
- public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse {
- ErrorUtilities.VerifyNonZeroLength(propertyName, "propertyName");
- ErrorUtilities.VerifyArgumentNamed(!this.clientScriptExtensions.ContainsValue(propertyName), "propertyName", OpenIdStrings.ClientScriptExtensionPropertyNameCollision, propertyName);
- foreach (var ext in this.clientScriptExtensions.Keys) {
- ErrorUtilities.VerifyArgument(ext != typeof(T), OpenIdStrings.ClientScriptExtensionTypeCollision, typeof(T).FullName);
- }
- this.clientScriptExtensions.Add(typeof(T), propertyName);
+ /// <param name="postDataKey">The key identifier for the control.</param>
+ /// <param name="postCollection">The collection of all incoming name values.</param>
+ /// <returns>
+ /// true if the server control's state changes as a result of the postback; otherwise, false.
+ /// </returns>
+ bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
+ return this.LoadPostData(postDataKey, postCollection);
}
- #region ICallbackEventHandler Members
-
/// <summary>
- /// Returns the result of discovery on some Identifier passed to <see cref="ICallbackEventHandler.RaiseCallbackEvent"/>.
+ /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
/// </summary>
- /// <returns>The result of the callback.</returns>
- /// <value>A whitespace delimited list of URLs that can be used to initiate authentication.</value>
- string ICallbackEventHandler.GetCallbackResult() {
- this.Page.Response.ContentType = "text/javascript";
- return this.discoveryResult;
- }
-
- /// <summary>
- /// Performs discovery on some OpenID Identifier. Called directly from the user agent via
- /// AJAX callback mechanisms.
- /// </summary>
- /// <param name="eventArgument">The identifier to perform discovery on.</param>
- void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) {
- string userSuppliedIdentifier = eventArgument;
-
- ErrorUtilities.VerifyNonZeroLength(userSuppliedIdentifier, "userSuppliedIdentifier");
- Logger.OpenId.InfoFormat("AJAX discovery on {0} requested.", userSuppliedIdentifier);
-
- // We prepare a JSON object with this interface:
- // class jsonResponse {
- // string claimedIdentifier;
- // Array requests; // never null
- // string error; // null if no error
- // }
- // Each element in the requests array looks like this:
- // class jsonAuthRequest {
- // string endpoint; // URL to the OP endpoint
- // string immediate; // URL to initiate an immediate request
- // string setup; // URL to initiate a setup request.
- // }
- StringBuilder discoveryResultBuilder = new StringBuilder();
- discoveryResultBuilder.Append("{");
- try {
- List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true).Where(req => this.OnLoggingIn(req)).ToList();
- if (requests.Count > 0) {
- discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests[0].ClaimedIdentifier));
- discoveryResultBuilder.Append("requests: [");
- foreach (IAuthenticationRequest request in requests) {
- discoveryResultBuilder.Append("{");
- discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri));
- request.Mode = AuthenticationRequestMode.Immediate;
- OutgoingWebResponse response = request.RedirectingResponse;
- discoveryResultBuilder.AppendFormat("immediate: {0},", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri));
- request.Mode = AuthenticationRequestMode.Setup;
- response = request.RedirectingResponse;
- discoveryResultBuilder.AppendFormat("setup: {0}", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri));
- discoveryResultBuilder.Append("},");
- }
- discoveryResultBuilder.Length -= 1; // trim off last comma
- discoveryResultBuilder.Append("]");
- } else {
- discoveryResultBuilder.Append("requests: new Array(),");
- discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound));
- }
- } catch (ProtocolException ex) {
- discoveryResultBuilder.Append("requests: new Array(),");
- discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message));
- }
- discoveryResultBuilder.Append("}");
- this.discoveryResult = discoveryResultBuilder.ToString();
+ void IPostBackDataHandler.RaisePostDataChangedEvent() {
+ this.RaisePostDataChangedEvent();
}
#endregion
/// <summary>
- /// Enables a server control to perform final clean up before it is released from memory.
- /// </summary>
- public sealed override void Dispose() {
- this.Dispose(true);
- base.Dispose();
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Prepares the control for loading.
+ /// Raises the <see cref="E:Load"/> event.
/// </summary>
- /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
- if (this.Page.IsPostBack) {
- // If the control was temporarily hidden, it won't be in the Form data,
- // and we'll just implicitly keep the last Text setting.
- if (this.Page.Request.Form[this.Name] != null) {
- this.Text = this.Page.Request.Form[this.Name];
- }
+ this.Page.RegisterRequiresPostBack(this);
+ }
- // If there is a response, and it is fresh (live object, not a snapshot object)...
- if (this.AuthenticationResponse != null && this.AuthenticationResponse.Status == AuthenticationStatus.Authenticated) {
- this.OnLoggedIn(this.AuthenticationResponse);
- }
- } else {
- NameValueCollection query = this.RelyingParty.Channel.GetRequestFromContext().GetQueryOrFormFromContext();
- string userSuppliedIdentifier = query["dotnetopenid.userSuppliedIdentifier"];
- if (!string.IsNullOrEmpty(userSuppliedIdentifier) && query["dotnetopenid.phase"] == "2") {
- this.ReportAuthenticationResult();
- }
- }
+ /// <summary>
+ /// Called when the <see cref="Identifier"/> property is changed.
+ /// </summary>
+ protected override void OnIdentifierChanged() {
+ this.ViewState.Remove(TextViewStateKey);
+ base.OnIdentifierChanged();
}
/// <summary>
@@ -981,219 +698,142 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
+ if (!this.Visible) {
+ return;
+ }
+
+ if (this.DownloadYahooUILibrary) {
+ // Although we'll add the <script> tag to download the YAHOO component,
+ // a download failure may have occurred, so protect ourselves from a
+ // script error using an if (YAHOO) block. But apparently at least in IE
+ // that's not even enough, so we use a try/catch.
+ string yuiLoadScript = @"try { if (YAHOO) {
+ var loader = new YAHOO.util.YUILoader({
+ require: ['button', 'menu'],
+ loadOptional: false,
+ combine: true
+ });
+
+ loader.insert();
+} } catch (e) { }";
+ this.Page.ClientScript.RegisterClientScriptInclude("yuiloader", this.Page.Request.Url.IsTransportSecure() ? YuiLoaderHttps : YuiLoaderHttp);
+ this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "requiredYuiComponents", yuiLoadScript, true);
+ }
+
+ var css = new HtmlLink();
+ css.Href = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedStylesheetResourceName);
+ css.Attributes["rel"] = "stylesheet";
+ css.Attributes["type"] = "text/css";
+ ErrorUtilities.VerifyHost(this.Page.Header != null, OpenIdStrings.HeadTagMustIncludeRunatServer);
+ this.Page.Header.Controls.AddAt(0, css); // insert at top so host page can override
+
this.PrepareClientJavascript();
+
+ // If an Identifier is preset on this control, preload discovery on that identifier,
+ // but only if we're not already persisting an authentication result since that would
+ // be redundant.
+ if (this.Identifier != null && this.AuthenticationResponse == null) {
+ this.PreloadDiscovery(this.Identifier);
+ }
}
/// <summary>
/// Renders the control.
/// </summary>
/// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
- protected override void Render(System.Web.UI.HtmlTextWriter writer) {
+ protected override void Render(HtmlTextWriter writer) {
+ base.Render(writer);
+
// We surround the textbox with a span so that the .js file can inject a
// login button within the text box with easy placement.
- writer.WriteBeginTag("span");
- writer.WriteAttribute("class", this.CssClass);
- writer.Write(" style='");
- writer.WriteStyleAttribute("display", "inline-block");
- writer.WriteStyleAttribute("position", "relative");
- writer.WriteStyleAttribute("font-size", "16px");
- writer.Write("'>");
-
- writer.WriteBeginTag("input");
- writer.WriteAttribute("name", this.Name);
- writer.WriteAttribute("id", this.ClientID);
- writer.WriteAttribute("value", this.Text, true);
- writer.WriteAttribute("size", this.Columns.ToString(CultureInfo.InvariantCulture));
+ string css = this.CssClass ?? string.Empty;
+ css += " OpenIdAjaxTextBox";
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, css);
+
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "inline-block");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "16px");
+ writer.RenderBeginTag(HtmlTextWriterTag.Span);
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Name, this.Name);
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
+ writer.AddAttribute(HtmlTextWriterAttribute.Size, this.Columns.ToString(CultureInfo.InvariantCulture));
+ if (!string.IsNullOrEmpty(this.Text)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text, true);
+ }
+
if (this.TabIndex > 0) {
- writer.WriteAttribute("tabindex", this.TabIndex.ToString(CultureInfo.InvariantCulture));
+ writer.AddAttribute(HtmlTextWriterAttribute.Tabindex, this.TabIndex.ToString(CultureInfo.InvariantCulture));
}
if (!this.Enabled) {
- writer.WriteAttribute("disabled", "true");
+ writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "true");
}
if (!string.IsNullOrEmpty(this.CssClass)) {
- writer.WriteAttribute("class", this.CssClass);
- }
- writer.Write(" style='");
- writer.WriteStyleAttribute("padding-left", "18px");
- writer.WriteStyleAttribute("border-style", "solid");
- writer.WriteStyleAttribute("border-width", "1px");
- writer.WriteStyleAttribute("border-color", "lightgray");
- writer.Write("'");
- writer.Write(" />");
-
- writer.WriteEndTag("span");
-
- // Emit a hidden field to let the javascript on the user agent know if an
- // authentication has already successfully taken place.
- string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
- if (!string.IsNullOrEmpty(viewstateAuthData)) {
- writer.WriteBeginTag("input");
- writer.WriteAttribute("type", "hidden");
- writer.WriteAttribute("name", this.OpenIdAuthDataFormKey);
- writer.WriteAttribute("value", viewstateAuthData, true);
- writer.Write(" />");
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
}
+ writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingLeft, "18px");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "1px");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "lightgray");
+ writer.RenderBeginTag(HtmlTextWriterTag.Input);
+ writer.RenderEndTag(); // </input>
+ writer.RenderEndTag(); // </span>
}
/// <summary>
- /// Filters a sequence of OP endpoints so that an OP hostname only appears once in the list.
+ /// When implemented by a class, processes postback data for an ASP.NET server control.
/// </summary>
- /// <param name="requests">The authentication requests against those OP endpoints.</param>
- /// <returns>The filtered list.</returns>
- private static List<IAuthenticationRequest> RemoveDuplicateEndpoints(List<IAuthenticationRequest> requests) {
- var filteredRequests = new List<IAuthenticationRequest>(requests.Count);
- foreach (IAuthenticationRequest request in requests) {
- // We'll distinguish based on the host name only, which
- // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well,
- // this multiple OP attempt thing was just a convenience feature anyway.
- if (!filteredRequests.Any(req => string.Equals(req.Provider.Uri.Host, request.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase))) {
- filteredRequests.Add(request);
- }
- }
-
- return filteredRequests;
- }
-
- /// <summary>
- /// Creates the relying party.
- /// </summary>
- /// <param name="verifySignature">
- /// A value indicating whether message protections should be applied to the processed messages.
- /// Use <c>false</c> to postpone verification to a later time without invalidating nonces.
- /// </param>
- /// <returns>The newly instantiated relying party.</returns>
- private static OpenIdRelyingParty CreateRelyingParty(bool verifySignature) {
- return verifySignature ? new OpenIdRelyingParty() : OpenIdRelyingParty.CreateNonVerifying();
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- private void Dispose(bool disposing) {
- if (disposing) {
- if (this.relyingParty != null) {
- this.relyingParty.Dispose();
- this.relyingParty = null;
- }
-
- if (this.relyingPartyNonVerifying != null) {
- this.relyingPartyNonVerifying.Dispose();
- this.relyingPartyNonVerifying = null;
+ /// <param name="postDataKey">The key identifier for the control.</param>
+ /// <param name="postCollection">The collection of all incoming name values.</param>
+ /// <returns>
+ /// true if the server control's state changes as a result of the postback; otherwise, false.
+ /// </returns>
+ protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
+ // If the control was temporarily hidden, it won't be in the Form data,
+ // and we'll just implicitly keep the last Text setting.
+ if (postCollection[this.Name] != null) {
+ Identifier identifier = postCollection[this.Name].Length == 0 ? null : postCollection[this.Name];
+ if (identifier != this.Identifier) {
+ this.Identifier = identifier;
+ return true;
}
}
- }
-
- /// <summary>
- /// Fires the <see cref="LoggingIn"/> event.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns><c>true</c> if the login should proceed; <c>false</c> otherwise.</returns>
- private bool OnLoggingIn(IAuthenticationRequest request) {
- var loggingIn = this.LoggingIn;
- if (loggingIn != null) {
- var args = new OpenIdEventArgs(request);
- loggingIn(this, args);
- return !args.Cancel;
- }
- return true;
+ return false;
}
/// <summary>
- /// Fires the <see cref="UnconfirmedPositiveAssertion"/> event.
+ /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
/// </summary>
- private void OnUnconfirmedPositiveAssertion() {
- var unconfirmedPositiveAssertion = this.UnconfirmedPositiveAssertion;
- if (unconfirmedPositiveAssertion != null) {
- unconfirmedPositiveAssertion(this, null);
- }
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Predefined signature.")]
+ protected virtual void RaisePostDataChangedEvent() {
+ this.OnTextChanged();
}
/// <summary>
- /// Fires the <see cref="LoggedIn"/> event.
+ /// Called on a postback when the Text property has changed.
/// </summary>
- /// <param name="response">The response.</param>
- private void OnLoggedIn(IAuthenticationResponse response) {
- var loggedIn = this.LoggedIn;
- if (loggedIn != null) {
- loggedIn(this, new OpenIdEventArgs(response));
+ protected virtual void OnTextChanged() {
+ EventHandler textChanged = this.TextChanged;
+ if (textChanged != null) {
+ textChanged(this, EventArgs.Empty);
}
}
/// <summary>
- /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
- /// and closes the calling popup window if applicable.
- /// </summary>
- /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
- /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
- private void CallbackUserAgentMethod(string methodCall) {
- this.CallbackUserAgentMethod(methodCall, null);
- }
-
- /// <summary>
- /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
- /// and closes the calling popup window if applicable.
- /// </summary>
- /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
- /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
- /// <param name="preAssignments">An optional list of assignments to make to the input box object before placing the method call.</param>
- private void CallbackUserAgentMethod(string methodCall, string[] preAssignments) {
- Logger.OpenId.InfoFormat("Sending Javascript callback: {0}", methodCall);
- Page.Response.Write(@"<html><body><script language='javascript'>
- var inPopup = !window.frameElement;
- var objSrc = inPopup ? window.opener.waiting_openidBox : window.frameElement.openidBox;
-");
- if (preAssignments != null) {
- foreach (string assignment in preAssignments) {
- Page.Response.Write(string.Format(CultureInfo.InvariantCulture, " objSrc.{0};\n", assignment));
- }
- }
-
- // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable,
- // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already
- // whether to call window.self.close() after the call.
- string htmlFormat = @" if (inPopup) {{
- objSrc.{0};
- window.self.close();
-}} else {{
- objSrc.{0};
-}}
-</script></body></html>";
- Page.Response.Write(string.Format(CultureInfo.InvariantCulture, htmlFormat, methodCall));
- Page.Response.End();
- }
-
- /// <summary>
/// Assembles the javascript to send to the client and registers it with ASP.NET for transmission.
/// </summary>
private void PrepareClientJavascript() {
- string identifierParameterName = "identifier";
- string discoveryCallbackResultParameterName = "resultFunction";
- string discoveryErrorCallbackParameterName = "errorCallback";
- string discoveryCallback = Page.ClientScript.GetCallbackEventReference(
- this,
- identifierParameterName,
- discoveryCallbackResultParameterName,
- identifierParameterName,
- discoveryErrorCallbackParameterName,
- true);
-
// Import the .js file where most of the code is.
this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdAjaxTextBox), EmbeddedScriptResourceName);
// Call into the .js file with initialization information.
StringBuilder startupScript = new StringBuilder();
- startupScript.AppendLine("<script language='javascript'>");
startupScript.AppendFormat("var box = document.getElementsByName('{0}')[0];{1}", this.Name, Environment.NewLine);
- if (this.focusCalled) {
- startupScript.AppendLine("box.focus();");
- }
startupScript.AppendFormat(
CultureInfo.InvariantCulture,
- "initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, function({18}, {19}, {20}) {{{21}}});{22}",
+ "initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, function() {{{20};}});{21}",
MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), OpenIdTextBox.EmbeddedLogoResourceName)),
- MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedDotNetOpenIdLogoResourceName)),
MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedSpinnerResourceName)),
MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginSuccessResourceName)),
MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginFailureResourceName)),
@@ -1202,6 +842,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
string.IsNullOrEmpty(this.OnClientAssertionReceived) ? "null" : "'" + this.OnClientAssertionReceived.Replace(@"\", @"\\").Replace("'", @"\'") + "'",
MessagingUtilities.GetSafeJavascriptValue(this.LogOnText),
MessagingUtilities.GetSafeJavascriptValue(this.LogOnToolTip),
+ this.ShowLogOnPostBackButton ? "true" : "false",
+ MessagingUtilities.GetSafeJavascriptValue(this.LogOnPostBackToolTip),
MessagingUtilities.GetSafeJavascriptValue(this.RetryText),
MessagingUtilities.GetSafeJavascriptValue(this.RetryToolTip),
MessagingUtilities.GetSafeJavascriptValue(this.BusyToolTip),
@@ -1210,124 +852,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
MessagingUtilities.GetSafeJavascriptValue(this.AuthenticationSucceededToolTip),
MessagingUtilities.GetSafeJavascriptValue(this.AuthenticatedAsToolTip),
MessagingUtilities.GetSafeJavascriptValue(this.AuthenticationFailedToolTip),
- identifierParameterName,
- discoveryCallbackResultParameterName,
- discoveryErrorCallbackParameterName,
- discoveryCallback,
+ this.AutoPostBack ? "true" : "false",
+ Page.ClientScript.GetPostBackEventReference(this, null),
Environment.NewLine);
- startupScript.AppendLine("</script>");
-
- Page.ClientScript.RegisterStartupScript(this.GetType(), "ajaxstartup", startupScript.ToString());
- string htmlFormat = @"
+ ScriptManager.RegisterStartupScript(this, this.GetType(), "ajaxstartup", startupScript.ToString(), true);
+ if (this.HookFormSubmit) {
+ string htmlFormat = @"
var openidbox = document.getElementsByName('{0}')[0];
if (!openidbox.dnoi_internal.onSubmit()) {{ return false; }}
";
- Page.ClientScript.RegisterOnSubmitStatement(
- this.GetType(),
- "loginvalidation",
- string.Format(CultureInfo.InvariantCulture, htmlFormat, this.Name));
- }
-
- /// <summary>
- /// Creates the authentication requests for a given user-supplied Identifier.
- /// </summary>
- /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
- /// <param name="immediate">A value indicating whether the authentication
- /// requests should be initialized for use in invisible iframes for background authentication.</param>
- /// <returns>The list of authentication requests, any one of which may be
- /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
- private IEnumerable<IAuthenticationRequest> CreateRequests(string userSuppliedIdentifier, bool immediate) {
- var requests = new List<IAuthenticationRequest>();
-
- // Approximate the returnTo (either based on the customize property or the page URL)
- // so we can use it to help with Realm resolution.
- Uri returnToApproximation = this.ReturnToUrl != null ? new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.ReturnToUrl) : this.Page.Request.Url;
-
- // Resolve the trust root, and swap out the scheme and port if necessary to match the
- // return_to URL, since this match is required by OpenId, and the consumer app
- // may be using HTTP at some times and HTTPS at others.
- UriBuilder realm = OpenIdUtilities.GetResolvedRealm(this.Page, this.RealmUrl, this.RelyingParty.Channel.GetRequestFromContext());
- realm.Scheme = returnToApproximation.Scheme;
- realm.Port = returnToApproximation.Port;
-
- // Initiate openid request
- // We use TryParse here to avoid throwing an exception which
- // might slip through our validator control if it is disabled.
- Realm typedRealm = new Realm(realm);
- if (string.IsNullOrEmpty(this.ReturnToUrl)) {
- requests.AddRange(this.RelyingParty.CreateRequests(userSuppliedIdentifier, typedRealm));
- } else {
- // Since the user actually gave us a return_to value,
- // the "approximation" is exactly what we want.
- requests.AddRange(this.RelyingParty.CreateRequests(userSuppliedIdentifier, typedRealm, returnToApproximation));
- }
-
- // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example).
- // Since we're gathering OPs to try one after the other, just take the first choice of each OP
- // and don't try it multiple times.
- requests = RemoveDuplicateEndpoints(requests);
-
- // Configure each generated request.
- int reqIndex = 0;
- foreach (var req in requests) {
- req.AddCallbackArguments("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
-
- if (req.Provider.IsExtensionSupported<UIRequest>()) {
- // Inform the OP that we'll be using a popup window.
- req.AddExtension(new UIRequest());
-
- // Provide a hint for the client javascript about whether the OP supports the UI extension.
- // This is so the window can be made the correct size for the extension.
- // If the OP doesn't advertise support for the extension, the javascript will use
- // a bigger popup window.
- req.AddCallbackArguments("dotnetopenid.popupUISupported", "1");
- }
-
- // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter
- if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)["dotnetopenid.userSuppliedIdentifier"])) {
- req.AddCallbackArguments("dotnetopenid.userSuppliedIdentifier", userSuppliedIdentifier);
- }
-
- // Our javascript needs to let the user know which endpoint responded. So we force it here.
- // This gives us the info even for 1.0 OPs and 2.0 setup_required responses.
- req.AddCallbackArguments("dotnetopenid.op_endpoint", req.Provider.Uri.AbsoluteUri);
- req.AddCallbackArguments("dotnetopenid.claimed_id", (string)req.ClaimedIdentifier ?? string.Empty);
- req.AddCallbackArguments("dotnetopenid.phase", "2");
- if (immediate) {
- req.Mode = AuthenticationRequestMode.Immediate;
- ((AuthenticationRequest)req).AssociationPreference = AssociationPreference.IfAlreadyEstablished;
- }
- }
-
- return requests;
- }
-
- /// <summary>
- /// Notifies the user agent via an AJAX response of a completed authentication attempt.
- /// </summary>
- private void ReportAuthenticationResult() {
- Logger.OpenId.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
- List<string> assignments = new List<string>();
-
- var authResponse = this.RelyingPartyNonVerifying.GetResponse();
- if (authResponse.Status == AuthenticationStatus.Authenticated) {
- this.OnUnconfirmedPositiveAssertion();
- foreach (var pair in this.clientScriptExtensions) {
- IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key);
- if (extension == null) {
- continue;
- }
- var positiveResponse = (PositiveAuthenticationResponse)authResponse;
- string js = extension.InitializeJavaScriptData(positiveResponse.Response);
- if (string.IsNullOrEmpty(js)) {
- js = "null";
- }
- assignments.Add(pair.Value + " = " + js);
- }
+ Page.ClientScript.RegisterOnSubmitStatement(
+ this.GetType(),
+ "loginvalidation",
+ string.Format(CultureInfo.InvariantCulture, htmlFormat, this.Name));
}
-
- this.CallbackUserAgentMethod("dnoi_internal.processAuthorizationResult(document.URL)", assignments.ToArray());
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.css b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.css
new file mode 100644
index 0000000..bed2e79
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.css
@@ -0,0 +1,49 @@
+.OpenIdAjaxTextBox input
+{
+ margin: 0px;
+}
+
+.OpenIdAjaxTextBox > span
+{
+ position: absolute;
+ right: -1px;
+ top: 2px;
+}
+
+.OpenIdAjaxTextBox input[type=button]
+{
+ visibility: hidden;
+ position: absolute;
+ padding: 0px;
+ font-size: 8px;
+ top: 1px;
+ bottom: 1px;
+ right: 2px;
+}
+
+.OpenIdAjaxTextBox .yui-split-button span button
+{
+ font-size: 50%;
+ font-size: 60%\9; /* the \9 is a hack that causes only IE7/8 to use this value. */
+ line-height: 1;
+ min-height: 1em;
+ padding-top: 2px;
+ padding-top: 3px\9;
+ padding-bottom: 1px;
+ padding-left: 5px;
+ height: auto;
+}
+
+.OpenIdAjaxTextBox .yuimenuitem .yuimenuitemlabel
+{
+ padding-left: 5px;
+}
+
+.OpenIdAjaxTextBox .yuimenuitem .yuimenuitemlabel img
+{
+ border: 0;
+ margin-right: 4px;
+ vertical-align: middle;
+ width: 16px;
+ height: 16px;
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
index 1078003..9907b4e 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
@@ -1,152 +1,101 @@
//-----------------------------------------------------------------------
// <copyright file="OpenIdAjaxTextBox.js" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
+// This file may be used and redistributed under the terms of the
+// Microsoft Public License (Ms-PL) http://opensource.org/licenses/ms-pl.html
// </copyright>
//-----------------------------------------------------------------------
-// Options that can be set on the host page:
-//window.openid_visible_iframe = true; // causes the hidden iframe to show up
-//window.openid_trace = true; // causes lots of messages
-
-function trace(msg) {
- if (window.openid_trace) {
- if (!window.tracediv) {
- window.tracediv = document.createElement("ol");
- document.body.appendChild(window.tracediv);
- }
- var el = document.createElement("li");
- el.appendChild(document.createTextNode(msg));
- window.tracediv.appendChild(el);
- //alert(msg);
- }
-}
-
-/// <summary>Removes a given element from the array.</summary>
-/// <returns>True if the element was in the array, or false if it was not found.</returns>
-Array.prototype.remove = function(element) {
- function elementToRemoveLast(a, b) {
- if (a == element) { return 1; }
- if (b == element) { return -1; }
- return 0;
- }
- this.sort(elementToRemoveLast);
- if (this[this.length - 1] == element) {
- this.pop();
- return true;
- } else {
- return false;
- }
-};
-
-function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url, success_icon_url, failure_icon_url,
+function initAjaxOpenId(box, openid_logo_url, spinner_url, success_icon_url, failure_icon_url,
throttle, timeout, assertionReceivedCode,
- loginButtonText, loginButtonToolTip, retryButtonText, retryButtonToolTip, busyToolTip,
+ loginButtonText, loginButtonToolTip, showLoginPostBackButton, loginPostBackToolTip,
+ retryButtonText, retryButtonToolTip, busyToolTip,
identifierRequiredMessage, loginInProgressMessage,
authenticatedByToolTip, authenticatedAsToolTip, authenticationFailedToolTip,
- discoverCallback, discoveryFailedCallback) {
- box.dnoi_internal = new Object();
+ autoPostback, postback) {
+ box.dnoi_internal = {
+ postback: postback
+ };
if (assertionReceivedCode) {
- box.dnoi_internal.onauthenticated = function(sender, e) { eval(assertionReceivedCode); }
+ box.dnoi_internal.onauthenticated = function(sender, e) { eval(assertionReceivedCode); };
}
box.dnoi_internal.originalBackground = box.style.background;
box.timeout = timeout;
- box.dnoi_internal.discoverIdentifier = discoverCallback;
- box.dnoi_internal.authenticationRequests = new Array();
-
- // The possible authentication results
- var authSuccess = new Object();
- var authRefused = new Object();
- var timedOut = new Object();
-
- function FrameManager(maxFrames) {
- this.queuedWork = new Array();
- this.frames = new Array();
- this.maxFrames = maxFrames;
-
- /// <summary>Called to queue up some work that will use an iframe as soon as it is available.</summary>
- /// <param name="job">
- /// A delegate that must return the url to point to iframe to.
- /// Its first parameter is the iframe created to service the request.
- /// It will only be called when the work actually begins.
- /// </param>
- this.enqueueWork = function(job) {
- // Assign an iframe to this task immediately if there is one available.
- if (this.frames.length < this.maxFrames) {
- this.createIFrame(job);
- } else {
- this.queuedWork.unshift(job);
- }
- };
- /// <summary>Clears the job queue and immediately closes all iframes.</summary>
- this.cancelAllWork = function() {
- trace('Canceling all open and pending iframes.');
- while (this.queuedWork.pop());
- this.closeFrames();
- };
-
- /// <summary>An event fired when a frame is closing.</summary>
- this.onJobCompleted = function() {
- // If there is a job in the queue, go ahead and start it up.
- if (job = this.queuedWork.pop()) {
- this.createIFrame(job);
- }
- }
-
- this.createIFrame = function(job) {
- var iframe = document.createElement("iframe");
- if (!window.openid_visible_iframe) {
- iframe.setAttribute("width", 0);
- iframe.setAttribute("height", 0);
- iframe.setAttribute("style", "display: none");
- }
- iframe.setAttribute("src", job(iframe));
- iframe.openidBox = box;
- box.parentNode.insertBefore(iframe, box);
- this.frames.push(iframe);
- return iframe;
- };
- this.closeFrames = function() {
- if (this.frames.length == 0) { return false; }
- for (var i = 0; i < this.frames.length; i++) {
- if (this.frames[i].parentNode) { this.frames[i].parentNode.removeChild(this.frames[i]); }
- }
- while (this.frames.length > 0) { this.frames.pop(); }
- return true;
- };
- this.closeFrame = function(frame) {
- if (frame.parentNode) { frame.parentNode.removeChild(frame); }
- var removed = this.frames.remove(frame);
- this.onJobCompleted();
- return removed;
- };
- }
-
- box.dnoi_internal.authenticationIFrames = new FrameManager(throttle);
+ box.dnoi_internal.authenticationIFrames = new window.dnoa_internal.FrameManager(throttle);
box.dnoi_internal.constructButton = function(text, tooltip, onclick) {
var button = document.createElement('input');
button.textContent = text; // Mozilla
button.value = text; // IE
button.type = 'button';
- button.title = tooltip != null ? tooltip : '';
+ button.title = tooltip || '';
button.onclick = onclick;
- button.style.visibility = 'hidden';
- button.style.position = 'absolute';
- button.style.padding = "0px";
- button.style.fontSize = '8px';
- button.style.top = "1px";
- button.style.bottom = "1px";
- button.style.right = "2px";
box.parentNode.appendChild(button);
return button;
- }
+ };
+
+ box.dnoi_internal.constructSplitButton = function(text, tooltip, onclick, menu) {
+ var htmlButton = box.dnoi_internal.constructButton(text, tooltip, onclick);
+
+ if (!box.parentNode.className || box.parentNode.className.indexOf(' yui-skin-sam') < 0) {
+ box.parentNode.className = (box.parentNode.className || '') + ' yui-skin-sam';
+ }
+
+ var splitButton = new YAHOO.widget.Button(htmlButton, {
+ type: 'split',
+ menu: menu
+ });
+
+ splitButton.on('click', onclick);
+
+ return splitButton;
+ };
+
+ box.dnoi_internal.createLoginPostBackButton = function() {
+ var postback = function() {
+ var discoveryResult = window.dnoa_internal.discoveryResults[box.value];
+ var respondingEndpoint = discoveryResult.findSuccessfulRequest();
+ box.dnoi_internal.postback(discoveryResult, respondingEndpoint, respondingEndpoint.extensionResponses, { background: false });
+ };
+ var button = box.dnoi_internal.constructButton(loginButtonText, loginPostBackToolTip, postback);
+ button.style.visibility = 'visible';
+ button.destroy = function() {
+ button.parentNode.removeChild(button);
+ };
+
+ return button;
+ };
+
+ box.dnoi_internal.createLoginButton = function(providers) {
+ var onMenuItemClick = function(p_sType, p_aArgs, p_oItem) {
+ var selectedProvider = (p_oItem && p_oItem.value) ? p_oItem.value : providers[0].value;
+ selectedProvider.loginPopup();
+ return false;
+ };
+
+ for (var i = 0; i < providers.length; i++) {
+ providers[i].onclick = { fn: onMenuItemClick };
+ }
+
+ // We'll use the split button if we have more than one Provider, and the YUI library is available.
+ if (providers.length > 1 && YAHOO && YAHOO.widget && YAHOO.widget.Button) {
+ return box.dnoi_internal.constructSplitButton(loginButtonText, loginButtonToolTip, onMenuItemClick, providers);
+ } else {
+ var button = box.dnoi_internal.constructButton(loginButtonText, loginButtonToolTip, onMenuItemClick);
+ button.style.visibility = 'visible';
+ button.destroy = function() {
+ button.parentNode.removeChild(button);
+ };
+ return button;
+ }
+ };
box.dnoi_internal.constructIcon = function(imageUrl, tooltip, rightSide, visible, height) {
var icon = document.createElement('img');
icon.src = imageUrl;
- icon.title = tooltip != null ? tooltip : '';
+ icon.title = tooltip || '';
icon.originalTitle = icon.title;
if (!visible) {
icon.style.visibility = 'hidden';
@@ -164,7 +113,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
}
box.parentNode.appendChild(icon);
return icon;
- }
+ };
box.dnoi_internal.prefetchImage = function(imageUrl) {
var img = document.createElement('img');
@@ -172,15 +121,15 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
img.style.display = 'none';
box.parentNode.appendChild(img);
return img;
- }
+ };
function findParentForm(element) {
- if (element == null || element.nodeName == "FORM") {
+ if (!element || element.nodeName == "FORM") {
return element;
}
return findParentForm(element.parentNode);
- };
+ }
box.parentForm = findParentForm(box);
@@ -196,22 +145,11 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
hiddenField.setAttribute("type", "hidden");
box.parentForm.appendChild(hiddenField);
return hiddenField;
- };
+ }
- box.dnoi_internal.loginButton = box.dnoi_internal.constructButton(loginButtonText, loginButtonToolTip, function() {
- var discoveryInfo = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier];
- if (discoveryInfo == null) {
- trace('Ooops! Somehow the login button click event was invoked, but no openid discovery information for ' + box.lastDiscoveredIdentifier + ' is available.');
- return;
- }
- // The login button always sends a setup message to the first OP.
- var selectedProvider = discoveryInfo[0];
- selectedProvider.trySetup();
- return false;
- });
box.dnoi_internal.retryButton = box.dnoi_internal.constructButton(retryButtonText, retryButtonToolTip, function() {
box.timeout += 5000; // give the retry attempt 5s longer than the last attempt
- box.dnoi_internal.performDiscovery(box.value);
+ box.dnoi_internal.performDiscovery();
return false;
});
box.dnoi_internal.openid_logo = box.dnoi_internal.constructIcon(openid_logo_url, null, false, true);
@@ -219,24 +157,30 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.op_logo.style.maxWidth = '16px';
box.dnoi_internal.spinner = box.dnoi_internal.constructIcon(spinner_url, busyToolTip, true);
box.dnoi_internal.success_icon = box.dnoi_internal.constructIcon(success_icon_url, authenticatedAsToolTip, true);
- //box.dnoi_internal.failure_icon = box.dnoi_internal.constructIcon(failure_icon_url, authenticationFailedToolTip, true);
+ box.dnoi_internal.failure_icon = box.dnoi_internal.constructIcon(failure_icon_url, authenticationFailedToolTip, true);
- // Disable the display of the DotNetOpenId logo
- //box.dnoi_internal.dnoi_logo = box.dnoi_internal.constructIcon(dotnetopenid_logo_url);
box.dnoi_internal.dnoi_logo = box.dnoi_internal.openid_logo;
- box.dnoi_internal.setVisualCue = function(state, authenticatedBy, authenticatedAs) {
+ box.dnoi_internal.setVisualCue = function(state, authenticatedBy, authenticatedAs, providers, errorMessage) {
box.dnoi_internal.openid_logo.style.visibility = 'hidden';
box.dnoi_internal.dnoi_logo.style.visibility = 'hidden';
box.dnoi_internal.op_logo.style.visibility = 'hidden';
box.dnoi_internal.openid_logo.title = box.dnoi_internal.openid_logo.originalTitle;
box.dnoi_internal.spinner.style.visibility = 'hidden';
box.dnoi_internal.success_icon.style.visibility = 'hidden';
- // box.dnoi_internal.failure_icon.style.visibility = 'hidden';
- box.dnoi_internal.loginButton.style.visibility = 'hidden';
+ box.dnoi_internal.failure_icon.style.visibility = 'hidden';
box.dnoi_internal.retryButton.style.visibility = 'hidden';
+ if (box.dnoi_internal.loginButton) {
+ box.dnoi_internal.loginButton.destroy();
+ box.dnoi_internal.loginButton = null;
+ }
+ if (box.dnoi_internal.postbackLoginButton) {
+ box.dnoi_internal.postbackLoginButton.destroy();
+ box.dnoi_internal.postbackLoginButton = null;
+ }
box.title = '';
box.dnoi_internal.state = state;
+ var opLogo;
if (state == "discovering") {
box.dnoi_internal.dnoi_logo.style.visibility = 'visible';
box.dnoi_internal.spinner.style.visibility = 'visible';
@@ -244,42 +188,55 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.title = '';
window.status = "Discovering OpenID Identifier '" + box.value + "'...";
} else if (state == "authenticated") {
- var opLogo = box.dnoi_internal.deriveOPFavIcon();
+ opLogo = box.dnoi_internal.deriveOPFavIcon();
if (opLogo) {
box.dnoi_internal.op_logo.src = opLogo;
box.dnoi_internal.op_logo.style.visibility = 'visible';
box.dnoi_internal.op_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
}
- trace("OP icon size: " + box.dnoi_internal.op_logo.fileSize);
- if (opLogo == null || box.dnoi_internal.op_logo.fileSize == -1 /*IE*/ || box.dnoi_internal.op_logo.fileSize === undefined /* FF */) {
+ //trace("OP icon size: " + box.dnoi_internal.op_logo.fileSize);
+ // The filesize check just doesn't seem to work any more.
+ if (!opLogo) {// || box.dnoi_internal.op_logo.fileSize == -1 /*IE*/ || box.dnoi_internal.op_logo.fileSize === undefined /* FF */) {
trace('recovering from missing OP icon');
box.dnoi_internal.op_logo.style.visibility = 'hidden';
box.dnoi_internal.openid_logo.style.visibility = 'visible';
box.dnoi_internal.openid_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
}
- box.dnoi_internal.success_icon.style.visibility = 'visible';
- box.dnoi_internal.success_icon.title = box.dnoi_internal.success_icon.originalTitle.replace('{0}', authenticatedAs);
+ if (showLoginPostBackButton) {
+ box.dnoi_internal.postbackLoginButton = box.dnoi_internal.createLoginPostBackButton();
+ } else {
+ box.dnoi_internal.success_icon.style.visibility = 'visible';
+ box.dnoi_internal.success_icon.title = box.dnoi_internal.success_icon.originalTitle.replace('{0}', authenticatedAs);
+ }
box.title = box.dnoi_internal.claimedIdentifier;
- window.status = "Authenticated as " + box.value;
+ window.status = "Authenticated as " + authenticatedAs;
} else if (state == "setup") {
- var opLogo = box.dnoi_internal.deriveOPFavIcon();
+ opLogo = box.dnoi_internal.deriveOPFavIcon();
if (opLogo) {
box.dnoi_internal.op_logo.src = opLogo;
box.dnoi_internal.op_logo.style.visibility = 'visible';
} else {
box.dnoi_internal.openid_logo.style.visibility = 'visible';
}
- box.dnoi_internal.loginButton.style.visibility = 'visible';
+
+ box.dnoi_internal.loginButton = box.dnoi_internal.createLoginButton(providers);
+
box.dnoi_internal.claimedIdentifier = null;
- window.status = "Authentication requires setup.";
+ window.status = "Authentication requires user interaction.";
} else if (state == "failed") {
box.dnoi_internal.openid_logo.style.visibility = 'visible';
- //box.dnoi_internal.failure_icon.style.visibility = 'visible';
box.dnoi_internal.retryButton.style.visibility = 'visible';
box.dnoi_internal.claimedIdentifier = null;
window.status = authenticationFailedToolTip;
box.title = authenticationFailedToolTip;
- } else if (state == '' || state == null) {
+ } else if (state == "failednoretry") {
+ box.dnoi_internal.failure_icon.title = errorMessage;
+ box.dnoi_internal.failure_icon.style.visibility = 'visible';
+ box.dnoi_internal.openid_logo.style.visibility = 'visible';
+ box.dnoi_internal.claimedIdentifier = null;
+ window.status = errorMessage;
+ box.title = errorMessage;
+ } else if (state == '' || !state) {
box.dnoi_internal.openid_logo.style.visibility = 'visible';
box.title = '';
box.dnoi_internal.claimedIdentifier = null;
@@ -288,35 +245,39 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.claimedIdentifier = null;
trace('unrecognized state ' + state);
}
- }
+
+ if (box.onStateChanged) {
+ box.onStateChanged(state);
+ }
+ };
box.dnoi_internal.isBusy = function() {
- var lastDiscovery = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier];
+ var lastDiscovery = window.dnoa_internal.discoveryResults[box.lastDiscoveredIdentifier];
return box.dnoi_internal.state == 'discovering' ||
(lastDiscovery && lastDiscovery.busy());
};
box.dnoi_internal.canAttemptLogin = function() {
- if (box.value.length == 0) return false;
- if (box.dnoi_internal.authenticationRequests[box.value] == null) return false;
- if (box.dnoi_internal.state == 'failed') return false;
+ if (box.value.length === 0) { return false; }
+ if (!window.dnoa_internal.discoveryResults[box.value]) { return false; }
+ if (box.dnoi_internal.state == 'failed') { return false; }
return true;
};
box.dnoi_internal.getUserSuppliedIdentifierResults = function() {
- return box.dnoi_internal.authenticationRequests[box.value];
- }
+ return window.dnoa_internal.discoveryResults[box.value];
+ };
box.dnoi_internal.isAuthenticated = function() {
var results = box.dnoi_internal.getUserSuppliedIdentifierResults();
- return results != null && results.findSuccessfulRequest() != null;
- }
+ return results && results.findSuccessfulRequest();
+ };
box.dnoi_internal.onSubmit = function() {
var hiddenField = findOrCreateHiddenField();
if (box.dnoi_internal.isAuthenticated()) {
// stick the result in a hidden field so the RP can verify it
- hiddenField.setAttribute("value", box.dnoi_internal.authenticationRequests[box.value].successAuthData);
+ hiddenField.setAttribute("value", window.dnoa_internal.discoveryResults[box.value].successAuthData);
} else {
hiddenField.setAttribute("value", '');
if (box.dnoi_internal.isBusy()) {
@@ -332,7 +293,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
// after leaving a note for ourselves to automatically click submit
// when login is complete.
box.dnoi_internal.submitPending = box.dnoi_internal.submitButtonJustClicked;
- if (box.dnoi_internal.submitPending == null) {
+ if (box.dnoi_internal.submitPending === null) {
box.dnoi_internal.submitPending = true;
}
box.dnoi_internal.loginButton.onclick();
@@ -380,398 +341,304 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
/// <summary>
/// Returns the URL of the authenticating OP's logo so it can be displayed to the user.
/// </summary>
- box.dnoi_internal.deriveOPFavIcon = function() {
- var response = box.dnoi_internal.getUserSuppliedIdentifierResults().successAuthData;
- if (!response || response.length == 0) return;
- var authResult = new Uri(response);
- var opUri;
- if (authResult.getQueryArgValue("openid.op_endpoint")) {
- opUri = new Uri(authResult.getQueryArgValue("openid.op_endpoint"));
- } if (authResult.getQueryArgValue("dotnetopenid.op_endpoint")) {
- opUri = new Uri(authResult.getQueryArgValue("dotnetopenid.op_endpoint"));
- } else if (authResult.getQueryArgValue("openid.user_setup_url")) {
- opUri = new Uri(authResult.getQueryArgValue("openid.user_setup_url"));
- } else return null;
- var favicon = opUri.getAuthority() + "/favicon.ico";
- return favicon;
- };
-
- box.dnoi_internal.createDiscoveryInfo = function(discoveryInfo, identifier) {
- this.identifier = identifier;
- // The claimed identifier may be null if the user provided an OP Identifier.
- this.claimedIdentifier = discoveryInfo.claimedIdentifier;
- trace('Discovered claimed identifier: ' + this.claimedIdentifier);
-
- // Add extra tracking bits and behaviors.
- this.findByEndpoint = function(opEndpoint) {
- for (var i = 0; i < this.length; i++) {
- if (this[i].endpoint == opEndpoint) {
- return this[i];
- }
+ /// <param name="opUri">The OP Endpoint, if known.</param>
+ box.dnoi_internal.deriveOPFavIcon = function(opUri) {
+ if (!opUri) {
+ var idresults = box.dnoi_internal.getUserSuppliedIdentifierResults();
+ var response = idresults ? idresults.successAuthData : null;
+ if (!response || response.length === 0) {
+ trace('No favicon because no successAuthData.');
+ return;
}
- };
- this.findSuccessfulRequest = function() {
- for (var i = 0; i < this.length; i++) {
- if (this[i].result == authSuccess) {
- return this[i];
- }
- }
- };
- this.busy = function() {
- for (var i = 0; i < this.length; i++) {
- if (this[i].busy()) {
- return true;
- }
- }
- };
- this.abortAll = function() {
- // Abort all other asynchronous authentication attempts that may be in progress.
- box.dnoi_internal.authenticationIFrames.cancelAllWork();
- for (var i = 0; i < this.length; i++) {
- this[i].abort();
- }
- };
- this.tryImmediate = function() {
- if (this.length > 0) {
- for (var i = 0; i < this.length; i++) {
- box.dnoi_internal.authenticationIFrames.enqueueWork(this[i].tryImmediate);
- }
+ var authResult = new window.dnoa_internal.Uri(response);
+ if (authResult.getQueryArgValue("openid.op_endpoint")) {
+ opUri = new window.dnoa_internal.Uri(authResult.getQueryArgValue("openid.op_endpoint"));
+ } else if (authResult.getQueryArgValue("dnoa.op_endpoint")) {
+ opUri = new window.dnoa_internal.Uri(authResult.getQueryArgValue("dnoa.op_endpoint"));
+ } else if (authResult.getQueryArgValue("openid.user_setup_url")) {
+ opUri = new window.dnoa_internal.Uri(authResult.getQueryArgValue("openid.user_setup_url"));
} else {
- box.dnoi_internal.discoveryFailed(null, this.identifier);
+ return null;
}
- };
-
- this.length = discoveryInfo.requests.length;
- for (var i = 0; i < discoveryInfo.requests.length; i++) {
- this[i] = new box.dnoi_internal.createTrackingRequest(discoveryInfo.requests[i], identifier);
}
+ var favicon = opUri.getAuthority() + "/favicon.ico";
+ trace('Guessing favicon location of: ' + favicon);
+ return favicon;
};
- box.dnoi_internal.createTrackingRequest = function(requestInfo, identifier) {
- // It's possible during a postback that discovered request URLs are not available.
- this.immediate = requestInfo.immediate ? new Uri(requestInfo.immediate) : null;
- this.setup = requestInfo.setup ? new Uri(requestInfo.setup) : null;
- this.endpoint = new Uri(requestInfo.endpoint);
- this.identifier = identifier;
- var self = this; // closure so that delegates have the right instance
-
- this.host = self.endpoint.getHost();
+ /*****************************************
+ * Event Handlers
+ *****************************************/
- this.getDiscoveryInfo = function() {
- return box.dnoi_internal.authenticationRequests[self.identifier];
+ window.dnoa_internal.addDiscoveryStarted(function(identifier) {
+ if (identifier == box.value) {
+ box.dnoi_internal.setVisualCue('discovering');
}
-
- this.busy = function() {
- return self.iframe != null || self.popup != null;
- };
-
- this.completeAttempt = function() {
- if (!self.busy()) return false;
- if (self.iframe) {
- trace('iframe hosting ' + self.endpoint + ' now CLOSING.');
- box.dnoi_internal.authenticationIFrames.closeFrame(self.iframe);
- self.iframe = null;
- }
- if (self.popup) {
- self.popup.close();
- self.popup = null;
- }
- if (self.timeout) {
- window.clearTimeout(self.timeout);
- self.timeout = null;
- }
-
- if (!self.getDiscoveryInfo().busy() && self.getDiscoveryInfo().findSuccessfulRequest() == null) {
- trace('No asynchronous authentication attempt is in progress. Display setup view.');
- // visual cue that auth failed
- box.dnoi_internal.setVisualCue('setup');
- }
-
- return true;
- };
-
- this.authenticationTimedOut = function() {
- if (self.completeAttempt()) {
- trace(self.host + " timed out");
- self.result = timedOut;
- }
- };
- this.authSuccess = function(authUri) {
- if (self.completeAttempt()) {
- trace(self.host + " authenticated!");
- self.result = authSuccess;
- self.response = authUri;
- box.dnoi_internal.authenticationRequests[self.identifier].abortAll();
- }
- };
- this.authFailed = function() {
- if (self.completeAttempt()) {
- //trace(self.host + " failed authentication");
- self.result = authRefused;
- }
- };
- this.abort = function() {
- if (self.completeAttempt()) {
- trace(self.host + " aborted");
- // leave the result as whatever it was before.
+ }, box);
+
+ window.dnoa_internal.addDiscoverySuccess(function(identifier, discoveryResult, state) {
+ if (identifier == box.value && (box.dnoi_internal.state == 'discovering' || !box.dnoi_internal.state)) {
+ // Start pre-fetching the OP favicons
+ for (var i = 0; i < discoveryResult.length; i++) {
+ var favicon = box.dnoi_internal.deriveOPFavIcon(discoveryResult[i].endpoint);
+ if (favicon) {
+ trace('Prefetching ' + favicon);
+ box.dnoi_internal.prefetchImage(favicon);
+ }
}
- };
-
- this.tryImmediate = function(iframe) {
- self.abort(); // ensure no concurrent attempts
- self.timeout = setTimeout(function() { self.authenticationTimedOut(); }, box.timeout);
- trace('iframe hosting ' + self.endpoint + ' now OPENING.');
- self.iframe = iframe;
- //trace('initiating auth attempt with: ' + self.immediate);
- return self.immediate.toString();
- };
- this.trySetup = function() {
- self.abort(); // ensure no concurrent attempts
- window.waiting_openidBox = box;
- var width = 800;
- var height = 600;
- if (self.setup.getQueryArgValue("openid.return_to").indexOf("dotnetopenid.popupUISupported") >= 0) {
- width = 450;
- height = 500;
+ if (discoveryResult.length > 0) {
+ discoveryResult.loginBackground(
+ box.dnoi_internal.authenticationIFrames,
+ null,
+ null,
+ null,
+ box.timeout);
+ } else {
+ // discovery completed successfully -- it just didn't yield any service endpoints.
+ box.dnoi_internal.setVisualCue('failednoretry', null, null, null, discoveryResult.error);
+ if (discoveryResult.error) { box.title = discoveryResult.error; }
}
+ }
+ }, box);
- var left = (screen.width - width) / 2;
- var top = (screen.height - height) / 2;
- self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
-
- // If the OP supports the UI extension it MAY close its own window
- // for a negative assertion. We must be able to recover from that scenario.
- var localSelf = self;
- self.popupCloseChecker = window.setInterval(function() {
- if (localSelf.popup && localSelf.popup.closed) {
- // So the user canceled and the window closed.
- // It turns out we hae nothing special to do.
- // If we were graying out the entire page while the child window was up,
- // we would probably revert that here.
- trace('User or OP canceled by closing the window.');
- window.clearInterval(localSelf.popupCloseChecker);
- localSelf.popup = null;
- }
- }, 250);
- };
- };
-
- /*****************************************
- * Flow
- *****************************************/
-
- /// <summary>Called to initiate discovery on some identifier.</summary>
- box.dnoi_internal.performDiscovery = function(identifier) {
- box.dnoi_internal.authenticationIFrames.closeFrames();
- box.dnoi_internal.setVisualCue('discovering');
- box.lastDiscoveredIdentifier = identifier;
- box.dnoi_internal.discoverIdentifier(identifier, box.dnoi_internal.discoveryResult, box.dnoi_internal.discoveryFailed);
- };
-
- /// <summary>Callback that is invoked when discovery fails.</summary>
- box.dnoi_internal.discoveryFailed = function(message, identifier) {
- box.dnoi_internal.setVisualCue('failed');
- if (message) { box.title = message; }
- }
-
- /// <summary>Callback that is invoked when discovery results are available.</summary>
- /// <param name="discoveryResult">The JSON object containing the OpenID auth requests.</param>
- /// <param name="identifier">The identifier that discovery was performed on.</param>
- box.dnoi_internal.discoveryResult = function(discoveryResult, identifier) {
- // Deserialize the JSON object and store the result if it was a successful discovery.
- discoveryResult = eval('(' + discoveryResult + ')');
- // Store the discovery results and added behavior for later use.
- box.dnoi_internal.authenticationRequests[identifier] = discoveryBehavior = new box.dnoi_internal.createDiscoveryInfo(discoveryResult, identifier);
-
- // Only act on the discovery event if we're still interested in the result.
- // If the user already changed the identifier since discovery was initiated,
- // we aren't interested in it any more.
- if (identifier == box.lastDiscoveredIdentifier) {
- discoveryBehavior.tryImmediate();
+ window.dnoa_internal.addDiscoveryFailed(function(identifier, message) {
+ if (identifier == box.value) {
+ box.dnoi_internal.setVisualCue('failed');
+ if (message) { box.title = message; }
}
- }
+ }, box);
- /// <summary>Invoked by RP web server when an authentication has completed.</summary>
- /// <remarks>The duty of this method is to distribute the notification to the appropriate tracking object.</remarks>
- box.dnoi_internal.processAuthorizationResult = function(resultUrl) {
- self.waiting_openidBox = null;
- //trace('processAuthorizationResult ' + resultUrl);
- var resultUri = new Uri(resultUrl);
-
- // Find the tracking object responsible for this request.
- var discoveryInfo = box.dnoi_internal.authenticationRequests[resultUri.getQueryArgValue('dotnetopenid.userSuppliedIdentifier')];
- if (discoveryInfo == null) {
- trace('processAuthorizationResult called but no userSuppliedIdentifier parameter was found. Exiting function.');
- return;
+ window.dnoa_internal.addAuthStarted(function(discoveryResult, serviceEndpoint, state) {
+ if (discoveryResult.userSuppliedIdentifier == box.value) {
+ box.dnoi_internal.setVisualCue('discovering');
}
- var opEndpoint = resultUri.getQueryArgValue("openid.op_endpoint") ? resultUri.getQueryArgValue("openid.op_endpoint") : resultUri.getQueryArgValue("dotnetopenid.op_endpoint");
- var tracker = discoveryInfo.findByEndpoint(opEndpoint);
- //trace('Auth result for ' + tracker.host + ' received:\n' + resultUrl);
-
- if (isAuthSuccessful(resultUri)) {
- tracker.authSuccess(resultUri);
-
- discoveryInfo.successAuthData = resultUrl;
- var claimed_id = resultUri.getQueryArgValue("openid.claimed_id");
- if (claimed_id && claimed_id != discoveryInfo.claimedIdentifier) {
- discoveryInfo.claimedIdentifier = resultUri.getQueryArgValue("openid.claimed_id");
- trace('Authenticated as ' + claimed_id);
- }
+ }, box);
+ window.dnoa_internal.addAuthSuccess(function(discoveryResult, serviceEndpoint, extensionResponses, state) {
+ if (discoveryResult.userSuppliedIdentifier == box.value) {
// visual cue that auth was successful
- box.dnoi_internal.claimedIdentifier = discoveryInfo.claimedIdentifier;
- box.dnoi_internal.setVisualCue('authenticated', tracker.endpoint, discoveryInfo.claimedIdentifier);
+ var parsedPositiveAssertion = new window.dnoa_internal.PositiveAssertion(discoveryResult.successAuthData);
+ box.dnoi_internal.claimedIdentifier = parsedPositiveAssertion.claimedIdentifier;
+
+ // If the OP doesn't support delegation, "correct" the identifier the user entered
+ // so he realizes his identity didn't stick. But don't change out OP Identifiers.
+ if (discoveryResult.claimedIdentifier && discoveryResult.claimedIdentifier != parsedPositiveAssertion.claimedIdentifier) {
+ box.value = parsedPositiveAssertion.claimedIdentifier;
+ box.lastDiscoveredIdentifier = box.value;
+
+ // Also inject a fake discovery result for this new identifier to keep the UI from performing
+ // discovery on the new identifier (the RP will perform the necessary verification server-side).
+ if (!window.dnoa_internal.discoveryResults[box.value]) {
+ // We must make sure that the only service endpoint from the earlier discovery that
+ // is copied over is the one that sent the assertion just now. Deep clone, then strip
+ // out the other SEPs.
+ window.dnoa_internal.discoveryResults[box.value] = discoveryResult.cloneWithOneServiceEndpoint(serviceEndpoint);
+ }
+ }
+ box.dnoi_internal.setVisualCue('authenticated', parsedPositiveAssertion.endpoint, parsedPositiveAssertion.claimedIdentifier);
if (box.dnoi_internal.onauthenticated) {
- box.dnoi_internal.onauthenticated(box);
+ box.dnoi_internal.onauthenticated(box, extensionResponses);
}
- if (box.dnoi_internal.submitPending) {
+
+ if (showLoginPostBackButton && !state.background) {
+ box.dnoi_internal.postback(discoveryResult, serviceEndpoint, extensionResponses, state);
+ } else if (box.dnoi_internal.submitPending) {
// We submit the form BEFORE resetting the submitPending so
// the submit handler knows we've already tried this route.
- if (box.dnoi_internal.submitPending == true) {
+ if (box.dnoi_internal.submitPending === true) {
box.parentForm.submit();
} else {
box.dnoi_internal.submitPending.click();
}
+
+ box.dnoi_internal.submitPending = null;
+ } else if (!state.deserialized && autoPostback) {
+ // as long as this is a fresh auth response, postback to the server if configured to do so.
+ box.dnoi_internal.postback(discoveryResult, serviceEndpoint, extensionResponses, state);
}
- } else {
- tracker.authFailed();
}
+ }, box);
- box.dnoi_internal.submitPending = null;
- };
+ window.dnoa_internal.addAuthFailed(function(discoveryResult, serviceEndpoint, state) {
+ if (discoveryResult.userSuppliedIdentifier == box.value) {
+ box.dnoi_internal.submitPending = null;
+ if (!serviceEndpoint || !state.background) { // if the last service endpoint just turned the user down
+ box.dnoi_internal.displayLoginButton(discoveryResult);
+ }
+ }
+ }, box);
+
+ window.dnoa_internal.addAuthCleared(function(discoveryResult, serviceEndpoint) {
+ if (discoveryResult.userSuppliedIdentifier == box.value) {
+ if (!discoveryResult.findSuccessfulRequest()) {
+ // attempt to renew the positive assertion.
+ discoveryResult.loginBackground(
+ box.dnoi_internal.authenticationIFrames,
+ null,
+ null,
+ null,
+ box.timeout);
+ }
+ }
+ }, box);
- function isAuthSuccessful(resultUri) {
- if (isOpenID2Response(resultUri)) {
- return resultUri.getQueryArgValue("openid.mode") == "id_res";
- } else {
- return resultUri.getQueryArgValue("openid.mode") == "id_res" && !resultUri.containsQueryArg("openid.user_setup_url");
+ /*****************************************
+ * Flow
+ *****************************************/
+
+ box.dnoi_internal.displayLoginButton = function(discoveryResult) {
+ trace('No asynchronous authentication attempt is in progress. Display setup view.');
+ var providers = [];
+ for (var i = 0; i < discoveryResult.length; i++) {
+ var favicon = box.dnoi_internal.deriveOPFavIcon(discoveryResult[i].endpoint);
+ var img = '<img src="' + favicon + '" />';
+ providers.push({ text: img + discoveryResult[i].host, value: discoveryResult[i] });
}
+
+ // visual cue that auth failed
+ box.dnoi_internal.setVisualCue('setup', null, null, providers);
};
- function isOpenID2Response(resultUri) {
- return resultUri.containsQueryArg("openid.ns");
+ /// <summary>Called to initiate discovery on some identifier.</summary>
+ box.dnoi_internal.performDiscovery = function() {
+ box.dnoi_internal.authenticationIFrames.closeFrames();
+ box.lastDiscoveredIdentifier = box.value;
+ var openid = new window.OpenIdIdentifier(box.value);
+ openid.discover();
};
box.onblur = function(event) {
- var discoveryInfo = box.dnoi_internal.authenticationRequests[box.value];
- if (discoveryInfo == null) {
+ if (box.lastDiscoveredIdentifier != box.value || !box.dnoi_internal.state) {
if (box.value.length > 0) {
- box.dnoi_internal.performDiscovery(box.value);
+ box.dnoi_internal.resetAndDiscover();
} else {
box.dnoi_internal.setVisualCue();
}
- } else {
- if ((priorSuccess = discoveryInfo.findSuccessfulRequest())) {
- box.dnoi_internal.setVisualCue('authenticated', priorSuccess.endpoint, discoveryInfo.claimedIdentifier);
- } else {
- discoveryInfo.tryImmediate();
- }
}
- return true;
- };
- box.onkeyup = function(event) {
- box.dnoi_internal.setVisualCue();
+
return true;
};
- box.getClaimedIdentifier = function() { return box.dnoi_internal.claimedIdentifier; };
-
- // Restore a previously achieved state (from pre-postback) if it is given.
- var oldAuth = findOrCreateHiddenField().value;
- if (oldAuth.length > 0) {
- var oldAuthResult = new Uri(oldAuth);
- // The control ensures that we ALWAYS have an OpenID 2.0-style claimed_id attribute, even against
- // 1.0 Providers via the return_to URL mechanism.
- var claimedId = oldAuthResult.getQueryArgValue("dotnetopenid.claimed_id");
- var endpoint = oldAuthResult.getQueryArgValue("dotnetopenid.op_endpoint");
- // We weren't given a full discovery history, but we can spoof this much from the
- // authentication assertion.
- box.dnoi_internal.authenticationRequests[box.value] = new box.dnoi_internal.createDiscoveryInfo({
- claimedIdentifier: claimedId,
- requests: [{ endpoint: endpoint }]
- }, box.value);
-
- box.dnoi_internal.processAuthorizationResult(oldAuthResult.toString());
- }
-}
+ //{
+ var rate = NaN;
+ var lastValue = box.value;
+ var keyPresses = 0;
+ var startTime = null;
+ var lastKeyPress = null;
+ var discoveryTimer;
+
+ function cancelTimer() {
+ if (discoveryTimer) {
+ trace('canceling timer', 'gray');
+ clearTimeout(discoveryTimer);
+ discoveryTimer = null;
+ }
+ }
-function Uri(url) {
- this.originalUri = url;
+ function identifierSanityCheck(id) {
+ return id.match("^[=@+$!(].+|.*?\\..*[^\\.]|\\w+://.+");
+ }
- this.toString = function() {
- return this.originalUri;
- };
+ function discover() {
+ cancelTimer();
+ trace('typist discovery candidate', 'gray');
+ if (identifierSanityCheck(box.value)) {
+ trace('typist discovery begun', 'gray');
+ box.dnoi_internal.performDiscovery();
+ } else {
+ trace('typist discovery canceled due to incomplete identifier.', 'gray');
+ }
+ }
- this.getAuthority = function() {
- var authority = this.getScheme() + "://" + this.getHost();
- return authority;
- }
+ function reset() {
+ keyPresses = 0;
+ startTime = null;
+ rate = NaN;
+ trace('resetting state', 'gray');
+ }
- this.getHost = function() {
- var hostStartIdx = this.originalUri.indexOf("://") + 3;
- var hostEndIndex = this.originalUri.indexOf("/", hostStartIdx);
- if (hostEndIndex < 0) hostEndIndex = this.originalUri.length;
- var host = this.originalUri.substr(hostStartIdx, hostEndIndex - hostStartIdx);
- return host;
- }
+ box.dnoi_internal.resetAndDiscover = function() {
+ reset();
+ discover();
+ };
- this.getScheme = function() {
- var schemeStartIdx = this.indexOf("://");
- return this.originalUri.substr(this.originalUri, schemeStartIdx);
- }
+ box.onkeyup = function(e) {
+ e = e || window.event; // for IE
- this.trimFragment = function() {
- var hashmark = this.originalUri.indexOf('#');
- if (hashmark >= 0) {
- return new Uri(this.originalUri.substr(0, hashmark));
- }
- return this;
- };
+ if (new Date() - lastKeyPress > 3000) {
+ // the user seems to have altogether stopped typing,
+ // so reset our typist speed detector.
+ reset();
+ }
+ lastKeyPress = new Date();
+
+ var newValue = box.value;
+ if (e.keyCode == 13) {
+ if (box.dnoi_internal.state === 'setup') {
+ box.dnoi_internal.loginButton.click();
+ } else if (box.dnoi_internal.postbackLoginButton) {
+ box.dnoi_internal.postbackLoginButton.click();
+ } else {
+ discover();
+ }
+ } else {
+ if (lastValue != newValue && newValue != box.lastDiscoveredIdentifier) {
+ box.dnoi_internal.setVisualCue();
+ if (newValue.length === 0) {
+ reset();
+ } else if (Math.abs((lastValue || '').length - newValue.length) > 1) {
+ // One key press is responsible for multiple character changes.
+ // The user may have pasted in his identifier in which case
+ // we want to begin discovery immediately.
+ trace(newValue + ': paste detected (old value ' + lastValue + ')', 'gray');
+ discover();
+ } else {
+ keyPresses++;
+ var timeout = 3000; // timeout to use if we don't have enough keying to figure out type rate
+ if (startTime === null) {
+ startTime = new Date();
+ } else if (keyPresses > 1) {
+ cancelTimer();
+ rate = (new Date() - startTime) / keyPresses;
+ var minTimeout = 300;
+ var maxTimeout = 3000;
+ var typistFactor = 5;
+ timeout = Math.max(minTimeout, Math.min(rate * typistFactor, maxTimeout));
+ }
- this.appendQueryVariable = function(name, value) {
- var pair = encodeURI(name) + "=" + encodeURI(value);
- if (this.originalUri.indexOf('?') >= 0) {
- this.originalUri = this.originalUri + "&" + pair;
- } else {
- this.originalUri = this.originalUri + "?" + pair;
- }
- };
+ trace(newValue + ': setting timer for ' + timeout, 'gray');
+ discoveryTimer = setTimeout(discover, timeout);
+ }
+ }
+ }
- function KeyValuePair(key, value) {
- this.key = key;
- this.value = value;
- };
+ trace(newValue + ': updating lastValue', 'gray');
+ lastValue = newValue;
- this.Pairs = new Array();
+ return true;
+ };
+ //}
- var queryBeginsAt = this.originalUri.indexOf('?');
- if (queryBeginsAt >= 0) {
- this.queryString = url.substr(queryBeginsAt + 1);
- var queryStringPairs = this.queryString.split('&');
+ box.getClaimedIdentifier = function() { return box.dnoi_internal.claimedIdentifier; };
- for (var i = 0; i < queryStringPairs.length; i++) {
- var equalsAt = queryStringPairs[i].indexOf('=');
- left = (equalsAt >= 0) ? queryStringPairs[i].substring(0, equalsAt) : null;
- right = (equalsAt >= 0) ? queryStringPairs[i].substring(equalsAt + 1) : queryStringPairs[i];
- this.Pairs.push(new KeyValuePair(unescape(left), unescape(right)));
- }
- };
+ // If an identifier is preset on the box, perform discovery on it, but only
+ // if there isn't a prior authentication that we're about to deserialize.
+ if (box.value.length > 0 && findOrCreateHiddenField().value.length === 0) {
+ trace('jumpstarting discovery on ' + box.value + ' because it was preset.');
+ box.dnoi_internal.performDiscovery();
+ }
+
+ // Restore a previously achieved state (from pre-postback) if it is given.
+ window.dnoa_internal.deserializePreviousAuthentication(findOrCreateHiddenField().value);
- this.getQueryArgValue = function(key) {
- for (var i = 0; i < this.Pairs.length; i++) {
- if (this.Pairs[i].key == key) {
- return this.Pairs[i].value;
- }
+ // public methods
+ box.setValue = function(value) {
+ box.value = value;
+ if (box.value) {
+ box.dnoi_internal.performDiscovery();
}
};
- this.containsQueryArg = function(key) {
- return this.getQueryArgValue(key);
- };
-
- this.indexOf = function(args) {
- return this.originalUri.indexOf(args);
- };
-
- return this;
-};
+ // public events
+ // box.onStateChanged(state)
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
index 8b517b4..dbf6944 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
@@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// An ASP.NET control that renders a button that initiates an
/// authentication when clicked.
/// </summary>
- public class OpenIdButton : OpenIdRelyingPartyControlBase, IPostBackEventHandler {
+ public class OpenIdButton : OpenIdRelyingPartyControlBase {
#region Property defaults
/// <summary>
@@ -110,21 +110,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
set { ErrorUtilities.VerifySupported(value == base.Popup, OpenIdStrings.PropertyValueNotSupported); }
}
- #region IPostBackEventHandler Members
-
/// <summary>
/// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
/// </summary>
/// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
- public void RaisePostBackEvent(string eventArgument) {
+ protected override void RaisePostBackEvent(string eventArgument) {
if (!this.PrecreateRequest) {
- IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
- request.RedirectToProvider();
+ try {
+ IAuthenticationRequest request = this.CreateRequests().First();
+ request.RedirectToProvider();
+ } catch (InvalidOperationException ex) {
+ throw ErrorUtilities.Wrap(ex, OpenIdStrings.OpenIdEndpointNotFound);
+ }
}
}
- #endregion
-
/// <summary>
/// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdEventArgs.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdEventArgs.cs
index 4d68fcc..5668cf4 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdEventArgs.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdEventArgs.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -21,7 +22,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="request">The outgoing authentication request.</param>
internal OpenIdEventArgs(IAuthenticationRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
this.Request = request;
this.ClaimedIdentifier = request.ClaimedIdentifier;
@@ -35,7 +36,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The incoming authentication response.</param>
internal OpenIdEventArgs(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.Response = response;
this.ClaimedIdentifier = response.ClaimedIdentifier;
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs
index fe1ce67..f89ec0a 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs
@@ -8,10 +8,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
+ using DotNetOpenAuth.Messaging;
/// <summary>
/// An ASP.NET control providing a complete OpenID login experience.
@@ -105,7 +108,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// The default value for the <see cref="RememberMe"/> property.
/// </summary>
- private const bool RememberMeDefault = UsePersistentCookieDefault;
+ private const bool RememberMeDefault = false;
/// <summary>
/// The default value for the <see cref="UriValidatorEnabled"/> property.
@@ -226,14 +229,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#region Events
/// <summary>
- /// Fired after the user clicks the log in button, but before the authentication
- /// process begins. Offers a chance for the web application to disallow based on
- /// OpenID URL before redirecting the user to the OpenID Provider.
- /// </summary>
- [Description("Fired after the user clicks the log in button, but before the authentication process begins. Offers a chance for the web application to disallow based on OpenID URL before redirecting the user to the OpenID Provider.")]
- public event EventHandler<OpenIdEventArgs> LoggingIn;
-
- /// <summary>
/// Fired when the Remember Me checkbox is changed by the user.
/// </summary>
[Description("Fires when the Remember Me checkbox is changed by the user.")]
@@ -242,6 +237,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
#region Properties
+
+ /// <summary>
+ /// Gets a <see cref="T:System.Web.UI.ControlCollection"/> object that represents the child controls for a specified server control in the UI hierarchy.
+ /// </summary>
+ /// <returns>
+ /// The collection of child controls for the specified server control.
+ /// </returns>
+ public override ControlCollection Controls {
+ get {
+ this.EnsureChildControls();
+ return base.Controls;
+ }
+ }
+
/// <summary>
/// Gets or sets the caption that appears before the text box.
/// </summary>
@@ -251,8 +260,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The caption that appears before the text box.")]
public string LabelText {
- get { return this.label.InnerText; }
- set { this.label.InnerText = value; }
+ get {
+ EnsureChildControls();
+ return this.label.InnerText;
+ }
+
+ set {
+ EnsureChildControls();
+ this.label.InnerText = value;
+ }
}
/// <summary>
@@ -264,8 +280,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text that introduces the example OpenID url.")]
public string ExamplePrefix {
- get { return this.examplePrefixLabel.Text; }
- set { this.examplePrefixLabel.Text = value; }
+ get {
+ EnsureChildControls();
+ return this.examplePrefixLabel.Text;
+ }
+
+ set {
+ EnsureChildControls();
+ this.examplePrefixLabel.Text = value;
+ }
}
/// <summary>
@@ -278,8 +301,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The example OpenID Identifier to display to the user.")]
public string ExampleUrl {
- get { return this.exampleUrlLabel.Text; }
- set { this.exampleUrlLabel.Text = value; }
+ get {
+ EnsureChildControls();
+ return this.exampleUrlLabel.Text;
+ }
+
+ set {
+ EnsureChildControls();
+ this.exampleUrlLabel.Text = value;
+ }
}
/// <summary>
@@ -292,8 +322,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text to display if the user attempts to login without providing an Identifier.")]
public string RequiredText {
- get { return this.requiredValidator.Text.Substring(0, this.requiredValidator.Text.Length - RequiredTextSuffix.Length); }
- set { this.requiredValidator.ErrorMessage = this.requiredValidator.Text = value + RequiredTextSuffix; }
+ get {
+ EnsureChildControls();
+ return this.requiredValidator.Text.Substring(0, this.requiredValidator.Text.Length - RequiredTextSuffix.Length);
+ }
+
+ set {
+ EnsureChildControls();
+ this.requiredValidator.ErrorMessage = this.requiredValidator.Text = value + RequiredTextSuffix;
+ }
}
/// <summary>
@@ -306,8 +343,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text to display if the user provides an invalid form for an Identifier.")]
public string UriFormatText {
- get { return this.identifierFormatValidator.Text.Substring(0, this.identifierFormatValidator.Text.Length - RequiredTextSuffix.Length); }
- set { this.identifierFormatValidator.ErrorMessage = this.identifierFormatValidator.Text = value + RequiredTextSuffix; }
+ get {
+ EnsureChildControls();
+ return this.identifierFormatValidator.Text.Substring(0, this.identifierFormatValidator.Text.Length - RequiredTextSuffix.Length);
+ }
+
+ set {
+ EnsureChildControls();
+ this.identifierFormatValidator.ErrorMessage = this.identifierFormatValidator.Text = value + RequiredTextSuffix;
+ }
}
/// <summary>
@@ -319,8 +363,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[DefaultValue(UriValidatorEnabledDefault)]
[Description("Whether to perform Identifier format validation prior to an authentication attempt.")]
public bool UriValidatorEnabled {
- get { return this.identifierFormatValidator.Enabled; }
- set { this.identifierFormatValidator.Enabled = value; }
+ get {
+ EnsureChildControls();
+ return this.identifierFormatValidator.Enabled;
+ }
+
+ set {
+ EnsureChildControls();
+ this.identifierFormatValidator.Enabled = value;
+ }
}
/// <summary>
@@ -332,8 +383,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text of the link users can click on to obtain an OpenID.")]
public string RegisterText {
- get { return this.registerLink.Text; }
- set { this.registerLink.Text = value; }
+ get {
+ EnsureChildControls();
+ return this.registerLink.Text;
+ }
+
+ set {
+ EnsureChildControls();
+ this.registerLink.Text = value;
+ }
}
/// <summary>
@@ -346,8 +404,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The URL to link users to who click the link to obtain a new OpenID.")]
public string RegisterUrl {
- get { return this.registerLink.NavigateUrl; }
- set { this.registerLink.NavigateUrl = value; }
+ get {
+ EnsureChildControls();
+ return this.registerLink.NavigateUrl;
+ }
+
+ set {
+ EnsureChildControls();
+ this.registerLink.NavigateUrl = value;
+ }
}
/// <summary>
@@ -360,8 +425,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text of the tooltip to display when the user hovers over the link to obtain a new OpenID.")]
public string RegisterToolTip {
- get { return this.registerLink.ToolTip; }
- set { this.registerLink.ToolTip = value; }
+ get {
+ EnsureChildControls();
+ return this.registerLink.ToolTip;
+ }
+
+ set {
+ EnsureChildControls();
+ this.registerLink.ToolTip = value;
+ }
}
/// <summary>
@@ -373,8 +445,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[DefaultValue(RegisterVisibleDefault)]
[Description("Whether to display a link to allow users to easily obtain a new OpenID.")]
public bool RegisterVisible {
- get { return this.registerLink.Visible; }
- set { this.registerLink.Visible = value; }
+ get {
+ EnsureChildControls();
+ return this.registerLink.Visible;
+ }
+
+ set {
+ EnsureChildControls();
+ this.registerLink.Visible = value;
+ }
}
/// <summary>
@@ -386,8 +465,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text that appears on the button that initiates login.")]
public string ButtonText {
- get { return this.loginButton.Text; }
- set { this.loginButton.Text = value; }
+ get {
+ EnsureChildControls();
+ return this.loginButton.Text;
+ }
+
+ set {
+ EnsureChildControls();
+ this.loginButton.Text = value;
+ }
}
/// <summary>
@@ -399,8 +485,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The text of the \"Remember Me\" checkbox.")]
public string RememberMeText {
- get { return this.rememberMeCheckBox.Text; }
- set { this.rememberMeCheckBox.Text = value; }
+ get {
+ EnsureChildControls();
+ return this.rememberMeCheckBox.Text;
+ }
+
+ set {
+ EnsureChildControls();
+ this.rememberMeCheckBox.Text = value;
+ }
}
/// <summary>
@@ -438,8 +531,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[DefaultValue(RememberMeVisibleDefault)]
[Description("Whether the \"Remember Me\" checkbox should be displayed.")]
public bool RememberMeVisible {
- get { return this.rememberMeCheckBox.Visible; }
- set { this.rememberMeCheckBox.Visible = value; }
+ get {
+ EnsureChildControls();
+ return this.rememberMeCheckBox.Visible;
+ }
+
+ set {
+ EnsureChildControls();
+ this.rememberMeCheckBox.Visible = value;
+ }
}
/// <summary>
@@ -448,11 +548,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
[Bindable(true)]
[Category("Appearance")]
- [DefaultValue(UsePersistentCookieDefault)]
+ [DefaultValue(RememberMeDefault)]
[Description("Whether a successful authentication should result in a persistent cookie being saved to the browser.")]
public bool RememberMe {
- get { return this.UsePersistentCookie; }
- set { this.UsePersistentCookie = value; }
+ get { return this.UsePersistentCookie != LogOnPersistence.Session; }
+ set { this.UsePersistentCookie = value ? LogOnPersistence.PersistentAuthentication : LogOnPersistence.Session; }
}
/// <summary>
@@ -468,7 +568,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
set {
unchecked {
- this.WrappedTextBox.TabIndex = (short)(value + TextBoxTabIndexOffset);
+ EnsureChildControls();
+ base.TabIndex = (short)(value + TextBoxTabIndexOffset);
this.loginButton.TabIndex = (short)(value + LoginButtonTabIndexOffset);
this.rememberMeCheckBox.TabIndex = (short)(value + RememberMeTabIndexOffset);
this.registerLink.TabIndex = (short)(value + RegisterTabIndexOffset);
@@ -485,8 +586,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Localizable(true)]
[Description("The tooltip to display when the user hovers over the login button.")]
public string ButtonToolTip {
- get { return this.loginButton.ToolTip; }
- set { this.loginButton.ToolTip = value; }
+ get {
+ EnsureChildControls();
+ return this.loginButton.ToolTip;
+ }
+
+ set {
+ EnsureChildControls();
+ this.loginButton.ToolTip = value;
+ }
}
/// <summary>
@@ -497,10 +605,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Description("The validation group that the login button and text box validator belong to.")]
public string ValidationGroup {
get {
+ EnsureChildControls();
return this.requiredValidator.ValidationGroup;
}
set {
+ EnsureChildControls();
this.requiredValidator.ValidationGroup = value;
this.loginButton.ValidationGroup = value;
}
@@ -525,7 +635,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// cookie should persist across user sessions.
/// </summary>
[Browsable(false), Bindable(false)]
- public override bool UsePersistentCookie {
+ public override LogOnPersistence UsePersistentCookie {
get {
return base.UsePersistentCookie;
}
@@ -535,8 +645,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// use conditional here to prevent infinite recursion
// with CheckedChanged event.
- if (this.rememberMeCheckBox.Checked != value) {
- this.rememberMeCheckBox.Checked = value;
+ bool rememberMe = value != LogOnPersistence.Session;
+ if (this.rememberMeCheckBox.Checked != rememberMe) {
+ this.rememberMeCheckBox.Checked = rememberMe;
}
}
}
@@ -544,27 +655,40 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
+ /// Outputs server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object and stores tracing information about the control if tracing is enabled.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HTmlTextWriter"/> object that receives the control content.</param>
+ public override void RenderControl(HtmlTextWriter writer) {
+ this.RenderChildren(writer);
+ }
+
+ /// <summary>
/// Creates the child controls.
/// </summary>
protected override void CreateChildControls() {
- // Don't call base.CreateChildControls(). This would add the WrappedTextBox
- // to the Controls collection, which would implicitly remove it from the table
- // we have already added it to.
+ this.InitializeControls();
// Just add the panel we've assembled earlier.
- this.Controls.Add(this.panel);
+ base.Controls.Add(this.panel);
+ }
- if (ShouldBeFocused) {
- WrappedTextBox.Focus();
- }
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ EnsureChildControls();
+ EnsureID();
+ this.requiredValidator.ControlToValidate = this.ID;
+ this.identifierFormatValidator.ControlToValidate = this.ID;
}
/// <summary>
/// Initializes the child controls.
/// </summary>
- protected override void InitializeControls() {
- base.InitializeControls();
-
+ protected void InitializeControls() {
this.panel = new Panel();
Table table = new Table();
@@ -583,7 +707,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// top row, middle cell
cell = new TableCell();
- cell.Controls.Add(this.WrappedTextBox);
+ cell.Controls.Add(new InPlaceControl(this));
row1.Cells.Add(cell);
// top row, right cell
@@ -611,7 +735,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.requiredValidator.ErrorMessage = RequiredTextDefault + RequiredTextSuffix;
this.requiredValidator.Text = RequiredTextDefault + RequiredTextSuffix;
this.requiredValidator.Display = ValidatorDisplay.Dynamic;
- this.requiredValidator.ControlToValidate = WrappedTextBox.ID;
this.requiredValidator.ValidationGroup = ValidationGroupDefault;
cell.Controls.Add(this.requiredValidator);
this.identifierFormatValidator = new CustomValidator();
@@ -620,7 +743,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.identifierFormatValidator.ServerValidate += this.IdentifierFormatValidator_ServerValidate;
this.identifierFormatValidator.Enabled = UriValidatorEnabledDefault;
this.identifierFormatValidator.Display = ValidatorDisplay.Dynamic;
- this.identifierFormatValidator.ControlToValidate = WrappedTextBox.ID;
this.identifierFormatValidator.ValidationGroup = ValidationGroupDefault;
cell.Controls.Add(this.identifierFormatValidator);
this.errorLabel = new Label();
@@ -680,26 +802,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Customizes HTML rendering of the control.
- /// </summary>
- /// <param name="writer">An <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
- protected override void Render(HtmlTextWriter writer) {
- // avoid writing begin and end SPAN tags for XHTML validity.
- RenderContents(writer);
- }
-
- /// <summary>
/// Renders the child controls.
/// </summary>
/// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the rendered content.</param>
protected override void RenderChildren(HtmlTextWriter writer) {
if (!this.DesignMode) {
- this.label.Attributes["for"] = this.WrappedTextBox.ClientID;
+ this.label.Attributes["for"] = this.ClientID;
if (!string.IsNullOrEmpty(this.IdSelectorIdentifier)) {
this.idselectorJavascript.Visible = true;
this.idselectorJavascript.Text = @"<script type='text/javascript'><!--
-idselector_input_id = '" + WrappedTextBox.ClientID + @"';
+idselector_input_id = '" + this.ClientID + @"';
// --></script>
<script type='text/javascript' id='__openidselector' src='https://www.idselector.com/selector/" + this.IdSelectorIdentifier + @"' charset='utf-8'></script>";
} else {
@@ -718,7 +831,7 @@ idselector_input_id = '" + WrappedTextBox.ClientID + @"';
base.OnFailed(response);
if (!string.IsNullOrEmpty(this.FailedMessageText)) {
- this.errorLabel.Text = string.Format(CultureInfo.CurrentCulture, this.FailedMessageText, response.Exception.Message);
+ this.errorLabel.Text = string.Format(CultureInfo.CurrentCulture, this.FailedMessageText, response.Exception.ToStringDescriptive());
this.errorLabel.Visible = true;
}
}
@@ -737,30 +850,6 @@ idselector_input_id = '" + WrappedTextBox.ClientID + @"';
}
/// <summary>
- /// Fires the <see cref="LoggingIn"/> event.
- /// </summary>
- /// <returns>
- /// Returns whether the login should proceed. False if some event handler canceled the request.
- /// </returns>
- protected virtual bool OnLoggingIn() {
- EventHandler<OpenIdEventArgs> loggingIn = this.LoggingIn;
- if (this.Request == null) {
- this.CreateRequest();
- }
-
- if (this.Request != null) {
- OpenIdEventArgs args = new OpenIdEventArgs(this.Request);
- if (loggingIn != null) {
- loggingIn(this, args);
- }
-
- return !args.Cancel;
- } else {
- return false;
- }
- }
-
- /// <summary>
/// Fires the <see cref="RememberMeChanged"/> event.
/// </summary>
protected virtual void OnRememberMeChanged() {
@@ -799,8 +888,49 @@ idselector_input_id = '" + WrappedTextBox.ClientID + @"';
return;
}
- if (this.OnLoggingIn()) {
- this.LogOn();
+ IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
+ if (request != null) {
+ this.LogOn(request);
+ } else {
+ if (!string.IsNullOrEmpty(this.FailedMessageText)) {
+ this.errorLabel.Text = string.Format(CultureInfo.CurrentCulture, this.FailedMessageText, OpenIdStrings.OpenIdEndpointNotFound);
+ this.errorLabel.Visible = true;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Renders the control inner.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ private void RenderControlInner(HtmlTextWriter writer) {
+ base.RenderControl(writer);
+ }
+
+ /// <summary>
+ /// A control that acts as a placeholder to indicate where
+ /// the OpenIdLogin control should render its OpenIdTextBox parent.
+ /// </summary>
+ private class InPlaceControl : PlaceHolder {
+ /// <summary>
+ /// The owning control to render.
+ /// </summary>
+ private OpenIdLogin renderControl;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InPlaceControl"/> class.
+ /// </summary>
+ /// <param name="renderControl">The render control.</param>
+ internal InPlaceControl(OpenIdLogin renderControl) {
+ this.renderControl = renderControl;
+ }
+
+ /// <summary>
+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ protected override void Render(HtmlTextWriter writer) {
+ this.renderControl.RenderControlInner(writer);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs
index a917e24..dbf9530 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdMobileTextBox.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Web.Security;
@@ -243,6 +244,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Initializes a new instance of the <see cref="OpenIdMobileTextBox"/> class.
/// </summary>
public OpenIdMobileTextBox() {
+ Reporting.RecordFeatureUse(this);
}
#region Events
@@ -581,8 +583,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
public IAuthenticationRequest CreateRequest() {
- ErrorUtilities.VerifyOperation(this.Request == null, OpenIdStrings.CreateRequestAlreadyCalled);
- ErrorUtilities.VerifyOperation(!string.IsNullOrEmpty(this.Text), OpenIdStrings.OpenIdTextBoxEmpty);
+ Contract.Requires<InvalidOperationException>(this.Request == null, OpenIdStrings.CreateRequestAlreadyCalled);
+ Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.Text), OpenIdStrings.OpenIdTextBoxEmpty);
try {
// Resolve the trust root, and swap out the scheme and port if necessary to match the
@@ -610,7 +612,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
// Add state that needs to survive across the redirect.
- this.Request.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
+ this.Request.SetUntrustedCallbackArgument(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
} else {
Logger.OpenId.WarnFormat("An invalid identifier was entered ({0}), but not caught by any validation routine.", this.Text);
this.Request = null;
@@ -635,7 +637,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
var response = this.RelyingParty.GetResponse();
if (response != null) {
- string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey);
+ string persistentString = response.GetUntrustedCallbackArgument(UsePersistentCookieCallbackKey);
bool persistentBool;
if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) {
this.UsePersistentCookie = persistentBool;
@@ -667,7 +669,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnLoggedIn(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Authenticated, "Firing OnLoggedIn event without an authenticated response.");
var loggedIn = this.LoggedIn;
@@ -686,7 +688,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnFailed(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Failed, "Firing Failed event for the wrong response type.");
var failed = this.Failed;
@@ -700,7 +702,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnCanceled(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Canceled, "Firing Canceled event for the wrong response type.");
var canceled = this.Canceled;
@@ -714,7 +716,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnSetupRequired(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.SetupRequired, "Firing SetupRequired event for the wrong response type.");
// Why are we firing Failed when we're OnSetupRequired? Backward compatibility.
@@ -732,7 +734,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="request">The authentication request to add the extensions to.</param>
private void AddProfileArgs(IAuthenticationRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
request.AddExtension(new ClaimsRequest() {
Nickname = this.RequestNickname,
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cd b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cd
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cd
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 5834b27..c9106de 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -30,7 +30,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <c>True</c> if the endpoint should be considered.
/// <c>False</c> to remove it from the pool of acceptable providers.
/// </returns>
- public delegate bool EndpointSelector(IXrdsProviderEndpoint endpoint);
+ public delegate bool EndpointSelector(IProviderEndpoint endpoint);
/// <summary>
/// Provides the programmatic facilities to act as an OpenId consumer.
@@ -41,7 +41,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The name of the key to use in the HttpApplication cache to store the
/// instance of <see cref="StandardRelyingPartyApplicationStore"/> to use.
/// </summary>
- private const string ApplicationStoreKey = "DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.ApplicationStore";
+ private const string ApplicationStoreKey = "DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.HttpApplicationStore";
/// <summary>
/// Backing store for the <see cref="Behaviors"/> property.
@@ -49,6 +49,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private readonly ObservableCollection<IRelyingPartyBehavior> behaviors = new ObservableCollection<IRelyingPartyBehavior>();
/// <summary>
+ /// Backing field for the <see cref="DiscoveryServices"/> property.
+ /// </summary>
+ private readonly IList<IIdentifierDiscoveryService> discoveryServices = new List<IIdentifierDiscoveryService>(2);
+
+ /// <summary>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private RelyingPartySecuritySettings securitySettings;
@@ -56,7 +61,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Backing store for the <see cref="EndpointOrder"/> property.
/// </summary>
- private Comparison<IXrdsProviderEndpoint> endpointOrder = DefaultEndpointOrder;
+ private Comparison<IdentifierDiscoveryResult> endpointOrder = DefaultEndpointOrder;
/// <summary>
/// Backing field for the <see cref="Channel"/> property.
@@ -87,10 +92,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// If we are a smart-mode RP (supporting associations), then we MUST also be
// capable of storing nonces to prevent replay attacks.
// If we're a dumb-mode RP, then 2.0 OPs are responsible for preventing replays.
- Contract.Requires(associationStore == null || nonceStore != null);
- ErrorUtilities.VerifyArgument(associationStore == null || nonceStore != null, OpenIdStrings.AssociationStoreRequiresNonceStore);
+ Contract.Requires<ArgumentException>(associationStore == null || nonceStore != null, OpenIdStrings.AssociationStoreRequiresNonceStore);
this.securitySettings = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecuritySettings.CreateSecuritySettings();
+
+ foreach (var discoveryService in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.DiscoveryServices.CreateInstances(true)) {
+ this.discoveryServices.Add(discoveryService);
+ }
+
this.behaviors.CollectionChanged += this.OnBehaviorsChanged;
foreach (var behavior in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.Behaviors.CreateInstances(false)) {
this.behaviors.Add(behavior);
@@ -105,6 +114,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.channel = new OpenIdChannel(associationStore, nonceStore, this.SecuritySettings);
this.AssociationManager = new AssociationManager(this.Channel, associationStore, this.SecuritySettings);
+
+ Reporting.RecordFeatureAndDependencyUse(this, associationStore, nonceStore);
}
/// <summary>
@@ -115,8 +126,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Endpoints lacking any priority value are sorted to the end of the list.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public static Comparison<IXrdsProviderEndpoint> DefaultEndpointOrder {
- get { return ServiceEndpoint.EndpointOrder; }
+ public static Comparison<IdentifierDiscoveryResult> DefaultEndpointOrder {
+ get { return IdentifierDiscoveryResult.EndpointOrder; }
}
/// <summary>
@@ -129,7 +140,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Contract.Ensures(Contract.Result<IRelyingPartyApplicationStore>() != null);
HttpContext context = HttpContext.Current;
- ErrorUtilities.VerifyOperation(context != null, OpenIdStrings.StoreRequiredWhenNoHttpContextAvailable, typeof(IRelyingPartyApplicationStore).Name);
+ ErrorUtilities.VerifyOperation(context != null, Strings.StoreRequiredWhenNoHttpContextAvailable, typeof(IRelyingPartyApplicationStore).Name);
var store = (IRelyingPartyApplicationStore)context.Application[ApplicationStoreKey];
if (store == null) {
context.Application.Lock();
@@ -155,8 +166,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.channel = value;
this.AssociationManager.Channel = value;
}
@@ -172,8 +182,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
internal set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.securitySettings = value;
this.AssociationManager.SecuritySettings = value;
}
@@ -200,14 +209,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// can be set to the value of <see cref="DefaultEndpointOrder"/>.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public Comparison<IXrdsProviderEndpoint> EndpointOrder {
+ public Comparison<IdentifierDiscoveryResult> EndpointOrder {
get {
return this.endpointOrder;
}
set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.Requires<ArgumentNullException>(value != null);
this.endpointOrder = value;
}
}
@@ -222,11 +230,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets a list of custom behaviors to apply to OpenID actions.
/// </summary>
- internal ICollection<IRelyingPartyBehavior> Behaviors {
+ /// <remarks>
+ /// Adding behaviors can impact the security settings of this <see cref="OpenIdRelyingParty"/>
+ /// instance in ways that subsequently removing the behaviors will not reverse.
+ /// </remarks>
+ public ICollection<IRelyingPartyBehavior> Behaviors {
get { return this.behaviors; }
}
/// <summary>
+ /// Gets the list of services that can perform discovery on identifiers given to this relying party.
+ /// </summary>
+ public IList<IIdentifierDiscoveryService> DiscoveryServices {
+ get { return this.discoveryServices; }
+ }
+
+ /// <summary>
/// Gets a value indicating whether this Relying Party can sign its return_to
/// parameter in outgoing authentication requests.
/// </summary>
@@ -269,9 +288,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </returns>
/// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception>
public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) {
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Requires(realm != null);
- Contract.Requires(returnToUrl != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(realm != null);
+ Contract.Requires<ArgumentNullException>(returnToUrl != null);
Contract.Ensures(Contract.Result<IAuthenticationRequest>() != null);
try {
return this.CreateRequests(userSuppliedIdentifier, realm, returnToUrl).First();
@@ -302,11 +321,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception>
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm) {
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Requires(realm != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(realm != null);
Contract.Ensures(Contract.Result<IAuthenticationRequest>() != null);
try {
- return this.CreateRequests(userSuppliedIdentifier, realm).First();
+ var result = this.CreateRequests(userSuppliedIdentifier, realm).First();
+ Contract.Assume(result != null);
+ return result;
} catch (InvalidOperationException ex) {
throw ErrorUtilities.Wrap(ex, OpenIdStrings.OpenIdEndpointNotFound);
}
@@ -329,7 +350,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception>
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier) {
- Contract.Requires(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
Contract.Ensures(Contract.Result<IAuthenticationRequest>() != null);
try {
return this.CreateRequests(userSuppliedIdentifier).First();
@@ -366,12 +387,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// An empty enumerable is returned instead.</para>
/// </remarks>
public IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) {
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Requires(realm != null);
- Contract.Requires(returnToUrl != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(realm != null);
+ Contract.Requires<ArgumentNullException>(returnToUrl != null);
Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null);
- ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
- ErrorUtilities.VerifyArgumentNotNull(returnToUrl, "returnToUrl");
return AuthenticationRequest.Create(userSuppliedIdentifier, this, realm, returnToUrl, true).Cast<IAuthenticationRequest>();
}
@@ -402,10 +421,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
public IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm) {
- Contract.Requires(userSuppliedIdentifier != null);
- Contract.Requires(realm != null);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(realm != null);
Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null);
- ErrorUtilities.VerifyHttpContext();
+
+ // This next code contract is a BAD idea, because it causes each authentication request to be generated
+ // at least an extra time.
+ ////Contract.Ensures(Contract.ForAll(Contract.Result<IEnumerable<IAuthenticationRequest>>(), el => el != null));
// Build the return_to URL
UriBuilder returnTo = new UriBuilder(this.Channel.GetRequestFromContext().UrlBeforeRewriting);
@@ -446,25 +469,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
public IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier) {
- Contract.Requires(userSuppliedIdentifier != null);
+ Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null);
- ErrorUtilities.VerifyHttpContext();
-
- // Build the realm URL
- UriBuilder realmUrl = new UriBuilder(this.Channel.GetRequestFromContext().UrlBeforeRewriting);
- realmUrl.Path = HttpContext.Current.Request.ApplicationPath;
- realmUrl.Query = null;
- realmUrl.Fragment = null;
-
- // For RP discovery, the realm url MUST NOT redirect. To prevent this for
- // virtual directory hosted apps, we need to make sure that the realm path ends
- // in a slash (since our calculation above guarantees it doesn't end in a specific
- // page like default.aspx).
- if (!realmUrl.Path.EndsWith("/", StringComparison.Ordinal)) {
- realmUrl.Path += "/";
- }
- return this.CreateRequests(userSuppliedIdentifier, new Realm(realmUrl.Uri));
+ return this.CreateRequests(userSuppliedIdentifier, Realm.AutoDetect);
}
/// <summary>
@@ -484,7 +493,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="httpRequestInfo">The HTTP request that may be carrying an authentication response from the Provider.</param>
/// <returns>The processed authentication response if there is any; <c>null</c> otherwise.</returns>
public IAuthenticationResponse GetResponse(HttpRequestInfo httpRequestInfo) {
- Contract.Requires(httpRequestInfo != null);
+ Contract.Requires<ArgumentNullException>(httpRequestInfo != null);
try {
var message = this.Channel.ReadFromRequest(httpRequestInfo);
PositiveAssertionResponse positiveAssertion;
@@ -567,17 +576,41 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return rp;
}
-#if CONTRACTS_FULL
/// <summary>
- /// Verifies conditions that should be true for any valid state of this object.
+ /// Performs discovery on the specified identifier.
/// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
- [ContractInvariantMethod]
- private void ObjectInvariant() {
- Contract.Invariant(this.SecuritySettings != null);
- Contract.Invariant(this.Channel != null);
+ /// <param name="identifier">The identifier to discover services for.</param>
+ /// <returns>A non-null sequence of services discovered for the identifier.</returns>
+ internal IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);
+
+ IEnumerable<IdentifierDiscoveryResult> results = Enumerable.Empty<IdentifierDiscoveryResult>();
+ foreach (var discoverer in this.DiscoveryServices) {
+ bool abortDiscoveryChain;
+ var discoveryResults = discoverer.Discover(identifier, this.WebRequestHandler, out abortDiscoveryChain).CacheGeneratedResults();
+ results = results.Concat(discoveryResults);
+ if (abortDiscoveryChain) {
+ Logger.OpenId.InfoFormat("Further discovery on '{0}' was stopped by the {1} discovery service.", identifier, discoverer.GetType().Name);
+ break;
+ }
+ }
+
+ // If any OP Identifier service elements were found, we must not proceed
+ // to use any Claimed Identifier services, per OpenID 2.0 sections 7.3.2.2 and 11.2.
+ // For a discussion on this topic, see
+ // http://groups.google.com/group/dotnetopenid/browse_thread/thread/4b5a8c6b2210f387/5e25910e4d2252c8
+ // Sometimes the IIdentifierDiscoveryService will automatically filter this for us, but
+ // just to be sure, we'll do it here as well.
+ if (!this.SecuritySettings.AllowDualPurposeIdentifiers) {
+ results = results.CacheGeneratedResults(); // avoid performing discovery repeatedly
+ var opIdentifiers = results.Where(result => result.ClaimedIdentifier == result.Protocol.ClaimedIdentifierForOPIdentifier);
+ var claimedIdentifiers = results.Where(result => result.ClaimedIdentifier != result.Protocol.ClaimedIdentifierForOPIdentifier);
+ results = opIdentifiers.Any() ? opIdentifiers : claimedIdentifiers;
+ }
+
+ return results;
}
-#endif
/// <summary>
/// Releases unmanaged and - optionally - managed resources
@@ -601,7 +634,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private void OnBehaviorsChanged(object sender, NotifyCollectionChangedEventArgs e) {
foreach (IRelyingPartyBehavior profile in e.NewItems) {
profile.ApplySecuritySettings(this.SecuritySettings);
+ Reporting.RecordFeatureUse(profile);
}
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.SecuritySettings != null);
+ Contract.Invariant(this.Channel != null);
+ Contract.Invariant(this.EndpointOrder != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
index da2a9ae..12d676b 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
@@ -12,24 +12,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
- using System.Drawing.Design;
using System.Globalization;
using System.Linq;
using System.Text;
- using System.Text.RegularExpressions;
using System.Web;
- using System.Web.Security;
using System.Web.UI;
- using DotNetOpenAuth.ComponentModel;
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions;
- using DotNetOpenAuth.OpenId.Extensions.UI;
/// <summary>
/// A common base class for OpenID Relying Party controls.
/// </summary>
- internal abstract class OpenIdRelyingPartyAjaxControlBase : OpenIdRelyingPartyControlBase, ICallbackEventHandler {
+ public abstract class OpenIdRelyingPartyAjaxControlBase : OpenIdRelyingPartyControlBase, ICallbackEventHandler {
/// <summary>
/// The manifest resource name of the javascript file to include on the hosting page.
/// </summary>
@@ -38,15 +33,71 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// The name of the javascript function that will initiate a synchronous callback.
/// </summary>
- protected const string CallbackJsFunction = "window.dnoa_internal.callback";
+ protected const string CallbackJSFunction = "window.dnoa_internal.callback";
/// <summary>
/// The name of the javascript function that will initiate an asynchronous callback.
/// </summary>
- protected const string CallbackJsFunctionAsync = "window.dnoa_internal.callbackAsync";
+ protected const string CallbackJSFunctionAsync = "window.dnoa_internal.callbackAsync";
/// <summary>
- /// Stores the result of a AJAX callback discovery.
+ /// The name of the javascript field that stores the maximum time a positive assertion is
+ /// good for before it must be refreshed.
+ /// </summary>
+ private const string MaxPositiveAssertionLifetimeJsName = "window.dnoa_internal.maxPositiveAssertionLifetime";
+
+ /// <summary>
+ /// The "dnoa.op_endpoint" string.
+ /// </summary>
+ private const string OPEndpointParameterName = OpenIdUtilities.CustomParameterPrefix + "op_endpoint";
+
+ /// <summary>
+ /// The "dnoa.claimed_id" string.
+ /// </summary>
+ private const string ClaimedIdParameterName = OpenIdUtilities.CustomParameterPrefix + "claimed_id";
+
+ #region Property viewstate keys
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of a successful authentication.
+ /// </summary>
+ private const string AuthDataViewStateKey = "AuthData";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticationResponse"/> property.
+ /// </summary>
+ private const string AuthenticationResponseViewStateKey = "AuthenticationResponse";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticationProcessedAlready"/> property.
+ /// </summary>
+ private const string AuthenticationProcessedAlreadyViewStateKey = "AuthenticationProcessedAlready";
+
+ #endregion
+
+ /// <summary>
+ /// Default value of the <see cref="Popup"/> property.
+ /// </summary>
+ private const PopupBehavior PopupDefault = PopupBehavior.Always;
+
+ /// <summary>
+ /// Default value of <see cref="LogOnMode"/> property..
+ /// </summary>
+ private const LogOnSiteNotification LogOnModeDefault = LogOnSiteNotification.None;
+
+ /// <summary>
+ /// Backing field for the <see cref="RelyingPartyNonVerifying"/> property.
+ /// </summary>
+ private static OpenIdRelyingParty relyingPartyNonVerifying;
+
+ /// <summary>
+ /// The authentication response that just came in.
+ /// </summary>
+ private IAuthenticationResponse authenticationResponse;
+
+ /// <summary>
+ /// Stores the result of an AJAX discovery request while it is waiting
+ /// to be picked up by ASP.NET on the way down to the user agent.
/// </summary>
private string discoveryResult;
@@ -61,19 +112,133 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
protected OpenIdRelyingPartyAjaxControlBase() {
// The AJAX login style always uses popups (or invisible iframes).
- this.Popup = PopupBehavior.Always;
+ base.Popup = PopupDefault;
+
+ // The expected use case for the AJAX login box is for comments... not logging in.
+ this.LogOnMode = LogOnModeDefault;
}
/// <summary>
+ /// Fired when a Provider sends back a positive assertion to this control,
+ /// but the authentication has not yet been verified.
+ /// </summary>
+ /// <remarks>
+ /// <b>No security critical decisions should be made within event handlers
+ /// for this event</b> as the authenticity of the assertion has not been
+ /// verified yet. All security related code should go in the event handler
+ /// for the <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/> event.
+ /// </remarks>
+ [Description("Fired when a Provider sends back a positive assertion to this control, but the authentication has not yet been verified.")]
+ public event EventHandler<OpenIdEventArgs> UnconfirmedPositiveAssertion;
+
+ /// <summary>
/// Gets or sets a value indicating when to use a popup window to complete the login experience.
/// </summary>
/// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
- [Bindable(false), Browsable(false)]
+ [Bindable(false), Browsable(false), DefaultValue(PopupDefault)]
public override PopupBehavior Popup {
get { return base.Popup; }
set { ErrorUtilities.VerifySupported(value == base.Popup, OpenIdStrings.PropertyValueNotSupported); }
}
+ /// <summary>
+ /// Gets or sets the way a completed login is communicated to the rest of the web site.
+ /// </summary>
+ [Bindable(true), DefaultValue(LogOnModeDefault), Category(BehaviorCategory)]
+ [Description("The way a completed login is communicated to the rest of the web site.")]
+ public override LogOnSiteNotification LogOnMode { // override to set new DefaultValue
+ get { return base.LogOnMode; }
+ set { base.LogOnMode = value; }
+ }
+
+ /// <summary>
+ /// Gets the completed authentication response.
+ /// </summary>
+ public IAuthenticationResponse AuthenticationResponse {
+ get {
+ if (this.authenticationResponse == null) {
+ // We will either validate a new response and return a live AuthenticationResponse
+ // or we will try to deserialize a previous IAuthenticationResponse (snapshot)
+ // from viewstate and return that.
+ IAuthenticationResponse viewstateResponse = this.ViewState[AuthenticationResponseViewStateKey] as IAuthenticationResponse;
+ string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
+ string formAuthData = this.Page.Request.Form[this.OpenIdAuthDataFormKey];
+
+ // First see if there is fresh auth data to be processed into a response.
+ if (!string.IsNullOrEmpty(formAuthData) && !string.Equals(viewstateAuthData, formAuthData, StringComparison.Ordinal)) {
+ this.ViewState[AuthDataViewStateKey] = formAuthData;
+
+ Uri authUri = new Uri(formAuthData);
+ HttpRequestInfo clientResponseInfo = new HttpRequestInfo {
+ UrlBeforeRewriting = authUri,
+ };
+
+ this.authenticationResponse = this.RelyingParty.GetResponse(clientResponseInfo);
+ Logger.Controls.DebugFormat(
+ "The {0} control checked for an authentication response and found: {1}",
+ this.ID,
+ this.authenticationResponse.Status);
+ this.AuthenticationProcessedAlready = false;
+
+ // Save out the authentication response to viewstate so we can find it on
+ // a subsequent postback.
+ this.ViewState[AuthenticationResponseViewStateKey] = new PositiveAuthenticationResponseSnapshot(this.authenticationResponse);
+ } else {
+ this.authenticationResponse = viewstateResponse;
+ }
+ }
+
+ return this.authenticationResponse;
+ }
+ }
+
+ /// <summary>
+ /// Gets the name of the open id auth data form key (for the value as stored at the user agent as a FORM field).
+ /// </summary>
+ /// <value>Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.</value>
+ protected abstract string OpenIdAuthDataFormKey { get; }
+
+ /// <summary>
+ /// Gets the relying party to use when verification of incoming messages is NOT wanted.
+ /// </summary>
+ private static OpenIdRelyingParty RelyingPartyNonVerifying {
+ get {
+ if (relyingPartyNonVerifying == null) {
+ relyingPartyNonVerifying = OpenIdRelyingParty.CreateNonVerifying();
+ }
+ return relyingPartyNonVerifying;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether an authentication in the page's view state
+ /// has already been processed and appropriate events fired.
+ /// </summary>
+ private bool AuthenticationProcessedAlready {
+ get { return (bool)(ViewState[AuthenticationProcessedAlreadyViewStateKey] ?? false); }
+ set { ViewState[AuthenticationProcessedAlreadyViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Allows an OpenID extension to read data out of an unverified positive authentication assertion
+ /// and send it down to the client browser so that Javascript running on the page can perform
+ /// some preprocessing on the extension data.
+ /// </summary>
+ /// <typeparam name="T">The extension <i>response</i> type that will read data from the assertion.</typeparam>
+ /// <param name="propertyName">The property name on the openid_identifier input box object that will be used to store the extension data. For example: sreg</param>
+ /// <remarks>
+ /// This method should be called from the <see cref="UnconfirmedPositiveAssertion"/> event handler.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
+ public void RegisterClientScriptExtension<T>(string propertyName) where T : IClientScriptExtensionResponse {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(propertyName));
+ ErrorUtilities.VerifyArgumentNamed(!this.clientScriptExtensions.ContainsValue(propertyName), "propertyName", OpenIdStrings.ClientScriptExtensionPropertyNameCollision, propertyName);
+ foreach (var ext in this.clientScriptExtensions.Keys) {
+ ErrorUtilities.VerifyArgument(ext != typeof(T), OpenIdStrings.ClientScriptExtensionTypeCollision, typeof(T).FullName);
+ }
+ this.clientScriptExtensions.Add(typeof(T), propertyName);
+ }
+
#region ICallbackEventHandler Members
/// <summary>
@@ -82,8 +247,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <returns>The result of the callback.</returns>
/// <value>A whitespace delimited list of URLs that can be used to initiate authentication.</value>
string ICallbackEventHandler.GetCallbackResult() {
- this.Page.Response.ContentType = "text/javascript";
- return this.discoveryResult;
+ return this.GetCallbackResult();
}
/// <summary>
@@ -91,12 +255,253 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// AJAX callback mechanisms.
/// </summary>
/// <param name="eventArgument">The identifier to perform discovery on.</param>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "We want to preserve the signature of the interface.")]
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) {
+ this.RaiseCallbackEvent(eventArgument);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Creates the authentication requests for a given user-supplied Identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to create a request for.</param>
+ /// <returns>
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
+ /// </returns>
+ protected internal override IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) {
+ // If this control is actually a member of another OpenID RP control,
+ // delegate creation of requests to the parent control.
+ var parentOwner = this.ParentControls.OfType<OpenIdRelyingPartyControlBase>().FirstOrDefault();
+ if (parentOwner != null) {
+ return parentOwner.CreateRequests(identifier);
+ } else {
+ // We delegate all our logic to another method, since invoking base. methods
+ // within an iterator method results in unverifiable code.
+ return this.CreateRequestsCore(base.CreateRequests(identifier));
+ }
+ }
+
+ /// <summary>
+ /// Returns the results of a callback event that targets a control.
+ /// </summary>
+ /// <returns>The result of the callback.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "We want to preserve the signature of the interface.")]
+ protected virtual string GetCallbackResult() {
+ this.Page.Response.ContentType = "text/javascript";
+ return this.discoveryResult;
+ }
+
+ /// <summary>
+ /// Processes a callback event that targets a control.
+ /// </summary>
+ /// <param name="eventArgument">A string that represents an event argument to pass to the event handler.</param>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "We want to preserve the signature of the interface.")]
+ protected virtual void RaiseCallbackEvent(string eventArgument) {
string userSuppliedIdentifier = eventArgument;
ErrorUtilities.VerifyNonZeroLength(userSuppliedIdentifier, "userSuppliedIdentifier");
Logger.OpenId.InfoFormat("AJAX discovery on {0} requested.", userSuppliedIdentifier);
+ this.Identifier = userSuppliedIdentifier;
+ this.discoveryResult = this.SerializeDiscoveryAsJson(this.Identifier);
+ }
+
+ /// <summary>
+ /// Pre-discovers an identifier and makes the results available to the
+ /// user agent for javascript as soon as the page loads.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ protected void PreloadDiscovery(Identifier identifier) {
+ this.PreloadDiscovery(new[] { identifier });
+ }
+
+ /// <summary>
+ /// Pre-discovers a given set of identifiers and makes the results available to the
+ /// user agent for javascript as soon as the page loads.
+ /// </summary>
+ /// <param name="identifiers">The identifiers to perform discovery on.</param>
+ protected void PreloadDiscovery(IEnumerable<Identifier> identifiers) {
+ string discoveryResults = this.SerializeDiscoveryAsJson(identifiers);
+ string script = "window.dnoa_internal.loadPreloadedDiscoveryResults(" + discoveryResults + ");";
+ this.Page.ClientScript.RegisterClientScriptBlock(typeof(OpenIdRelyingPartyAjaxControlBase), this.ClientID, script, true);
+ }
+
+ /// <summary>
+ /// Fires the <see cref="UnconfirmedPositiveAssertion"/> event.
+ /// </summary>
+ protected virtual void OnUnconfirmedPositiveAssertion() {
+ var unconfirmedPositiveAssertion = this.UnconfirmedPositiveAssertion;
+ if (unconfirmedPositiveAssertion != null) {
+ unconfirmedPositiveAssertion(this, null);
+ }
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:Load"/> event.
+ /// </summary>
+ /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
+ protected override void OnLoad(EventArgs e) {
+ base.OnLoad(e);
+
+ // Our parent control ignores all OpenID messages included in a postback,
+ // but our AJAX controls hide an old OpenID message in a postback payload,
+ // so we deserialize it and process it when appropriate.
+ if (this.Page.IsPostBack) {
+ if (this.AuthenticationResponse != null && !this.AuthenticationProcessedAlready) {
+ // Only process messages targeted at this control.
+ // Note that Stateless mode causes no receiver to be indicated.
+ string receiver = this.AuthenticationResponse.GetUntrustedCallbackArgument(ReturnToReceivingControlId);
+ if (receiver == null || receiver == this.ClientID) {
+ this.ProcessResponse(this.AuthenticationResponse);
+ this.AuthenticationProcessedAlready = true;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Called when the <see cref="Identifier"/> property is changed.
+ /// </summary>
+ protected override void OnIdentifierChanged() {
+ base.OnIdentifierChanged();
+
+ // Since the identifier changed, make sure we reset any cached authentication on the user agent.
+ this.ViewState.Remove(AuthDataViewStateKey);
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ this.SetWebAppPathOnUserAgent();
+ this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdRelyingPartyAjaxControlBase), EmbeddedAjaxJavascriptResource);
+
+ StringBuilder initScript = new StringBuilder();
+
+ initScript.AppendLine(CallbackJSFunctionAsync + " = " + this.GetJsCallbackConvenienceFunction(true));
+ initScript.AppendLine(CallbackJSFunction + " = " + this.GetJsCallbackConvenienceFunction(false));
+
+ // Positive assertions can last no longer than this library is willing to consider them valid,
+ // and when they come with OP private associations they last no longer than the OP is willing
+ // to consider them valid. We assume the OP will hold them valid for at least five minutes.
+ double assertionLifetimeInMilliseconds = Math.Min(TimeSpan.FromMinutes(5).TotalMilliseconds, Math.Min(DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime.TotalMilliseconds, DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime.TotalMilliseconds));
+ initScript.AppendLine(MaxPositiveAssertionLifetimeJsName + " = " + assertionLifetimeInMilliseconds.ToString(CultureInfo.InvariantCulture) + ";");
+
+ // We register this callback code explicitly with a specific type rather than the derived-type of the control
+ // to ensure that this discovery callback function is only set ONCE for the HTML document.
+ this.Page.ClientScript.RegisterClientScriptBlock(typeof(OpenIdRelyingPartyControlBase), "initializer", initScript.ToString(), true);
+ }
+
+ /// <summary>
+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ protected override void Render(HtmlTextWriter writer) {
+ base.Render(writer);
+
+ // Emit a hidden field to let the javascript on the user agent know if an
+ // authentication has already successfully taken place.
+ string viewstateAuthData = this.ViewState[AuthDataViewStateKey] as string;
+ if (!string.IsNullOrEmpty(viewstateAuthData)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Name, this.OpenIdAuthDataFormKey);
+ writer.AddAttribute(HtmlTextWriterAttribute.Value, viewstateAuthData, true);
+ writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
+ writer.RenderBeginTag(HtmlTextWriterTag.Input);
+ writer.RenderEndTag();
+ }
+ }
+
+ /// <summary>
+ /// Notifies the user agent via an AJAX response of a completed authentication attempt.
+ /// </summary>
+ protected override void ScriptClosingPopupOrIFrame() {
+ Logger.OpenId.DebugFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
+ string extensionsJson = null;
+
+ var authResponse = RelyingPartyNonVerifying.GetResponse();
+ Logger.Controls.DebugFormat(
+ "The {0} control checked for an authentication response from a popup window or iframe using a non-verifying RP and found: {1}",
+ this.ID,
+ authResponse.Status);
+ if (authResponse.Status == AuthenticationStatus.Authenticated) {
+ this.OnUnconfirmedPositiveAssertion(); // event handler will fill the clientScriptExtensions collection.
+ var extensionsDictionary = new Dictionary<string, string>();
+ foreach (var pair in this.clientScriptExtensions) {
+ IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key);
+ if (extension == null) {
+ continue;
+ }
+ var positiveResponse = (PositiveAuthenticationResponse)authResponse;
+ string js = extension.InitializeJavaScriptData(positiveResponse.Response);
+ if (!string.IsNullOrEmpty(js)) {
+ extensionsDictionary[pair.Value] = js;
+ }
+ }
+
+ extensionsJson = MessagingUtilities.CreateJsonObject(extensionsDictionary, true);
+ }
+
+ string payload = "document.URL";
+ if (Page.Request.HttpMethod == "POST") {
+ // Promote all form variables to the query string, but since it won't be passed
+ // to any server (this is a javascript window-to-window transfer) the length of
+ // it can be arbitrarily long, whereas it was POSTed here probably because it
+ // was too long for HTTP transit.
+ UriBuilder payloadUri = new UriBuilder(Page.Request.Url);
+ payloadUri.AppendQueryArgs(Page.Request.Form.ToDictionary());
+ payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri);
+ }
+
+ if (!string.IsNullOrEmpty(extensionsJson)) {
+ payload += ", " + extensionsJson;
+ }
+
+ this.CallbackUserAgentMethod("dnoa_internal.processAuthorizationResult(" + payload + ")");
+ }
+
+ /// <summary>
+ /// Serializes the discovery of multiple identifiers as a JSON object.
+ /// </summary>
+ /// <param name="identifiers">The identifiers to perform discovery on and create requests for.</param>
+ /// <returns>The serialized JSON object.</returns>
+ private string SerializeDiscoveryAsJson(IEnumerable<Identifier> identifiers) {
+ ErrorUtilities.VerifyArgumentNotNull(identifiers, "identifiers");
+
+ // We prepare a JSON object with this interface:
+ // Array discoveryWrappers;
+ // Where each element in the above array has this interface:
+ // class discoveryWrapper {
+ // string userSuppliedIdentifier;
+ // jsonResponse discoveryResult; // contains result of call to SerializeDiscoveryAsJson(Identifier)
+ // }
+ StringBuilder discoveryResultBuilder = new StringBuilder();
+ discoveryResultBuilder.Append("[");
+ foreach (var identifier in identifiers) { // TODO: parallelize discovery on these identifiers
+ discoveryResultBuilder.Append("{");
+ discoveryResultBuilder.AppendFormat("userSuppliedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(identifier));
+ discoveryResultBuilder.AppendFormat("discoveryResult: {0}", this.SerializeDiscoveryAsJson(identifier));
+ discoveryResultBuilder.Append("},");
+ }
+
+ discoveryResultBuilder.Length -= 1; // trim last comma
+ discoveryResultBuilder.Append("]");
+ return discoveryResultBuilder.ToString();
+ }
+
+ /// <summary>
+ /// Serializes the results of discovery and the created auth requests as a JSON object
+ /// for the user agent to initiate.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <returns>The JSON string.</returns>
+ private string SerializeDiscoveryAsJson(Identifier identifier) {
+ ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
+
// We prepare a JSON object with this interface:
// class jsonResponse {
// string claimedIdentifier;
@@ -112,13 +517,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
StringBuilder discoveryResultBuilder = new StringBuilder();
discoveryResultBuilder.Append("{");
try {
- this.Identifier = userSuppliedIdentifier;
- IEnumerable<IAuthenticationRequest> requests = this.CreateRequests().CacheGeneratedResults();
+ IEnumerable<IAuthenticationRequest> requests = this.CreateRequests(identifier).CacheGeneratedResults();
if (requests.Any()) {
discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests.First().ClaimedIdentifier));
discoveryResultBuilder.Append("requests: [");
foreach (IAuthenticationRequest request in requests) {
- this.OnLoggingIn(request);
discoveryResultBuilder.Append("{");
discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri));
request.Mode = AuthenticationRequestMode.Immediate;
@@ -132,47 +535,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
discoveryResultBuilder.Length -= 1; // trim off last comma
discoveryResultBuilder.Append("]");
} else {
- discoveryResultBuilder.Append("requests: new Array(),");
+ discoveryResultBuilder.Append("requests: [],");
discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound));
}
} catch (ProtocolException ex) {
- discoveryResultBuilder.Append("requests: new Array(),");
+ discoveryResultBuilder.Append("requests: [],");
discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message));
}
- discoveryResultBuilder.Append("}");
- this.discoveryResult = discoveryResultBuilder.ToString();
- }
-
- #endregion
-
- /// <summary>
- /// Creates the authentication requests for a given user-supplied Identifier.
- /// </summary>
- /// <returns>A sequence of authentication requests, any one of which may be
- /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
- protected override IEnumerable<IAuthenticationRequest> CreateRequests() {
- ErrorUtilities.VerifyOperation(this.Identifier != null, OpenIdStrings.NoIdentifierSet);
- // We delegate all our logic to another method, since invoking base. methods
- // within an iterator method results in unverifiable code.
- return this.CreateRequestsCore(base.CreateRequests());
- }
-
- /// <summary>
- /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
- /// </summary>
- /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
- protected override void OnPreRender(EventArgs e) {
- base.OnPreRender(e);
-
- this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdRelyingPartyAjaxControlBase), EmbeddedAjaxJavascriptResource);
-
- StringBuilder initScript = new StringBuilder();
-
- initScript.AppendLine(CallbackJsFunctionAsync + " = " + this.GetJsCallbackConvenienceFunction(true));
- initScript.AppendLine(CallbackJsFunction + " = " + this.GetJsCallbackConvenienceFunction(false));
-
- this.Page.ClientScript.RegisterClientScriptBlock(typeof(OpenIdRelyingPartyControlBase), "initializer", initScript.ToString(), true);
+ discoveryResultBuilder.Append("}");
+ return discoveryResultBuilder.ToString();
}
/// <summary>
@@ -184,30 +556,26 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
/// </returns>
private IEnumerable<IAuthenticationRequest> CreateRequestsCore(IEnumerable<IAuthenticationRequest> requests) {
- Contract.Requires(requests != null);
+ ErrorUtilities.VerifyArgumentNotNull(requests, "requests"); // NO CODE CONTRACTS! (yield return used here)
// Configure each generated request.
int reqIndex = 0;
foreach (var req in requests) {
- req.AddCallbackArguments("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
-
- if (req.Provider.IsExtensionSupported<UIRequest>()) {
- // Provide a hint for the client javascript about whether the OP supports the UI extension.
- // This is so the window can be made the correct size for the extension.
- // If the OP doesn't advertise support for the extension, the javascript will use
- // a bigger popup window.
- req.AddCallbackArguments("dotnetopenid.popupUISupported", "1");
- }
+ req.SetUntrustedCallbackArgument("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
// If the ReturnToUrl was explicitly set, we'll need to reset our first parameter
- if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)["dotnetopenid.userSuppliedIdentifier"])) {
- req.AddCallbackArguments("dotnetopenid.userSuppliedIdentifier", this.Identifier);
+ if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)[AuthenticationRequest.UserSuppliedIdentifierParameterName])) {
+ Identifier userSuppliedIdentifier = ((AuthenticationRequest)req).DiscoveryResult.UserSuppliedIdentifier;
+ req.SetUntrustedCallbackArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName, userSuppliedIdentifier.OriginalString);
}
// Our javascript needs to let the user know which endpoint responded. So we force it here.
// This gives us the info even for 1.0 OPs and 2.0 setup_required responses.
- req.AddCallbackArguments("dotnetopenid.op_endpoint", req.Provider.Uri.AbsoluteUri);
- req.AddCallbackArguments("dotnetopenid.claimed_id", (string)req.ClaimedIdentifier ?? string.Empty);
+ req.SetUntrustedCallbackArgument(OPEndpointParameterName, req.Provider.Uri.AbsoluteUri);
+ req.SetUntrustedCallbackArgument(ClaimedIdParameterName, (string)req.ClaimedIdentifier ?? string.Empty);
+
+ // Inform ourselves in return_to that we're in a popup or iframe.
+ req.SetUntrustedCallbackArgument(UIPopupCallbackKey, "1");
// We append a # at the end so that if the OP happens to support it,
// the OpenID response "query string" is appended after the hash rather than before, resulting in the
@@ -247,73 +615,38 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Notifies the user agent via an AJAX response of a completed authentication attempt.
- /// </summary>
- private void ReportAuthenticationResult() {
- Logger.OpenId.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
- List<string> assignments = new List<string>();
-
- var authResponse = this.RelyingParty.GetResponse();
- if (authResponse.Status == AuthenticationStatus.Authenticated) {
- this.OnLoggedIn(authResponse);
- foreach (var pair in this.clientScriptExtensions) {
- IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key);
- if (extension == null) {
- continue;
- }
- var positiveResponse = (PositiveAuthenticationResponse)authResponse;
- string js = extension.InitializeJavaScriptData(positiveResponse.Response);
- if (string.IsNullOrEmpty(js)) {
- js = "null";
- }
- assignments.Add(pair.Value + " = " + js);
- }
- }
-
- this.CallbackUserAgentMethod("dnoi_internal.processAuthorizationResult(document.URL)", assignments.ToArray());
- }
-
- /// <summary>
/// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
/// and closes the calling popup window if applicable.
/// </summary>
/// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
/// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
private void CallbackUserAgentMethod(string methodCall) {
- this.CallbackUserAgentMethod(methodCall, null);
- }
-
- /// <summary>
- /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
- /// and closes the calling popup window if applicable.
- /// </summary>
- /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
- /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
- /// <param name="preAssignments">An optional list of assignments to make to the input box object before placing the method call.</param>
- private void CallbackUserAgentMethod(string methodCall, string[] preAssignments) {
- Logger.OpenId.InfoFormat("Sending Javascript callback: {0}", methodCall);
+ Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall);
Page.Response.Write(@"<html><body><script language='javascript'>
var inPopup = !window.frameElement;
- var objSrc = inPopup ? window.opener.waiting_openidBox : window.frameElement.openidBox;
+ var objSrc = inPopup ? window.opener : window.frameElement;
");
- if (preAssignments != null) {
- foreach (string assignment in preAssignments) {
- Page.Response.Write(string.Format(CultureInfo.InvariantCulture, " objSrc.{0};\n", assignment));
- }
- }
// Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable,
// so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already
// whether to call window.self.close() after the call.
string htmlFormat = @" if (inPopup) {{
- objSrc.{0};
- window.self.close();
-}} else {{
- objSrc.{0};
-}}
+ objSrc.{0};
+ window.self.close();
+ }} else {{
+ objSrc.{0};
+ }}
</script></body></html>";
Page.Response.Write(string.Format(CultureInfo.InvariantCulture, htmlFormat, methodCall));
Page.Response.End();
}
+
+ /// <summary>
+ /// Sets the window.aspnetapppath variable on the user agent so that cookies can be set with the proper path.
+ /// </summary>
+ private void SetWebAppPathOnUserAgent() {
+ string script = "window.aspnetapppath = " + MessagingUtilities.GetSafeJavascriptValue(this.Page.Request.ApplicationPath) + ";";
+ this.Page.ClientScript.RegisterClientScriptBlock(typeof(OpenIdRelyingPartyAjaxControlBase), "webapppath", script, true);
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
index 65b1b99..6faad56 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
@@ -1,150 +1,747 @@
//-----------------------------------------------------------------------
-// <copyright file="OpenIdRelyingPartyControlBase.js" company="Andrew Arnott">
+// <copyright file="OpenIdRelyingPartyAjaxControlBase.js" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
+// This file may be used and redistributed under the terms of the
+// Microsoft Public License (Ms-PL) http://opensource.org/licenses/ms-pl.html
// </copyright>
//-----------------------------------------------------------------------
if (window.dnoa_internal === undefined) {
- window.dnoa_internal = new Object();
+ window.dnoa_internal = {};
+}
+
+/// <summary>Removes a given element from the array.</summary>
+/// <returns>True if the element was in the array, or false if it was not found.</returns>
+Array.prototype.remove = function(element) {
+ function elementToRemoveLast(a, b) {
+ if (a == element) { return 1; }
+ if (b == element) { return -1; }
+ return 0;
+ }
+ this.sort(elementToRemoveLast);
+ if (this[this.length - 1] == element) {
+ this.pop();
+ return true;
+ } else {
+ return false;
+ }
+};
+
+// Renders all the parameters in their string form, surrounded by parentheses.
+window.dnoa_internal.argsToString = function() {
+ result = "(";
+ for (var i = 0; i < arguments.length; i++) {
+ if (i > 0) { result += ', '; }
+ var arg = arguments[i];
+ if (typeof (arg) == 'string') {
+ arg = '"' + arg + '"';
+ } else if (arg === null) {
+ arg = '[null]';
+ } else if (arg === undefined) {
+ arg = '[undefined]';
+ }
+ result += arg.toString();
+ }
+ result += ')';
+ return result;
};
-window.dnoa_internal.discoveryResults = new Array(); // user supplied identifiers and discovery results
+window.dnoa_internal.registerEvent = function(name) {
+ var filterOnApplicability = function(fn, domElement) {
+ /// <summary>Wraps a given function with a check so that the function only executes when a given element is still in the DOM.</summary>
+ return function() {
+ var args = Array.prototype.slice.call(arguments);
+ if (!domElement) {
+ // no element used as a basis of applicability indicates we always fire this callback.
+ fn.apply(null, args);
+ } else {
+ var elements = document.getElementsByTagName(domElement.tagName);
+ var isElementInDom = false;
+ for (var i = 0; i < elements.length; i++) {
+ if (elements[i] === domElement) {
+ isElementInDom = true;
+ break;
+ }
+ }
+ if (isElementInDom) {
+ fn.apply(null, args);
+ }
+ }
+ }
+ };
+
+ window.dnoa_internal[name + 'Listeners'] = [];
+ window.dnoa_internal['add' + name] = function(fn, whileDomElementApplicable) { window.dnoa_internal[name + 'Listeners'].push(filterOnApplicability(fn, whileDomElementApplicable)); };
+ window.dnoa_internal['remove' + name] = function(fn) { window.dnoa_internal[name + 'Listeners'].remove(fn); };
+ window.dnoa_internal['fire' + name] = function() {
+ var args = Array.prototype.slice.call(arguments);
+ trace('Firing event ' + name + window.dnoa_internal.argsToString.apply(null, args), 'blue');
+ var listeners = window.dnoa_internal[name + 'Listeners'];
+ for (var i = 0; i < listeners.length; i++) {
+ listeners[i].apply(null, args);
+ }
+ };
+};
+
+window.dnoa_internal.registerEvent('DiscoveryStarted'); // (identifier) - fired when a discovery callback is ACTUALLY made to the RP
+window.dnoa_internal.registerEvent('DiscoverySuccess'); // (identifier, discoveryResult, { fresh: true|false }) - fired after a discovery callback is returned from the RP successfully or a cached result is retrieved
+window.dnoa_internal.registerEvent('DiscoveryFailed'); // (identifier, message) - fired after a discovery callback fails
+window.dnoa_internal.registerEvent('AuthStarted'); // (discoveryResult, serviceEndpoint, { background: true|false })
+window.dnoa_internal.registerEvent('AuthFailed'); // (discoveryResult, serviceEndpoint, { background: true|false }) - fired for each individual ServiceEndpoint, and once at last with serviceEndpoint==null if all failed
+window.dnoa_internal.registerEvent('AuthSuccess'); // (discoveryResult, serviceEndpoint, extensionResponses, { background: true|false, deserialized: true|false })
+window.dnoa_internal.registerEvent('AuthCleared'); // (discoveryResult, serviceEndpoint)
+
+window.dnoa_internal.discoveryResults = []; // user supplied identifiers and discovery results
+window.dnoa_internal.discoveryInProgress = []; // identifiers currently being discovered and their callbacks
+
+// The possible authentication results
+window.dnoa_internal.authSuccess = 'auth-success';
+window.dnoa_internal.authRefused = 'auth-refused';
+window.dnoa_internal.timedOut = 'timed-out';
+
+/// <summary>Instantiates a new FrameManager.</summary>
+/// <param name="maxFrames">The maximum number of concurrent 'jobs' (authentication attempts).</param>
+window.dnoa_internal.FrameManager = function(maxFrames) {
+ this.queuedWork = [];
+ this.frames = [];
+ this.maxFrames = maxFrames;
+
+ /// <summary>Called to queue up some work that will use an iframe as soon as it is available.</summary>
+ /// <param name="job">
+ /// A delegate that must return { url: /*to point the iframe to*/, onCanceled: /* callback */ }
+ /// Its first parameter is the iframe created to service the request.
+ /// It will only be called when the work actually begins.
+ /// </param>
+ /// <param name="p1">Arbitrary additional parameter to pass to the job.</param>
+ this.enqueueWork = function(job, p1) {
+ // Assign an iframe to this task immediately if there is one available.
+ if (this.frames.length < this.maxFrames) {
+ this.createIFrame(job, p1);
+ } else {
+ this.queuedWork.unshift({ job: job, p1: p1 });
+ }
+ };
+
+ /// <summary>Clears the job queue and immediately closes all iframes.</summary>
+ this.cancelAllWork = function() {
+ trace('Canceling all open and pending iframes.');
+ while (this.queuedWork.pop()) { }
+ this.closeFrames();
+ };
+
+ /// <summary>An event fired when a frame is closing.</summary>
+ this.onJobCompleted = function() {
+ // If there is a job in the queue, go ahead and start it up.
+ if (jobDesc = this.queuedWork.pop()) {
+ this.createIFrame(jobDesc.job, jobDesc.p1);
+ }
+ };
+
+ this.createIFrame = function(job, p1) {
+ var iframe = document.createElement("iframe");
+ if (!window.openid_visible_iframe) {
+ iframe.setAttribute("width", 0);
+ iframe.setAttribute("height", 0);
+ iframe.setAttribute("style", "display: none");
+ }
+ var jobDescription = job(iframe, p1);
+ iframe.setAttribute("src", jobDescription.url);
+ iframe.onCanceled = jobDescription.onCanceled;
+ iframe.dnoa_internal = window.dnoa_internal;
+ document.body.insertBefore(iframe, document.body.firstChild);
+ this.frames.push(iframe);
+ return iframe;
+ };
+
+ this.closeFrames = function() {
+ if (this.frames.length === 0) { return false; }
+ for (var i = 0; i < this.frames.length; i++) {
+ this.frames[i].src = "about:blank"; // doesn't have to exist. Just stop its processing.
+ if (this.frames[i].parentNode) { this.frames[i].parentNode.removeChild(this.frames[i]); }
+ }
+ while (this.frames.length > 0) {
+ var frame = this.frames.pop();
+ if (frame.onCanceled) { frame.onCanceled(); }
+ }
+ return true;
+ };
+
+ this.closeFrame = function(frame) {
+ frame.src = "about:blank"; // doesn't have to exist. Just stop its processing.
+ if (frame.parentNode) { frame.parentNode.removeChild(frame); }
+ var removed = this.frames.remove(frame);
+ this.onJobCompleted();
+ return removed;
+ };
+};
/// <summary>Instantiates an object that represents an OpenID Identifier.</summary>
-window.OpenId = function(identifier) {
+window.OpenIdIdentifier = function(identifier) {
+ if (!identifier || identifier.length === 0) {
+ throw 'Error: trying to create OpenIdIdentifier for null or empty string.';
+ }
+
/// <summary>Performs discovery on the identifier.</summary>
- /// <param name="onCompleted">A function(DiscoveryResult) callback to be called when discovery has completed.</param>
- this.discover = function(onCompleted) {
- /// <summary>Instantiates an object that stores discovery results of some identifier.</summary>
- function DiscoveryResult(identifier, discoveryInfo) {
- /// <summary>
- /// Instantiates an object that describes an OpenID service endpoint and facilitates
- /// initiating and tracking an authentication request.
- /// </summary>
- function ServiceEndpoint(requestInfo, userSuppliedIdentifier) {
- this.immediate = requestInfo.immediate ? new window.dnoa_internal.Uri(requestInfo.immediate) : null;
- this.setup = requestInfo.setup ? new window.dnoa_internal.Uri(requestInfo.setup) : null;
- this.endpoint = new window.dnoa_internal.Uri(requestInfo.endpoint);
- this.userSuppliedIdentifier = userSuppliedIdentifier;
- var self = this; // closure so that delegates have the right instance
- this.loginPopup = function(onSuccess, onFailure) {
- //self.abort(); // ensure no concurrent attempts
- window.dnoa_internal.processAuthorizationResult = function(childLocation) {
- window.dnoa_internal.processAuthorizationResult = null;
- trace('Received event from child window: ' + childLocation);
- var success = true; // TODO: discern between success and failure, and fire the correct event.
-
- if (success) {
- if (onSuccess) {
- onSuccess();
- }
- } else {
- if (onFailure) {
- onFailure();
- }
- }
- };
- var width = 800;
- var height = 600;
- if (self.setup.getQueryArgValue("openid.return_to").indexOf("dotnetopenid.popupUISupported") >= 0) {
- width = 450;
- height = 500;
+ /// <param name="onDiscoverSuccess">A function(DiscoveryResult) callback to be called when discovery has completed successfully.</param>
+ /// <param name="onDiscoverFailure">A function callback to be called when discovery has completed in failure.</param>
+ this.discover = function(onDiscoverSuccess, onDiscoverFailure) {
+ /// <summary>Receives the results of a successful discovery (even if it yielded 0 results).</summary>
+ function discoverSuccessCallback(discoveryResult, identifier) {
+ trace('Discovery completed for: ' + identifier);
+
+ // Deserialize the JSON object and store the result if it was a successful discovery.
+ discoveryResult = eval('(' + discoveryResult + ')');
+
+ // Add behavior for later use.
+ discoveryResult = new window.dnoa_internal.DiscoveryResult(identifier, discoveryResult);
+ window.dnoa_internal.discoveryResults[identifier] = discoveryResult;
+
+ window.dnoa_internal.fireDiscoverySuccess(identifier, discoveryResult, { fresh: true });
+
+ // Clear our "in discovery" state and fire callbacks
+ var callbacks = window.dnoa_internal.discoveryInProgress[identifier];
+ window.dnoa_internal.discoveryInProgress[identifier] = null;
+
+ if (callbacks) {
+ for (var i = 0; i < callbacks.onSuccess.length; i++) {
+ if (callbacks.onSuccess[i]) {
+ callbacks.onSuccess[i](discoveryResult);
+ }
+ }
+ }
+ }
+
+ /// <summary>Receives the discovery failure notification.</summary>
+ function discoverFailureCallback(message, userSuppliedIdentifier) {
+ trace('Discovery failed for: ' + identifier);
+
+ // Clear our "in discovery" state and fire callbacks
+ var callbacks = window.dnoa_internal.discoveryInProgress[identifier];
+ window.dnoa_internal.discoveryInProgress[identifier] = null;
+
+ if (callbacks) {
+ for (var i = 0; i < callbacks.onSuccess.length; i++) {
+ if (callbacks.onFailure[i]) {
+ callbacks.onFailure[i](message);
+ }
+ }
+ }
+
+ window.dnoa_internal.fireDiscoveryFailed(identifier, message);
+ }
+
+ if (window.dnoa_internal.discoveryResults[identifier]) {
+ trace("We've already discovered " + identifier + " so we're using the cached version.");
+
+ // In this special case, we never fire the DiscoveryStarted event.
+ window.dnoa_internal.fireDiscoverySuccess(identifier, window.dnoa_internal.discoveryResults[identifier], { fresh: false });
+
+ if (onDiscoverSuccess) {
+ onDiscoverSuccess(window.dnoa_internal.discoveryResults[identifier]);
+ }
+
+ return;
+ }
+
+ window.dnoa_internal.fireDiscoveryStarted(identifier);
+
+ if (!window.dnoa_internal.discoveryInProgress[identifier]) {
+ trace('starting discovery on ' + identifier);
+ window.dnoa_internal.discoveryInProgress[identifier] = {
+ onSuccess: [onDiscoverSuccess],
+ onFailure: [onDiscoverFailure]
+ };
+ window.dnoa_internal.callbackAsync(identifier, discoverSuccessCallback, discoverFailureCallback);
+ } else {
+ trace('Discovery on ' + identifier + ' already started. Registering an additional callback.');
+ window.dnoa_internal.discoveryInProgress[identifier].onSuccess.push(onDiscoverSuccess);
+ window.dnoa_internal.discoveryInProgress[identifier].onFailure.push(onDiscoverFailure);
+ }
+ };
+
+ /// <summary>Performs discovery and immediately begins checkid_setup to authenticate the user using a given identifier.</summary>
+ this.login = function(onSuccess, onLoginFailure) {
+ this.discover(function(discoveryResult) {
+ if (discoveryResult) {
+ trace('Discovery succeeded and found ' + discoveryResult.length + ' OpenID service endpoints.');
+ if (discoveryResult.length > 0) {
+ discoveryResult[0].loginPopup(onSuccess, onLoginFailure);
+ } else {
+ trace("This doesn't look like an OpenID Identifier. Aborting login.");
+ if (onLoginFailure) {
+ onLoginFailure();
}
+ }
+ }
+ });
+ };
+
+ /// <summary>Performs discovery and immediately begins checkid_immediate on all discovered endpoints.</summary>
+ this.loginBackground = function(frameManager, onLoginSuccess, onLoginFailure, timeout, onLoginLastFailure) {
+ this.discover(function(discoveryResult) {
+ if (discoveryResult) {
+ trace('Discovery succeeded and found ' + discoveryResult.length + ' OpenID service endpoints.');
+ if (discoveryResult.length > 0) {
+ discoveryResult.loginBackground(frameManager, onLoginSuccess, onLoginFailure, onLoginLastFailure || onLoginFailure, timeout);
+ } else {
+ trace("This doesn't look like an OpenID Identifier. Aborting login.");
+ if (onLoginFailure) {
+ onLoginFailure();
+ }
+ }
+ }
+ });
+ };
+
+ this.toString = function() {
+ return identifier;
+ };
+};
+
+/// <summary>Invoked by RP web server when an authentication has completed.</summary>
+/// <remarks>The duty of this method is to distribute the notification to the appropriate tracking object.</remarks>
+window.dnoa_internal.processAuthorizationResult = function(resultUrl, extensionResponses) {
+ //trace('processAuthorizationResult ' + resultUrl);
+ var resultUri = new window.dnoa_internal.Uri(resultUrl);
+ trace('processing auth result with extensionResponses: ' + extensionResponses);
+ if (extensionResponses) {
+ extensionResponses = eval(extensionResponses);
+ }
+
+ // Find the tracking object responsible for this request.
+ var userSuppliedIdentifier = resultUri.getQueryArgValue('dnoa.userSuppliedIdentifier');
+ if (!userSuppliedIdentifier) {
+ throw 'processAuthorizationResult called but no userSuppliedIdentifier parameter was found. Exiting function.';
+ }
+ var discoveryResult = window.dnoa_internal.discoveryResults[userSuppliedIdentifier];
+ if (!discoveryResult) {
+ throw 'processAuthorizationResult called but no discovery result matching user supplied identifier ' + userSuppliedIdentifier + ' was found. Exiting function.';
+ }
+
+ var opEndpoint = resultUri.getQueryArgValue("openid.op_endpoint") ? resultUri.getQueryArgValue("openid.op_endpoint") : resultUri.getQueryArgValue("dnoa.op_endpoint");
+ var respondingEndpoint = discoveryResult.findByEndpoint(opEndpoint);
+ trace('Auth result for ' + respondingEndpoint.host + ' received.'); //: ' + resultUrl);
- var left = (screen.width - width) / 2;
- var top = (screen.height - height) / 2;
- self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
+ if (window.dnoa_internal.isAuthSuccessful(resultUri)) {
+ discoveryResult.successAuthData = resultUrl;
+ respondingEndpoint.onAuthSuccess(resultUri, extensionResponses);
- // If the OP supports the UI extension it MAY close its own window
- // for a negative assertion. We must be able to recover from that scenario.
- var localSelf = self;
- self.popupCloseChecker = window.setInterval(function() {
- if (localSelf.popup && localSelf.popup.closed) {
+ var parsedPositiveAssertion = new window.dnoa_internal.PositiveAssertion(resultUri);
+ if (parsedPositiveAssertion.claimedIdentifier && parsedPositiveAssertion.claimedIdentifier != discoveryResult.claimedIdentifier) {
+ discoveryResult.claimedIdentifier = parsedPositiveAssertion.claimedIdentifier;
+ trace('Authenticated as ' + parsedPositiveAssertion.claimedIdentifier);
+ }
+ } else {
+ respondingEndpoint.onAuthFailed();
+ }
+};
+
+window.dnoa_internal.isAuthSuccessful = function(resultUri) {
+ if (window.dnoa_internal.isOpenID2Response(resultUri)) {
+ return resultUri.getQueryArgValue("openid.mode") == "id_res";
+ } else {
+ return resultUri.getQueryArgValue("openid.mode") == "id_res" && !resultUri.containsQueryArg("openid.user_setup_url");
+ }
+};
+
+window.dnoa_internal.isOpenID2Response = function(resultUri) {
+ return resultUri.containsQueryArg("openid.ns");
+};
+
+/// <summary>Instantiates an object that stores discovery results of some identifier.</summary>
+window.dnoa_internal.DiscoveryResult = function(identifier, discoveryInfo) {
+ var thisDiscoveryResult = this;
+
+ /// <summary>
+ /// Instantiates an object that describes an OpenID service endpoint and facilitates
+ /// initiating and tracking an authentication request.
+ /// </summary>
+ function ServiceEndpoint(requestInfo, userSuppliedIdentifier) {
+ this.immediate = requestInfo.immediate ? new window.dnoa_internal.Uri(requestInfo.immediate) : null;
+ this.setup = requestInfo.setup ? new window.dnoa_internal.Uri(requestInfo.setup) : null;
+ this.endpoint = new window.dnoa_internal.Uri(requestInfo.endpoint);
+ this.host = this.endpoint.getHost();
+ this.userSuppliedIdentifier = userSuppliedIdentifier;
+ var thisServiceEndpoint = this; // closure so that delegates have the right instance
+ this.loginPopup = function(onAuthSuccess, onAuthFailed) {
+ thisServiceEndpoint.abort(); // ensure no concurrent attempts
+ window.dnoa_internal.fireAuthStarted(thisDiscoveryResult, thisServiceEndpoint, { background: false });
+ thisDiscoveryResult.onAuthSuccess = onAuthSuccess;
+ thisDiscoveryResult.onAuthFailed = onAuthFailed;
+ var chromeHeight = 55; // estimated height of browser title bar and location bar
+ var bottomMargin = 45; // estimated bottom space on screen likely to include a task bar
+ var width = 1000;
+ var height = 600;
+ if (thisServiceEndpoint.setup.getQueryArgValue("openid.return_to").indexOf("dnoa.popupUISupported") >= 0) {
+ trace('This OP supports the UI extension. Using smaller window size.');
+ width = 500; // spec calls for 450px, but Yahoo needs 500px
+ height = 500;
+ } else {
+ trace("This OP doesn't appear to support the UI extension. Using larger window size.");
+ }
+
+ var left = (screen.width - width) / 2;
+ var top = (screen.height - bottomMargin - height - chromeHeight) / 2;
+ thisServiceEndpoint.popup = window.open(thisServiceEndpoint.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
+
+ // If the OP supports the UI extension it MAY close its own window
+ // for a negative assertion. We must be able to recover from that scenario.
+ var thisServiceEndpointLocal = thisServiceEndpoint;
+ thisServiceEndpoint.popupCloseChecker = window.setInterval(function() {
+ if (thisServiceEndpointLocal.popup) {
+ try {
+ if (thisServiceEndpointLocal.popup.closed) {
// The window closed, either because the user closed it, canceled at the OP,
// or approved at the OP and the popup window closed itself due to our script.
// If we were graying out the entire page while the child window was up,
// we would probably revert that here.
- window.clearInterval(localSelf.popupCloseChecker);
- localSelf.popup = null;
+ window.clearInterval(thisServiceEndpointLocal.popupCloseChecker);
+ thisServiceEndpointLocal.popup = null;
// The popup may have managed to inform us of the result already,
// so check whether the callback method was cleared already, which
// would indicate we've already processed this.
if (window.dnoa_internal.processAuthorizationResult) {
trace('User or OP canceled by closing the window.');
- if (onFailure) {
- onFailure();
+ window.dnoa_internal.fireAuthFailed(thisDiscoveryResult, thisServiceEndpoint, { background: false });
+ if (thisDiscoveryResult.onAuthFailed) {
+ thisDiscoveryResult.onAuthFailed(thisDiscoveryResult, thisServiceEndpoint);
}
- window.dnoa_internal.processAuthorizationResult = null;
}
}
- }, 250);
- };
+ } catch (e) {
+ // This usually happens because the popup is currently displaying the OP's
+ // page from another domain, which makes the popup temporarily off limits to us.
+ // Just skip this interval and wait for the next callback.
+ }
+ } else {
+ // if there's no popup, there's no reason to keep this timer up.
+ window.clearInterval(thisServiceEndpointLocal.popupCloseChecker);
+ }
+ }, 250);
+ };
+
+ this.loginBackgroundJob = function(iframe, timeout) {
+ thisServiceEndpoint.abort(); // ensure no concurrent attempts
+ if (timeout) {
+ thisServiceEndpoint.timeout = setTimeout(function() { thisServiceEndpoint.onAuthenticationTimedOut(); }, timeout);
+ }
+ window.dnoa_internal.fireAuthStarted(thisDiscoveryResult, thisServiceEndpoint, { background: true });
+ trace('iframe hosting ' + thisServiceEndpoint.endpoint + ' now OPENING (timeout ' + timeout + ').');
+ //trace('initiating auth attempt with: ' + thisServiceEndpoint.immediate);
+ thisServiceEndpoint.iframe = iframe;
+ return {
+ url: thisServiceEndpoint.immediate.toString(),
+ onCanceled: function() {
+ thisServiceEndpoint.abort();
+ window.dnoa_internal.fireAuthFailed(thisDiscoveryResult, thisServiceEndpoint, { background: true });
+ }
};
+ };
- this.userSuppliedIdentifier = identifier;
- this.claimedIdentifier = discoveryInfo.claimedIdentifier; // The claimed identifier may be null if the user provided an OP Identifier.
- trace('Discovered claimed identifier: ' + (this.claimedIdentifier ? this.claimedIdentifier : "(directed identity)"));
+ this.busy = function() {
+ return thisServiceEndpoint.iframe || thisServiceEndpoint.popup;
+ };
- if (discoveryInfo) {
- this.length = discoveryInfo.requests.length;
- for (var i = 0; i < discoveryInfo.requests.length; i++) {
- this[i] = new ServiceEndpoint(discoveryInfo.requests[i], identifier);
+ this.completeAttempt = function(successful) {
+ if (!thisServiceEndpoint.busy()) { return false; }
+ window.clearInterval(thisServiceEndpoint.timeout);
+ var background = thisServiceEndpoint.iframe !== null;
+ if (thisServiceEndpoint.iframe) {
+ trace('iframe hosting ' + thisServiceEndpoint.endpoint + ' now CLOSING.');
+ thisDiscoveryResult.frameManager.closeFrame(thisServiceEndpoint.iframe);
+ thisServiceEndpoint.iframe = null;
+ }
+ if (thisServiceEndpoint.popup) {
+ thisServiceEndpoint.popup.close();
+ thisServiceEndpoint.popup = null;
+ }
+ if (thisServiceEndpoint.timeout) {
+ window.clearTimeout(thisServiceEndpoint.timeout);
+ thisServiceEndpoint.timeout = null;
+ }
+
+ if (!successful && !thisDiscoveryResult.busy() && !thisDiscoveryResult.findSuccessfulRequest()) {
+ // fire the failed event with NO service endpoint indicating the entire auth attempt has failed.
+ window.dnoa_internal.fireAuthFailed(thisDiscoveryResult, null, { background: background });
+ if (thisDiscoveryResult.onLastAttemptFailed) {
+ thisDiscoveryResult.onLastAttemptFailed(thisDiscoveryResult);
}
- } else {
- this.length = 0;
}
- };
- /// <summary>Receives the results of a successful discovery (even if it yielded 0 results).</summary>
- function successCallback(discoveryResult, identifier) {
- trace('Discovery completed for: ' + identifier);
+ return true;
+ };
- // Deserialize the JSON object and store the result if it was a successful discovery.
- discoveryResult = eval('(' + discoveryResult + ')');
+ this.onAuthenticationTimedOut = function() {
+ var background = thisServiceEndpoint.iframe !== null;
+ if (thisServiceEndpoint.completeAttempt()) {
+ trace(thisServiceEndpoint.host + " timed out");
+ thisServiceEndpoint.result = window.dnoa_internal.timedOut;
+ }
+ window.dnoa_internal.fireAuthFailed(thisDiscoveryResult, thisServiceEndpoint, { background: background });
+ };
- // Add behavior for later use.
- discoveryResult = new DiscoveryResult(identifier, discoveryResult);
- window.dnoa_internal.discoveryResults[identifier] = discoveryResult;
+ this.onAuthSuccess = function(authUri, extensionResponses) {
+ var background = thisServiceEndpoint.iframe !== null;
+ if (thisServiceEndpoint.completeAttempt(true)) {
+ trace(thisServiceEndpoint.host + " authenticated!");
+ thisServiceEndpoint.result = window.dnoa_internal.authSuccess;
+ thisServiceEndpoint.successReceived = new Date();
+ thisServiceEndpoint.claimedIdentifier = authUri.getQueryArgValue('openid.claimed_id');
+ thisServiceEndpoint.response = authUri;
+ thisServiceEndpoint.extensionResponses = extensionResponses;
+ thisDiscoveryResult.abortAll();
+ if (thisDiscoveryResult.onAuthSuccess) {
+ thisDiscoveryResult.onAuthSuccess(thisDiscoveryResult, thisServiceEndpoint, extensionResponses);
+ }
+ window.dnoa_internal.fireAuthSuccess(thisDiscoveryResult, thisServiceEndpoint, extensionResponses, { background: background });
+ }
+ };
- if (onCompleted) {
- onCompleted(discoveryResult);
+ this.onAuthFailed = function() {
+ var background = thisServiceEndpoint.iframe !== null;
+ if (thisServiceEndpoint.completeAttempt()) {
+ trace(thisServiceEndpoint.host + " failed authentication");
+ thisServiceEndpoint.result = window.dnoa_internal.authRefused;
+ window.dnoa_internal.fireAuthFailed(thisDiscoveryResult, thisServiceEndpoint, { background: background });
+ if (thisDiscoveryResult.onAuthFailed) {
+ thisDiscoveryResult.onAuthFailed(thisDiscoveryResult, thisServiceEndpoint);
+ }
}
};
- /// <summary>Receives the discovery failure notification.</summary>
- failureCallback = function(message, userSuppliedIdentifier) {
- trace('Discovery failed for: ' + identifier);
+ this.abort = function() {
+ if (thisServiceEndpoint.completeAttempt()) {
+ trace(thisServiceEndpoint.host + " aborted");
+ // leave the result as whatever it was before.
+ }
+ };
- if (onCompleted) {
- onCompleted();
+ this.clear = function() {
+ thisServiceEndpoint.result = null;
+ thisServiceEndpoint.extensionResponses = null;
+ thisServiceEndpoint.successReceived = null;
+ thisServiceEndpoint.claimedIdentifier = null;
+ thisServiceEndpoint.response = null;
+ if (this.onCleared) {
+ this.onCleared(thisServiceEndpoint, thisDiscoveryResult);
+ }
+ if (thisDiscoveryResult.onCleared) {
+ thisDiscoveryResult.onCleared(thisDiscoveryResult, thisServiceEndpoint);
}
+ window.dnoa_internal.fireAuthCleared(thisDiscoveryResult, thisServiceEndpoint);
};
- if (window.dnoa_internal.discoveryResults[identifier]) {
- trace("We've already discovered " + identifier + " so we're skipping it this time.");
- onCompleted(window.dnoa_internal.discoveryResults[identifier]);
+ this.toString = function() {
+ return "[ServiceEndpoint: " + thisServiceEndpoint.host + "]";
+ };
+ }
+
+ this.cloneWithOneServiceEndpoint = function(serviceEndpoint) {
+ var clone = window.dnoa_internal.clone(this);
+ clone.userSuppliedIdentifier = serviceEndpoint.claimedIdentifier;
+
+ // Erase all SEPs except the given one, and put it into first position.
+ clone.length = 1;
+ for (var i = 0; i < this.length; i++) {
+ if (clone[i].endpoint.toString() == serviceEndpoint.endpoint.toString()) {
+ var tmp = clone[i];
+ clone[i] = null;
+ clone[0] = tmp;
+ } else {
+ clone[i] = null;
+ }
}
- trace('starting discovery on ' + identifier);
- window.dnoa_internal.callbackAsync(identifier, successCallback, failureCallback);
+ return clone;
};
- /// <summary>Performs discovery and immediately begins checkid_setup to authenticate the user using a given identifier.</summary>
- this.login = function(onSuccess, onFailure) {
- this.discover(function(discoveryResult) {
- if (discoveryResult) {
- trace('Discovery succeeded and found ' + discoveryResult.length + ' OpenID service endpoints.');
- if (discoveryResult.length > 0) {
- discoveryResult[0].loginPopup(onSuccess, onFailure);
- } else {
- trace("This doesn't look like an OpenID Identifier. Aborting login.");
- if (onFailure) {
- onFailure();
- }
+ this.userSuppliedIdentifier = identifier;
+ this.error = discoveryInfo.error;
+
+ if (discoveryInfo) {
+ this.claimedIdentifier = discoveryInfo.claimedIdentifier; // The claimed identifier may be null if the user provided an OP Identifier.
+ this.length = discoveryInfo.requests.length;
+ for (var i = 0; i < discoveryInfo.requests.length; i++) {
+ this[i] = new ServiceEndpoint(discoveryInfo.requests[i], identifier);
+ }
+ } else {
+ this.length = 0;
+ }
+
+ if (this.length === 0) {
+ trace('Discovery completed, but yielded no service endpoints.');
+ } else {
+ trace('Discovered claimed identifier: ' + (this.claimedIdentifier ? this.claimedIdentifier : "(directed identity)"));
+ }
+
+ // Add extra tracking bits and behaviors.
+ this.findByEndpoint = function(opEndpoint) {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ if (thisDiscoveryResult[i].endpoint == opEndpoint) {
+ return thisDiscoveryResult[i];
+ }
+ }
+ };
+
+ this.busy = function() {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ if (thisDiscoveryResult[i].busy()) {
+ return true;
+ }
+ }
+ };
+
+ // Add extra tracking bits and behaviors.
+ this.findSuccessfulRequest = function() {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ if (thisDiscoveryResult[i].result === window.dnoa_internal.authSuccess) {
+ return thisDiscoveryResult[i];
+ }
+ }
+ };
+
+ this.abortAll = function() {
+ if (thisDiscoveryResult.frameManager) {
+ // Abort all other asynchronous authentication attempts that may be in progress
+ // for this particular claimed identifier.
+ thisDiscoveryResult.frameManager.cancelAllWork();
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ thisDiscoveryResult[i].abort();
+ }
+ } else {
+ trace('abortAll called without a frameManager being previously set.');
+ }
+ };
+
+ /// <summary>Initiates an asynchronous checkid_immediate login attempt against all possible service endpoints for an Identifier.</summary>
+ /// <param name="frameManager">The work queue for authentication iframes.</param>
+ /// <param name="onAuthSuccess">Fired when an endpoint responds affirmatively.</param>
+ /// <param name="onAuthFailed">Fired when an endpoint responds negatively.</param>
+ /// <param name="onLastAuthFailed">Fired when all authentication attempts have responded negatively or timed out.</param>
+ /// <param name="timeout">Timeout for an individual service endpoint to respond before the iframe closes.</param>
+ this.loginBackground = function(frameManager, onAuthSuccess, onAuthFailed, onLastAuthFailed, timeout) {
+ if (!frameManager) {
+ throw "No frameManager specified.";
+ }
+ var priorSuccessRespondingEndpoint = thisDiscoveryResult.findSuccessfulRequest();
+ if (priorSuccessRespondingEndpoint) {
+ // In this particular case, we do not fire an AuthStarted event.
+ window.dnoa_internal.fireAuthSuccess(thisDiscoveryResult, priorSuccessRespondingEndpoint, priorSuccessRespondingEndpoint.extensionResponses, { background: true });
+ if (onAuthSuccess) {
+ onAuthSuccess(thisDiscoveryResult, priorSuccessRespondingEndpoint);
+ }
+ } else {
+ if (thisDiscoveryResult.busy()) {
+ trace('Warning: DiscoveryResult.loginBackground invoked while a login attempt is already in progress. Discarding second login request.', 'red');
+ return;
+ }
+ thisDiscoveryResult.frameManager = frameManager;
+ thisDiscoveryResult.onAuthSuccess = onAuthSuccess;
+ thisDiscoveryResult.onAuthFailed = onAuthFailed;
+ thisDiscoveryResult.onLastAttemptFailed = onLastAuthFailed;
+ // Notify listeners that general authentication is beginning. Individual ServiceEndpoints
+ // will fire their own events as each of them begin their iframe 'job'.
+ window.dnoa_internal.fireAuthStarted(thisDiscoveryResult, null, { background: true });
+ if (thisDiscoveryResult.length > 0) {
+ for (var i = 0; i < thisDiscoveryResult.length; i++) {
+ thisDiscoveryResult.frameManager.enqueueWork(thisDiscoveryResult[i].loginBackgroundJob, timeout);
}
}
- });
+ }
+ };
+
+ this.toString = function() {
+ return "[DiscoveryResult: " + thisDiscoveryResult.userSuppliedIdentifier + "]";
+ };
+};
+
+/// <summary>
+/// Called in a page had an AJAX control that had already obtained a positive assertion
+/// when a postback occurred, and now that control wants to restore its 'authenticated' state.
+/// </summary>
+/// <param name="positiveAssertion">The string form of the URI that contains the positive assertion.</param>
+window.dnoa_internal.deserializePreviousAuthentication = function(positiveAssertion) {
+ if (!positiveAssertion || positiveAssertion.length === 0) {
+ return;
+ }
+
+ trace('Revitalizing an old positive assertion from a prior postback.');
+
+ // The control ensures that we ALWAYS have an OpenID 2.0-style claimed_id attribute, even against
+ // 1.0 Providers via the return_to URL mechanism.
+ var parsedPositiveAssertion = new window.dnoa_internal.PositiveAssertion(positiveAssertion);
+
+ // We weren't given a full discovery history, but we can spoof this much from the
+ // authentication assertion.
+ trace('Deserialized claimed_id: ' + parsedPositiveAssertion.claimedIdentifier + ' and endpoint: ' + parsedPositiveAssertion.endpoint);
+ var discoveryInfo = {
+ claimedIdentifier: parsedPositiveAssertion.claimedIdentifier,
+ requests: [{ endpoint: parsedPositiveAssertion.endpoint}]
};
+
+ discoveryResult = new window.dnoa_internal.DiscoveryResult(parsedPositiveAssertion.userSuppliedIdentifier, discoveryInfo);
+ window.dnoa_internal.discoveryResults[parsedPositiveAssertion.userSuppliedIdentifier] = discoveryResult;
+ discoveryResult[0].result = window.dnoa_internal.authSuccess;
+ discoveryResult.successAuthData = positiveAssertion;
+
+ // restore old state from before postback
+ window.dnoa_internal.fireAuthSuccess(discoveryResult, discoveryResult[0], null, { background: true, deserialized: true });
+};
+
+window.dnoa_internal.PositiveAssertion = function(uri) {
+ uri = new window.dnoa_internal.Uri(uri.toString());
+ this.endpoint = new window.dnoa_internal.Uri(uri.getQueryArgValue("dnoa.op_endpoint"));
+ this.userSuppliedIdentifier = uri.getQueryArgValue('dnoa.userSuppliedIdentifier');
+ this.claimedIdentifier = uri.getQueryArgValue('openid.claimed_id');
+ if (!this.claimedIdentifier) {
+ this.claimedIdentifier = uri.getQueryArgValue('dnoa.claimed_id');
+ }
+ this.toString = function() { return uri.toString(); };
+};
+
+window.dnoa_internal.clone = function(obj) {
+ if (obj === null || typeof (obj) != 'object') {
+ return obj;
+ }
+
+ var temp = {};
+ for (var key in obj) {
+ temp[key] = window.dnoa_internal.clone(obj[key]);
+ }
+
+ return temp;
+};
+
+// Deserialized the preloaded discovery results
+window.dnoa_internal.loadPreloadedDiscoveryResults = function(preloadedDiscoveryResults) {
+ trace('found ' + preloadedDiscoveryResults.length + ' preloaded discovery results.');
+ for (var i = 0; i < preloadedDiscoveryResults.length; i++) {
+ var result = preloadedDiscoveryResults[i];
+ if (!window.dnoa_internal.discoveryResults[result.userSuppliedIdentifier]) {
+ window.dnoa_internal.discoveryResults[result.userSuppliedIdentifier] = new window.dnoa_internal.DiscoveryResult(result.userSuppliedIdentifier, result.discoveryResult);
+ trace('Preloaded discovery on: ' + window.dnoa_internal.discoveryResults[result.userSuppliedIdentifier].userSuppliedIdentifier);
+ } else {
+ trace('Skipped preloaded discovery on: ' + window.dnoa_internal.discoveryResults[result.userSuppliedIdentifier].userSuppliedIdentifier + ' because we have a cached discovery result on it.');
+ }
+ }
+};
+
+window.dnoa_internal.clearExpiredPositiveAssertions = function() {
+ for (identifier in window.dnoa_internal.discoveryResults) {
+ var discoveryResult = window.dnoa_internal.discoveryResults[identifier];
+ if (typeof (discoveryResult) != 'object') { continue; } // skip functions
+ for (var i = 0; i < discoveryResult.length; i++) {
+ if (discoveryResult[i].result === window.dnoa_internal.authSuccess) {
+ if (new Date() - discoveryResult[i].successReceived > window.dnoa_internal.maxPositiveAssertionLifetime) {
+ // This positive assertion is too old, and may eventually be rejected by DNOA during verification.
+ // Let's clear out the positive assertion so it can be renewed.
+ trace('Clearing out expired positive assertion from ' + discoveryResult[i].host);
+ discoveryResult[i].clear();
+ }
+ }
+ }
+ }
};
+window.setInterval(window.dnoa_internal.clearExpiredPositiveAssertions, 1000);
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
index 8451dbd..a4a60d1 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
@@ -9,6 +9,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
@@ -25,17 +26,72 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions;
using DotNetOpenAuth.OpenId.Extensions.UI;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Methods of indicating to the rest of the web site that the user has logged in.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "OnSite", Justification = "Two words intended.")]
+ public enum LogOnSiteNotification {
+ /// <summary>
+ /// The rest of the web site is unaware that the user just completed an OpenID login.
+ /// </summary>
+ None,
+
+ /// <summary>
+ /// After the <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/> event is fired
+ /// the control automatically calls <see cref="System.Web.Security.FormsAuthentication.RedirectFromLoginPage(string, bool)"/>
+ /// with the <see cref="IAuthenticationResponse.ClaimedIdentifier"/> as the username
+ /// unless the <see cref="OpenIdRelyingPartyControlBase.LoggedIn"/> event handler sets
+ /// <see cref="OpenIdEventArgs.Cancel"/> property to true.
+ /// </summary>
+ FormsAuthentication,
+ }
+
+ /// <summary>
+ /// How an OpenID user session should be persisted across visits.
+ /// </summary>
+ public enum LogOnPersistence {
+ /// <summary>
+ /// The user should only be logged in as long as the browser window remains open.
+ /// Nothing is persisted to help the user on a return visit. Public kiosk mode.
+ /// </summary>
+ Session,
+
+ /// <summary>
+ /// The user should only be logged in as long as the browser window remains open.
+ /// The OpenID Identifier is persisted to help expedite re-authentication when
+ /// the user visits the next time.
+ /// </summary>
+ SessionAndPersistentIdentifier,
+
+ /// <summary>
+ /// The user is issued a persistent authentication ticket so that no login is
+ /// necessary on their return visit.
+ /// </summary>
+ PersistentAuthentication,
+ }
/// <summary>
/// A common base class for OpenID Relying Party controls.
/// </summary>
[DefaultProperty("Identifier"), ValidationProperty("Identifier")]
- public abstract class OpenIdRelyingPartyControlBase : Control {
+ public abstract class OpenIdRelyingPartyControlBase : Control, IPostBackEventHandler, IDisposable {
/// <summary>
/// The manifest resource name of the javascript file to include on the hosting page.
/// </summary>
internal const string EmbeddedJavascriptResource = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdRelyingPartyControlBase.js";
+ /// <summary>
+ /// The cookie used to persist the Identifier the user logged in with.
+ /// </summary>
+ internal const string PersistentIdentifierCookieName = OpenIdUtilities.CustomParameterPrefix + "OpenIDIdentifier";
+
+ /// <summary>
+ /// The callback parameter name to use to store which control initiated the auth request.
+ /// </summary>
+ internal const string ReturnToReceivingControlId = OpenIdUtilities.CustomParameterPrefix + "receiver";
+
#region Property category constants
/// <summary>
@@ -55,6 +111,31 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
+ #region Callback parameter names
+
+ /// <summary>
+ /// The callback parameter to use for recognizing when the callback is in a popup window or hidden iframe.
+ /// </summary>
+ protected const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
+
+ /// <summary>
+ /// The parameter name to include in the formulated auth request so that javascript can know whether
+ /// the OP advertises support for the UI extension.
+ /// </summary>
+ protected const string PopupUISupportedJSHint = OpenIdUtilities.CustomParameterPrefix + "popupUISupported";
+
+ /// <summary>
+ /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
+ /// </summary>
+ private const string UsePersistentCookieCallbackKey = OpenIdUtilities.CustomParameterPrefix + "UsePersistentCookie";
+
+ /// <summary>
+ /// The callback parameter to use for recognizing when the callback is in the parent window.
+ /// </summary>
+ private const string UIPopupCallbackParentKey = OpenIdUtilities.CustomParameterPrefix + "uipopupParent";
+
+ #endregion
+
#region Property default values
/// <summary>
@@ -70,7 +151,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Default value of <see cref="UsePersistentCookie"/>.
/// </summary>
- private const bool UsePersistentCookieDefault = false;
+ private const LogOnPersistence UsePersistentCookieDefault = LogOnPersistence.Session;
+
+ /// <summary>
+ /// Default value of <see cref="LogOnMode"/>.
+ /// </summary>
+ private const LogOnSiteNotification LogOnModeDefault = LogOnSiteNotification.FormsAuthentication;
/// <summary>
/// The default value for the <see cref="RealmUrl"/> property.
@@ -92,6 +178,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#region Property view state keys
/// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="Extensions"/> property.
+ /// </summary>
+ private const string ExtensionsViewStateKey = "Extensions";
+
+ /// <summary>
/// The viewstate key to use for the <see cref="Stateless"/> property.
/// </summary>
private const string StatelessViewStateKey = "Stateless";
@@ -102,6 +193,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string UsePersistentCookieViewStateKey = "UsePersistentCookie";
/// <summary>
+ /// The viewstate key to use for the <see cref="LogOnMode"/> property.
+ /// </summary>
+ private const string LogOnModeViewStateKey = "LogOnMode";
+
+ /// <summary>
/// The viewstate key to use for the <see cref="RealmUrl"/> property.
/// </summary>
private const string RealmUrlViewStateKey = "RealmUrl";
@@ -128,55 +224,36 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
- #region Callback parameter names
-
- /// <summary>
- /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
- /// </summary>
- private const string UsePersistentCookieCallbackKey = OpenIdUtilities.CustomParameterPrefix + "UsePersistentCookie";
-
/// <summary>
- /// The callback parameter to use for recognizing when the callback is in a popup window.
+ /// The lifetime of the cookie used to persist the Identifier the user logged in with.
/// </summary>
- private const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
+ private static readonly TimeSpan PersistentIdentifierTimeToLiveDefault = TimeSpan.FromDays(14);
/// <summary>
- /// The callback parameter to use for recognizing when the callback is in the parent window.
- /// </summary>
- private const string UIPopupCallbackParentKey = OpenIdUtilities.CustomParameterPrefix + "uipopupParent";
-
- /// <summary>
- /// The callback parameter name to use to store which control initiated the auth request.
- /// </summary>
- private const string ReturnToReceivingControlId = OpenIdUtilities.CustomParameterPrefix + "receiver";
-
- /// <summary>
- /// The parameter name to include in the formulated auth request so that javascript can know whether
- /// the OP advertises support for the UI extension.
+ /// Backing field for the <see cref="RelyingParty"/> property.
/// </summary>
- private const string PopupUISupportedJsHint = "dotnetopenid.popupUISupported";
-
- #endregion
+ private OpenIdRelyingParty relyingParty;
/// <summary>
- /// Backing field for the <see cref="RelyingParty"/> property.
+ /// A value indicating whether the <see cref="relyingParty"/> field contains
+ /// an instance that we own and should Dispose.
/// </summary>
- private OpenIdRelyingParty relyingParty;
+ private bool relyingPartyOwned;
/// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingPartyControlBase"/> class.
/// </summary>
protected OpenIdRelyingPartyControlBase() {
+ Reporting.RecordFeatureUse(this);
}
#region Events
/// <summary>
- /// Fired after the user clicks the log in button, but before the authentication
- /// process begins. Offers a chance for the web application to disallow based on
- /// OpenID URL before redirecting the user to the OpenID Provider.
+ /// Fired when the user has typed in their identifier, discovery was successful
+ /// and a login attempt is about to begin.
/// </summary>
- [Description("Fired after the user clicks the log in button, but before the authentication process begins. Offers a chance for the web application to disallow based on OpenID URL before redirecting the user to the OpenID Provider."), Category(OpenIdCategory)]
+ [Description("Fired when the user has typed in their identifier, discovery was successful and a login attempt is about to begin."), Category(OpenIdCategory)]
public event EventHandler<OpenIdEventArgs> LoggingIn;
/// <summary>
@@ -197,6 +274,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Description("Fired when an authentication attempt is canceled at the OpenID Provider."), Category(OpenIdCategory)]
public event EventHandler<OpenIdEventArgs> Canceled;
+ /// <summary>
+ /// Occurs when the <see cref="Identifier"/> property is changed.
+ /// </summary>
+ protected event EventHandler IdentifierChanged;
+
#endregion
/// <summary>
@@ -215,12 +297,34 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
get {
if (this.relyingParty == null) {
this.relyingParty = this.CreateRelyingParty();
+ this.relyingPartyOwned = true;
}
return this.relyingParty;
}
set {
+ if (this.relyingPartyOwned && this.relyingParty != null) {
+ this.relyingParty.Dispose();
+ }
+
this.relyingParty = value;
+ this.relyingPartyOwned = false;
+ }
+ }
+
+ /// <summary>
+ /// Gets the collection of extension requests this selector should include in generated requests.
+ /// </summary>
+ [PersistenceMode(PersistenceMode.InnerProperty)]
+ public Collection<IOpenIdMessageExtension> Extensions {
+ get {
+ if (this.ViewState[ExtensionsViewStateKey] == null) {
+ var extensions = new Collection<IOpenIdMessageExtension>();
+ this.ViewState[ExtensionsViewStateKey] = extensions;
+ return extensions;
+ } else {
+ return (Collection<IOpenIdMessageExtension>)this.ViewState[ExtensionsViewStateKey];
+ }
}
}
@@ -306,12 +410,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Bindable(true), DefaultValue(UsePersistentCookieDefault), Category(BehaviorCategory)]
[Description("Whether to send a persistent cookie upon successful " +
"login so the user does not have to log in upon returning to this site.")]
- public virtual bool UsePersistentCookie {
- get { return (bool)(this.ViewState[UsePersistentCookieViewStateKey] ?? UsePersistentCookieDefault); }
+ public virtual LogOnPersistence UsePersistentCookie {
+ get { return (LogOnPersistence)(this.ViewState[UsePersistentCookieViewStateKey] ?? UsePersistentCookieDefault); }
set { this.ViewState[UsePersistentCookieViewStateKey] = value; }
}
/// <summary>
+ /// Gets or sets the way a completed login is communicated to the rest of the web site.
+ /// </summary>
+ [Bindable(true), DefaultValue(LogOnModeDefault), Category(BehaviorCategory)]
+ [Description("The way a completed login is communicated to the rest of the web site.")]
+ public virtual LogOnSiteNotification LogOnMode {
+ get { return (LogOnSiteNotification)(this.ViewState[LogOnModeViewStateKey] ?? LogOnModeDefault); }
+ set { this.ViewState[LogOnModeViewStateKey] = value; }
+ }
+
+ /// <summary>
/// Gets or sets a value indicating when to use a popup window to complete the login experience.
/// </summary>
/// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
@@ -334,15 +448,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Gets or sets the URL to your privacy policy page that describes how
- /// claims will be used and/or shared.
+ /// Gets or sets the Identifier that will be used to initiate login.
/// </summary>
[Bindable(true), Category(OpenIdCategory)]
[Description("The OpenID Identifier that this button will use to initiate login.")]
[TypeConverter(typeof(IdentifierConverter))]
- public Identifier Identifier {
- get { return (Identifier)ViewState[IdentifierViewStateKey]; }
- set { ViewState[IdentifierViewStateKey] = value; }
+ public virtual Identifier Identifier {
+ get {
+ return (Identifier)ViewState[IdentifierViewStateKey];
+ }
+
+ set {
+ ViewState[IdentifierViewStateKey] = value;
+ this.OnIdentifierChanged();
+ }
}
/// <summary>
@@ -351,11 +470,52 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
internal AssociationPreference AssociationPreference { get; set; }
/// <summary>
+ /// Gets ancestor controls, starting with the immediate parent, and progressing to more distant ancestors.
+ /// </summary>
+ protected IEnumerable<Control> ParentControls {
+ get {
+ Control parent = this;
+ while ((parent = parent.Parent) != null) {
+ yield return parent;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this control is a child control of a composite OpenID control.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is embedded in parent OpenID control; otherwise, <c>false</c>.
+ /// </value>
+ protected bool IsEmbeddedInParentOpenIdControl {
+ get { return this.ParentControls.OfType<OpenIdRelyingPartyControlBase>().Any(); }
+ }
+
+ /// <summary>
+ /// Clears any cookie set by this control to help the user on a returning visit next time.
+ /// </summary>
+ public static void LogOff() {
+ HttpContext.Current.Response.SetCookie(CreateIdentifierPersistingCookie(null));
+ }
+
+ /// <summary>
/// Immediately redirects to the OpenID Provider to verify the Identifier
/// provided in the text box.
/// </summary>
public void LogOn() {
IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
+ ErrorUtilities.VerifyProtocol(request != null, OpenIdStrings.OpenIdEndpointNotFound);
+ this.LogOn(request);
+ }
+
+ /// <summary>
+ /// Immediately redirects to the OpenID Provider to verify the Identifier
+ /// provided in the text box.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void LogOn(IAuthenticationRequest request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+
if (this.IsPopupAppropriate(request)) {
this.ScriptPopupWindow(request);
} else {
@@ -363,73 +523,74 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
}
+ #region IPostBackEventHandler Members
+
/// <summary>
- /// Creates the authentication requests for a given user-supplied Identifier.
+ /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
/// </summary>
- /// <returns>A sequence of authentication requests, any one of which may be
- /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
- protected virtual IEnumerable<IAuthenticationRequest> CreateRequests() {
- Contract.Requires(this.Identifier != null, OpenIdStrings.NoIdentifierSet);
- ErrorUtilities.VerifyOperation(this.Identifier != null, OpenIdStrings.NoIdentifierSet);
- IEnumerable<IAuthenticationRequest> requests;
-
- // Approximate the returnTo (either based on the customize property or the page URL)
- // so we can use it to help with Realm resolution.
- Uri returnToApproximation = this.ReturnToUrl != null ? new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.ReturnToUrl) : this.Page.Request.Url;
-
- // Resolve the trust root, and swap out the scheme and port if necessary to match the
- // return_to URL, since this match is required by OpenId, and the consumer app
- // may be using HTTP at some times and HTTPS at others.
- UriBuilder realm = OpenIdUtilities.GetResolvedRealm(this.Page, this.RealmUrl, this.RelyingParty.Channel.GetRequestFromContext());
- realm.Scheme = returnToApproximation.Scheme;
- realm.Port = returnToApproximation.Port;
-
- // Initiate openid request
- // We use TryParse here to avoid throwing an exception which
- // might slip through our validator control if it is disabled.
- Realm typedRealm = new Realm(realm);
- if (string.IsNullOrEmpty(this.ReturnToUrl)) {
- requests = this.RelyingParty.CreateRequests(this.Identifier, typedRealm);
- } else {
- // Since the user actually gave us a return_to value,
- // the "approximation" is exactly what we want.
- requests = this.RelyingParty.CreateRequests(this.Identifier, typedRealm, returnToApproximation);
- }
+ /// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
+ void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
+ this.RaisePostBackEvent(eventArgument);
+ }
- // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example).
- // Since we're gathering OPs to try one after the other, just take the first choice of each OP
- // and don't try it multiple times.
- requests = requests.Distinct(DuplicateRequestedHostsComparer.Instance);
+ #endregion
- // Configure each generated request.
- foreach (var req in requests) {
- if (this.IsPopupAppropriate(req)) {
- // Inform the OP that we'll be using a popup window.
- req.AddExtension(new UIRequest());
+ /// <summary>
+ /// Enables a server control to perform final clean up before it is released from memory.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Base class doesn't implement virtual Dispose(bool), so we must call its Dispose() method.")]
+ public sealed override void Dispose() {
+ this.Dispose(true);
+ base.Dispose();
+ GC.SuppressFinalize(this);
+ }
- // Inform ourselves in return_to that we're in a popup.
- req.AddCallbackArguments(UIPopupCallbackKey, "1");
+ /// <summary>
+ /// Creates the authentication requests for a given user-supplied Identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to create a request for.</param>
+ /// <returns>
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
+ /// </returns>
+ protected internal virtual IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
- if (req.Provider.IsExtensionSupported<UIRequest>()) {
- // Provide a hint for the client javascript about whether the OP supports the UI extension.
- // This is so the window can be made the correct size for the extension.
- // If the OP doesn't advertise support for the extension, the javascript will use
- // a bigger popup window.
- req.AddCallbackArguments(PopupUISupportedJsHint, "1");
- }
- }
+ // Delegate to a private method to keep 'yield return' and Code Contract separate.
+ return this.CreateRequestsCore(identifier);
+ }
- // Add state that needs to survive across the redirect.
- if (!this.Stateless) {
- req.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
- req.AddCallbackArguments(ReturnToReceivingControlId, this.ClientID);
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ if (this.relyingPartyOwned && this.relyingParty != null) {
+ this.relyingParty.Dispose();
+ this.relyingParty = null;
}
+ }
+ }
- ((AuthenticationRequest)req).AssociationPreference = this.AssociationPreference;
- this.OnLoggingIn(req);
+ /// <summary>
+ /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
+ /// </summary>
+ /// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Predefined signature.")]
+ protected virtual void RaisePostBackEvent(string eventArgument) {
+ }
- yield return req;
- }
+ /// <summary>
+ /// Creates the authentication requests for the value set in the <see cref="Identifier"/> property.
+ /// </summary>
+ /// <returns>
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
+ /// </returns>
+ protected IEnumerable<IAuthenticationRequest> CreateRequests() {
+ Contract.Requires<InvalidOperationException>(this.Identifier != null, OpenIdStrings.NoIdentifierSet);
+ return this.CreateRequests(this.Identifier);
}
/// <summary>
@@ -444,47 +605,75 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return;
}
+ if (this.Identifier == null) {
+ this.TryPresetIdentifierWithCookie();
+ }
+
// Take an unreliable sneek peek to see if we're in a popup and an OpenID
// assertion is coming in. We shouldn't process assertions in a popup window.
if (this.Page.Request.QueryString[UIPopupCallbackKey] == "1" && this.Page.Request.QueryString[UIPopupCallbackParentKey] == null) {
// We're in a popup window. We need to close it and pass the
// message back to the parent window for processing.
- this.ScriptClosingPopup();
+ this.ScriptClosingPopupOrIFrame();
return; // don't do any more processing on it now
}
- // Only sniff for an OpenID response if it is targeted at this control. Note that
- // Stateless mode causes no receiver to be indicated.
+ // Only sniff for an OpenID response if it is targeted at this control.
+ // Note that Stateless mode causes no receiver to be indicated, and
+ // we want to handle that, but only if there isn't a parent control that
+ // will be handling that.
string receiver = this.Page.Request.QueryString[ReturnToReceivingControlId] ?? this.Page.Request.Form[ReturnToReceivingControlId];
- if (receiver == null || receiver == this.ClientID) {
+ if (receiver == this.ClientID || (receiver == null && !this.IsEmbeddedInParentOpenIdControl)) {
var response = this.RelyingParty.GetResponse();
- if (response != null) {
- string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey);
- bool persistentBool;
- if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) {
- this.UsePersistentCookie = persistentBool;
- }
+ Logger.Controls.DebugFormat(
+ "The {0} control checked for an authentication response and found: {1}",
+ this.ID,
+ response != null ? response.Status.ToString() : "nothing");
+ this.ProcessResponse(response);
+ }
+ }
- switch (response.Status) {
- case AuthenticationStatus.Authenticated:
- this.OnLoggedIn(response);
- break;
- case AuthenticationStatus.Canceled:
- this.OnCanceled(response);
- break;
- case AuthenticationStatus.Failed:
- this.OnFailed(response);
- break;
- case AuthenticationStatus.SetupRequired:
- case AuthenticationStatus.ExtensionsOnly:
- default:
- // The NotApplicable (extension-only assertion) is NOT one that we support
- // in this control because that scenario is primarily interesting to RPs
- // that are asking a specific OP, and it is not user-initiated as this textbox
- // is designed for.
- throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
- }
- }
+ /// <summary>
+ /// Called when the <see cref="Identifier"/> property is changed.
+ /// </summary>
+ protected virtual void OnIdentifierChanged() {
+ var identifierChanged = this.IdentifierChanged;
+ if (identifierChanged != null) {
+ identifierChanged(this, EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// Processes the response.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ protected virtual void ProcessResponse(IAuthenticationResponse response) {
+ if (response == null) {
+ return;
+ }
+ string persistentString = response.GetUntrustedCallbackArgument(UsePersistentCookieCallbackKey);
+ if (persistentString != null) {
+ this.UsePersistentCookie = (LogOnPersistence)Enum.Parse(typeof(LogOnPersistence), persistentString);
+ }
+
+ switch (response.Status) {
+ case AuthenticationStatus.Authenticated:
+ this.OnLoggedIn(response);
+ break;
+ case AuthenticationStatus.Canceled:
+ this.OnCanceled(response);
+ break;
+ case AuthenticationStatus.Failed:
+ this.OnFailed(response);
+ break;
+ case AuthenticationStatus.SetupRequired:
+ case AuthenticationStatus.ExtensionsOnly:
+ default:
+ // The NotApplicable (extension-only assertion) is NOT one that we support
+ // in this control because that scenario is primarily interesting to RPs
+ // that are asking a specific OP, and it is not user-initiated as this textbox
+ // is designed for.
+ throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
}
}
@@ -503,10 +692,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnLoggedIn(IAuthenticationResponse response) {
- Contract.Requires(response != null);
- Contract.Requires(response.Status == AuthenticationStatus.Authenticated);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Authenticated, "Firing OnLoggedIn event without an authenticated response.");
+ Contract.Requires<ArgumentNullException>(response != null);
+ Contract.Requires<ArgumentException>(response.Status == AuthenticationStatus.Authenticated);
var loggedIn = this.LoggedIn;
OpenIdEventArgs args = new OpenIdEventArgs(response);
@@ -515,7 +702,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
if (!args.Cancel) {
- FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, this.UsePersistentCookie);
+ if (this.UsePersistentCookie == LogOnPersistence.SessionAndPersistentIdentifier) {
+ Page.Response.SetCookie(CreateIdentifierPersistingCookie(response));
+ }
+
+ switch (this.LogOnMode) {
+ case LogOnSiteNotification.FormsAuthentication:
+ FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, this.UsePersistentCookie == LogOnPersistence.PersistentAuthentication);
+ break;
+ case LogOnSiteNotification.None:
+ default:
+ break;
+ }
}
}
@@ -527,8 +725,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Returns whether the login should proceed. False if some event handler canceled the request.
/// </returns>
protected virtual bool OnLoggingIn(IAuthenticationRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
EventHandler<OpenIdEventArgs> loggingIn = this.LoggingIn;
@@ -545,10 +742,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnCanceled(IAuthenticationResponse response) {
- Contract.Requires(response != null);
- Contract.Requires(response.Status == AuthenticationStatus.Canceled);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Canceled, "Firing Canceled event for the wrong response type.");
+ Contract.Requires<ArgumentNullException>(response != null);
+ Contract.Requires<ArgumentException>(response.Status == AuthenticationStatus.Canceled);
var canceled = this.Canceled;
if (canceled != null) {
@@ -561,10 +756,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="response">The response.</param>
protected virtual void OnFailed(IAuthenticationResponse response) {
- Contract.Requires(response != null);
- Contract.Requires(response.Status == AuthenticationStatus.Failed);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Failed, "Firing Failed event for the wrong response type.");
+ Contract.Requires<ArgumentNullException>(response != null);
+ Contract.Requires<ArgumentException>(response.Status == AuthenticationStatus.Failed);
var failed = this.Failed;
if (failed != null) {
@@ -577,8 +770,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <returns>The instantiated relying party.</returns>
protected virtual OpenIdRelyingParty CreateRelyingParty() {
+ return this.CreateRelyingParty(true);
+ }
+
+ /// <summary>
+ /// Creates the relying party instance used to generate authentication requests.
+ /// </summary>
+ /// <param name="verifySignature">
+ /// A value indicating whether message protections should be applied to the processed messages.
+ /// Use <c>false</c> to postpone verification to a later time without invalidating nonces.
+ /// </param>
+ /// <returns>The instantiated relying party.</returns>
+ protected virtual OpenIdRelyingParty CreateRelyingParty(bool verifySignature) {
IRelyingPartyApplicationStore store = this.Stateless ? null : DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore);
- var rp = new OpenIdRelyingParty(store);
+ var rp = verifySignature ? new OpenIdRelyingParty(store) : OpenIdRelyingParty.CreateNonVerifying();
// Only set RequireSsl to true, as we don't want to override
// a .config setting of true with false.
@@ -597,8 +802,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <c>true</c> if a popup should be used; <c>false</c> otherwise.
/// </returns>
protected virtual bool IsPopupAppropriate(IAuthenticationRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
switch (this.Popup) {
case PopupBehavior.Never:
@@ -606,7 +810,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
case PopupBehavior.Always:
return true;
case PopupBehavior.IfProviderSupported:
- return request.Provider.IsExtensionSupported<UIRequest>();
+ return request.DiscoveryResult.IsExtensionSupported<UIRequest>();
default:
throw ErrorUtilities.ThrowInternal("Unexpected value for Popup property.");
}
@@ -620,10 +824,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="request">The outgoing authentication request.</param>
/// <param name="windowStatus">The text to try to display in the status bar on mouse hover.</param>
protected void RenderOpenIdMessageTransmissionAsAnchorAttributes(HtmlTextWriter writer, IAuthenticationRequest request, string windowStatus) {
- Contract.Requires(writer != null);
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(writer, "writer");
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(writer != null);
+ Contract.Requires<ArgumentNullException>(request != null);
// We render a standard HREF attribute for non-javascript browsers.
writer.AddAttribute(HtmlTextWriterAttribute.Href, request.RedirectingResponse.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri);
@@ -639,13 +841,149 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Wires the popup window to close itself and pass the authentication result to the parent window.
+ /// </summary>
+ protected virtual void ScriptClosingPopupOrIFrame() {
+ StringBuilder startupScript = new StringBuilder();
+ startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL);");
+ startupScript.AppendLine("window.close();");
+
+ this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true);
+
+ // TODO: alternately we should probably take over rendering this page here to avoid
+ // a lot of unnecessary work on the server and possible momentary display of the
+ // page in the popup window.
+ }
+
+ /// <summary>
+ /// Creates the identifier-persisting cookie, either for saving or deleting.
+ /// </summary>
+ /// <param name="response">The positive authentication response; or <c>null</c> to clear the cookie.</param>
+ /// <returns>An persistent cookie.</returns>
+ private static HttpCookie CreateIdentifierPersistingCookie(IAuthenticationResponse response) {
+ HttpCookie cookie = new HttpCookie(PersistentIdentifierCookieName);
+ bool clearingCookie = false;
+
+ // We'll try to store whatever it was the user originally typed in, but fallback
+ // to the final claimed_id.
+ if (response != null && response.Status == AuthenticationStatus.Authenticated) {
+ var positiveResponse = (PositiveAuthenticationResponse)response;
+
+ // We must escape the value because XRIs start with =, and any leading '=' gets dropped (by ASP.NET?)
+ cookie.Value = Uri.EscapeDataString(positiveResponse.Endpoint.UserSuppliedIdentifier ?? response.ClaimedIdentifier);
+ } else {
+ clearingCookie = true;
+ cookie.Value = string.Empty;
+ if (HttpContext.Current.Request.Browser["supportsEmptyStringInCookieValue"] == "false") {
+ cookie.Value = "NoCookie";
+ }
+ }
+
+ if (clearingCookie) {
+ // mark the cookie has having already expired to cause the user agent to delete
+ // the old persisted cookie.
+ cookie.Expires = DateTime.Now.Subtract(TimeSpan.FromDays(1));
+ } else {
+ // Make the cookie persistent by setting an expiration date
+ cookie.Expires = DateTime.Now + PersistentIdentifierTimeToLiveDefault;
+ }
+
+ return cookie;
+ }
+
+ /// <summary>
+ /// Creates the authentication requests for a given user-supplied Identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to create a request for.</param>
+ /// <returns>
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
+ /// </returns>
+ private IEnumerable<IAuthenticationRequest> CreateRequestsCore(Identifier identifier) {
+ ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier"); // NO CODE CONTRACTS! (yield return used here)
+ IEnumerable<IAuthenticationRequest> requests;
+
+ // Approximate the returnTo (either based on the customize property or the page URL)
+ // so we can use it to help with Realm resolution.
+ Uri returnToApproximation = this.ReturnToUrl != null ? new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.ReturnToUrl) : this.Page.Request.Url;
+
+ // Resolve the trust root, and swap out the scheme and port if necessary to match the
+ // return_to URL, since this match is required by OpenId, and the consumer app
+ // may be using HTTP at some times and HTTPS at others.
+ UriBuilder realm = OpenIdUtilities.GetResolvedRealm(this.Page, this.RealmUrl, this.RelyingParty.Channel.GetRequestFromContext());
+ realm.Scheme = returnToApproximation.Scheme;
+ realm.Port = returnToApproximation.Port;
+
+ // Initiate openid request
+ // We use TryParse here to avoid throwing an exception which
+ // might slip through our validator control if it is disabled.
+ Realm typedRealm = new Realm(realm);
+ if (string.IsNullOrEmpty(this.ReturnToUrl)) {
+ requests = this.RelyingParty.CreateRequests(identifier, typedRealm);
+ } else {
+ // Since the user actually gave us a return_to value,
+ // the "approximation" is exactly what we want.
+ requests = this.RelyingParty.CreateRequests(identifier, typedRealm, returnToApproximation);
+ }
+
+ // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example).
+ // Since we're gathering OPs to try one after the other, just take the first choice of each OP
+ // and don't try it multiple times.
+ requests = requests.Distinct(DuplicateRequestedHostsComparer.Instance);
+
+ // Configure each generated request.
+ foreach (var req in requests) {
+ if (this.IsPopupAppropriate(req)) {
+ // Inform ourselves in return_to that we're in a popup.
+ req.SetUntrustedCallbackArgument(UIPopupCallbackKey, "1");
+
+ if (req.DiscoveryResult.IsExtensionSupported<UIRequest>()) {
+ // Inform the OP that we'll be using a popup window consistent with the UI extension.
+ req.AddExtension(new UIRequest());
+
+ // Provide a hint for the client javascript about whether the OP supports the UI extension.
+ // This is so the window can be made the correct size for the extension.
+ // If the OP doesn't advertise support for the extension, the javascript will use
+ // a bigger popup window.
+ req.SetUntrustedCallbackArgument(PopupUISupportedJSHint, "1");
+ }
+ }
+
+ // Add the extensions injected into the control.
+ foreach (var extension in this.Extensions) {
+ req.AddExtension(extension);
+ }
+
+ // Add state that needs to survive across the redirect, but at this point
+ // only save those properties that are not expected to be changed by a
+ // LoggingIn event handler.
+ req.SetUntrustedCallbackArgument(ReturnToReceivingControlId, this.ClientID);
+
+ // Apply the control's association preference to this auth request, but only if
+ // it is less demanding (greater ordinal value) than the existing one.
+ // That way, we protect against retrying an association that was already attempted.
+ var authReq = ((AuthenticationRequest)req);
+ if (authReq.AssociationPreference < this.AssociationPreference) {
+ authReq.AssociationPreference = this.AssociationPreference;
+ }
+
+ if (this.OnLoggingIn(req)) {
+ // We save this property after firing OnLoggingIn so that the host page can
+ // change its value and have that value saved.
+ req.SetUntrustedCallbackArgument(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString());
+
+ yield return req;
+ }
+ }
+ }
+
+ /// <summary>
/// Gets the javascript to executee to redirect or POST an OpenID message to a remote party.
/// </summary>
/// <param name="request">The authentication request to send.</param>
/// <returns>The javascript that should execute.</returns>
private string CreateGetOrPostAHrefValue(IAuthenticationRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
Uri directUri = request.RedirectingResponse.GetDirectUriRequest(this.RelyingParty.Channel);
return "window.dnoa_internal.GetOrPost(" + MessagingUtilities.GetSafeJavascriptValue(directUri.AbsoluteUri) + ");";
@@ -656,37 +994,40 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="request">The request.</param>
private void ScriptPopupWindow(IAuthenticationRequest request) {
- Contract.Requires(request != null);
- Contract.Requires(this.RelyingParty != null);
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<InvalidOperationException>(this.RelyingParty != null);
StringBuilder startupScript = new StringBuilder();
// Add a callback function that the popup window can call on this, the
// parent window, to pass back the authentication result.
- startupScript.AppendLine("window.dnoa_internal = new Object();");
+ startupScript.AppendLine("window.dnoa_internal = {};");
startupScript.AppendLine("window.dnoa_internal.processAuthorizationResult = function(uri) { window.location = uri; };");
startupScript.AppendLine("window.dnoa_internal.popupWindow = function() {");
- startupScript.AppendFormat(
- @"\tvar openidPopup = {0}",
- UIUtilities.GetWindowPopupScript(this.RelyingParty, request, "openidPopup"));
+ startupScript.AppendFormat(
+ @"\tvar openidPopup = {0}",
+ UIUtilities.GetWindowPopupScript(this.RelyingParty, request, "openidPopup"));
startupScript.AppendLine("};");
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "loginPopup", startupScript.ToString(), true);
}
/// <summary>
- /// Wires the popup window to close itself and pass the authentication result to the parent window.
+ /// Tries to preset the <see cref="Identifier"/> property based on a persistent
+ /// cookie on the browser.
/// </summary>
- private void ScriptClosingPopup() {
- StringBuilder startupScript = new StringBuilder();
- startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL + '&" + UIPopupCallbackParentKey + "=1');");
- startupScript.AppendLine("window.close();");
-
- this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true);
+ /// <returns>
+ /// A value indicating whether the <see cref="Identifier"/> property was
+ /// successfully preset to some non-empty value.
+ /// </returns>
+ private bool TryPresetIdentifierWithCookie() {
+ HttpCookie cookie = this.Page.Request.Cookies[PersistentIdentifierCookieName];
+ if (cookie != null) {
+ this.Identifier = Uri.UnescapeDataString(cookie.Value);
+ return true;
+ }
- // TODO: alternately we should probably take over rendering this page here to avoid
- // a lot of unnecessary work on the server and possible momentary display of the
- // page in the popup window.
+ return false;
}
/// <summary>
@@ -716,8 +1057,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
- /// <param name="x">The first object of type <paramref name="T"/> to compare.</param>
- /// <param name="y">The second object of type <paramref name="T"/> to compare.</param>
+ /// <param name="x">The first object to compare.</param>
+ /// <param name="y">The second object to compare.</param>
/// <returns>
/// true if the specified objects are equal; otherwise, false.
/// </returns>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
index 3a17b7b..58b283d 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
@@ -1,6 +1,8 @@
//-----------------------------------------------------------------------
// <copyright file="OpenIdRelyingPartyControlBase.js" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
+// This file may be used and redistributed under the terms of the
+// Microsoft Public License (Ms-PL) http://opensource.org/licenses/ms-pl.html
// </copyright>
//-----------------------------------------------------------------------
@@ -8,13 +10,14 @@
//window.openid_visible_iframe = true; // causes the hidden iframe to show up
//window.openid_trace = true; // causes lots of messages
-trace = function(msg) {
+trace = function(msg, color) {
if (window.openid_trace) {
if (!window.openid_tracediv) {
window.openid_tracediv = document.createElement("ol");
document.body.appendChild(window.openid_tracediv);
}
var el = document.createElement("li");
+ if (color) { el.style.color = color; }
el.appendChild(document.createTextNode(msg));
window.openid_tracediv.appendChild(el);
//alert(msg);
@@ -22,13 +25,8 @@ trace = function(msg) {
};
if (window.dnoa_internal === undefined) {
- window.dnoa_internal = new Object();
-};
-
-// The possible authentication results
-window.dnoa_internal.authSuccess = new Object();
-window.dnoa_internal.authRefused = new Object();
-window.dnoa_internal.timedOut = new Object();
+ window.dnoa_internal = {};
+}
/// <summary>Instantiates an object that provides string manipulation services for URIs.</summary>
window.dnoa_internal.Uri = function(url) {
@@ -41,20 +39,20 @@ window.dnoa_internal.Uri = function(url) {
this.getAuthority = function() {
var authority = this.getScheme() + "://" + this.getHost();
return authority;
- }
+ };
this.getHost = function() {
var hostStartIdx = this.originalUri.indexOf("://") + 3;
var hostEndIndex = this.originalUri.indexOf("/", hostStartIdx);
- if (hostEndIndex < 0) hostEndIndex = this.originalUri.length;
+ if (hostEndIndex < 0) { hostEndIndex = this.originalUri.length; }
var host = this.originalUri.substr(hostStartIdx, hostEndIndex - hostStartIdx);
return host;
- }
+ };
this.getScheme = function() {
var schemeStartIdx = this.indexOf("://");
return this.originalUri.substr(this.originalUri, schemeStartIdx);
- }
+ };
this.trimFragment = function() {
var hashmark = this.originalUri.indexOf('#');
@@ -76,13 +74,13 @@ window.dnoa_internal.Uri = function(url) {
function KeyValuePair(key, value) {
this.key = key;
this.value = value;
- };
+ }
- this.pairs = new Array();
+ this.pairs = [];
var queryBeginsAt = this.originalUri.indexOf('?');
if (queryBeginsAt >= 0) {
- this.queryString = url.substr(queryBeginsAt + 1);
+ this.queryString = this.originalUri.substr(queryBeginsAt + 1);
var queryStringPairs = this.queryString.split('&');
for (var i = 0; i < queryStringPairs.length; i++) {
@@ -91,7 +89,7 @@ window.dnoa_internal.Uri = function(url) {
right = (equalsAt >= 0) ? queryStringPairs[i].substring(equalsAt + 1) : queryStringPairs[i];
this.pairs.push(new KeyValuePair(unescape(left), unescape(right)));
}
- };
+ }
this.getQueryArgValue = function(key) {
for (var i = 0; i < this.pairs.length; i++) {
@@ -103,7 +101,7 @@ window.dnoa_internal.Uri = function(url) {
this.getPairs = function() {
return this.pairs;
- }
+ };
this.containsQueryArg = function(key) {
return this.getQueryArgValue(key);
@@ -141,7 +139,7 @@ window.dnoa_internal.createHiddenIFrame = function() {
}
return iframe;
-}
+};
/// <summary>Redirects the current window/frame to the given URI,
/// either using a GET or a POST as required by the length of the URL.</summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs
new file mode 100644
index 0000000..e93383d
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.cs
@@ -0,0 +1,394 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdSelector.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdSelector.EmbeddedScriptResourceName, "text/javascript")]
+[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdSelector.EmbeddedStylesheetResourceName, "text/css")]
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.ComponentModel;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.IdentityModel.Claims;
+ using System.Linq;
+ using System.Text;
+ using System.Web;
+ using System.Web.UI;
+ using System.Web.UI.HtmlControls;
+ using System.Web.UI.WebControls;
+ using DotNetOpenAuth.ComponentModel;
+ using DotNetOpenAuth.InfoCard;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// An ASP.NET control that provides a user-friendly way of logging into a web site using OpenID.
+ /// </summary>
+ [ToolboxData("<{0}:OpenIdSelector runat=\"server\"></{0}:OpenIdSelector>")]
+ [ParseChildren(true), PersistChildren(false)]
+ public class OpenIdSelector : OpenIdRelyingPartyAjaxControlBase {
+ /// <summary>
+ /// The name of the manifest stream containing the OpenIdButtonPanel.js file.
+ /// </summary>
+ internal const string EmbeddedScriptResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdSelector.js";
+
+ /// <summary>
+ /// The name of the manifest stream containing the OpenIdButtonPanel.css file.
+ /// </summary>
+ internal const string EmbeddedStylesheetResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdSelector.css";
+
+ /// <summary>
+ /// The substring to append to the end of the id or name of this control to form the
+ /// unique name of the hidden field that will carry the positive assertion on postback.
+ /// </summary>
+ private const string AuthDataFormKeySuffix = "_openidAuthData";
+
+ #region ViewState keys
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="Buttons"/> property.
+ /// </summary>
+ private const string ButtonsViewStateKey = "Buttons";
+
+ /// <summary>
+ /// The viewstate key to use for storing the value of the <see cref="AuthenticatedAsToolTip"/> property.
+ /// </summary>
+ private const string AuthenticatedAsToolTipViewStateKey = "AuthenticatedAsToolTip";
+
+ #endregion
+
+ #region Property defaults
+
+ /// <summary>
+ /// The default value for the <see cref="AuthenticatedAsToolTip"/> property.
+ /// </summary>
+ private const string AuthenticatedAsToolTipDefault = "We recognize you!";
+
+ #endregion
+
+ /// <summary>
+ /// The OpenIdAjaxTextBox that remains hidden until the user clicks the OpenID button.
+ /// </summary>
+ private OpenIdAjaxTextBox textBox;
+
+ /// <summary>
+ /// The hidden field that will transmit the positive assertion to the RP.
+ /// </summary>
+ private HiddenField positiveAssertionField;
+
+ /// <summary>
+ /// A field to store the value to set on the <see cref="textBox"/> control after it's created.
+ /// </summary>
+ private bool downloadYuiLibrary = OpenIdAjaxTextBox.DownloadYahooUILibraryDefault;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdSelector"/> class.
+ /// </summary>
+ public OpenIdSelector() {
+ }
+
+ /// <summary>
+ /// Occurs when an InfoCard has been submitted and decoded.
+ /// </summary>
+ public event EventHandler<ReceivedTokenEventArgs> ReceivedToken;
+
+ /// <summary>
+ /// Occurs when [token processing error].
+ /// </summary>
+ public event EventHandler<TokenProcessingErrorEventArgs> TokenProcessingError;
+
+ /// <summary>
+ /// Gets or sets the tool tip text that appears on the green checkmark when authentication succeeds.
+ /// </summary>
+ [Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category(AppearanceCategory)]
+ [Description("The tool tip text that appears on the green checkmark when authentication succeeds.")]
+ public string AuthenticatedAsToolTip {
+ get { return (string)(this.ViewState[AuthenticatedAsToolTipViewStateKey] ?? AuthenticatedAsToolTipDefault); }
+ set { this.ViewState[AuthenticatedAsToolTipViewStateKey] = value ?? string.Empty; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the Yahoo! User Interface Library (YUI)
+ /// will be downloaded in order to provide a login split button.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> to use a split button; otherwise, <c>false</c> to use a standard HTML button
+ /// or a split button by downloading the YUI library yourself on the hosting web page.
+ /// </value>
+ /// <remarks>
+ /// The split button brings in about 180KB of YUI javascript dependencies.
+ /// </remarks>
+ [Bindable(true), DefaultValue(OpenIdAjaxTextBox.DownloadYahooUILibraryDefault), Category(BehaviorCategory)]
+ [Description("Whether a split button will be used for the \"log in\" when the user provides an identifier that delegates to more than one Provider.")]
+ public bool DownloadYahooUILibrary {
+ get {
+ return this.textBox != null ? this.textBox.DownloadYahooUILibrary : this.downloadYuiLibrary;
+ }
+
+ set {
+ // We don't just call EnsureChildControls() and then set the property on
+ // this.textBox itself because (apparently) setting this property in the ASPX
+ // page and thus calling this EnsureID() via EnsureChildControls() this early
+ // results in no ID.
+ if (this.textBox != null) {
+ this.textBox.DownloadYahooUILibrary = value;
+ } else {
+ this.downloadYuiLibrary = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the collection of buttons this selector should render to the browser.
+ /// </summary>
+ [PersistenceMode(PersistenceMode.InnerProperty)]
+ public Collection<SelectorButton> Buttons {
+ get {
+ if (this.ViewState[ButtonsViewStateKey] == null) {
+ var providers = new Collection<SelectorButton>();
+ this.ViewState[ButtonsViewStateKey] = providers;
+ return providers;
+ } else {
+ return (Collection<SelectorButton>)this.ViewState[ButtonsViewStateKey];
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a <see cref="T:System.Web.UI.ControlCollection"/> object that represents the child controls for a specified server control in the UI hierarchy.
+ /// </summary>
+ /// <returns>
+ /// The collection of child controls for the specified server control.
+ /// </returns>
+ public override ControlCollection Controls {
+ get {
+ this.EnsureChildControls();
+ return base.Controls;
+ }
+ }
+
+ /// <summary>
+ /// Gets the name of the open id auth data form key (for the value as stored at the user agent as a FORM field).
+ /// </summary>
+ /// <value>
+ /// Usually a concatenation of the control's name and <c>"_openidAuthData"</c>.
+ /// </value>
+ protected override string OpenIdAuthDataFormKey {
+ get { return this.UniqueID + AuthDataFormKeySuffix; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether some button in the selector will want
+ /// to display the <see cref="OpenIdAjaxTextBox"/> control.
+ /// </summary>
+ protected virtual bool OpenIdTextBoxVisible {
+ get { return this.Buttons.OfType<SelectorOpenIdButton>().Any(); }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing) {
+ if (disposing) {
+ foreach (var button in this.Buttons.OfType<IDisposable>()) {
+ button.Dispose();
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ /// <summary>
+ /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
+ /// </summary>
+ protected override void CreateChildControls() {
+ base.CreateChildControls();
+ this.EnsureID();
+ ErrorUtilities.VerifyInternal(!string.IsNullOrEmpty(this.UniqueID), "Control.EnsureID() failed to give us a unique ID. Try setting an ID on the OpenIdSelector control. But please also file this bug with the project owners.");
+
+ var selectorButton = this.Buttons.OfType<SelectorInfoCardButton>().FirstOrDefault();
+ if (selectorButton != null) {
+ var selector = selectorButton.InfoCardSelector;
+ selector.ClaimsRequested.Add(new ClaimType { Name = ClaimTypes.PPID });
+ selector.ImageSize = InfoCardImageSize.Size60x42;
+ selector.ReceivedToken += this.InfoCardSelector_ReceivedToken;
+ selector.TokenProcessingError += this.InfoCardSelector_TokenProcessingError;
+ this.Controls.Add(selector);
+ }
+
+ this.textBox = new OpenIdAjaxTextBox();
+ this.textBox.ID = "openid_identifier";
+ this.textBox.HookFormSubmit = false;
+ this.textBox.ShowLogOnPostBackButton = true;
+ this.textBox.DownloadYahooUILibrary = this.downloadYuiLibrary;
+ this.Controls.Add(this.textBox);
+
+ this.positiveAssertionField = new HiddenField();
+ this.positiveAssertionField.ID = this.ID + AuthDataFormKeySuffix;
+ this.Controls.Add(this.positiveAssertionField);
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnInit(EventArgs e) {
+ base.OnInit(e);
+
+ // We force child control creation here so that they can get postback events.
+ EnsureChildControls();
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ this.EnsureValidButtons();
+
+ var css = new HtmlLink();
+ css.Href = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedStylesheetResourceName);
+ css.Attributes["rel"] = "stylesheet";
+ css.Attributes["type"] = "text/css";
+ ErrorUtilities.VerifyHost(this.Page.Header != null, OpenIdStrings.HeadTagMustIncludeRunatServer);
+ this.Page.Header.Controls.AddAt(0, css); // insert at top so host page can override
+
+ // Import the .js file where most of the code is.
+ this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdSelector), EmbeddedScriptResourceName);
+
+ // Provide javascript with a way to post the login assertion.
+ const string PostLoginAssertionMethodName = "postLoginAssertion";
+ const string PositiveAssertionParameterName = "positiveAssertion";
+ const string ScriptFormat = @"window.{2} = function({0}) {{
+ $('#{3}')[0].setAttribute('value', {0});
+ {1};
+}};";
+ string script = string.Format(
+ CultureInfo.InvariantCulture,
+ ScriptFormat,
+ PositiveAssertionParameterName,
+ this.Page.ClientScript.GetPostBackEventReference(this, null, false),
+ PostLoginAssertionMethodName,
+ this.positiveAssertionField.ClientID);
+ this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "Postback", script, true);
+
+ this.PreloadDiscovery(this.Buttons.OfType<SelectorProviderButton>().Select(op => op.OPIdentifier).Where(id => id != null));
+ this.textBox.Visible = this.OpenIdTextBoxVisible;
+ }
+
+ /// <summary>
+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ protected override void Render(HtmlTextWriter writer) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "OpenIdProviders");
+ writer.RenderBeginTag(HtmlTextWriterTag.Ul);
+
+ foreach (var button in this.Buttons) {
+ button.RenderLeadingAttributes(writer);
+
+ writer.RenderBeginTag(HtmlTextWriterTag.Li);
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Href, "#");
+ writer.RenderBeginTag(HtmlTextWriterTag.A);
+
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+
+ button.RenderButtonContent(writer, this);
+
+ writer.RenderEndTag(); // </div>
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "ui-widget-overlay");
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+ writer.RenderEndTag();
+
+ writer.RenderEndTag(); // </div>
+ writer.RenderEndTag(); // </a>
+ writer.RenderEndTag(); // </li>
+ }
+
+ writer.RenderEndTag(); // </ul>
+
+ if (this.textBox.Visible) {
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, "OpenIDForm");
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+
+ this.textBox.RenderControl(writer);
+
+ writer.RenderEndTag(); // </div>
+ }
+
+ this.positiveAssertionField.RenderControl(writer);
+ }
+
+ /// <summary>
+ /// Fires the <see cref="ReceivedToken"/> event.
+ /// </summary>
+ /// <param name="e">The token, if it was decrypted.</param>
+ protected virtual void OnReceivedToken(ReceivedTokenEventArgs e) {
+ Contract.Requires(e != null);
+ ErrorUtilities.VerifyArgumentNotNull(e, "e");
+
+ var receivedInfoCard = this.ReceivedToken;
+ if (receivedInfoCard != null) {
+ receivedInfoCard(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:TokenProcessingError"/> event.
+ /// </summary>
+ /// <param name="e">The <see cref="DotNetOpenAuth.InfoCard.TokenProcessingErrorEventArgs"/> instance containing the event data.</param>
+ protected virtual void OnTokenProcessingError(TokenProcessingErrorEventArgs e) {
+ Contract.Requires(e != null);
+ ErrorUtilities.VerifyArgumentNotNull(e, "e");
+
+ var tokenProcessingError = this.TokenProcessingError;
+ if (tokenProcessingError != null) {
+ tokenProcessingError(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Handles the ReceivedToken event of the infoCardSelector control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="DotNetOpenAuth.InfoCard.ReceivedTokenEventArgs"/> instance containing the event data.</param>
+ private void InfoCardSelector_ReceivedToken(object sender, ReceivedTokenEventArgs e) {
+ this.Page.Response.SetCookie(new HttpCookie("openid_identifier", "infocard") {
+ Path = this.Page.Request.ApplicationPath,
+ });
+ this.OnReceivedToken(e);
+ }
+
+ /// <summary>
+ /// Handles the TokenProcessingError event of the infoCardSelector control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="DotNetOpenAuth.InfoCard.TokenProcessingErrorEventArgs"/> instance containing the event data.</param>
+ private void InfoCardSelector_TokenProcessingError(object sender, TokenProcessingErrorEventArgs e) {
+ this.OnTokenProcessingError(e);
+ }
+
+ /// <summary>
+ /// Ensures the <see cref="Buttons"/> collection has a valid set of buttons.
+ /// </summary>
+ private void EnsureValidButtons() {
+ foreach (var button in this.Buttons) {
+ button.EnsureValid();
+ }
+
+ // Also make sure that there are appropriate numbers of each type of button.
+ // TODO: code here
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.css b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.css
new file mode 100644
index 0000000..e7eafc7
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.css
@@ -0,0 +1,109 @@
+ul.OpenIdProviders
+{
+ padding: 0;
+ margin: 0px 0px 0px 0px;
+ list-style-type: none;
+ text-align: center;
+}
+
+ul.OpenIdProviders li
+{
+ background-color: White;
+ display: inline-block;
+ border: 1px solid #DDD;
+ margin: 0px 2px 4px 2px;
+ height: 50px;
+ width: 100px;
+ text-align: center;
+ vertical-align: middle;
+}
+
+ul.OpenIdProviders li div
+{
+ margin: 0;
+ padding: 0;
+ height: 50px;
+ width: 100px;
+ text-align: center;
+ display: table;
+ position: relative;
+ overflow: hidden;
+}
+
+ul.OpenIdProviders li div div
+{
+ margin: 0;
+ padding: 0;
+ top: 50%;
+ display: table-cell;
+ vertical-align: middle;
+ position: static;
+}
+
+ul.OpenIdProviders li img
+{
+}
+
+ul.OpenIdProviders li a img
+{
+ border-width: 0;
+}
+
+ul.OpenIdProviders li img.loginSuccess
+{
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ display: none;
+}
+
+ul.OpenIdProviders li.loginSuccess img.loginSuccess
+{
+ display: inline;
+}
+
+ul.OpenIdProviders li a
+{
+ display: block; /* Chrome needs this for proper position of grayed out overlay */
+ position: relative;
+}
+
+ul.OpenIdProviders li div.ui-widget-overlay
+{
+ display: none;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ bottom: 0;
+}
+
+ul.OpenIdProviders li.grayedOut div.ui-widget-overlay
+{
+ display: block;
+}
+
+ul.OpenIdProviders li.focused
+{
+ border: solid 2px yellow;
+}
+
+ul.OpenIdProviders li.infocard
+{
+ display: none; /* default to hiding InfoCard until the user agent determines it's supported */
+ cursor: pointer;
+}
+
+#openid_identifier
+{
+ width: 298px;
+}
+
+#OpenIDForm
+{
+ text-align: center;
+}
+
+#openid_login_button
+{
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js
new file mode 100644
index 0000000..c58e06e
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js
@@ -0,0 +1,192 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdSelector.js" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// This file may be used and redistributed under the terms of the
+// Microsoft Public License (Ms-PL) http://opensource.org/licenses/ms-pl.html
+// </copyright>
+//-----------------------------------------------------------------------
+
+$(function() {
+ var hint = $.cookie('openid_identifier') || '';
+
+ var ajaxbox = document.getElementsByName('openid_identifier')[0];
+ if (ajaxbox && hint != 'infocard') {
+ ajaxbox.setValue(hint);
+ }
+
+ if (document.infoCard && document.infoCard.isSupported()) {
+ $('ul.OpenIdProviders li.infocard')[0].style.display = 'inline-block';
+ }
+
+ if (hint.length > 0) {
+ var ops = $('ul.OpenIdProviders li');
+ ops.addClass('grayedOut');
+ var matchFound = false;
+ ops.each(function(i, li) {
+ if (li.id == hint || (hint == 'infocard' && $(li).hasClass('infocard'))) {
+ $(li)
+ .removeClass('grayedOut')
+ .addClass('focused');
+ matchFound = true;
+ }
+ });
+ if (!matchFound) {
+ if (ajaxbox) {
+ $('#OpenIDButton')
+ .removeClass('grayedOut')
+ .addClass('focused');
+ $('#OpenIDForm').show('slow', function() {
+ ajaxbox.focus();
+ });
+ } else {
+ // No OP button matched the last identifier, and there is no text box,
+ // so just un-gray all buttons.
+ ops.removeClass('grayedOut');
+ }
+ }
+ }
+
+ function showLoginSuccess(userSuppliedIdentifier, success) {
+ var li = document.getElementById(userSuppliedIdentifier);
+ if (li) {
+ if (success) {
+ $(li).addClass('loginSuccess');
+ } else {
+ $(li).removeClass('loginSuccess');
+ }
+ }
+ }
+
+ window.dnoa_internal.addAuthSuccess(function(discoveryResult, serviceEndpoint, extensionResponses, state) {
+ showLoginSuccess(discoveryResult.userSuppliedIdentifier, true);
+ });
+
+ window.dnoa_internal.addAuthCleared(function(discoveryResult, serviceEndpoint) {
+ showLoginSuccess(discoveryResult.userSuppliedIdentifier, false);
+
+ // If this is an OP button, renew the positive assertion.
+ var li = document.getElementById(discoveryResult.userSuppliedIdentifier);
+ if (li) {
+ li.loginBackground();
+ }
+ });
+
+ if (ajaxbox) {
+ ajaxbox.onStateChanged = function(state) {
+ if (state == "authenticated") {
+ showLoginSuccess('OpenIDButton', true);
+ } else {
+ showLoginSuccess('OpenIDButton', false); // hide checkmark
+ }
+ };
+ }
+
+ function checkidSetup(identifier, timerBased) {
+ var openid = new window.OpenIdIdentifier(identifier);
+ if (!openid) { throw 'checkidSetup called without an identifier.'; }
+ openid.login(function(discoveryResult, respondingEndpoint, extensionResponses) {
+ doLogin(discoveryResult, respondingEndpoint);
+ });
+ }
+
+ // Sends the positive assertion we've collected to the server and actually logs the user into the RP.
+ function doLogin(discoveryResult, respondingEndpoint) {
+ var retain = true; //!$('#NotMyComputer')[0].selected;
+ $.cookie('openid_identifier', retain ? discoveryResult.userSuppliedIdentifier : null, { path: window.aspnetapppath });
+ window.postLoginAssertion(respondingEndpoint.response.toString(), window.parent.location.href);
+ }
+
+ if (ajaxbox) {
+ // take over how the text box does postbacks.
+ ajaxbox.dnoi_internal.postback = doLogin;
+ }
+
+ // This FrameManager will be used for background logins for the OP buttons
+ // and the last used identifier. It is NOT the frame manager used by the
+ // OpenIdAjaxTextBox, as it has its own.
+ var backgroundTimeout = 3000;
+
+ $(document).ready(function() {
+ var ops = $('ul.OpenIdProviders li');
+ ops.each(function(i, li) {
+ if ($(li).hasClass('OPButton')) {
+ li.authenticationIFrames = new window.dnoa_internal.FrameManager(1/*throttle*/);
+ var openid = new window.OpenIdIdentifier(li.id);
+ var authFrames = li.authenticationIFrames;
+ if ($(li).hasClass('NoAsyncAuth')) {
+ li.loginBackground = function() { };
+ } else {
+ li.loginBackground = function() {
+ openid.loginBackground(authFrames, null, null, backgroundTimeout);
+ };
+ }
+ li.loginBackground();
+ }
+ });
+ });
+
+ $('ul.OpenIdProviders li').click(function() {
+ var lastFocus = $('.focused')[0];
+ if (lastFocus != $(this)[0]) {
+ $('ul.OpenIdProviders li').removeClass('focused');
+ $(this).addClass('focused');
+ }
+
+ // Make sure we're not graying out any OPs if the user clicked on a gray button.
+ var wasGrayedOut = false;
+ if ($(this).hasClass('grayedOut')) {
+ wasGrayedOut = true;
+ $('ul.OpenIdProviders li').removeClass('grayedOut');
+ }
+
+ // Be sure to hide the openid_identifier text box unless the OpenID button is selected.
+ if ($(this)[0] != $('#OpenIDButton')[0] && $('#OpenIDForm').is(':visible')) {
+ $('#OpenIDForm').hide('slow');
+ }
+
+ var relevantUserSuppliedIdentifier = null;
+ // Don't immediately login if the user clicked OpenID and he can't see the identifier box.
+ if ($(this)[0].id != 'OpenIDButton') {
+ relevantUserSuppliedIdentifier = $(this)[0].id;
+ } else if (ajaxbox && $('#OpenIDForm').is(':visible')) {
+ relevantUserSuppliedIdentifier = ajaxbox.value;
+ }
+
+ var discoveryResult = window.dnoa_internal.discoveryResults[relevantUserSuppliedIdentifier];
+ var respondingEndpoint = discoveryResult ? discoveryResult.findSuccessfulRequest() : null;
+
+ // If the user clicked on a button that has the "we're ready to log you in immediately",
+ // then log them in!
+ if (respondingEndpoint) {
+ doLogin(discoveryResult, respondingEndpoint);
+ } else if ($(this).hasClass('OPButton')) {
+ checkidSetup($(this)[0].id);
+ } else if ($(this).hasClass('infocard') && wasGrayedOut) {
+ // we need to forward the click onto the InfoCard image so it is handled, since our
+ // gray overlaying div captured the click event.
+ $('img', this)[0].click();
+ }
+ });
+ if (ajaxbox) {
+ $('#OpenIDButton').click(function() {
+ // Be careful to only try to select the text box once it is available.
+ if ($('#OpenIDForm').is(':hidden')) {
+ $('#OpenIDForm').show('slow', function() {
+ ajaxbox.focus();
+ });
+ } else {
+ ajaxbox.focus();
+ }
+ });
+ }
+
+ // Make popup window close on escape (the dialog style is already taken care of)
+ $(document).keydown(function(e) {
+ if (e.keyCode == $.ui.keyCode.ESCAPE) {
+ window.close();
+ } else if (e.keyCode == $.ui.keyCode.ENTER) {
+ // we do NOT want to submit the form on ENTER.
+ e.preventDefault();
+ }
+ });
+}); \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
index b7c879e..4d635fb 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
@@ -40,7 +40,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
[DefaultProperty("Text"), ValidationProperty("Text")]
[ToolboxData("<{0}:OpenIdTextBox runat=\"server\" />")]
- public class OpenIdTextBox : CompositeControl, IEditableTextControl, ITextControl {
+ public class OpenIdTextBox : OpenIdRelyingPartyControlBase, IEditableTextControl, ITextControl, IPostBackDataHandler {
/// <summary>
/// The name of the manifest stream containing the
/// OpenID logo that is placed inside the text box.
@@ -52,38 +52,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
protected const short TabIndexDefault = 0;
- /// <summary>
- /// Default value of <see cref="UsePersistentCookie"/>.
- /// </summary>
- protected const bool UsePersistentCookieDefault = false;
-
#region Property category constants
/// <summary>
- /// The "Appearance" category for properties.
- /// </summary>
- private const string AppearanceCategory = "Appearance";
-
- /// <summary>
/// The "Simple Registration" category for properties.
/// </summary>
private const string ProfileCategory = "Simple Registration";
- /// <summary>
- /// The "Behavior" category for properties.
- /// </summary>
- private const string BehaviorCategory = "Behavior";
-
#endregion
#region Property viewstate keys
/// <summary>
- /// The viewstate key to use for the <see cref="Popup"/> property.
- /// </summary>
- private const string PopupViewStateKey = "Popup";
-
- /// <summary>
/// The viewstate key to use for the <see cref="RequestEmail"/> property.
/// </summary>
private const string RequestEmailViewStateKey = "RequestEmail";
@@ -104,11 +84,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string RequestCountryViewStateKey = "RequestCountry";
/// <summary>
- /// The viewstate key to use for the <see cref="RequireSsl"/> property.
- /// </summary>
- private const string RequireSslViewStateKey = "RequireSsl";
-
- /// <summary>
/// The viewstate key to use for the <see cref="RequestLanguage"/> property.
/// </summary>
private const string RequestLanguageViewStateKey = "RequestLanguage";
@@ -144,43 +119,53 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string ShowLogoViewStateKey = "ShowLogo";
/// <summary>
- /// The viewstate key to use for the <see cref="UsePersistentCookie"/> property.
+ /// The viewstate key to use for the <see cref="RequestGender"/> property.
/// </summary>
- private const string UsePersistentCookieViewStateKey = "UsePersistentCookie";
+ private const string RequestGenderViewStateKey = "RequestGender";
/// <summary>
- /// The viewstate key to use for the <see cref="RequestGender"/> property.
+ /// The viewstate key to use for the <see cref="RequestBirthDate"/> property.
/// </summary>
- private const string RequestGenderViewStateKey = "RequestGender";
+ private const string RequestBirthDateViewStateKey = "RequestBirthDate";
/// <summary>
- /// The viewstate key to use for the <see cref="ReturnToUrl"/> property.
+ /// The viewstate key to use for the <see cref="CssClass"/> property.
/// </summary>
- private const string ReturnToUrlViewStateKey = "ReturnToUrl";
+ private const string CssClassViewStateKey = "CssClass";
/// <summary>
- /// The viewstate key to use for the <see cref="Stateless"/> property.
+ /// The viewstate key to use for the <see cref="MaxLength"/> property.
/// </summary>
- private const string StatelessViewStateKey = "Stateless";
+ private const string MaxLengthViewStateKey = "MaxLength";
/// <summary>
- /// The viewstate key to use for the <see cref="RequestBirthDate"/> property.
+ /// The viewstate key to use for the <see cref="Columns"/> property.
/// </summary>
- private const string RequestBirthDateViewStateKey = "RequestBirthDate";
+ private const string ColumnsViewStateKey = "Columns";
/// <summary>
- /// The viewstate key to use for the <see cref="RealmUrl"/> property.
+ /// The viewstate key to use for the <see cref="TabIndex"/> property.
/// </summary>
- private const string RealmUrlViewStateKey = "RealmUrl";
+ private const string TabIndexViewStateKey = "TabIndex";
- #endregion
+ /// <summary>
+ /// The viewstate key to use for the <see cref="Enabled"/> property.
+ /// </summary>
+ private const string EnabledViewStateKey = "Enabled";
- #region Property defaults
+ /// <summary>
+ /// The viewstate key to use for the <see cref="Name"/> property.
+ /// </summary>
+ private const string NameViewStateKey = "Name";
/// <summary>
- /// The default value for the <see cref="Popup"/> property.
+ /// The viewstate key to use for the <see cref="Text"/> property.
/// </summary>
- private const PopupBehavior PopupDefault = PopupBehavior.Never;
+ private const string TextViewStateKey = "Text";
+
+ #endregion
+
+ #region Property defaults
/// <summary>
/// The default value for the <see cref="Columns"/> property.
@@ -193,19 +178,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const int MaxLengthDefault = 40;
/// <summary>
- /// The default value for the <see cref="EnableRequestProfile"/> property.
- /// </summary>
- private const bool EnableRequestProfileDefault = true;
-
- /// <summary>
- /// The default value for the <see cref="RequireSsl"/> property.
+ /// The default value for the <see cref="Name"/> property.
/// </summary>
- private const bool RequireSslDefault = false;
+ private const string NameDefault = "openid_identifier";
/// <summary>
- /// The default value for the <see cref="Stateless"/> property.
+ /// The default value for the <see cref="EnableRequestProfile"/> property.
/// </summary>
- private const bool StatelessDefault = false;
+ private const bool EnableRequestProfileDefault = true;
/// <summary>
/// The default value for the <see cref="ShowLogo"/> property.
@@ -228,21 +208,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string CssClassDefault = "openid";
/// <summary>
- /// The default value for the <see cref="ReturnToUrl"/> property.
- /// </summary>
- private const string ReturnToUrlDefault = "";
-
- /// <summary>
/// The default value for the <see cref="Text"/> property.
/// </summary>
private const string TextDefault = "";
/// <summary>
- /// The default value for the <see cref="RealmUrl"/> property.
- /// </summary>
- private const string RealmUrlDefault = "~/";
-
- /// <summary>
/// The default value for the <see cref="RequestEmail"/> property.
/// </summary>
private const DemandLevel RequestEmailDefault = DemandLevel.NoRequest;
@@ -290,79 +260,22 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
- /// The callback parameter to use for recognizing when the callback is in a popup window.
- /// </summary>
- private const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
-
- /// <summary>
- /// The callback parameter to use for recognizing when the callback is in the parent window.
- /// </summary>
- private const string UIPopupCallbackParentKey = OpenIdUtilities.CustomParameterPrefix + "uipopupParent";
-
- /// <summary>
- /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
- /// </summary>
- private const string UsePersistentCookieCallbackKey = "OpenIdTextBox_UsePersistentCookie";
-
- /// <summary>
- /// The text in the text box before the text box is instantiated.
+ /// An empty sreg request, used to compare with others to see if they too are empty.
/// </summary>
- private string text = TextDefault;
-
- /// <summary>
- /// The text box itself.
- /// </summary>
- private TextBox wrappedTextBox;
-
- /// <summary>
- /// Backing field for the <see cref="RelyingParty"/> property.
- /// </summary>
- private OpenIdRelyingParty relyingParty;
+ private static readonly ClaimsRequest EmptyClaimsRequest = new ClaimsRequest();
/// <summary>
/// Initializes a new instance of the <see cref="OpenIdTextBox"/> class.
/// </summary>
public OpenIdTextBox() {
- this.InitializeControls();
}
- #region Events
-
- /// <summary>
- /// Fired upon completion of a successful login.
- /// </summary>
- [Description("Fired upon completion of a successful login.")]
- public event EventHandler<OpenIdEventArgs> LoggedIn;
-
- /// <summary>
- /// Fired when a login attempt fails.
- /// </summary>
- [Description("Fired when a login attempt fails.")]
- public event EventHandler<OpenIdEventArgs> Failed;
-
- /// <summary>
- /// Fired when an authentication attempt is canceled at the OpenID Provider.
- /// </summary>
- [Description("Fired when an authentication attempt is canceled at the OpenID Provider.")]
- public event EventHandler<OpenIdEventArgs> Canceled;
-
- /// <summary>
- /// Fired when an Immediate authentication attempt fails, and the Provider suggests using non-Immediate mode.
- /// </summary>
- [Description("Fired when an Immediate authentication attempt fails, and the Provider suggests using non-Immediate mode.")]
- public event EventHandler<OpenIdEventArgs> SetupRequired;
-
- #endregion
-
#region IEditableTextControl Members
/// <summary>
/// Occurs when the content of the text changes between posts to the server.
/// </summary>
- public event EventHandler TextChanged {
- add { this.WrappedTextBox.TextChanged += value; }
- remove { this.WrappedTextBox.TextChanged -= value; }
- }
+ public event EventHandler TextChanged;
#endregion
@@ -375,101 +288,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Description("The content of the text box.")]
public string Text {
get {
- return this.WrappedTextBox != null ? this.WrappedTextBox.Text : this.text;
- }
-
- set {
- this.text = value;
- if (this.WrappedTextBox != null) {
- this.WrappedTextBox.Text = value;
- }
- }
- }
-
- /// <summary>
- /// Gets or sets the OpenID <see cref="Realm"/> of the relying party web site.
- /// </summary>
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "DotNetOpenAuth.OpenId.Realm", Justification = "Using ctor for validation.")]
- [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Bindable property must be simple type")]
- [Bindable(true), DefaultValue(RealmUrlDefault), Category(BehaviorCategory)]
- [Description("The OpenID Realm of the relying party web site.")]
- [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
- public string RealmUrl {
- get {
- return (string)(ViewState[RealmUrlViewStateKey] ?? RealmUrlDefault);
- }
-
- set {
- if (Page != null && !DesignMode) {
- // Validate new value by trying to construct a Realm object based on it.
- new Realm(OpenIdUtilities.GetResolvedRealm(this.Page, value, this.RelyingParty.Channel.GetRequestFromContext())); // throws an exception on failure.
- } else {
- // We can't fully test it, but it should start with either ~/ or a protocol.
- if (Regex.IsMatch(value, @"^https?://")) {
- new Uri(value.Replace("*.", string.Empty)); // make sure it's fully-qualified, but ignore wildcards
- } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
- // this is valid too
- } else {
- throw new UriFormatException();
- }
- }
- ViewState[RealmUrlViewStateKey] = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the OpenID ReturnTo of the relying party web site.
- /// </summary>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Bindable property must be simple type")]
- [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
- [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Bindable property must be simple type")]
- [Bindable(true), DefaultValue(ReturnToUrlDefault), Category(BehaviorCategory)]
- [Description("The OpenID ReturnTo of the relying party web site.")]
- [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
- public string ReturnToUrl {
- get {
- return (string)(this.ViewState[ReturnToUrlViewStateKey] ?? ReturnToUrlDefault);
+ return this.Identifier != null ? this.Identifier.OriginalString : (this.ViewState[TextViewStateKey] as string ?? string.Empty);
}
set {
- if (this.Page != null && !this.DesignMode) {
- // Validate new value by trying to construct a Uri based on it.
- new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.Page.ResolveUrl(value)); // throws an exception on failure.
+ // Try to store it as a validated identifier,
+ // but failing that at least store the text.
+ Identifier id;
+ if (Identifier.TryParse(value, out id)) {
+ this.Identifier = id;
} else {
- // We can't fully test it, but it should start with either ~/ or a protocol.
- if (Regex.IsMatch(value, @"^https?://")) {
- new Uri(value); // make sure it's fully-qualified, but ignore wildcards
- } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
- // this is valid too
- } else {
- throw new UriFormatException();
- }
+ // Be sure to set the viewstate AFTER setting the Identifier,
+ // since setting the Identifier clears the viewstate in OnIdentifierChanged.
+ this.Identifier = null;
+ this.ViewState[TextViewStateKey] = value;
}
-
- this.ViewState[ReturnToUrlViewStateKey] = value;
}
}
/// <summary>
- /// Gets or sets a value indicating when to use a popup window to complete the login experience.
+ /// Gets or sets the form name to use for this input field.
/// </summary>
- /// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
- [Bindable(true), DefaultValue(PopupDefault), Category(BehaviorCategory)]
- [Description("When to use a popup window to complete the login experience.")]
- public PopupBehavior Popup {
- get { return (PopupBehavior)(ViewState[PopupViewStateKey] ?? PopupDefault); }
- set { ViewState[PopupViewStateKey] = value; }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether stateless mode is used.
- /// </summary>
- [Bindable(true), DefaultValue(StatelessDefault), Category(BehaviorCategory)]
- [Description("Controls whether stateless mode is used.")]
- public bool Stateless {
- get { return (bool)(ViewState[StatelessViewStateKey] ?? StatelessDefault); }
- set { ViewState[StatelessViewStateKey] = value; }
+ [Bindable(true), DefaultValue(NameDefault), Category(BehaviorCategory)]
+ [Description("The form name of this input field.")]
+ public string Name {
+ get { return (string)(this.ViewState[NameViewStateKey] ?? NameDefault); }
+ set { this.ViewState[NameViewStateKey] = value; }
}
/// <summary>
@@ -477,9 +321,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
[Bindable(true), DefaultValue(CssClassDefault), Category(AppearanceCategory)]
[Description("The CSS class assigned to the text box.")]
- public override string CssClass {
- get { return this.WrappedTextBox.CssClass; }
- set { this.WrappedTextBox.CssClass = value; }
+ public string CssClass {
+ get { return (string)this.ViewState[CssClassViewStateKey]; }
+ set { this.ViewState[CssClassViewStateKey] = value; }
}
/// <summary>
@@ -503,25 +347,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
- /// Gets or sets a value indicating whether to send a persistent cookie upon successful
- /// login so the user does not have to log in upon returning to this site.
- /// </summary>
- [Bindable(true), DefaultValue(UsePersistentCookieDefault), Category(BehaviorCategory)]
- [Description("Whether to send a persistent cookie upon successful " +
- "login so the user does not have to log in upon returning to this site.")]
- public virtual bool UsePersistentCookie {
- get { return (bool)(this.ViewState[UsePersistentCookieViewStateKey] ?? UsePersistentCookieDefault); }
- set { this.ViewState[UsePersistentCookieViewStateKey] = value; }
- }
-
- /// <summary>
/// Gets or sets the width of the text box in characters.
/// </summary>
[Bindable(true), DefaultValue(ColumnsDefault), Category(AppearanceCategory)]
[Description("The width of the text box in characters.")]
public int Columns {
- get { return this.WrappedTextBox.Columns; }
- set { this.WrappedTextBox.Columns = value; }
+ get { return (int)(this.ViewState[ColumnsViewStateKey] ?? ColumnsDefault); }
+ set { this.ViewState[ColumnsViewStateKey] = value; }
}
/// <summary>
@@ -530,8 +362,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[Bindable(true), DefaultValue(MaxLengthDefault), Category(AppearanceCategory)]
[Description("The maximum number of characters the browser should allow.")]
public int MaxLength {
- get { return this.WrappedTextBox.MaxLength; }
- set { this.WrappedTextBox.MaxLength = value; }
+ get { return (int)(this.ViewState[MaxLengthViewStateKey] ?? MaxLengthDefault); }
+ set { this.ViewState[MaxLengthViewStateKey] = value; }
}
/// <summary>
@@ -546,9 +378,21 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </exception>
[Bindable(true), DefaultValue(TabIndexDefault), Category(BehaviorCategory)]
[Description("The tab index of the text box control.")]
- public override short TabIndex {
- get { return this.WrappedTextBox.TabIndex; }
- set { this.WrappedTextBox.TabIndex = value; }
+ public virtual short TabIndex {
+ get { return (short)(this.ViewState[TabIndexViewStateKey] ?? TabIndexDefault); }
+ set { this.ViewState[TabIndexViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this <see cref="OpenIdTextBox"/> is enabled
+ /// in the browser for editing and will respond to incoming OpenID messages.
+ /// </summary>
+ /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
+ [Bindable(true), DefaultValue(true), Category(BehaviorCategory)]
+ [Description("Whether the control is editable in the browser and will respond to OpenID messages.")]
+ public bool Enabled {
+ get { return (bool)(this.ViewState[EnabledViewStateKey] ?? true); }
+ set { this.ViewState[EnabledViewStateKey] = value; }
}
/// <summary>
@@ -673,344 +517,45 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
set { ViewState[EnableRequestProfileViewStateKey] = value; }
}
- /// <summary>
- /// Gets or sets a value indicating whether to enforce on high security mode,
- /// which requires the full authentication pipeline to be protected by SSL.
- /// </summary>
- [Bindable(true), DefaultValue(RequireSslDefault), Category(BehaviorCategory)]
- [Description("Turns on high security mode, requiring the full authentication pipeline to be protected by SSL.")]
- public bool RequireSsl {
- get { return (bool)(ViewState[RequireSslViewStateKey] ?? RequireSslDefault); }
- set { ViewState[RequireSslViewStateKey] = value; }
- }
-
- /// <summary>
- /// Gets or sets the type of the custom application store to use, or <c>null</c> to use the default.
- /// </summary>
- /// <remarks>
- /// If set, this property must be set in each Page Load event
- /// as it is not persisted across postbacks.
- /// </remarks>
- public IRelyingPartyApplicationStore CustomApplicationStore { get; set; }
-
#endregion
- #region Properties to hide
-
- /// <summary>
- /// Gets or sets the foreground color (typically the color of the text) of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the foreground color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color ForeColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the background color of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the background color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>, which indicates that this property is not set.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color BackColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border color of the Web control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Drawing.Color"/> that represents the border color of the control. The default is <see cref="F:System.Drawing.Color.Empty"/>, which indicates that this property is not set.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override System.Drawing.Color BorderColor {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border width of the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the border width of a Web server control. The default value is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>, which indicates that this property is not set.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The specified border width is a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit BorderWidth {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets the border style of the Web server control.
- /// </summary>
- /// <returns>
- /// One of the <see cref="T:System.Web.UI.WebControls.BorderStyle"/> enumeration values. The default is NotSet.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override BorderStyle BorderStyle {
- get { return BorderStyle.None; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets the font properties associated with the Web server control.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.FontInfo"/> that represents the font properties of the Web server control.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override FontInfo Font {
- get { return null; }
- }
+ #region IPostBackDataHandler Members
/// <summary>
- /// Gets or sets the height of the Web server control.
+ /// When implemented by a class, processes postback data for an ASP.NET server control.
/// </summary>
+ /// <param name="postDataKey">The key identifier for the control.</param>
+ /// <param name="postCollection">The collection of all incoming name values.</param>
/// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the height of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>.
+ /// true if the server control's state changes as a result of the postback; otherwise, false.
/// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The height was set to a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit Height {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
+ bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
+ return this.LoadPostData(postDataKey, postCollection);
}
/// <summary>
- /// Gets or sets the width of the Web server control.
+ /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
/// </summary>
- /// <returns>
- /// A <see cref="T:System.Web.UI.WebControls.Unit"/> that represents the width of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The width of the Web server control was set to a negative value.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override Unit Width {
- get { return Unit.Empty; }
- set { throw new NotSupportedException(); }
+ void IPostBackDataHandler.RaisePostDataChangedEvent() {
+ this.RaisePostDataChangedEvent();
}
- /// <summary>
- /// Gets or sets the text displayed when the mouse pointer hovers over the Web server control.
- /// </summary>
- /// <returns>
- /// The text displayed when the mouse pointer hovers over the Web server control. The default is <see cref="F:System.String.Empty"/>.
- /// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override string ToolTip {
- get { return string.Empty; }
- set { throw new NotSupportedException(); }
- }
+ #endregion
/// <summary>
- /// Gets or sets the skin to apply to the control.
+ /// Creates the authentication requests for a given user-supplied Identifier.
/// </summary>
+ /// <param name="identifier">The identifier to create a request for.</param>
/// <returns>
- /// The name of the skin to apply to the control. The default is <see cref="F:System.String.Empty"/>.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">
- /// The skin specified in the <see cref="P:System.Web.UI.WebControls.WebControl.SkinID"/> property does not exist in the theme.
- /// </exception>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override string SkinID {
- get { return string.Empty; }
- set { throw new NotSupportedException(); }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether themes apply to this control.
- /// </summary>
- /// <returns>true to use themes; otherwise, false. The default is false.
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
/// </returns>
- [Obsolete("This property does not do anything."), Browsable(false), Bindable(false)]
- public override bool EnableTheming {
- get { return false; }
- set { throw new NotSupportedException(); }
- }
-
- #endregion
-
- /// <summary>
- /// Gets or sets the <see cref="OpenIdRelyingParty"/> instance to use.
- /// </summary>
- /// <value>The default value is an <see cref="OpenIdRelyingParty"/> instance initialized according to the web.config file.</value>
- /// <remarks>
- /// A performance optimization would be to store off the
- /// instance as a static member in your web site and set it
- /// to this property in your <see cref="Control.Load">Page.Load</see>
- /// event since instantiating these instances can be expensive on
- /// heavily trafficked web pages.
- /// </remarks>
- public OpenIdRelyingParty RelyingParty {
- get {
- if (this.relyingParty == null) {
- this.relyingParty = this.CreateRelyingParty();
- }
- return this.relyingParty;
- }
-
- set {
- this.relyingParty = value;
- }
- }
-
- /// <summary>
- /// Gets the <see cref="TextBox"/> control that this control wraps.
- /// </summary>
- protected TextBox WrappedTextBox {
- get { return this.wrappedTextBox; }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether the text box should
- /// receive input focus when the web page appears.
- /// </summary>
- protected bool ShouldBeFocused { get; set; }
-
- /// <summary>
- /// Gets or sets the OpenID authentication request that is about to be sent.
- /// </summary>
- protected IAuthenticationRequest Request { get; set; }
-
- /// <summary>
- /// Sets the input focus to start on the text box when the page appears
- /// in the user's browser.
- /// </summary>
- public override void Focus() {
- if (Controls.Count == 0) {
- this.ShouldBeFocused = true;
- } else {
- this.WrappedTextBox.Focus();
- }
- }
-
- /// <summary>
- /// Constructs the authentication request and returns it.
- /// </summary>
- /// <returns>The instantiated authentication request.</returns>
- /// <remarks>
- /// <para>This method need not be called before calling the <see cref="LogOn"/> method,
- /// but is offered in the event that adding extensions to the request is desired.</para>
- /// <para>The Simple Registration extension arguments are added to the request
- /// before returning if <see cref="EnableRequestProfile"/> is set to true.</para>
- /// </remarks>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
- public IAuthenticationRequest CreateRequest() {
- ErrorUtilities.VerifyOperation(this.Request == null, OpenIdStrings.CreateRequestAlreadyCalled);
- ErrorUtilities.VerifyOperation(!string.IsNullOrEmpty(this.Text), OpenIdStrings.OpenIdTextBoxEmpty);
-
- try {
- // Approximate the returnTo (either based on the customize property or the page URL)
- // so we can use it to help with Realm resolution.
- var requestContext = this.RelyingParty.Channel.GetRequestFromContext();
- Uri returnToApproximation = this.ReturnToUrl != null ? new Uri(requestContext.UrlBeforeRewriting, this.ReturnToUrl) : this.Page.Request.Url;
-
- // Resolve the trust root, and swap out the scheme and port if necessary to match the
- // return_to URL, since this match is required by OpenId, and the consumer app
- // may be using HTTP at some times and HTTPS at others.
- UriBuilder realm = OpenIdUtilities.GetResolvedRealm(this.Page, this.RealmUrl, this.RelyingParty.Channel.GetRequestFromContext());
- realm.Scheme = returnToApproximation.Scheme;
- realm.Port = returnToApproximation.Port;
-
- // Initiate openid request
- // We use TryParse here to avoid throwing an exception which
- // might slip through our validator control if it is disabled.
- Identifier userSuppliedIdentifier;
- if (Identifier.TryParse(this.Text, out userSuppliedIdentifier)) {
- Realm typedRealm = new Realm(realm);
- if (string.IsNullOrEmpty(this.ReturnToUrl)) {
- this.Request = this.RelyingParty.CreateRequest(userSuppliedIdentifier, typedRealm);
- } else {
- // Since the user actually gave us a return_to value,
- // the "approximation" is exactly what we want.
- this.Request = this.RelyingParty.CreateRequest(userSuppliedIdentifier, typedRealm, returnToApproximation);
- }
-
- if (this.EnableRequestProfile) {
- this.AddProfileArgs(this.Request);
- }
-
- if (this.IsPopupAppropriate()) {
- // Inform the OP that it will appear in a popup window.
- this.Request.AddExtension(new UIRequest());
- }
-
- // Add state that needs to survive across the redirect.
- if (!this.Stateless) {
- this.Request.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
- }
- } else {
- Logger.OpenId.WarnFormat("An invalid identifier was entered ({0}), but not caught by any validation routine.", this.Text);
- this.Request = null;
- }
- } catch (ProtocolException ex) {
- this.OnFailed(new FailedAuthenticationResponse(ex));
- }
+ protected internal override IEnumerable<IAuthenticationRequest> CreateRequests(Identifier identifier) {
+ ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
- return this.Request;
- }
-
- /// <summary>
- /// Immediately redirects to the OpenID Provider to verify the Identifier
- /// provided in the text box.
- /// </summary>
- public void LogOn() {
- if (this.Request == null) {
- this.CreateRequest(); // sets this.Request
- }
-
- if (this.Request != null) {
- if (this.IsPopupAppropriate()) {
- this.ScriptPopupWindow();
- } else {
- this.Request.RedirectToProvider();
- }
- }
- }
-
- /// <summary>
- /// Enables a server control to perform final clean up before it is released from memory.
- /// </summary>
- [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Base class doesn't implement virtual Dispose(bool), so we must call its Dispose() method.")]
- public sealed override void Dispose() {
- this.Dispose(true);
- base.Dispose();
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Creates the text box control.
- /// </summary>
- protected override void CreateChildControls() {
- base.CreateChildControls();
-
- this.Controls.Add(this.wrappedTextBox);
- if (this.ShouldBeFocused) {
- this.WrappedTextBox.Focus();
- }
- }
-
- /// <summary>
- /// Initializes the text box control.
- /// </summary>
- protected virtual void InitializeControls() {
- this.wrappedTextBox = new TextBox();
- this.wrappedTextBox.ID = "wrappedTextBox";
- this.wrappedTextBox.CssClass = CssClassDefault;
- this.wrappedTextBox.Columns = ColumnsDefault;
- this.wrappedTextBox.Text = this.text;
- this.wrappedTextBox.TabIndex = TabIndexDefault;
+ // We delegate all our logic to another method, since invoking base. methods
+ // within an iterator method results in unverifiable code.
+ return this.CreateRequestsCore(base.CreateRequests(identifier));
}
/// <summary>
@@ -1018,165 +563,125 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
protected override void OnLoad(EventArgs e) {
- base.OnLoad(e);
-
- if (!Enabled || Page.IsPostBack) {
+ if (!this.Enabled) {
return;
}
- // Take an unreliable sneek peek to see if we're in a popup and an OpenID
- // assertion is coming in. We shouldn't process assertions in a popup window.
- if (this.Page.Request.QueryString[UIPopupCallbackKey] == "1" && this.Page.Request.QueryString[UIPopupCallbackParentKey] == null) {
- // We're in a popup window. We need to close it and pass the
- // message back to the parent window for processing.
- this.ScriptClosingPopup();
- return; // don't do any more processing on it now
- }
-
- var response = this.RelyingParty.GetResponse();
- if (response != null) {
- string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey);
- bool persistentBool;
- if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) {
- this.UsePersistentCookie = persistentBool;
- }
-
- switch (response.Status) {
- case AuthenticationStatus.Canceled:
- this.OnCanceled(response);
- break;
- case AuthenticationStatus.Authenticated:
- this.OnLoggedIn(response);
- break;
- case AuthenticationStatus.SetupRequired:
- this.OnSetupRequired(response);
- break;
- case AuthenticationStatus.Failed:
- this.OnFailed(response);
- break;
- case AuthenticationStatus.ExtensionsOnly:
- default:
- // The NotApplicable (extension-only assertion) is NOT one that we support
- // in this control because that scenario is primarily interesting to RPs
- // that are asking a specific OP, and it is not user-initiated as this textbox
- // is designed for.
- throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
- }
- }
+ this.Page.RegisterRequiresPostBack(this);
+ base.OnLoad(e);
}
/// <summary>
- /// Prepares the text box to be rendered.
+ /// Called when the <see cref="Identifier"/> property is changed.
/// </summary>
- /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
- protected override void OnPreRender(EventArgs e) {
- base.OnPreRender(e);
+ protected override void OnIdentifierChanged() {
+ this.ViewState.Remove(TextViewStateKey);
+ base.OnIdentifierChanged();
+ }
+ /// <summary>
+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ protected override void Render(HtmlTextWriter writer) {
if (this.ShowLogo) {
string logoUrl = Page.ClientScript.GetWebResourceUrl(
typeof(OpenIdTextBox), EmbeddedLogoResourceName);
- this.WrappedTextBox.Style[HtmlTextWriterStyle.BackgroundImage] = string.Format(
- CultureInfo.InvariantCulture, "url({0})", HttpUtility.HtmlEncode(logoUrl));
- this.WrappedTextBox.Style["background-repeat"] = "no-repeat";
- this.WrappedTextBox.Style["background-position"] = "0 50%";
- this.WrappedTextBox.Style[HtmlTextWriterStyle.PaddingLeft] = "18px";
+ writer.AddStyleAttribute(
+ HtmlTextWriterStyle.BackgroundImage,
+ string.Format(CultureInfo.InvariantCulture, "url({0})", HttpUtility.HtmlEncode(logoUrl)));
+ writer.AddStyleAttribute("background-repeat", "no-repeat");
+ writer.AddStyleAttribute("background-position", "0 50%");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingLeft, "18px");
}
if (this.PresetBorder) {
- this.WrappedTextBox.Style[HtmlTextWriterStyle.BorderStyle] = "solid";
- this.WrappedTextBox.Style[HtmlTextWriterStyle.BorderWidth] = "1px";
- this.WrappedTextBox.Style[HtmlTextWriterStyle.BorderColor] = "lightgray";
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "1px");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "lightgray");
}
- }
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool disposing) {
- if (disposing) {
- if (this.relyingParty != null) {
- this.relyingParty.Dispose();
- this.relyingParty = null;
- }
+ if (!string.IsNullOrEmpty(this.CssClass)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
}
- }
- #region Events
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
+ writer.AddAttribute(HtmlTextWriterAttribute.Name, HttpUtility.HtmlEncode(this.Name));
+ writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
+ writer.AddAttribute(HtmlTextWriterAttribute.Size, this.Columns.ToString(CultureInfo.InvariantCulture));
+ writer.AddAttribute(HtmlTextWriterAttribute.Value, HttpUtility.HtmlEncode(this.Text));
+ writer.AddAttribute(HtmlTextWriterAttribute.Tabindex, this.TabIndex.ToString(CultureInfo.CurrentCulture));
+
+ writer.RenderBeginTag(HtmlTextWriterTag.Input);
+ writer.RenderEndTag();
+ }
/// <summary>
- /// Fires the <see cref="LoggedIn"/> event.
+ /// When implemented by a class, processes postback data for an ASP.NET server control.
/// </summary>
- /// <param name="response">The response.</param>
- protected virtual void OnLoggedIn(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Authenticated, "Firing OnLoggedIn event without an authenticated response.");
-
- var loggedIn = this.LoggedIn;
- OpenIdEventArgs args = new OpenIdEventArgs(response);
- if (loggedIn != null) {
- loggedIn(this, args);
+ /// <param name="postDataKey">The key identifier for the control.</param>
+ /// <param name="postCollection">The collection of all incoming name values.</param>
+ /// <returns>
+ /// true if the server control's state changes as a result of the postback; otherwise, false.
+ /// </returns>
+ protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
+ // If the control was temporarily hidden, it won't be in the Form data,
+ // and we'll just implicitly keep the last Text setting.
+ if (postCollection[this.Name] != null) {
+ this.Text = postCollection[this.Name];
+ return true;
}
- if (!args.Cancel) {
- FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, this.UsePersistentCookie);
- }
+ return false;
}
/// <summary>
- /// Fires the <see cref="Failed"/> event.
+ /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
/// </summary>
- /// <param name="response">The response.</param>
- protected virtual void OnFailed(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Failed, "Firing Failed event for the wrong response type.");
-
- var failed = this.Failed;
- if (failed != null) {
- failed(this, new OpenIdEventArgs(response));
- }
+ [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Preserve signature of interface we're implementing.")]
+ protected virtual void RaisePostDataChangedEvent() {
+ this.OnTextChanged();
}
/// <summary>
- /// Fires the <see cref="Canceled"/> event.
+ /// Called on a postback when the Text property has changed.
/// </summary>
- /// <param name="response">The response.</param>
- protected virtual void OnCanceled(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Canceled, "Firing Canceled event for the wrong response type.");
-
- var canceled = this.Canceled;
- if (canceled != null) {
- canceled(this, new OpenIdEventArgs(response));
+ protected virtual void OnTextChanged() {
+ EventHandler textChanged = this.TextChanged;
+ if (textChanged != null) {
+ textChanged(this, EventArgs.Empty);
}
}
/// <summary>
- /// Fires the <see cref="SetupRequired"/> event.
+ /// Creates the authentication requests for a given user-supplied Identifier.
/// </summary>
- /// <param name="response">The response.</param>
- protected virtual void OnSetupRequired(IAuthenticationResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
- ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.SetupRequired, "Firing SetupRequired event for the wrong response type.");
+ /// <param name="requests">The authentication requests to prepare.</param>
+ /// <returns>
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
+ /// </returns>
+ private IEnumerable<IAuthenticationRequest> CreateRequestsCore(IEnumerable<IAuthenticationRequest> requests) {
+ Contract.Requires(requests != null);
+
+ foreach (var request in requests) {
+ if (this.EnableRequestProfile) {
+ this.AddProfileArgs(request);
+ }
- // Why are we firing Failed when we're OnSetupRequired? Backward compatibility.
- var setupRequired = this.SetupRequired;
- if (setupRequired != null) {
- setupRequired(this, new OpenIdEventArgs(response));
+ yield return request;
}
}
- #endregion
-
/// <summary>
/// Adds extensions to a given authentication request to ask the Provider
/// for user profile data.
/// </summary>
/// <param name="request">The authentication request to add the extensions to.</param>
private void AddProfileArgs(IAuthenticationRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.Requires<ArgumentNullException>(request != null);
- request.AddExtension(new ClaimsRequest() {
+ var sreg = new ClaimsRequest() {
Nickname = this.RequestNickname,
Email = this.RequestEmail,
FullName = this.RequestFullName,
@@ -1188,75 +693,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
TimeZone = this.RequestTimeZone,
PolicyUrl = string.IsNullOrEmpty(this.PolicyUrl) ?
null : new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.Page.ResolveUrl(this.PolicyUrl)),
- });
- }
+ };
- /// <summary>
- /// Creates the relying party instance used to generate authentication requests.
- /// </summary>
- /// <returns>The instantiated relying party.</returns>
- private OpenIdRelyingParty CreateRelyingParty() {
- // If we're in stateful mode, first use the explicitly given one on this control if there
- // is one. Then try the configuration file specified one. Finally, use the default
- // in-memory one that's built into OpenIdRelyingParty.
- IRelyingPartyApplicationStore store = this.Stateless ? null :
- (this.CustomApplicationStore ?? DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore));
- var rp = new OpenIdRelyingParty(store);
-
- // Only set RequireSsl to true, as we don't want to override
- // a .config setting of true with false.
- if (this.RequireSsl) {
- rp.SecuritySettings.RequireSsl = true;
+ // Only actually add the extension request if fields are actually being requested.
+ if (!sreg.Equals(EmptyClaimsRequest)) {
+ request.AddExtension(sreg);
}
- return rp;
- }
-
- /// <summary>
- /// Detects whether a popup window should be used to show the Provider's UI
- /// and applies the UI extension to the request when appropriate.
- /// </summary>
- /// <returns><c>true</c> if a popup should be used; <c>false</c> otherwise.</returns>
- private bool IsPopupAppropriate() {
- Contract.Requires(this.Request != null);
-
- return this.Popup == PopupBehavior.Always || this.Request.Provider.IsExtensionSupported<UIRequest>();
- }
-
- /// <summary>
- /// Wires the return page to immediately display a popup window with the Provider in it.
- /// </summary>
- private void ScriptPopupWindow() {
- Contract.Requires(this.Request != null);
- Contract.Requires(this.RelyingParty != null);
-
- this.Request.AddCallbackArguments(UIPopupCallbackKey, "1");
-
- StringBuilder startupScript = new StringBuilder();
-
- // Add a callback function that the popup window can call on this, the
- // parent window, to pass back the authentication result.
- startupScript.AppendLine("window.dnoa_internal = new Object();");
- startupScript.AppendLine("window.dnoa_internal.processAuthorizationResult = function(uri) { window.location = uri; };");
-
- // Open the popup window.
- startupScript.AppendFormat(
- @"var openidPopup = {0}",
- UIUtilities.GetWindowPopupScript(this.RelyingParty, this.Request, "openidPopup"));
-
- this.Page.ClientScript.RegisterStartupScript(this.GetType(), "loginPopup", startupScript.ToString(), true);
- }
-
- /// <summary>
- /// Wires the popup window to close itself and pass the authentication result to the parent window.
- /// </summary>
- private void ScriptClosingPopup() {
- StringBuilder startupScript = new StringBuilder();
- startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL + '&" + UIPopupCallbackParentKey + "=1');");
- startupScript.AppendLine("window.close();");
-
- // We're referencing the OpenIdRelyingPartyControlBase type here to avoid double-registering this script
- // if the other control exists on the page.
- this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
index 13eb1a2..5cfa191 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
@@ -26,20 +26,28 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Information about the OP endpoint that issued this assertion.
/// </summary>
- private readonly ProviderEndpointDescription provider;
+ private readonly IProviderEndpoint provider;
/// <summary>
/// Initializes a new instance of the <see cref="PositiveAnonymousResponse"/> class.
/// </summary>
/// <param name="response">The response message.</param>
protected internal PositiveAnonymousResponse(IndirectSignedResponse response) {
- Contract.Requires(response != null);
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
if (response.ProviderEndpoint != null && response.Version != null) {
this.provider = new ProviderEndpointDescription(response.ProviderEndpoint, response.Version);
}
+
+ // Derived types of this are responsible to log an appropriate message for themselves.
+ if (Logger.OpenId.IsInfoEnabled && this.GetType() == typeof(PositiveAnonymousResponse)) {
+ Logger.OpenId.Info("Received anonymous (identity-less) positive assertion.");
+ }
+
+ if (response.ProviderEndpoint != null) {
+ Reporting.RecordEventOccurrence(this, response.ProviderEndpoint.AbsoluteUri);
+ }
}
#region IAuthenticationResponse Properties
@@ -129,6 +137,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
+ /// Gets a value indicating whether trusted callback arguments are available.
+ /// </summary>
+ /// <remarks>
+ /// We use this internally to avoid logging a warning during a standard snapshot creation.
+ /// </remarks>
+ internal bool TrustedCallbackArgumentsAvailable {
+ get { return this.response.ReturnToParametersSignatureValidated; }
+ }
+
+ /// <summary>
/// Gets the positive extension-only message the Relying Party received that this instance wraps.
/// </summary>
protected internal IndirectSignedResponse Response {
@@ -154,13 +172,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
public string GetCallbackArgument(string key) {
if (this.response.ReturnToParametersSignatureValidated) {
- return this.response.GetReturnToArgument(key);
+ return this.GetUntrustedCallbackArgument(key);
} else {
+ Logger.OpenId.WarnFormat(OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name);
return null;
}
}
/// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public string GetUntrustedCallbackArgument(string key) {
+ return this.response.GetReturnToArgument(key);
+ }
+
+ /// <summary>
/// Gets all the callback arguments that were previously added using
/// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
/// of the return_to URL.
@@ -175,22 +212,40 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </remarks>
public IDictionary<string, string> GetCallbackArguments() {
if (this.response.ReturnToParametersSignatureValidated) {
- var args = new Dictionary<string, string>();
-
- // Return all the return_to arguments, except for the OpenID-supporting ones.
- // The only arguments that should be returned here are the ones that the host
- // web site adds explicitly.
- foreach (string key in this.response.GetReturnToParameterNames().Where(key => !OpenIdRelyingParty.IsOpenIdSupportingParameter(key))) {
- args[key] = this.response.GetReturnToArgument(key);
- }
-
- return args;
+ return this.GetUntrustedCallbackArguments();
} else {
+ Logger.OpenId.WarnFormat(OpenIdStrings.CallbackArgumentsRequireSecretStore, typeof(IAssociationStore<Uri>).Name, typeof(OpenIdRelyingParty).Name);
return EmptyDictionary<string, string>.Instance;
}
}
/// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available if they are complete and untampered with
+ /// since the original request message (as proven by a signature).
+ /// If the relying party is operating in stateless mode an empty dictionary is always
+ /// returned since the callback arguments could not be signed to protect against
+ /// tampering.
+ /// </remarks>
+ public IDictionary<string, string> GetUntrustedCallbackArguments() {
+ var args = new Dictionary<string, string>();
+
+ // Return all the return_to arguments, except for the OpenID-supporting ones.
+ // The only arguments that should be returned here are the ones that the host
+ // web site adds explicitly.
+ foreach (string key in this.response.GetReturnToParameterNames().Where(key => !OpenIdRelyingParty.IsOpenIdSupportingParameter(key))) {
+ args[key] = this.response.GetReturnToArgument(key);
+ }
+
+ return args;
+ }
+
+ /// <summary>
/// Tries to get an OpenID extension that may be present in the response.
/// </summary>
/// <typeparam name="T">The type of extension to look for in the response message.</typeparam>
@@ -235,7 +290,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// have not been tampered with since the Provider sent the message.</para>
/// </remarks>
public IOpenIdMessageExtension GetExtension(Type extensionType) {
- ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
return this.response.SignedExtensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
}
@@ -285,7 +339,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// have not been tampered with since the Provider sent the message.</para>
/// </remarks>
public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) {
- ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
return this.response.Extensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault();
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
index 69a6eaa..b6a1b76 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
@@ -19,27 +20,15 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
[DebuggerDisplay("Status: {Status}, ClaimedIdentifier: {ClaimedIdentifier}")]
internal class PositiveAuthenticationResponse : PositiveAnonymousResponse {
/// <summary>
- /// The OpenID service endpoint reconstructed from the assertion message.
- /// </summary>
- /// <remarks>
- /// This information is straight from the Provider, and therefore must not
- /// be trusted until verified as matching the discovery information for
- /// the claimed identifier to avoid a Provider asserting an Identifier
- /// for which it has no authority.
- /// </remarks>
- private readonly ServiceEndpoint endpoint;
-
- /// <summary>
/// Initializes a new instance of the <see cref="PositiveAuthenticationResponse"/> class.
/// </summary>
/// <param name="response">The positive assertion response that was just received by the Relying Party.</param>
/// <param name="relyingParty">The relying party.</param>
internal PositiveAuthenticationResponse(PositiveAssertionResponse response, OpenIdRelyingParty relyingParty)
: base(response) {
- Contract.Requires(relyingParty != null);
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
+ Contract.Requires<ArgumentNullException>(relyingParty != null);
- this.endpoint = ServiceEndpoint.CreateForClaimedIdentifier(
+ this.Endpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
this.Response.ClaimedIdentifier,
this.Response.GetReturnToArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName),
this.Response.LocalIdentifier,
@@ -48,6 +37,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
null);
this.VerifyDiscoveryMatchesAssertion(relyingParty);
+
+ Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.Response.ClaimedIdentifier, this.Provider.Uri);
}
#region IAuthenticationResponse Properties
@@ -69,7 +60,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </para>
/// </remarks>
public override Identifier ClaimedIdentifier {
- get { return this.endpoint.ClaimedIdentifier; }
+ get { return this.Endpoint.ClaimedIdentifier; }
}
/// <summary>
@@ -102,7 +93,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </para>
/// </remarks>
public override string FriendlyIdentifierForDisplay {
- get { return this.endpoint.FriendlyIdentifierForDisplay; }
+ get { return this.Endpoint.FriendlyIdentifierForDisplay; }
}
/// <summary>
@@ -115,6 +106,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
#endregion
/// <summary>
+ /// Gets the OpenID service endpoint reconstructed from the assertion message.
+ /// </summary>
+ /// <remarks>
+ /// This information is straight from the Provider, and therefore must not
+ /// be trusted until verified as matching the discovery information for
+ /// the claimed identifier to avoid a Provider asserting an Identifier
+ /// for which it has no authority.
+ /// </remarks>
+ internal IdentifierDiscoveryResult Endpoint { get; private set; }
+
+ /// <summary>
/// Gets the positive assertion response message.
/// </summary>
protected internal new PositiveAssertionResponse Response {
@@ -153,11 +155,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// is signed by the RP before it's considered reliable. In 1.x stateless mode, this RP
// doesn't (and can't) sign its own return_to URL, so its cached discovery information
// is merely a hint that must be verified by performing discovery again here.
- var discoveryResults = claimedId.Discover(relyingParty.WebRequestHandler);
+ var discoveryResults = relyingParty.Discover(claimedId);
ErrorUtilities.VerifyProtocol(
- discoveryResults.Contains(this.endpoint),
+ discoveryResults.Contains(this.Endpoint),
OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery,
- this.endpoint,
+ this.Endpoint,
discoveryResults.ToStringDeferred(true));
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshot.cs
index 3fd7d20..80b424a 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponseSnapshot.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-// <copyright file="AuthenticationResponseSnapshot.cs" company="Andrew Arnott">
+// <copyright file="PositiveAuthenticationResponseSnapshot.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Text;
using System.Web;
using DotNetOpenAuth.Messaging;
@@ -16,24 +17,37 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// A serializable snapshot of a verified authentication message.
/// </summary>
[Serializable]
- internal class AuthenticationResponseSnapshot : IAuthenticationResponse {
+ internal class PositiveAuthenticationResponseSnapshot : IAuthenticationResponse {
/// <summary>
/// The callback arguments that came with the authentication response.
/// </summary>
private IDictionary<string, string> callbackArguments;
/// <summary>
- /// Initializes a new instance of the <see cref="AuthenticationResponseSnapshot"/> class.
+ /// The untrusted callback arguments that came with the authentication response.
+ /// </summary>
+ private IDictionary<string, string> untrustedCallbackArguments;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PositiveAuthenticationResponseSnapshot"/> class.
/// </summary>
/// <param name="copyFrom">The authentication response to copy from.</param>
- internal AuthenticationResponseSnapshot(IAuthenticationResponse copyFrom) {
- ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
+ internal PositiveAuthenticationResponseSnapshot(IAuthenticationResponse copyFrom) {
+ Contract.Requires<ArgumentNullException>(copyFrom != null);
this.ClaimedIdentifier = copyFrom.ClaimedIdentifier;
this.FriendlyIdentifierForDisplay = copyFrom.FriendlyIdentifierForDisplay;
this.Status = copyFrom.Status;
this.Provider = copyFrom.Provider;
- this.callbackArguments = copyFrom.GetCallbackArguments();
+ this.untrustedCallbackArguments = copyFrom.GetUntrustedCallbackArguments();
+
+ // Do this special check to avoid logging a warning for trying to clone a dictionary.
+ var anonResponse = copyFrom as PositiveAnonymousResponse;
+ if (anonResponse == null || anonResponse.TrustedCallbackArgumentsAvailable) {
+ this.callbackArguments = copyFrom.GetCallbackArguments();
+ } else {
+ this.callbackArguments = EmptyDictionary<string, string>.Instance;
+ }
}
#region IAuthenticationResponse Members
@@ -229,6 +243,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets all the callback arguments that were previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/> or as a natural part
+ /// of the return_to URL.
+ /// </summary>
+ /// <returns>A name-value dictionary. Never null.</returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public IDictionary<string, string> GetUntrustedCallbackArguments() {
+ // Return a copy so that the caller cannot change the contents.
+ return new Dictionary<string, string>(this.untrustedCallbackArguments);
+ }
+
+ /// <summary>
/// Gets a callback argument's value that was previously added using
/// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
/// </summary>
@@ -243,13 +274,31 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <para>Note that these values are NOT protected against tampering in transit.</para>
/// </remarks>
public string GetCallbackArgument(string key) {
- ErrorUtilities.VerifyArgumentNotNull(key, "key");
-
string value;
this.callbackArguments.TryGetValue(key, out value);
return value;
}
+ /// <summary>
+ /// Gets a callback argument's value that was previously added using
+ /// <see cref="IAuthenticationRequest.AddCallbackArguments(string, string)"/>.
+ /// </summary>
+ /// <param name="key">The name of the parameter whose value is sought.</param>
+ /// <returns>
+ /// The value of the argument, or null if the named parameter could not be found.
+ /// </returns>
+ /// <remarks>
+ /// Callback parameters are only available even if the RP is in stateless mode,
+ /// or the callback parameters are otherwise unverifiable as untampered with.
+ /// Therefore, use this method only when the callback argument is not to be
+ /// used to make a security-sensitive decision.
+ /// </remarks>
+ public string GetUntrustedCallbackArgument(string key) {
+ string value;
+ this.untrustedCallbackArguments.TryGetValue(key, out value);
+ return value;
+ }
+
#endregion
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PrivateSecretManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PrivateSecretManager.cs
index 0895d77..6d55e39 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PrivateSecretManager.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PrivateSecretManager.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
@@ -43,8 +44,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <param name="securitySettings">The security settings.</param>
/// <param name="store">The association store.</param>
internal PrivateSecretManager(RelyingPartySecuritySettings securitySettings, IAssociationStore<Uri> store) {
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
- ErrorUtilities.VerifyArgumentNotNull(store, "store");
+ Contract.Requires<ArgumentNullException>(securitySettings != null);
+ Contract.Requires<ArgumentNullException>(store != null);
this.securitySettings = securitySettings;
this.store = store;
@@ -74,8 +75,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <exception cref="ProtocolException">Thrown when an association with the given handle could not be found.
/// This most likely happens if the association was near the end of its life and the user took too long to log in.</exception>
internal byte[] Sign(byte[] buffer, string handle) {
- ErrorUtilities.VerifyArgumentNotNull(buffer, "buffer");
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
+ Contract.Requires<ArgumentNullException>(buffer != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(handle));
Association association = this.store.GetAssociation(SecretUri, handle);
ErrorUtilities.VerifyProtocol(association != null, OpenIdStrings.PrivateRPSecretNotFound, handle);
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
index ff29498..071a488 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
@@ -111,11 +111,26 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public bool RequireAssociation { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether identifiers that are both OP Identifiers and Claimed Identifiers
+ /// should ever be recognized as claimed identifiers.
+ /// </summary>
+ /// <value>
+ /// The default value is <c>false</c>, per the OpenID 2.0 spec.
+ /// </value>
+ /// <remarks>
+ /// OpenID 2.0 sections 7.3.2.2 and 11.2 specify that OP Identifiers never be recognized as Claimed Identifiers.
+ /// However, for some scenarios it may be desirable for an RP to override this behavior and allow this.
+ /// The security ramifications of setting this property to <c>true</c> have not been fully explored and
+ /// therefore this setting should only be changed with caution.
+ /// </remarks>
+ public bool AllowDualPurposeIdentifiers { get; set; }
+
+ /// <summary>
/// Filters out any disallowed endpoints.
/// </summary>
/// <param name="endpoints">The endpoints discovered on an Identifier.</param>
/// <returns>A sequence of endpoints that satisfy all security requirements.</returns>
- internal IEnumerable<ServiceEndpoint> FilterEndpoints(IEnumerable<ServiceEndpoint> endpoints) {
+ internal IEnumerable<IdentifierDiscoveryResult> FilterEndpoints(IEnumerable<IdentifierDiscoveryResult> endpoints) {
return endpoints
.Where(se => !this.RejectDelegatingIdentifiers || se.ClaimedIdentifier == se.ProviderLocalIdentifier)
.Where(se => !this.RequireDirectedIdentity || se.ClaimedIdentifier == se.Protocol.ClaimedIdentifierForOPIdentifier);
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButton.cs
new file mode 100644
index 0000000..0be3a5f
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButton.cs
@@ -0,0 +1,46 @@
+//-----------------------------------------------------------------------
+// <copyright file="SelectorButton.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.Web.UI;
+
+ /// <summary>
+ /// A button that would appear in the <see cref="OpenIdSelector"/> control via its <see cref="OpenIdSelector.Buttons"/> collection.
+ /// </summary>
+ [ContractClass(typeof(SelectorButtonContract))]
+ public abstract class SelectorButton {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SelectorButton"/> class.
+ /// </summary>
+ protected SelectorButton() {
+ }
+
+ /// <summary>
+ /// Ensures that this button has been initialized to a valid state.
+ /// </summary>
+ /// <remarks>
+ /// This is "internal" -- NOT "protected internal" deliberately. It makes it impossible
+ /// to derive from this class outside the assembly, which suits our purposes since the
+ /// <see cref="OpenIdSelector"/> control is not designed for an extensible set of button types.
+ /// </remarks>
+ internal abstract void EnsureValid();
+
+ /// <summary>
+ /// Renders the leading attributes for the LI tag.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ protected internal abstract void RenderLeadingAttributes(HtmlTextWriter writer);
+
+ /// <summary>
+ /// Renders the content of the button.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="selector">The containing selector control.</param>
+ protected internal abstract void RenderButtonContent(HtmlTextWriter writer, OpenIdSelector selector);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButtonContract.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButtonContract.cs
new file mode 100644
index 0000000..c70218a
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorButtonContract.cs
@@ -0,0 +1,46 @@
+//-----------------------------------------------------------------------
+// <copyright file="SelectorButtonContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.Web.UI;
+
+ /// <summary>
+ /// The contract class for the <see cref="SelectorButton"/> class.
+ /// </summary>
+ [ContractClassFor(typeof(SelectorButton))]
+ internal abstract class SelectorButtonContract : SelectorButton {
+ /// <summary>
+ /// Ensures that this button has been initialized to a valid state.
+ /// </summary>
+ /// <remarks>
+ /// This is "internal" -- NOT "protected internal" deliberately. It makes it impossible
+ /// to derive from this class outside the assembly, which suits our purposes since the
+ /// <see cref="OpenIdSelector"/> control is not designed for an extensible set of button types.
+ /// </remarks>
+ internal override void EnsureValid() {
+ }
+
+ /// <summary>
+ /// Renders the leading attributes for the LI tag.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ protected internal override void RenderLeadingAttributes(HtmlTextWriter writer) {
+ Contract.Requires<ArgumentNullException>(writer != null);
+ }
+
+ /// <summary>
+ /// Renders the content of the button.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="selector">The containing selector control.</param>
+ protected internal override void RenderButtonContent(HtmlTextWriter writer, OpenIdSelector selector) {
+ Contract.Requires<ArgumentNullException>(writer != null);
+ Contract.Requires<ArgumentNullException>(selector != null);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorInfoCardButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorInfoCardButton.cs
new file mode 100644
index 0000000..c5dda1c
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorInfoCardButton.cs
@@ -0,0 +1,102 @@
+//-----------------------------------------------------------------------
+// <copyright file="SelectorInfoCardButton.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.ObjectModel;
+ using System.ComponentModel;
+ using System.Diagnostics.Contracts;
+ using System.Web.UI;
+ using DotNetOpenAuth.InfoCard;
+
+ /// <summary>
+ /// A button that appears in the <see cref="OpenIdSelector"/> control that
+ /// activates the Information Card selector on the browser, if one is available.
+ /// </summary>
+ public class SelectorInfoCardButton : SelectorButton, IDisposable {
+ /// <summary>
+ /// The backing field for the <see cref="InfoCardSelector"/> property.
+ /// </summary>
+ private InfoCardSelector infoCardSelector;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SelectorInfoCardButton"/> class.
+ /// </summary>
+ public SelectorInfoCardButton() {
+ Reporting.RecordFeatureUse(this);
+ }
+
+ /// <summary>
+ /// Gets or sets the InfoCard selector which may be displayed alongside the OP buttons.
+ /// </summary>
+ [PersistenceMode(PersistenceMode.InnerProperty)]
+ public InfoCardSelector InfoCardSelector {
+ get {
+ if (this.infoCardSelector == null) {
+ this.infoCardSelector = new InfoCardSelector();
+ }
+
+ return this.infoCardSelector;
+ }
+
+ set {
+ Contract.Requires<ArgumentNullException>(value != null);
+ if (this.infoCardSelector != null) {
+ Logger.Library.WarnFormat("{0}.InfoCardSelector property is being set multiple times.", GetType().Name);
+ }
+
+ this.infoCardSelector = value;
+ }
+ }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose() {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Ensures that this button has been initialized to a valid state.
+ /// </summary>
+ internal override void EnsureValid() {
+ }
+
+ /// <summary>
+ /// Renders the leading attributes for the LI tag.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ protected internal override void RenderLeadingAttributes(HtmlTextWriter writer) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "infocard");
+ }
+
+ /// <summary>
+ /// Renders the content of the button.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="selector">The containing selector control.</param>
+ protected internal override void RenderButtonContent(HtmlTextWriter writer, OpenIdSelector selector) {
+ this.InfoCardSelector.RenderControl(writer);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ if (this.infoCardSelector != null) {
+ this.infoCardSelector.Dispose();
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs
new file mode 100644
index 0000000..15b6ca7
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorOpenIdButton.cs
@@ -0,0 +1,70 @@
+//-----------------------------------------------------------------------
+// <copyright file="SelectorOpenIdButton.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System.ComponentModel;
+ using System.Diagnostics.Contracts;
+ using System.Drawing.Design;
+ using System.Web.UI;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A button that appears in the <see cref="OpenIdSelector"/> control that
+ /// allows the user to type in a user-supplied identifier.
+ /// </summary>
+ public class SelectorOpenIdButton : SelectorButton {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SelectorOpenIdButton"/> class.
+ /// </summary>
+ public SelectorOpenIdButton() {
+ Reporting.RecordFeatureUse(this);
+ }
+
+ /// <summary>
+ /// Gets or sets the path to the image to display on the button's surface.
+ /// </summary>
+ /// <value>The virtual path to the image.</value>
+ [Editor("System.Web.UI.Design.ImageUrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
+ [UrlProperty]
+ public string Image { get; set; }
+
+ /// <summary>
+ /// Ensures that this button has been initialized to a valid state.
+ /// </summary>
+ internal override void EnsureValid() {
+ Contract.Ensures(!string.IsNullOrEmpty(this.Image));
+
+ // Every button must have an image.
+ ErrorUtilities.VerifyOperation(!string.IsNullOrEmpty(this.Image), OpenIdStrings.PropertyNotSet, "SelectorButton.Image");
+ }
+
+ /// <summary>
+ /// Renders the leading attributes for the LI tag.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ protected internal override void RenderLeadingAttributes(HtmlTextWriter writer) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, "OpenIDButton");
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "OpenIDButton");
+ }
+
+ /// <summary>
+ /// Renders the content of the button.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="selector">The containing selector control.</param>
+ protected internal override void RenderButtonContent(HtmlTextWriter writer, OpenIdSelector selector) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Src, selector.Page.ResolveUrl(this.Image));
+ writer.RenderBeginTag(HtmlTextWriterTag.Img);
+ writer.RenderEndTag();
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Src, selector.Page.ClientScript.GetWebResourceUrl(typeof(OpenIdAjaxTextBox), OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName));
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "loginSuccess");
+ writer.AddAttribute(HtmlTextWriterAttribute.Title, selector.AuthenticatedAsToolTip);
+ writer.RenderBeginTag(HtmlTextWriterTag.Img);
+ writer.RenderEndTag();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs
new file mode 100644
index 0000000..3a05287
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SelectorProviderButton.cs
@@ -0,0 +1,98 @@
+//-----------------------------------------------------------------------
+// <copyright file="SelectorProviderButton.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System.ComponentModel;
+ using System.Diagnostics.Contracts;
+ using System.Drawing.Design;
+ using System.Web.UI;
+ using DotNetOpenAuth.ComponentModel;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A button that appears in the <see cref="OpenIdSelector"/> control that
+ /// provides one-click access to a popular OpenID Provider.
+ /// </summary>
+ public class SelectorProviderButton : SelectorButton {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SelectorProviderButton"/> class.
+ /// </summary>
+ public SelectorProviderButton() {
+ Reporting.RecordFeatureUse(this);
+ }
+
+ /// <summary>
+ /// Gets or sets the path to the image to display on the button's surface.
+ /// </summary>
+ /// <value>The virtual path to the image.</value>
+ [Editor("System.Web.UI.Design.ImageUrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
+ [UrlProperty]
+ public string Image { get; set; }
+
+ /// <summary>
+ /// Gets or sets the OP Identifier represented by the button.
+ /// </summary>
+ /// <value>
+ /// The OP identifier, which may be provided in the easiest "user-supplied identifier" form,
+ /// but for security should be provided with a leading https:// if possible.
+ /// For example: "yahoo.com" or "https://me.yahoo.com/".
+ /// </value>
+ [TypeConverter(typeof(IdentifierConverter))]
+ public Identifier OPIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this Provider doesn't handle
+ /// checkid_immediate messages correctly and background authentication
+ /// should not be attempted.
+ /// </summary>
+ public bool SkipBackgroundAuthentication { get; set; }
+
+ /// <summary>
+ /// Ensures that this button has been initialized to a valid state.
+ /// </summary>
+ internal override void EnsureValid() {
+ Contract.Ensures(!string.IsNullOrEmpty(this.Image));
+ Contract.Ensures(this.OPIdentifier != null);
+
+ // Every button must have an image.
+ ErrorUtilities.VerifyOperation(!string.IsNullOrEmpty(this.Image), OpenIdStrings.PropertyNotSet, "SelectorButton.Image");
+
+ // Every button must have exactly one purpose.
+ ErrorUtilities.VerifyOperation(this.OPIdentifier != null, OpenIdStrings.PropertyNotSet, "SelectorButton.OPIdentifier");
+ }
+
+ /// <summary>
+ /// Renders the leading attributes for the LI tag.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ protected internal override void RenderLeadingAttributes(HtmlTextWriter writer) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, this.OPIdentifier);
+
+ string style = "OPButton";
+ if (this.SkipBackgroundAuthentication) {
+ style += " NoAsyncAuth";
+ }
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, style);
+ }
+
+ /// <summary>
+ /// Renders the content of the button.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="selector">The containing selector control.</param>
+ protected internal override void RenderButtonContent(HtmlTextWriter writer, OpenIdSelector selector) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Src, selector.Page.ResolveUrl(this.Image));
+ writer.RenderBeginTag(HtmlTextWriterTag.Img);
+ writer.RenderEndTag();
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Src, selector.Page.ClientScript.GetWebResourceUrl(typeof(OpenIdAjaxTextBox), OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName));
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "loginSuccess");
+ writer.AddAttribute(HtmlTextWriterAttribute.Title, selector.AuthenticatedAsToolTip);
+ writer.RenderBeginTag(HtmlTextWriterTag.Img);
+ writer.RenderEndTag();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs
index 912b8f4..678f69a 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/SimpleXrdsProviderEndpoint.cs
@@ -6,6 +6,8 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
+ using System.Collections.ObjectModel;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
/// <summary>
@@ -13,7 +15,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// assertions (particularly unsolicited ones) are received from OP endpoints that
/// are deemed permissible by the host RP.
/// </summary>
- internal class SimpleXrdsProviderEndpoint : IXrdsProviderEndpoint {
+ internal class SimpleXrdsProviderEndpoint : IProviderEndpoint {
/// <summary>
/// Initializes a new instance of the <see cref="SimpleXrdsProviderEndpoint"/> class.
/// </summary>
@@ -23,29 +25,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.Version = positiveAssertion.Version;
}
- #region IXrdsProviderEndpoint Properties
-
- /// <summary>
- /// Gets the priority associated with this service that may have been given
- /// in the XRDS document.
- /// </summary>
- public int? ServicePriority {
- get { return null; }
- }
-
- /// <summary>
- /// Gets the priority associated with the service endpoint URL.
- /// </summary>
- /// <remarks>
- /// When sorting by priority, this property should be considered second after
- /// <see cref="ServicePriority"/>.
- /// </remarks>
- public int? UriPriority {
- get { return null; }
- }
-
- #endregion
-
#region IProviderEndpoint Members
/// <summary>
@@ -56,7 +35,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets the URL that the OpenID Provider receives authentication requests at.
/// </summary>
- /// <value></value>
public Uri Uri { get; private set; }
/// <summary>
@@ -73,8 +51,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The only way to be sure of support for a given extension is to include
/// the extension in the request and see if a response comes back for that extension.
/// </remarks>
- public bool IsExtensionSupported<T>() where T : DotNetOpenAuth.OpenId.Messages.IOpenIdMessageExtension, new() {
- throw new NotSupportedException();
+ bool IProviderEndpoint.IsExtensionSupported<T>() {
+ throw new NotImplementedException();
}
/// <summary>
@@ -91,23 +69,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// The only way to be sure of support for a given extension is to include
/// the extension in the request and see if a response comes back for that extension.
/// </remarks>
- public bool IsExtensionSupported(Type extensionType) {
- throw new NotSupportedException();
- }
-
- #endregion
-
- #region IXrdsProviderEndpoint Methods
-
- /// <summary>
- /// Checks for the presence of a given Type URI in an XRDS service.
- /// </summary>
- /// <param name="typeUri">The type URI to check for.</param>
- /// <returns>
- /// <c>true</c> if the service type uri is present; <c>false</c> otherwise.
- /// </returns>
- public bool IsTypeUriPresent(string typeUri) {
- throw new NotSupportedException();
+ bool IProviderEndpoint.IsExtensionSupported(Type extensionType) {
+ throw new NotImplementedException();
}
#endregion
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
index 8499178..f0a1574 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
@@ -106,7 +106,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
/// <param name="nonce">A series of random characters.</param>
- /// <param name="timestamp">The timestamp that together with the nonce string make it unique.
+ /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
/// <returns>
/// True if the nonce+timestamp (combination) was not previously in the database.
@@ -119,8 +119,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string context, string nonce, DateTime timestamp) {
- return this.nonceStore.StoreNonce(context, nonce, timestamp);
+ public bool StoreNonce(string context, string nonce, DateTime timestampUtc) {
+ return this.nonceStore.StoreNonce(context, nonce, timestampUtc);
}
#endregion
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs
index 2e2ab61..ad1a11a 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs
@@ -30,6 +30,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public static readonly Identifier MyOpenId = "https://www.myopenid.com/";
/// <summary>
+ /// The Verisign OP Identifier.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Verisign", Justification = "The spelling is correct.")]
+ [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "Immutable type")]
+ public static readonly Identifier Verisign = "https://pip.verisignlabs.com/";
+
+ /// <summary>
+ /// The MyVidoop OP Identifier.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Vidoop", Justification = "The spelling is correct.")]
+ [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "Immutable type")]
+ public static readonly Identifier MyVidoop = "https://myvidoop.com/";
+
+ /// <summary>
/// Prevents a default instance of the <see cref="WellKnownProviders"/> class from being created.
/// </summary>
private WellKnownProviders() {
diff --git a/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs b/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs
index 6b82966..279ca30 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -26,8 +27,8 @@ namespace DotNetOpenAuth.OpenId {
/// The Type URIs of supported services advertised on a relying party's XRDS document.
/// </param>
internal RelyingPartyEndpointDescription(Uri returnTo, string[] supportedServiceTypeUris) {
- ErrorUtilities.VerifyArgumentNotNull(returnTo, "returnTo");
- ErrorUtilities.VerifyArgumentNotNull(supportedServiceTypeUris, "supportedServiceTypeUris");
+ Contract.Requires<ArgumentNullException>(returnTo != null);
+ Contract.Requires<ArgumentNullException>(supportedServiceTypeUris != null);
this.ReturnToEndpoint = returnTo;
this.Protocol = GetProtocolFromServices(supportedServiceTypeUris);
diff --git a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
index d4df697..26f6d2a 100644
--- a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -87,7 +88,7 @@ namespace DotNetOpenAuth.OpenId {
/// <c>true</c> if the association is permitted given the security requirements; otherwise, <c>false</c>.
/// </returns>
internal bool IsAssociationInPermittedRange(Association association) {
- ErrorUtilities.VerifyArgumentNotNull(association, "association");
+ Contract.Requires<ArgumentNullException>(association != null);
return association.HashBitLength >= this.MinimumHashBitLength && association.HashBitLength <= this.MaximumHashBitLength;
}
}
diff --git a/src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs
new file mode 100644
index 0000000..f017d9f
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/UriDiscoveryService.cs
@@ -0,0 +1,138 @@
+//-----------------------------------------------------------------------
+// <copyright file="UriDiscoveryService.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Text.RegularExpressions;
+ using System.Web.UI.HtmlControls;
+ using System.Xml;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Xrds;
+ using DotNetOpenAuth.Yadis;
+
+ /// <summary>
+ /// The discovery service for URI identifiers.
+ /// </summary>
+ public class UriDiscoveryService : IIdentifierDiscoveryService {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriDiscoveryService"/> class.
+ /// </summary>
+ public UriDiscoveryService() {
+ }
+
+ #region IDiscoveryService Members
+
+ /// <summary>
+ /// Performs discovery on the specified identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
+ /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
+ /// <returns>
+ /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
+ /// </returns>
+ public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) {
+ abortDiscoveryChain = false;
+ var uriIdentifier = identifier as UriIdentifier;
+ if (uriIdentifier == null) {
+ return Enumerable.Empty<IdentifierDiscoveryResult>();
+ }
+
+ var endpoints = new List<IdentifierDiscoveryResult>();
+
+ // Attempt YADIS discovery
+ DiscoveryResult yadisResult = Yadis.Discover(requestHandler, uriIdentifier, identifier.IsDiscoverySecureEndToEnd);
+ if (yadisResult != null) {
+ if (yadisResult.IsXrds) {
+ try {
+ XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
+ var xrdsEndpoints = xrds.XrdElements.CreateServiceEndpoints(yadisResult.NormalizedUri, uriIdentifier);
+
+ // Filter out insecure endpoints if high security is required.
+ if (uriIdentifier.IsDiscoverySecureEndToEnd) {
+ xrdsEndpoints = xrdsEndpoints.Where(se => se.ProviderEndpoint.IsTransportSecure());
+ }
+ endpoints.AddRange(xrdsEndpoints);
+ } catch (XmlException ex) {
+ Logger.Yadis.Error("Error while parsing the XRDS document. Falling back to HTML discovery.", ex);
+ }
+ }
+
+ // Failing YADIS discovery of an XRDS document, we try HTML discovery.
+ if (endpoints.Count == 0) {
+ var htmlEndpoints = new List<IdentifierDiscoveryResult>(DiscoverFromHtml(yadisResult.NormalizedUri, uriIdentifier, yadisResult.ResponseText));
+ if (htmlEndpoints.Any()) {
+ Logger.Yadis.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count);
+ Logger.Yadis.Debug(htmlEndpoints.ToStringDeferred(true));
+ endpoints.AddRange(htmlEndpoints.Where(ep => !uriIdentifier.IsDiscoverySecureEndToEnd || ep.ProviderEndpoint.IsTransportSecure()));
+ if (endpoints.Count == 0) {
+ Logger.Yadis.Info("No HTML discovered endpoints met the security requirements.");
+ }
+ } else {
+ Logger.Yadis.Debug("HTML discovery failed to find any endpoints.");
+ }
+ } else {
+ Logger.Yadis.Debug("Skipping HTML discovery because XRDS contained service endpoints.");
+ }
+ }
+ return endpoints;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Searches HTML for the HEAD META tags that describe OpenID provider services.
+ /// </summary>
+ /// <param name="claimedIdentifier">The final URL that provided this HTML document.
+ /// This may not be the same as (this) userSuppliedIdentifier if the
+ /// userSuppliedIdentifier pointed to a 301 Redirect.</param>
+ /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
+ /// <param name="html">The HTML that was downloaded and should be searched.</param>
+ /// <returns>
+ /// A sequence of any discovered ServiceEndpoints.
+ /// </returns>
+ private static IEnumerable<IdentifierDiscoveryResult> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) {
+ var linkTags = new List<HtmlLink>(HtmlParser.HeadTags<HtmlLink>(html));
+ foreach (var protocol in Protocol.AllPracticalVersions) {
+ // rel attributes are supposed to be interpreted with case INsensitivity,
+ // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes)
+ var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase));
+ if (serverLinkTag == null) {
+ continue;
+ }
+
+ Uri providerEndpoint = null;
+ if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) {
+ // See if a LocalId tag of the discovered version exists
+ Identifier providerLocalIdentifier = null;
+ var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase));
+ if (delegateLinkTag != null) {
+ if (Identifier.IsValid(delegateLinkTag.Href)) {
+ providerLocalIdentifier = delegateLinkTag.Href;
+ } else {
+ Logger.Yadis.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href);
+ continue; // skip to next version
+ }
+ }
+
+ // Choose the TypeURI to match the OpenID version detected.
+ string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI };
+ yield return IdentifierDiscoveryResult.CreateForClaimedIdentifier(
+ claimedIdentifier,
+ userSuppliedIdentifier,
+ providerLocalIdentifier,
+ new ProviderEndpointDescription(providerEndpoint, typeURIs),
+ (int?)null,
+ (int?)null);
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
index 512200d..0b7a0e3 100644
--- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
@@ -35,6 +35,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="uri">The value this identifier will represent.</param>
internal UriIdentifier(string uri)
: this(uri, false) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(uri));
}
/// <summary>
@@ -43,8 +44,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="uri">The value this identifier will represent.</param>
/// <param name="requireSslDiscovery">if set to <c>true</c> [require SSL discovery].</param>
internal UriIdentifier(string uri, bool requireSslDiscovery)
- : base(requireSslDiscovery) {
- ErrorUtilities.VerifyNonZeroLength(uri, "uri");
+ : base(uri, requireSslDiscovery) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(uri));
Uri canonicalUri;
bool schemePrepended;
if (!TryCanonicalize(uri, out canonicalUri, requireSslDiscovery, out schemePrepended)) {
@@ -70,8 +71,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="uri">The value this identifier will represent.</param>
/// <param name="requireSslDiscovery">if set to <c>true</c> [require SSL discovery].</param>
internal UriIdentifier(Uri uri, bool requireSslDiscovery)
- : base(requireSslDiscovery) {
- ErrorUtilities.VerifyArgumentNotNull(uri, "uri");
+ : base(uri != null ? uri.OriginalString : null, requireSslDiscovery) {
+ Contract.Requires<ArgumentNullException>(uri != null);
if (!TryCanonicalize(new UriBuilder(uri), out uri)) {
throw new UriFormatException();
}
@@ -206,54 +207,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Performs discovery on the Identifier.
- /// </summary>
- /// <param name="requestHandler">The web request handler to use for discovery.</param>
- /// <returns>
- /// An initialized structure containing the discovered provider endpoint information.
- /// </returns>
- internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) {
- List<ServiceEndpoint> endpoints = new List<ServiceEndpoint>();
-
- // Attempt YADIS discovery
- DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this, IsDiscoverySecureEndToEnd);
- if (yadisResult != null) {
- if (yadisResult.IsXrds) {
- try {
- XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
- var xrdsEndpoints = xrds.CreateServiceEndpoints(yadisResult.NormalizedUri, this);
-
- // Filter out insecure endpoints if high security is required.
- if (IsDiscoverySecureEndToEnd) {
- xrdsEndpoints = xrdsEndpoints.Where(se => se.IsSecure);
- }
- endpoints.AddRange(xrdsEndpoints);
- } catch (XmlException ex) {
- Logger.Yadis.Error("Error while parsing the XRDS document. Falling back to HTML discovery.", ex);
- }
- }
-
- // Failing YADIS discovery of an XRDS document, we try HTML discovery.
- if (endpoints.Count == 0) {
- var htmlEndpoints = new List<ServiceEndpoint>(DiscoverFromHtml(yadisResult.NormalizedUri, this, yadisResult.ResponseText));
- if (htmlEndpoints.Any()) {
- Logger.Yadis.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count);
- Logger.Yadis.Debug(htmlEndpoints.ToStringDeferred(true));
- endpoints.AddRange(htmlEndpoints.Where(ep => !IsDiscoverySecureEndToEnd || ep.IsSecure));
- if (endpoints.Count == 0) {
- Logger.Yadis.Info("No HTML discovered endpoints met the security requirements.");
- }
- } else {
- Logger.Yadis.Debug("HTML discovery failed to find any endpoints.");
- }
- } else {
- Logger.Yadis.Debug("Skipping HTML discovery because XRDS contained service endpoints.");
- }
- }
- return endpoints;
- }
-
- /// <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.
@@ -317,54 +270,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Searches HTML for the HEAD META tags that describe OpenID provider services.
- /// </summary>
- /// <param name="claimedIdentifier">The final URL that provided this HTML document.
- /// This may not be the same as (this) userSuppliedIdentifier if the
- /// userSuppliedIdentifier pointed to a 301 Redirect.</param>
- /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
- /// <param name="html">The HTML that was downloaded and should be searched.</param>
- /// <returns>
- /// A sequence of any discovered ServiceEndpoints.
- /// </returns>
- private static IEnumerable<ServiceEndpoint> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) {
- var linkTags = new List<HtmlLink>(HtmlParser.HeadTags<HtmlLink>(html));
- foreach (var protocol in Protocol.AllPracticalVersions) {
- // rel attributes are supposed to be interpreted with case INsensitivity,
- // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes)
- var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase));
- if (serverLinkTag == null) {
- continue;
- }
-
- Uri providerEndpoint = null;
- if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) {
- // See if a LocalId tag of the discovered version exists
- Identifier providerLocalIdentifier = null;
- var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase));
- if (delegateLinkTag != null) {
- if (Identifier.IsValid(delegateLinkTag.Href)) {
- providerLocalIdentifier = delegateLinkTag.Href;
- } else {
- Logger.Yadis.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href);
- continue; // skip to next version
- }
- }
-
- // Choose the TypeURI to match the OpenID version detected.
- string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI };
- yield return ServiceEndpoint.CreateForClaimedIdentifier(
- claimedIdentifier,
- userSuppliedIdentifier,
- providerLocalIdentifier,
- new ProviderEndpointDescription(providerEndpoint, typeURIs),
- (int?)null,
- (int?)null);
- }
- }
- }
-
- /// <summary>
/// Determines whether the given URI is using a scheme in the list of allowed schemes.
/// </summary>
/// <param name="uri">The URI whose scheme is to be checked.</param>
@@ -416,8 +321,7 @@ namespace DotNetOpenAuth.OpenId {
/// require network access are also done, such as lower-casing the hostname in the URI.
/// </remarks>
private static bool TryCanonicalize(string uri, out Uri canonicalUri, bool forceHttpsDefaultScheme, out bool schemePrepended) {
- Contract.Requires(!string.IsNullOrEmpty(uri));
- ErrorUtilities.VerifyNonZeroLength(uri, "uri");
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(uri));
uri = uri.Trim();
canonicalUri = null;
@@ -462,6 +366,7 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
private void ObjectInvariant() {
diff --git a/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs
new file mode 100644
index 0000000..b1a3430
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs
@@ -0,0 +1,108 @@
+//-----------------------------------------------------------------------
+// <copyright file="XriDiscoveryProxyService.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Xml;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Xrds;
+ using DotNetOpenAuth.Yadis;
+
+ /// <summary>
+ /// The discovery service for XRI identifiers that uses an XRI proxy resolver for discovery.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Justification = "Acronym")]
+ public class XriDiscoveryProxyService : IIdentifierDiscoveryService {
+ /// <summary>
+ /// The magic URL that will provide us an XRDS document for a given XRI identifier.
+ /// </summary>
+ /// <remarks>
+ /// We use application/xrd+xml instead of application/xrds+xml because it gets
+ /// xri.net to automatically give us exactly the right XRD element for community i-names
+ /// automatically, saving us having to choose which one to use out of the result.
+ /// The ssl=true parameter tells the proxy resolver to accept only SSL connections
+ /// when resolving community i-names.
+ /// </remarks>
+ private const string XriResolverProxyTemplate = "https://{1}/{0}?_xrd_r=application/xrd%2Bxml;sep=false";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="XriDiscoveryProxyService"/> class.
+ /// </summary>
+ public XriDiscoveryProxyService() {
+ }
+
+ #region IDiscoveryService Members
+
+ /// <summary>
+ /// Performs discovery on the specified identifier.
+ /// </summary>
+ /// <param name="identifier">The identifier to perform discovery on.</param>
+ /// <param name="requestHandler">The means to place outgoing HTTP requests.</param>
+ /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param>
+ /// <returns>
+ /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
+ /// </returns>
+ public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) {
+ abortDiscoveryChain = false;
+ var xriIdentifier = identifier as XriIdentifier;
+ if (xriIdentifier == null) {
+ return Enumerable.Empty<IdentifierDiscoveryResult>();
+ }
+
+ return DownloadXrds(xriIdentifier, requestHandler).XrdElements.CreateServiceEndpoints(xriIdentifier);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Downloads the XRDS document for this XRI.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <returns>The XRDS document.</returns>
+ private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) {
+ Contract.Requires<ArgumentNullException>(identifier != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Ensures(Contract.Result<XrdsDocument>() != null);
+ XrdsDocument doc;
+ using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) {
+ doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));
+ }
+ ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed);
+ return doc;
+ }
+
+ /// <summary>
+ /// Gets the URL from which this XRI's XRDS document may be downloaded.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>The URI to HTTP GET from to get the services.</returns>
+ private static Uri GetXrdsUrl(XriIdentifier identifier) {
+ ErrorUtilities.VerifyProtocol(DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Enabled, OpenIdStrings.XriResolutionDisabled);
+ string xriResolverProxy = XriResolverProxyTemplate;
+ if (identifier.IsDiscoverySecureEndToEnd) {
+ // Indicate to xri.net that we require SSL to be used for delegated resolution
+ // of community i-names.
+ xriResolverProxy += ";https=true";
+ }
+
+ return new Uri(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ xriResolverProxy,
+ identifier,
+ DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Proxy.Name));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
index c659982..729f603 100644
--- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
@@ -35,23 +35,6 @@ namespace DotNetOpenAuth.OpenId {
private const string XriScheme = "xri://";
/// <summary>
- /// The magic URL that will provide us an XRDS document for a given XRI identifier.
- /// </summary>
- /// <remarks>
- /// We use application/xrd+xml instead of application/xrds+xml because it gets
- /// xri.net to automatically give us exactly the right XRD element for community i-names
- /// automatically, saving us having to choose which one to use out of the result.
- /// The ssl=true parameter tells the proxy resolver to accept only SSL connections
- /// when resolving community i-names.
- /// </remarks>
- private const string XriResolverProxyTemplate = "https://{1}/{0}?_xrd_r=application/xrd%2Bxml;sep=false";
-
- /// <summary>
- /// The XRI proxy resolver to use for finding XRDS documents from an XRI.
- /// </summary>
- private readonly string xriResolverProxy;
-
- /// <summary>
/// Backing store for the <see cref="CanonicalXri"/> property.
/// </summary>
private readonly string canonicalXri;
@@ -62,7 +45,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="xri">The string value of the XRI.</param>
internal XriIdentifier(string xri)
: this(xri, false) {
- Contract.Requires((xri != null && xri.Length > 0) || !string.IsNullOrEmpty(xri));
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(xri));
+ Contract.Requires<FormatException>(IsValidXri(xri), OpenIdStrings.InvalidXri);
}
/// <summary>
@@ -74,16 +58,10 @@ namespace DotNetOpenAuth.OpenId {
/// only succeed if it can be done entirely using SSL.
/// </param>
internal XriIdentifier(string xri, bool requireSsl)
- : base(requireSsl) {
- Contract.Requires((xri != null && xri.Length > 0) || !string.IsNullOrEmpty(xri));
- ErrorUtilities.VerifyFormat(IsValidXri(xri), OpenIdStrings.InvalidXri, xri);
+ : base(xri, requireSsl) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(xri));
+ Contract.Requires<FormatException>(IsValidXri(xri), OpenIdStrings.InvalidXri);
Contract.Assume(xri != null); // Proven by IsValidXri
- this.xriResolverProxy = XriResolverProxyTemplate;
- if (requireSsl) {
- // Indicate to xri.net that we require SSL to be used for delegated resolution
- // of community i-names.
- this.xriResolverProxy += ";https=true";
- }
this.OriginalXri = xri;
this.canonicalXri = CanonicalizeXri(xri);
}
@@ -104,21 +82,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Gets the URL from which this XRI's XRDS document may be downloaded.
- /// </summary>
- private Uri XrdsUrl {
- get {
- ErrorUtilities.VerifyProtocol(DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Enabled, OpenIdStrings.XriResolutionDisabled);
- return new Uri(
- string.Format(
- CultureInfo.InvariantCulture,
- this.xriResolverProxy,
- this,
- DotNetOpenAuthSection.Configuration.OpenId.XriResolver.Proxy.Name));
- }
- }
-
- /// <summary>
/// Tests equality between this XRI and another XRI.
/// </summary>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
@@ -131,7 +94,9 @@ namespace DotNetOpenAuth.OpenId {
public override bool Equals(object obj) {
XriIdentifier other = obj as XriIdentifier;
if (obj != null && other == null && Identifier.EqualityOnStrings) { // test hook to enable MockIdentifier comparison
- other = Identifier.Parse(obj.ToString()) as XriIdentifier;
+ string objString = obj.ToString();
+ ErrorUtilities.VerifyInternal(!string.IsNullOrEmpty(objString), "Identifier.ToString() returned a null or empty string.");
+ other = Identifier.Parse(objString) as XriIdentifier;
}
if (other == null) {
return false;
@@ -167,8 +132,7 @@ namespace DotNetOpenAuth.OpenId {
/// <c>true</c> if the given string constitutes a valid XRI; otherwise, <c>false</c>.
/// </returns>
internal static bool IsValidXri(string xri) {
- Contract.Requires((xri != null && xri.Length > 0) || !string.IsNullOrEmpty(xri));
- ErrorUtilities.VerifyNonZeroLength(xri, "xri");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(xri));
xri = xri.Trim();
// TODO: better validation code here
@@ -178,30 +142,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Performs discovery on the Identifier.
- /// </summary>
- /// <param name="requestHandler">The web request handler to use for discovery.</param>
- /// <returns>
- /// An initialized structure containing the discovered provider endpoint information.
- /// </returns>
- internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) {
- return this.DownloadXrds(requestHandler).CreateServiceEndpoints(this);
- }
-
- /// <summary>
- /// Performs discovery on THIS identifier, but generates <see cref="ServiceEndpoint"/>
- /// instances that treat another given identifier as the user-supplied identifier.
- /// </summary>
- /// <param name="requestHandler">The request handler to use in discovery.</param>
- /// <param name="userSuppliedIdentifier">The user supplied identifier, which may differ from this XRI instance due to multiple discovery steps.</param>
- /// <returns>A list of service endpoints offered for this identifier.</returns>
- internal IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler, XriIdentifier userSuppliedIdentifier) {
- Contract.Requires(requestHandler != null);
- Contract.Requires(userSuppliedIdentifier != null);
- return this.DownloadXrds(requestHandler).CreateServiceEndpoints(userSuppliedIdentifier);
- }
-
- /// <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.
@@ -243,7 +183,7 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>The canonicalized form of the XRI.</returns>
/// <remarks>The canonical form, per the OpenID spec, is no scheme and no whitespace on either end.</remarks>
private static string CanonicalizeXri(string xri) {
- Contract.Requires(xri != null);
+ Contract.Requires<ArgumentNullException>(xri != null);
Contract.Ensures(Contract.Result<string>() != null);
xri = xri.Trim();
if (xri.StartsWith(XriScheme, StringComparison.OrdinalIgnoreCase)) {
@@ -253,30 +193,14 @@ namespace DotNetOpenAuth.OpenId {
return xri;
}
- /// <summary>
- /// Downloads the XRDS document for this XRI.
- /// </summary>
- /// <param name="requestHandler">The request handler.</param>
- /// <returns>The XRDS document.</returns>
- private XrdsDocument DownloadXrds(IDirectWebRequestHandler requestHandler) {
- Contract.Requires(requestHandler != null);
- Contract.Ensures(Contract.Result<XrdsDocument>() != null);
- XrdsDocument doc;
- using (var xrdsResponse = Yadis.Request(requestHandler, this.XrdsUrl, this.IsDiscoverySecureEndToEnd)) {
- doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));
- }
- ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed);
- return doc;
- }
-
#if CONTRACTS_FULL
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
[ContractInvariantMethod]
private void ObjectInvariant() {
- Contract.Invariant(this.xriResolverProxy != null);
Contract.Invariant(this.canonicalXri != null);
}
#endif
diff --git a/src/DotNetOpenAuth/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth/Properties/AssemblyInfo.cs
index 69d4dc4..0bba853 100644
--- a/src/DotNetOpenAuth/Properties/AssemblyInfo.cs
+++ b/src/DotNetOpenAuth/Properties/AssemblyInfo.cs
@@ -58,7 +58,7 @@ using System.Web.UI;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7d73990c-47c0-4256-9f20-a893add9e289")]
-[assembly: ContractVerification(false)]
+[assembly: ContractVerification(true)]
#if StrongNameSigned
// See comment at top of this file. We need this so that strong-naming doesn't
@@ -83,13 +83,15 @@ using System.Web.UI;
// match the one used by hosting providers. Listing them individually seems to be more common.
[assembly: WebPermission(SecurityAction.RequestMinimum, ConnectPattern = @"http://.*")]
[assembly: WebPermission(SecurityAction.RequestMinimum, ConnectPattern = @"https://.*")]
-
#if PARTIAL_TRUST
// Allows hosting this assembly in an ASP.NET setting. Not all applications
// will host this using ASP.NET, so this is optional. Besides, we need at least
// one optional permission to activate CAS permission shrinking.
[assembly: AspNetHostingPermission(SecurityAction.RequestOptional, Level = AspNetHostingPermissionLevel.Medium)]
+// Allows this assembly to store reporting data.
+[assembly: IsolatedStorageFilePermission(SecurityAction.RequestOptional, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByUser)]
+
// The following are only required for diagnostic logging (Trace.Write, Debug.Assert, etc.).
#if TRACE || DEBUG
[assembly: KeyContainerPermission(SecurityAction.RequestOptional, Unrestricted = true)]
diff --git a/src/DotNetOpenAuth/Reporting.cs b/src/DotNetOpenAuth/Reporting.cs
new file mode 100644
index 0000000..2235986
--- /dev/null
+++ b/src/DotNetOpenAuth/Reporting.cs
@@ -0,0 +1,847 @@
+//-----------------------------------------------------------------------
+// <copyright file="Reporting.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.IO;
+ using System.IO.IsolatedStorage;
+ using System.Linq;
+ using System.Net;
+ using System.Reflection;
+ using System.Security;
+ using System.Text;
+ using System.Threading;
+ using System.Web;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// The statistical reporting mechanism used so this library's project authors
+ /// know what versions and features are in use.
+ /// </summary>
+ internal static class Reporting {
+ /// <summary>
+ /// The isolated storage to use for collecting data in between published reports.
+ /// </summary>
+ private static IsolatedStorageFile file;
+
+ /// <summary>
+ /// The GUID that shows up at the top of all reports from this user/machine/domain.
+ /// </summary>
+ private static Guid reportOriginIdentity;
+
+ /// <summary>
+ /// The recipient of collected reports.
+ /// </summary>
+ private static Uri wellKnownPostLocation = new Uri("https://reports.dotnetopenauth.net/ReportingPost.ashx");
+
+ /// <summary>
+ /// The outgoing HTTP request handler to use for publishing reports.
+ /// </summary>
+ private static IDirectWebRequestHandler webRequestHandler;
+
+ /// <summary>
+ /// A few HTTP request hosts and paths we've seen.
+ /// </summary>
+ private static PersistentHashSet observedRequests;
+
+ /// <summary>
+ /// Cultures that have come in via HTTP requests.
+ /// </summary>
+ private static PersistentHashSet observedCultures;
+
+ /// <summary>
+ /// Features that have been used.
+ /// </summary>
+ private static PersistentHashSet observedFeatures;
+
+ /// <summary>
+ /// A collection of all the observations to include in the report.
+ /// </summary>
+ private static List<PersistentHashSet> observations = new List<PersistentHashSet>();
+
+ /// <summary>
+ /// The named events that we have counters for.
+ /// </summary>
+ private static Dictionary<string, PersistentCounter> events = new Dictionary<string, PersistentCounter>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// The lock acquired while considering whether to publish a report.
+ /// </summary>
+ private static object publishingConsiderationLock = new object();
+
+ /// <summary>
+ /// The time that we last published reports.
+ /// </summary>
+ private static DateTime lastPublished = DateTime.Now;
+
+ /// <summary>
+ /// Initializes static members of the <see cref="Reporting"/> class.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "We do more than field initialization here.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Reporting MUST NOT cause unhandled exceptions.")]
+ static Reporting() {
+ Enabled = DotNetOpenAuthSection.Configuration.Reporting.Enabled;
+ if (Enabled) {
+ try {
+ file = GetIsolatedStorage();
+ reportOriginIdentity = GetOrCreateOriginIdentity();
+
+ webRequestHandler = new StandardWebRequestHandler();
+ observations.Add(observedRequests = new PersistentHashSet(file, "requests.txt", 3));
+ observations.Add(observedCultures = new PersistentHashSet(file, "cultures.txt", 20));
+ observations.Add(observedFeatures = new PersistentHashSet(file, "features.txt", int.MaxValue));
+
+ // Record site-wide features in use.
+ if (HttpContext.Current != null && HttpContext.Current.ApplicationInstance != null) {
+ // MVC or web forms?
+ // front-end or back end web farm?
+ // url rewriting?
+ ////RecordFeatureUse(IsMVC ? "ASP.NET MVC" : "ASP.NET Web Forms");
+ }
+ } catch (Exception e) {
+ // This is supposed to be as low-risk as possible, so if it fails, just disable reporting
+ // and avoid rethrowing.
+ Enabled = false;
+ Logger.Library.Error("Error while trying to initialize reporting.", e);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this reporting is enabled.
+ /// </summary>
+ /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
+ internal static bool Enabled { get; set; }
+
+ /// <summary>
+ /// Gets the configuration to use for reporting.
+ /// </summary>
+ private static ReportingElement Configuration {
+ get { return DotNetOpenAuthSection.Configuration.Reporting; }
+ }
+
+ /// <summary>
+ /// Records an event occurrence.
+ /// </summary>
+ /// <param name="eventName">Name of the event.</param>
+ /// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param>
+ internal static void RecordEventOccurrence(string eventName, string category) {
+ Contract.Requires(!String.IsNullOrEmpty(eventName));
+
+ // In release builds, just quietly return.
+ if (string.IsNullOrEmpty(eventName)) {
+ return;
+ }
+
+ if (Enabled && Configuration.IncludeEventStatistics) {
+ PersistentCounter counter;
+ lock (events) {
+ if (!events.TryGetValue(eventName, out counter)) {
+ events[eventName] = counter = new PersistentCounter(file, "event-" + SanitizeFileName(eventName) + ".txt");
+ }
+ }
+
+ counter.Increment(category);
+ Touch();
+ }
+ }
+
+ /// <summary>
+ /// Records an event occurence.
+ /// </summary>
+ /// <param name="eventNameByObjectType">The object whose type name is the event name to record.</param>
+ /// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param>
+ internal static void RecordEventOccurrence(object eventNameByObjectType, string category) {
+ Contract.Requires(eventNameByObjectType != null);
+
+ // In release builds, just quietly return.
+ if (eventNameByObjectType == null) {
+ return;
+ }
+
+ if (Enabled && Configuration.IncludeEventStatistics) {
+ RecordEventOccurrence(eventNameByObjectType.GetType().Name, category);
+ }
+ }
+
+ /// <summary>
+ /// Records the use of a feature by name.
+ /// </summary>
+ /// <param name="feature">The feature.</param>
+ internal static void RecordFeatureUse(string feature) {
+ Contract.Requires(!String.IsNullOrEmpty(feature));
+
+ // In release builds, just quietly return.
+ if (string.IsNullOrEmpty(feature)) {
+ return;
+ }
+
+ if (Enabled && Configuration.IncludeFeatureUsage) {
+ observedFeatures.Add(feature);
+ Touch();
+ }
+ }
+
+ /// <summary>
+ /// Records the use of a feature by object type.
+ /// </summary>
+ /// <param name="value">The object whose type is the feature to set as used.</param>
+ internal static void RecordFeatureUse(object value) {
+ Contract.Requires(value != null);
+
+ // In release builds, just quietly return.
+ if (value == null) {
+ return;
+ }
+
+ if (Enabled && Configuration.IncludeFeatureUsage) {
+ observedFeatures.Add(value.GetType().Name);
+ Touch();
+ }
+ }
+
+ /// <summary>
+ /// Records the use of a feature by object type.
+ /// </summary>
+ /// <param name="value">The object whose type is the feature to set as used.</param>
+ /// <param name="dependency1">Some dependency used by <paramref name="value"/>.</param>
+ /// <param name="dependency2">Some dependency used by <paramref name="value"/>.</param>
+ internal static void RecordFeatureAndDependencyUse(object value, object dependency1, object dependency2) {
+ Contract.Requires(value != null);
+
+ // In release builds, just quietly return.
+ if (value == null) {
+ return;
+ }
+
+ if (Enabled && Configuration.IncludeFeatureUsage) {
+ StringBuilder builder = new StringBuilder();
+ builder.Append(value.GetType().Name);
+ builder.Append(" ");
+ builder.Append(dependency1 != null ? dependency1.GetType().Name : "(null)");
+ builder.Append(" ");
+ builder.Append(dependency2 != null ? dependency2.GetType().Name : "(null)");
+ observedFeatures.Add(builder.ToString());
+ Touch();
+ }
+ }
+
+ /// <summary>
+ /// Records the feature and dependency use.
+ /// </summary>
+ /// <param name="value">The consumer or service provider.</param>
+ /// <param name="service">The service.</param>
+ /// <param name="tokenManager">The token manager.</param>
+ /// <param name="nonceStore">The nonce store.</param>
+ internal static void RecordFeatureAndDependencyUse(object value, ServiceProviderDescription service, ITokenManager tokenManager, INonceStore nonceStore) {
+ Contract.Requires(value != null);
+ Contract.Requires(service != null);
+ Contract.Requires(tokenManager != null);
+
+ // In release builds, just quietly return.
+ if (value == null || service == null || tokenManager == null) {
+ return;
+ }
+
+ if (Enabled && Configuration.IncludeFeatureUsage) {
+ StringBuilder builder = new StringBuilder();
+ builder.Append(value.GetType().Name);
+ builder.Append(" ");
+ builder.Append(tokenManager.GetType().Name);
+ if (nonceStore != null) {
+ builder.Append(" ");
+ builder.Append(nonceStore.GetType().Name);
+ }
+ builder.Append(" ");
+ builder.Append(service.Version);
+ builder.Append(" ");
+ builder.Append(service.UserAuthorizationEndpoint);
+ observedFeatures.Add(builder.ToString());
+ Touch();
+ }
+ }
+
+ /// <summary>
+ /// Records statistics collected from incoming requests.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal static void RecordRequestStatistics(HttpRequestInfo request) {
+ Contract.Requires(request != null);
+
+ // In release builds, just quietly return.
+ if (request == null) {
+ return;
+ }
+
+ if (Enabled) {
+ if (Configuration.IncludeCultures) {
+ observedCultures.Add(Thread.CurrentThread.CurrentCulture.Name);
+ }
+
+ if (Configuration.IncludeLocalRequestUris && !observedRequests.IsFull) {
+ var requestBuilder = new UriBuilder(request.UrlBeforeRewriting);
+ requestBuilder.Query = null;
+ requestBuilder.Fragment = null;
+ observedRequests.Add(requestBuilder.Uri.AbsoluteUri);
+ }
+
+ Touch();
+ }
+ }
+
+ /// <summary>
+ /// Assembles a report for submission.
+ /// </summary>
+ /// <returns>A stream that contains the report.</returns>
+ private static Stream GetReport() {
+ var stream = new MemoryStream();
+ var writer = new StreamWriter(stream, Encoding.UTF8);
+ writer.WriteLine(reportOriginIdentity.ToString("B"));
+ writer.WriteLine(Util.LibraryVersion);
+ writer.WriteLine(".NET Framework {0}", Environment.Version);
+
+ foreach (var observation in observations) {
+ observation.Flush();
+ writer.WriteLine("====================================");
+ writer.WriteLine(observation.FileName);
+ try {
+ using (var fileStream = new IsolatedStorageFileStream(observation.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, file)) {
+ writer.Flush();
+ fileStream.CopyTo(writer.BaseStream);
+ }
+ } catch (FileNotFoundException) {
+ writer.WriteLine("(missing)");
+ }
+ }
+
+ // Not all event counters may have even loaded in this app instance.
+ // We flush the ones in memory, and then read all of them off disk.
+ foreach (var counter in events.Values) {
+ counter.Flush();
+ }
+
+ foreach (string eventFile in file.GetFileNames("event-*.txt")) {
+ writer.WriteLine("====================================");
+ writer.WriteLine(eventFile);
+ using (var fileStream = new IsolatedStorageFileStream(eventFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, file)) {
+ writer.Flush();
+ fileStream.CopyTo(writer.BaseStream);
+ }
+ }
+
+ // Make sure the stream is positioned at the beginning.
+ writer.Flush();
+ stream.Position = 0;
+ return stream;
+ }
+
+ /// <summary>
+ /// Sends the usage reports to the library authors.
+ /// </summary>
+ /// <returns>A value indicating whether submitting the report was successful.</returns>
+ private static bool SendStats() {
+ try {
+ var request = (HttpWebRequest)WebRequest.Create(wellKnownPostLocation);
+ request.UserAgent = Util.LibraryVersion;
+ request.AllowAutoRedirect = false;
+ request.Method = "POST";
+ request.ContentType = "text/dnoa-report1";
+ Stream report = GetReport();
+ request.ContentLength = report.Length;
+ using (var requestStream = webRequestHandler.GetRequestStream(request)) {
+ report.CopyTo(requestStream);
+ }
+
+ using (var response = webRequestHandler.GetResponse(request)) {
+ Logger.Library.Info("Statistical report submitted successfully.");
+
+ // The response stream may contain a message for the webmaster.
+ // Since as part of the report we submit the library version number,
+ // the report receiving service may have alerts such as:
+ // "You're using an obsolete version with exploitable security vulnerabilities."
+ using (var responseReader = response.GetResponseReader()) {
+ string line = responseReader.ReadLine();
+ if (line != null) {
+ DemuxLogMessage(line);
+ }
+ }
+ }
+
+ // Report submission was successful. Reset all counters.
+ lock (events) {
+ foreach (PersistentCounter counter in events.Values) {
+ counter.Reset();
+ counter.Flush();
+ }
+
+ // We can just delete the files for counters that are not currently loaded.
+ foreach (string eventFile in file.GetFileNames("event-*.txt")) {
+ if (!events.Values.Any(e => string.Equals(e.FileName, eventFile, StringComparison.OrdinalIgnoreCase))) {
+ file.DeleteFile(eventFile);
+ }
+ }
+ }
+
+ return true;
+ } catch (ProtocolException ex) {
+ Logger.Library.Error("Unable to submit statistical report due to an HTTP error.", ex);
+ } catch (FileNotFoundException ex) {
+ Logger.Library.Error("Unable to submit statistical report because the report file is missing.", ex);
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Interprets the reporting response as a log message if possible.
+ /// </summary>
+ /// <param name="line">The line from the HTTP response to interpret as a log message.</param>
+ private static void DemuxLogMessage(string line) {
+ if (line != null) {
+ string[] parts = line.Split(new char[] { ' ' }, 2);
+ if (parts.Length == 2) {
+ string level = parts[0];
+ string message = parts[1];
+ switch (level) {
+ case "INFO":
+ Logger.Library.Info(message);
+ break;
+ case "WARN":
+ Logger.Library.Warn(message);
+ break;
+ case "ERROR":
+ Logger.Library.Error(message);
+ break;
+ case "FATAL":
+ Logger.Library.Fatal(message);
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Called by every internal/public method on this class to give
+ /// periodic operations a chance to run.
+ /// </summary>
+ private static void Touch() {
+ // Publish stats if it's time to do so.
+ lock (publishingConsiderationLock) {
+ if (DateTime.Now - lastPublished > Configuration.MinimumReportingInterval) {
+ lastPublished = DateTime.Now;
+ SendStatsAsync();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sends the stats report asynchronously, and careful to not throw any unhandled exceptions.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Unhandled exceptions MUST NOT be thrown from here.")]
+ private static void SendStatsAsync() {
+ // Do it on a background thread since it could take a while and we
+ // don't want to slow down this request we're borrowing.
+ ThreadPool.QueueUserWorkItem(state => {
+ try {
+ SendStats();
+ } catch (Exception ex) {
+ // Something bad and unexpected happened. Just deactivate to avoid more trouble.
+ Logger.Library.Error("Error while trying to submit statistical report.", ex);
+ Enabled = false;
+ }
+ });
+ }
+
+ /// <summary>
+ /// Gets the isolated storage to use for reporting.
+ /// </summary>
+ /// <returns>An isolated storage location appropriate for our host.</returns>
+ private static IsolatedStorageFile GetIsolatedStorage() {
+ Contract.Ensures(Contract.Result<IsolatedStorageFile>() != null);
+
+ IsolatedStorageFile result = null;
+
+ // We'll try for whatever storage location we can get,
+ // and not catch exceptions from the last attempt so that
+ // the overall failure is caught by our caller.
+ try {
+ // This works on Personal Web Server
+ result = IsolatedStorageFile.GetUserStoreForDomain();
+ } catch (SecurityException) {
+ } catch (IsolatedStorageException) {
+ }
+
+ // This works on IIS when full trust is granted.
+ if (result == null) {
+ result = IsolatedStorageFile.GetMachineStoreForDomain();
+ }
+
+ Logger.Library.InfoFormat("Reporting will use isolated storage with scope: {0}", result.Scope);
+ return result;
+ }
+
+ /// <summary>
+ /// Gets a unique, pseudonymous identifier for this particular web site or application.
+ /// </summary>
+ /// <returns>A GUID that will serve as the identifier.</returns>
+ /// <remarks>
+ /// The identifier is made persistent by storing the identifier in isolated storage.
+ /// If an existing identifier is not found, a new one is created, persisted, and returned.
+ /// </remarks>
+ private static Guid GetOrCreateOriginIdentity() {
+ Contract.Requires<InvalidOperationException>(file != null);
+ Contract.Ensures(Contract.Result<Guid>() != Guid.Empty);
+
+ Guid identityGuid = Guid.Empty;
+ const int GuidLength = 16;
+ using (var identityFileStream = new IsolatedStorageFileStream("identity.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, file)) {
+ if (identityFileStream.Length == GuidLength) {
+ byte[] guidBytes = new byte[GuidLength];
+ if (identityFileStream.Read(guidBytes, 0, GuidLength) == GuidLength) {
+ identityGuid = new Guid(guidBytes);
+ }
+ }
+
+ if (identityGuid == Guid.Empty) {
+ identityGuid = Guid.NewGuid();
+ byte[] guidBytes = identityGuid.ToByteArray();
+ identityFileStream.SetLength(0);
+ identityFileStream.Write(guidBytes, 0, guidBytes.Length);
+ }
+
+ return identityGuid;
+ }
+ }
+
+ /// <summary>
+ /// Sanitizes the name of the file so it only includes valid filename characters.
+ /// </summary>
+ /// <param name="fileName">The filename to sanitize.</param>
+ /// <returns>The filename, with any and all invalid filename characters replaced with the hyphen (-) character.</returns>
+ private static string SanitizeFileName(string fileName) {
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(fileName));
+ char[] invalidCharacters = Path.GetInvalidFileNameChars();
+ if (fileName.IndexOfAny(invalidCharacters) < 0) {
+ return fileName; // nothing invalid about this filename.
+ }
+
+ // Use a stringbuilder since we may be replacing several characters
+ // and we don't want to instantiate a new string buffer for each new version.
+ StringBuilder sanitized = new StringBuilder(fileName);
+ foreach (char invalidChar in invalidCharacters) {
+ sanitized.Replace(invalidChar, '-');
+ }
+
+ return sanitized.ToString();
+ }
+
+ /// <summary>
+ /// A set of values that persist the set to disk.
+ /// </summary>
+ private class PersistentHashSet : IDisposable {
+ /// <summary>
+ /// The isolated persistent storage.
+ /// </summary>
+ private readonly FileStream fileStream;
+
+ /// <summary>
+ /// The persistent reader.
+ /// </summary>
+ private readonly StreamReader reader;
+
+ /// <summary>
+ /// The persistent writer.
+ /// </summary>
+ private readonly StreamWriter writer;
+
+ /// <summary>
+ /// The total set of elements.
+ /// </summary>
+ private readonly HashSet<string> memorySet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// The maximum number of elements to track before not storing new elements.
+ /// </summary>
+ private readonly int maximumElements;
+
+ /// <summary>
+ /// The set of new elements added to the <see cref="memorySet"/> since the last flush.
+ /// </summary>
+ private List<string> newElements = new List<string>();
+
+ /// <summary>
+ /// The time the last flush occurred.
+ /// </summary>
+ private DateTime lastFlushed;
+
+ /// <summary>
+ /// A flag indicating whether the set has changed since it was last flushed.
+ /// </summary>
+ private bool dirty;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PersistentHashSet"/> class.
+ /// </summary>
+ /// <param name="storage">The storage location.</param>
+ /// <param name="fileName">Name of the file.</param>
+ /// <param name="maximumElements">The maximum number of elements to track.</param>
+ internal PersistentHashSet(IsolatedStorageFile storage, string fileName, int maximumElements) {
+ Contract.Requires<ArgumentNullException>(storage != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(fileName));
+ this.FileName = fileName;
+ this.maximumElements = maximumElements;
+
+ // Load the file into memory.
+ this.fileStream = new IsolatedStorageFileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, storage);
+ this.reader = new StreamReader(this.fileStream, Encoding.UTF8);
+ while (!this.reader.EndOfStream) {
+ this.memorySet.Add(this.reader.ReadLine());
+ }
+
+ this.writer = new StreamWriter(this.fileStream, Encoding.UTF8);
+ this.lastFlushed = DateTime.Now;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the hashset has reached capacity and is not storing more elements.
+ /// </summary>
+ /// <value><c>true</c> if this instance is full; otherwise, <c>false</c>.</value>
+ internal bool IsFull {
+ get {
+ lock (this.memorySet) {
+ return this.memorySet.Count >= this.maximumElements;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the name of the file.
+ /// </summary>
+ /// <value>The name of the file.</value>
+ internal string FileName { get; private set; }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose() {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Adds a value to the set.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ internal void Add(string value) {
+ lock (this.memorySet) {
+ if (!this.IsFull) {
+ if (this.memorySet.Add(value)) {
+ this.newElements.Add(value);
+ this.dirty = true;
+
+ if (this.IsFull) {
+ this.Flush();
+ }
+ }
+
+ if (this.dirty && DateTime.Now - this.lastFlushed > Configuration.MinimumFlushInterval) {
+ this.Flush();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Flushes any newly added values to disk.
+ /// </summary>
+ internal void Flush() {
+ lock (this.memorySet) {
+ foreach (string element in this.newElements) {
+ this.writer.WriteLine(element);
+ }
+ this.writer.Flush();
+
+ // Assign a whole new list since future lists might be smaller in order to
+ // decrease demand on memory.
+ this.newElements = new List<string>();
+ this.dirty = false;
+ this.lastFlushed = DateTime.Now;
+ }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ this.writer.Dispose();
+ this.reader.Dispose();
+ this.fileStream.Dispose();
+ }
+ }
+ }
+
+ /// <summary>
+ /// A feature usage counter.
+ /// </summary>
+ private class PersistentCounter : IDisposable {
+ /// <summary>
+ /// The separator to use between category names and their individual counters.
+ /// </summary>
+ private static readonly char[] separator = new char[] { '\t' };
+
+ /// <summary>
+ /// The isolated persistent storage.
+ /// </summary>
+ private readonly FileStream fileStream;
+
+ /// <summary>
+ /// The persistent reader.
+ /// </summary>
+ private readonly StreamReader reader;
+
+ /// <summary>
+ /// The persistent writer.
+ /// </summary>
+ private readonly StreamWriter writer;
+
+ /// <summary>
+ /// The time the last flush occurred.
+ /// </summary>
+ private DateTime lastFlushed;
+
+ /// <summary>
+ /// The in-memory copy of the counter.
+ /// </summary>
+ private Dictionary<string, int> counters = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// A flag indicating whether the set has changed since it was last flushed.
+ /// </summary>
+ private bool dirty;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PersistentCounter"/> class.
+ /// </summary>
+ /// <param name="storage">The storage location.</param>
+ /// <param name="fileName">Name of the file.</param>
+ internal PersistentCounter(IsolatedStorageFile storage, string fileName) {
+ Contract.Requires<ArgumentNullException>(storage != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(fileName));
+ this.FileName = fileName;
+
+ // Load the file into memory.
+ this.fileStream = new IsolatedStorageFileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, storage);
+ this.reader = new StreamReader(this.fileStream, Encoding.UTF8);
+ while (!this.reader.EndOfStream) {
+ string line = this.reader.ReadLine();
+ string[] parts = line.Split(separator, 2);
+ int counter;
+ if (int.TryParse(parts[0], out counter)) {
+ string category = string.Empty;
+ if (parts.Length > 1) {
+ category = parts[1];
+ }
+ this.counters[category] = counter;
+ }
+ }
+
+ this.writer = new StreamWriter(this.fileStream, Encoding.UTF8);
+ this.lastFlushed = DateTime.Now;
+ }
+
+ /// <summary>
+ /// Gets the name of the file.
+ /// </summary>
+ /// <value>The name of the file.</value>
+ internal string FileName { get; private set; }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose() {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Increments the counter.
+ /// </summary>
+ /// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param>
+ internal void Increment(string category) {
+ if (category == null) {
+ category = string.Empty;
+ }
+ lock (this) {
+ int counter;
+ this.counters.TryGetValue(category, out counter);
+ this.counters[category] = counter + 1;
+ this.dirty = true;
+ if (this.dirty && DateTime.Now - this.lastFlushed > Configuration.MinimumFlushInterval) {
+ this.Flush();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Flushes any newly added values to disk.
+ /// </summary>
+ internal void Flush() {
+ lock (this) {
+ this.writer.BaseStream.Position = 0;
+ this.writer.BaseStream.SetLength(0); // truncate file
+ foreach (var pair in this.counters) {
+ this.writer.Write(pair.Value);
+ this.writer.Write(separator[0]);
+ this.writer.WriteLine(pair.Key);
+ }
+ this.writer.Flush();
+ this.dirty = false;
+ this.lastFlushed = DateTime.Now;
+ }
+ }
+
+ /// <summary>
+ /// Resets all counters.
+ /// </summary>
+ internal void Reset() {
+ lock (this) {
+ this.counters.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ this.writer.Dispose();
+ this.reader.Dispose();
+ this.fileStream.Dispose();
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Strings.Designer.cs b/src/DotNetOpenAuth/Strings.Designer.cs
index 43fec22..38c89f7 100644
--- a/src/DotNetOpenAuth/Strings.Designer.cs
+++ b/src/DotNetOpenAuth/Strings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.4918
+// Runtime Version:2.0.50727.4927
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -70,6 +70,15 @@ namespace DotNetOpenAuth {
}
/// <summary>
+ /// Looks up a localized string similar to No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}..
+ /// </summary>
+ internal static string StoreRequiredWhenNoHttpContextAvailable {
+ get {
+ return ResourceManager.GetString("StoreRequiredWhenNoHttpContextAvailable", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The configuration XAML reference to {0} requires a current HttpContext to resolve..
/// </summary>
internal static string ConfigurationXamlReferenceRequiresHttpContext {
diff --git a/src/DotNetOpenAuth/Strings.resx b/src/DotNetOpenAuth/Strings.resx
index bbfa162..a7f080d 100644
--- a/src/DotNetOpenAuth/Strings.resx
+++ b/src/DotNetOpenAuth/Strings.resx
@@ -120,7 +120,10 @@
<data name="ConfigurationTypeMustBePublic" xml:space="preserve">
<value>The configuration-specified type {0} must be public, and is not.</value>
</data>
+ <data name="StoreRequiredWhenNoHttpContextAvailable" xml:space="preserve">
+ <value>No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}.</value>
+ </data>
<data name="ConfigurationXamlReferenceRequiresHttpContext" xml:space="preserve">
<value>The configuration XAML reference to {0} requires a current HttpContext to resolve.</value>
</data>
-</root> \ No newline at end of file
+</root>
diff --git a/src/DotNetOpenAuth/Strings.sr.resx b/src/DotNetOpenAuth/Strings.sr.resx
new file mode 100644
index 0000000..5112265
--- /dev/null
+++ b/src/DotNetOpenAuth/Strings.sr.resx
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ConfigurationTypeMustBePublic" xml:space="preserve">
+ <value>Konfiguraciono zavisni tip {0} mora biti javan, a on to nije.</value>
+ </data>
+ <data name="ConfigurationXamlReferenceRequiresHttpContext" xml:space="preserve">
+ <value>Konfiguraciona XAML referenca na {0} zahteva da se trenutni HttpContext razreši.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/UriUtil.cs b/src/DotNetOpenAuth/UriUtil.cs
index d04491d..819c406 100644
--- a/src/DotNetOpenAuth/UriUtil.cs
+++ b/src/DotNetOpenAuth/UriUtil.cs
@@ -30,8 +30,7 @@ namespace DotNetOpenAuth {
/// </returns>
[ContractVerification(false)] // bugs/limitations in CC static analysis
internal static bool QueryStringContainPrefixedParameters(this Uri uri, string prefix) {
- Contract.Requires(prefix != null && prefix.Length > 0);
- ErrorUtilities.VerifyNonZeroLength(prefix, "prefix");
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(prefix));
if (uri == null) {
return false;
}
@@ -49,10 +48,7 @@ namespace DotNetOpenAuth {
/// <c>true</c> if the URI represents an encrypted request; otherwise, <c>false</c>.
/// </returns>
internal static bool IsTransportSecure(this Uri uri) {
- if (uri == null) {
- throw new ArgumentNullException("uri");
- }
-
+ Contract.Requires<ArgumentNullException>(uri != null);
return string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase);
}
@@ -63,9 +59,8 @@ namespace DotNetOpenAuth {
/// <param name="builder">The UriBuilder to render as a string.</param>
/// <returns>The string version of the Uri.</returns>
internal static string ToStringWithImpliedPorts(this UriBuilder builder) {
- Contract.Requires(builder != null);
+ Contract.Requires<ArgumentNullException>(builder != null);
Contract.Ensures(Contract.Result<string>() != null);
- ErrorUtilities.VerifyArgumentNotNull(builder, "builder");
// We only check for implied ports on HTTP and HTTPS schemes since those
// are the only ones supported by OpenID anyway.
diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs
index 1a67966..9f8b30c 100644
--- a/src/DotNetOpenAuth/Util.cs
+++ b/src/DotNetOpenAuth/Util.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth {
using System.Net;
using System.Reflection;
using System.Text;
+ using DotNetOpenAuth.Messaging;
/// <summary>
/// A grab-bag utility class.
@@ -74,7 +75,8 @@ namespace DotNetOpenAuth {
return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
pairs,
p => {
- Contract.Requires(pairs != null);
+ ////Contract.Requires(pairs != null); // CC: anonymous method can't handle it
+ ErrorUtilities.VerifyArgumentNotNull(pairs, "pairs");
var dictionary = pairs as IDictionary<K, V>;
StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
foreach (var pair in pairs) {
@@ -102,13 +104,16 @@ namespace DotNetOpenAuth {
/// <param name="list">The list of elements.</param>
/// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
/// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ [ContractVerification(false)]
internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
return new DelayedToString<IEnumerable<T>>(
list,
l => {
- Contract.Requires(l != null);
+ // Code contracts not allowed in generator methods.
+ ErrorUtilities.VerifyArgumentNotNull(l, "l");
+
string newLine = Environment.NewLine;
- Contract.Assume(newLine != null && newLine.Length > 0);
+ ////Contract.Assume(newLine != null && newLine.Length > 0);
StringBuilder sb = new StringBuilder();
if (multiLineElements) {
sb.AppendLine("[{");
@@ -169,7 +174,7 @@ namespace DotNetOpenAuth {
/// <param name="obj">The object that may be serialized to string form.</param>
/// <param name="toString">The method that will serialize the object if called upon.</param>
public DelayedToString(T obj, Func<T, string> toString) {
- Contract.Requires(toString != null);
+ Contract.Requires<ArgumentNullException>(toString != null);
this.obj = obj;
this.toString = toString;
diff --git a/src/DotNetOpenAuth/Xrds/TypeElement.cs b/src/DotNetOpenAuth/Xrds/TypeElement.cs
index f6c2217..c413629 100644
--- a/src/DotNetOpenAuth/Xrds/TypeElement.cs
+++ b/src/DotNetOpenAuth/Xrds/TypeElement.cs
@@ -5,6 +5,8 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Xrds {
+ using System;
+ using System.Diagnostics.Contracts;
using System.Xml.XPath;
/// <summary>
@@ -18,6 +20,8 @@ namespace DotNetOpenAuth.Xrds {
/// <param name="parent">The parent.</param>
public TypeElement(XPathNavigator typeElement, ServiceElement parent) :
base(typeElement, parent) {
+ Contract.Requires<ArgumentNullException>(typeElement != null);
+ Contract.Requires<ArgumentNullException>(parent != null);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Xrds/XrdElement.cs b/src/DotNetOpenAuth/Xrds/XrdElement.cs
index 72c5078..2cdc720 100644
--- a/src/DotNetOpenAuth/Xrds/XrdElement.cs
+++ b/src/DotNetOpenAuth/Xrds/XrdElement.cs
@@ -96,6 +96,13 @@ namespace DotNetOpenAuth.Xrds {
}
/// <summary>
+ /// Gets the services that would be discoverable at an RP for the UI extension icon.
+ /// </summary>
+ public IEnumerable<ServiceElement> OpenIdRelyingPartyIcons {
+ get { return this.SearchForServiceTypeUris(p => "http://specs.openid.net/extensions/ui/icon"); }
+ }
+
+ /// <summary>
/// Gets an enumeration of all Service/URI elements, sorted in priority order.
/// </summary>
public IEnumerable<UriElement> ServiceUris {
@@ -126,7 +133,7 @@ namespace DotNetOpenAuth.Xrds {
/// </summary>
/// <param name="p">A function that selects what element of the OpenID Protocol we're interested in finding.</param>
/// <returns>A sequence of service elements that match the search criteria, sorted in XRDS @priority attribute order.</returns>
- private IEnumerable<ServiceElement> SearchForServiceTypeUris(Func<Protocol, string> p) {
+ internal IEnumerable<ServiceElement> SearchForServiceTypeUris(Func<Protocol, string> p) {
var xpath = new StringBuilder();
xpath.Append("xrd:Service[");
foreach (var protocol in Protocol.AllVersions) {
diff --git a/src/DotNetOpenAuth/Xrds/XrdsDocument.cs b/src/DotNetOpenAuth/Xrds/XrdsDocument.cs
index dd0e19f..e2c2d72 100644
--- a/src/DotNetOpenAuth/Xrds/XrdsDocument.cs
+++ b/src/DotNetOpenAuth/Xrds/XrdsDocument.cs
@@ -18,6 +18,16 @@ namespace DotNetOpenAuth.Xrds {
/// </summary>
internal class XrdsDocument : XrdsNode {
/// <summary>
+ /// The namespace used by XML digital signatures.
+ /// </summary>
+ private const string XmlDSigNamespace = "http://www.w3.org/2000/09/xmldsig#";
+
+ /// <summary>
+ /// The namespace used by Google Apps for Domains for OpenID URI templates.
+ /// </summary>
+ private const string GoogleOpenIdNamespace = "http://namespace.google.com/openid/xmlns";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="XrdsDocument"/> class.
/// </summary>
/// <param name="xrdsNavigator">The root node of the XRDS document.</param>
@@ -26,6 +36,8 @@ namespace DotNetOpenAuth.Xrds {
XmlNamespaceResolver.AddNamespace("xrd", XrdsNode.XrdNamespace);
XmlNamespaceResolver.AddNamespace("xrds", XrdsNode.XrdsNamespace);
XmlNamespaceResolver.AddNamespace("openid10", Protocol.V10.XmlNamespace);
+ XmlNamespaceResolver.AddNamespace("ds", XmlDSigNamespace);
+ XmlNamespaceResolver.AddNamespace("google", GoogleOpenIdNamespace);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Xrds/XrdsNode.cs b/src/DotNetOpenAuth/Xrds/XrdsNode.cs
index e27a1b2..39bd9b9 100644
--- a/src/DotNetOpenAuth/Xrds/XrdsNode.cs
+++ b/src/DotNetOpenAuth/Xrds/XrdsNode.cs
@@ -5,6 +5,8 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Xrds {
+ using System;
+ using System.Diagnostics.Contracts;
using System.Xml;
using System.Xml.XPath;
using DotNetOpenAuth.Messaging;
@@ -29,8 +31,8 @@ namespace DotNetOpenAuth.Xrds {
/// <param name="node">The node represented by this instance.</param>
/// <param name="parentNode">The parent node.</param>
protected XrdsNode(XPathNavigator node, XrdsNode parentNode) {
- ErrorUtilities.VerifyArgumentNotNull(node, "node");
- ErrorUtilities.VerifyArgumentNotNull(parentNode, "parentNode");
+ Contract.Requires<ArgumentNullException>(node != null);
+ Contract.Requires<ArgumentNullException>(parentNode != null);
this.Node = node;
this.ParentNode = parentNode;
@@ -42,7 +44,8 @@ namespace DotNetOpenAuth.Xrds {
/// </summary>
/// <param name="document">The document's root node, which this instance represents.</param>
protected XrdsNode(XPathNavigator document) {
- ErrorUtilities.VerifyArgumentNotNull(document, "document");
+ Contract.Requires<ArgumentNullException>(document != null);
+ Contract.Requires<ArgumentException>(document.NameTable != null);
this.Node = document;
this.XmlNamespaceResolver = new XmlNamespaceManager(document.NameTable);
@@ -51,16 +54,16 @@ namespace DotNetOpenAuth.Xrds {
/// <summary>
/// Gets the node.
/// </summary>
- protected XPathNavigator Node { get; private set; }
+ internal XPathNavigator Node { get; private set; }
/// <summary>
/// Gets the parent node, or null if this is the root node.
/// </summary>
- protected XrdsNode ParentNode { get; private set; }
+ protected internal XrdsNode ParentNode { get; private set; }
/// <summary>
/// Gets the XML namespace resolver to use in XPath expressions.
/// </summary>
- protected XmlNamespaceManager XmlNamespaceResolver { get; private set; }
+ protected internal XmlNamespaceManager XmlNamespaceResolver { get; private set; }
}
}
diff --git a/src/DotNetOpenAuth/Xrds/XrdsStrings.sr.resx b/src/DotNetOpenAuth/Xrds/XrdsStrings.sr.resx
new file mode 100644
index 0000000..8e2d09a
--- /dev/null
+++ b/src/DotNetOpenAuth/Xrds/XrdsStrings.sr.resx
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="CIDVerificationFailed" xml:space="preserve">
+ <value>XRI CanonicalID provera neuspešna.</value>
+ </data>
+ <data name="InvalidXRDSDocument" xml:space="preserve">
+ <value>Greška u obradi XRDS dokumenta.</value>
+ </data>
+ <data name="MissingCanonicalIDElement" xml:space="preserve">
+ <value>XRDS dokumentu za XRI {0} nedostaje neophodni CanonicalID element.</value>
+ </data>
+ <data name="XriResolutionStatusMissing" xml:space="preserve">
+ <value>Ne može se pronaći XRI resolution Status tag ili je code attribute neispravan.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/XrdsPublisher.cs b/src/DotNetOpenAuth/XrdsPublisher.cs
index 332e3fa..e7c04d8 100644
--- a/src/DotNetOpenAuth/XrdsPublisher.cs
+++ b/src/DotNetOpenAuth/XrdsPublisher.cs
@@ -95,6 +95,13 @@ namespace DotNetOpenAuth {
/// </summary>
private const string EnabledViewStateKey = "Enabled";
+ /// <summary>
+ /// Initializes a new instance of the <see cref="XrdsPublisher"/> class.
+ /// </summary>
+ public XrdsPublisher() {
+ Reporting.RecordFeatureUse(this);
+ }
+
#region Properties
/// <summary>
diff --git a/src/DotNetOpenAuth/Yadis/HtmlParser.cs b/src/DotNetOpenAuth/Yadis/HtmlParser.cs
index 406cb4b..a6b64c1 100644
--- a/src/DotNetOpenAuth/Yadis/HtmlParser.cs
+++ b/src/DotNetOpenAuth/Yadis/HtmlParser.cs
@@ -98,8 +98,8 @@ namespace DotNetOpenAuth.Yadis {
/// <param name="attribute">The attribute.</param>
/// <returns>A filtered sequence of attributes.</returns>
internal static IEnumerable<T> WithAttribute<T>(this IEnumerable<T> sequence, string attribute) where T : HtmlControl {
- Contract.Requires(sequence != null);
- Contract.Requires(!String.IsNullOrEmpty(attribute));
+ Contract.Requires<ArgumentNullException>(sequence != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(attribute));
return sequence.Where(tag => tag.Attributes[attribute] != null);
}
diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs
index 14aea62..f1c8be3 100644
--- a/src/DotNetOpenAuth/Yadis/Yadis.cs
+++ b/src/DotNetOpenAuth/Yadis/Yadis.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Yadis {
using System;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using System.Net.Cache;
@@ -72,6 +73,7 @@ namespace DotNetOpenAuth.Yadis {
CachedDirectWebResponse response2 = null;
if (IsXrdsDocument(response)) {
Logger.Yadis.Debug("An XRDS response was received from GET at user-supplied identifier.");
+ Reporting.RecordEventOccurrence("Yadis", "XRDS in initial response");
response2 = response;
} else {
string uriString = response.Headers.Get(HeaderName);
@@ -79,12 +81,14 @@ namespace DotNetOpenAuth.Yadis {
if (uriString != null) {
if (Uri.TryCreate(uriString, UriKind.Absolute, out url)) {
Logger.Yadis.DebugFormat("{0} found in HTTP header. Preparing to pull XRDS from {1}", HeaderName, url);
+ Reporting.RecordEventOccurrence("Yadis", "XRDS referenced in HTTP header");
}
}
if (url == null && response.ContentType != null && (response.ContentType.MediaType == ContentTypes.Html || response.ContentType.MediaType == ContentTypes.XHtml)) {
url = FindYadisDocumentLocationInHtmlMetaTags(response.GetResponseString());
if (url != null) {
Logger.Yadis.DebugFormat("{0} found in HTML Http-Equiv tag. Preparing to pull XRDS from {1}", HeaderName, url);
+ Reporting.RecordEventOccurrence("Yadis", "XRDS referenced in HTML");
}
}
if (url != null) {
@@ -132,8 +136,10 @@ namespace DotNetOpenAuth.Yadis {
/// <param name="acceptTypes">The value of the Accept HTTP header to include in the request.</param>
/// <returns>The HTTP response retrieved from the request.</returns>
internal static IncomingWebResponse Request(IDirectWebRequestHandler requestHandler, Uri uri, bool requireSsl, params string[] acceptTypes) {
- ErrorUtilities.VerifyArgumentNotNull(requestHandler, "requestHandler");
- ErrorUtilities.VerifyArgumentNotNull(uri, "uri");
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Requires<ArgumentNullException>(uri != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.CachePolicy = IdentifierDiscoveryCachePolicy;
diff --git a/src/official-build-key.pub b/src/official-build-key.pub
new file mode 100644
index 0000000..6e68f53
--- /dev/null
+++ b/src/official-build-key.pub
Binary files differ
diff --git a/src/version.txt b/src/version.txt
index e4604e3..1545d96 100644
--- a/src/version.txt
+++ b/src/version.txt
@@ -1 +1 @@
-3.2.1
+3.5.0