From b077658a87bfd7021de30d458ca1cd6212e9bfeb Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 28 Jun 2024 17:08:12 +0300 Subject: [PATCH] Restore openicf-dotnet-framework --- dotnet/framework/.gitignore | 185 + .../BooScriptExecutorFactory.cs | 89 + .../BooScriptExecutorFactory.csproj | 100 + .../lib/Boo.Lang.Compiler.dll | Bin 0 -> 729088 bytes .../lib/Boo.Lang.Interpreter.dll | Bin 0 -> 86016 bytes .../lib/Boo.Lang.Parser.dll | Bin 0 -> 421888 bytes .../lib/Boo.Lang.Useful.dll | Bin 0 -> 81920 bytes .../BooScriptExecutorFactory/lib/Boo.Lang.dll | Bin 0 -> 110592 bytes .../BooScriptExecutorFactory/version.template | 1 + dotnet/framework/Common/Assertions.cs | 114 + dotnet/framework/Common/AsyncHandler.cs | 58 + dotnet/framework/Common/CollectionUtil.cs | 1185 ++++ dotnet/framework/Common/Common.csproj | 109 + dotnet/framework/Common/DateTimeUtil.cs | 54 + .../Common/FrameworkInternalBridge.cs | 58 + dotnet/framework/Common/IOUtil.cs | 446 ++ dotnet/framework/Common/Locale.cs | 240 + dotnet/framework/Common/Pair.cs | 103 + dotnet/framework/Common/Pooling.cs | 251 + dotnet/framework/Common/Proxy.cs | 272 + dotnet/framework/Common/ReflectionUtil.cs | 171 + dotnet/framework/Common/SafeType.cs | 143 + dotnet/framework/Common/Script.cs | 182 + dotnet/framework/Common/Security.cs | 1008 +++ dotnet/framework/Common/StringUtil.cs | 645 ++ dotnet/framework/Common/TraceUtil.cs | 123 + dotnet/framework/Common/XmlUtil.cs | 225 + dotnet/framework/Common/version.template | 1 + dotnet/framework/ConnectorFramework.sln | 279 + .../ConnectorServerService/App.config | 90 + .../ConnectorServerService.Designer.cs | 37 + .../ConnectorServerService.cs | 621 ++ .../ConnectorServerService.csproj | 154 + .../ConnectorServerService/FR_ICF_sq_med.ico | Bin 0 -> 32038 bytes .../ConnectorServerService/Program.cs | 274 + .../ProjectInstaller.cs | 93 + .../Properties/Resources.Designer.cs | 81 + .../Properties/Resources.resx | 212 + .../ConnectorServerService/packages.config | 5 + .../ConnectorServerService/version.template | 1 + dotnet/framework/Framework.targets | 105 + dotnet/framework/Framework/Api.cs | 940 +++ dotnet/framework/Framework/ApiOperations.cs | 560 ++ dotnet/framework/Framework/Common.cs | 992 +++ .../framework/Framework/CommonExceptions.cs | 841 +++ dotnet/framework/Framework/CommonObjects.cs | 6256 +++++++++++++++++ .../Framework/CommonObjectsFilter.cs | 2535 +++++++ .../framework/Framework/CommonSerializer.cs | 330 + dotnet/framework/Framework/Framework.csproj | 106 + dotnet/framework/Framework/Spi.cs | 547 ++ dotnet/framework/Framework/SpiOperations.cs | 520 ++ dotnet/framework/Framework/version.template | 1 + dotnet/framework/FrameworkInternal/Api.cs | 1550 ++++ .../framework/FrameworkInternal/ApiLocal.cs | 1616 +++++ .../FrameworkInternal/ApiLocalOperations.cs | 2193 ++++++ .../framework/FrameworkInternal/ApiRemote.cs | 698 ++ .../FrameworkInternal/ApiRemoteMessages.cs | 369 + .../FrameworkInternal/ExceptionUtil.cs | 83 + .../FrameworkInternal.csproj | 123 + .../FrameworkInternal/Resources.resx | 573 ++ .../framework/FrameworkInternal/Security.cs | 136 + .../framework/FrameworkInternal/Serializer.cs | 3169 +++++++++ .../FrameworkInternal/SerializerBinary.cs | 908 +++ .../FrameworkInternal/SerializerXml.cs | 952 +++ dotnet/framework/FrameworkInternal/Server.cs | 1154 +++ dotnet/framework/FrameworkInternal/Test.cs | 242 + .../FrameworkInternal/version.template | 1 + .../FrameworkProtoBuf.csproj | 82 + .../protobuf/CommonObjectMessages.proto | 89 + .../protobuf/OperationMessages.proto | 277 + .../protobuf/RPCMessages.proto | 87 + .../FrameworkProtoBuf/version.template | 1 + .../FrameworkRPC/FrameworkRpc.csproj | 69 + dotnet/framework/FrameworkRPC/Rpc.cs | 1105 +++ .../framework/FrameworkRPC/version.template | 1 + dotnet/framework/FrameworkServer/Async.cs | 663 ++ dotnet/framework/FrameworkServer/AsyncImpl.cs | 2989 ++++++++ dotnet/framework/FrameworkServer/Client.cs | 598 ++ .../ConnectorEventSubscriptionApiOpImpl.cs | 649 ++ dotnet/framework/FrameworkServer/Framework.cs | 670 ++ .../FrameworkServer/FrameworkServer.csproj | 109 + dotnet/framework/FrameworkServer/Local.cs | 58 + dotnet/framework/FrameworkServer/Remote.cs | 2408 +++++++ dotnet/framework/FrameworkServer/Rpc.cs | 941 +++ .../framework/FrameworkServer/packages.config | 4 + .../FrameworkServer/version.template | 1 + .../FrameworkServerTests.csproj | 155 + .../FrameworkServerTests/WcfServiceTests.cs | 635 ++ .../FrameworkServerTests/packages.config | 8 + .../FrameworkServerTests/version.template | 1 + .../FrameworkTests/CollectionUtilTests.cs | 161 + .../ConnectorAttributeUtilTests.cs | 371 + .../ConnectorFacadeExceptionTests.cs | 122 + .../FrameworkTests/ConnectorFacadeTests.cs | 663 ++ .../ConnectorInfoManagerTests.cs | 1150 +++ .../FrameworkTests/ExceptionUtilTests.cs | 114 + .../FrameworkTests/FilterTranslatorTests.cs | 642 ++ .../FrameworkTests/FrameworkTests.csproj | 191 + .../FrameworkTests/GuardedByteArrayTests.cs | 102 + .../FrameworkTests/GuardedStringTests.cs | 105 + .../framework/FrameworkTests/LocaleTests.cs | 210 + .../framework/FrameworkTests/MockConnector.cs | 379 + .../FrameworkTests/ObjectClassUtilTests.cs | 45 + .../ObjectNormalizerFacadeTests.cs | 250 + .../FrameworkTests/ObjectPoolTests.cs | 291 + .../ObjectSerializationTests.cs | 1313 ++++ .../config/config.xml | 28 + .../config/converter/config.xml | 58 + .../config/myconfig/config.xml | 27 + .../FrameworkTests/PropertyBagTests.cs | 134 + dotnet/framework/FrameworkTests/ProxyTests.cs | 96 + .../framework/FrameworkTests/SafeTypeTest.cs | 46 + .../framework/FrameworkTests/ScriptTests.cs | 146 + .../FrameworkTests/StringUtilTests.cs | 224 + .../FrameworkTests/TestHelperTests.cs | 494 ++ dotnet/framework/FrameworkTests/TestUtil.cs | 53 + .../FrameworkTests/UpdateImplTests.cs | 226 + .../FrameworkTests/VersionRangeTests.cs | 166 + dotnet/framework/FrameworkTests/app.config | 23 + .../framework/FrameworkTests/packages.config | 8 + .../framework/FrameworkTests/version.template | 1 + .../PowerShellScriptExecutorFactory.cs | 127 + .../PowerShellScriptExecutorFactory.csproj | 96 + .../version.template | 1 + dotnet/framework/Service/FR_ICF_sq_med.ico | Bin 0 -> 32038 bytes dotnet/framework/Service/Program.cs | 332 + dotnet/framework/Service/ProjectInstaller.cs | 86 + .../Service/Properties/Resources.Designer.cs | 85 + .../Service/Properties/Resources.resx | 184 + dotnet/framework/Service/Service.cs | 185 + dotnet/framework/Service/Service.csproj | 140 + dotnet/framework/Service/app.config | 62 + dotnet/framework/Service/version.template | 1 + dotnet/framework/ServiceInstall/ExtBuild.proj | 71 + dotnet/framework/ServiceInstall/File.bottom | 4 + dotnet/framework/ServiceInstall/File.top | 37 + .../ServiceInstall/Localize/UILoc_en-us.wxl | 29 + .../Resources/FR_ICF_sq_med.ico | Bin 0 -> 32038 bytes .../ServiceInstall/Resources/SetupBanner.bmp | Bin 0 -> 85896 bytes .../Resources/product_License.rtf | 309 + .../ServiceInstall/Resources/setupDialog.bmp | Bin 0 -> 461816 bytes .../ServiceInstall/ServiceInstall.wixproj | 146 + .../framework/ServiceInstall/SettingsDlg.wxs | 95 + dotnet/framework/ServiceInstall/Setup.wxs | 267 + .../ShellScriptExecutorFactory.cs | 221 + .../ShellScriptExecutorFactory.csproj | 86 + .../version.template | 1 + .../TestBundles/TestBundleV1/AssemblyInfo.cs | 56 + .../TestBundleV1/Messages.es-ES.resx | 129 + .../TestBundles/TestBundleV1/Messages.es.resx | 129 + .../TestBundles/TestBundleV1/Messages.resx | 129 + .../TestBundleV1/TestBundleV1.csproj | 94 + .../TestBundles/TestBundleV1/TestConnector.cs | 1304 ++++ .../TestBundles/TestBundleV2/AssemblyInfo.cs | 54 + .../TestBundleV2/TestBundleV2.csproj | 85 + .../TestBundles/TestBundleV2/TestConnector.cs | 63 + .../TestCommon/FrameworkInternalBridge.cs | 58 + dotnet/framework/TestCommon/PropertyBag.cs | 367 + dotnet/framework/TestCommon/Test.cs | 566 ++ dotnet/framework/TestCommon/TestCommon.csproj | 78 + dotnet/framework/TestCommon/TestHelpersSpi.cs | 58 + dotnet/framework/TestCommon/config.xsd | 38 + dotnet/framework/TestCommon/version.template | 1 + .../ConnectorServerService.cs | 514 ++ .../WcfServiceLibrary/IWebSocketService.cs | 45 + .../WcfServiceLibrary.csproj | 120 + .../WcfServiceLibrary/version.template | 1 + dotnet/framework/legal/CDDLv1.txt | 383 + dotnet/framework/legal/ForgeRock_License.txt | 144 + .../lib/Google.Protobuf.dll | Bin 0 -> 252928 bytes .../lib/Google.Protobuf.pdb | Bin 0 -> 808448 bytes .../Google.ProtocolBuffers.3/tools/protoc.exe | Bin 0 -> 2127872 bytes dotnet/framework/server.pfx | Bin 0 -> 1704 bytes 173 files changed, 61530 insertions(+) create mode 100644 dotnet/framework/.gitignore create mode 100644 dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.cs create mode 100644 dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj create mode 100644 dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll create mode 100644 dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll create mode 100644 dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll create mode 100644 dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll create mode 100644 dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.dll create mode 100644 dotnet/framework/BooScriptExecutorFactory/version.template create mode 100644 dotnet/framework/Common/Assertions.cs create mode 100755 dotnet/framework/Common/AsyncHandler.cs create mode 100644 dotnet/framework/Common/CollectionUtil.cs create mode 100644 dotnet/framework/Common/Common.csproj create mode 100644 dotnet/framework/Common/DateTimeUtil.cs create mode 100644 dotnet/framework/Common/FrameworkInternalBridge.cs create mode 100644 dotnet/framework/Common/IOUtil.cs create mode 100644 dotnet/framework/Common/Locale.cs create mode 100644 dotnet/framework/Common/Pair.cs create mode 100644 dotnet/framework/Common/Pooling.cs create mode 100644 dotnet/framework/Common/Proxy.cs create mode 100644 dotnet/framework/Common/ReflectionUtil.cs create mode 100644 dotnet/framework/Common/SafeType.cs create mode 100644 dotnet/framework/Common/Script.cs create mode 100644 dotnet/framework/Common/Security.cs create mode 100644 dotnet/framework/Common/StringUtil.cs create mode 100644 dotnet/framework/Common/TraceUtil.cs create mode 100644 dotnet/framework/Common/XmlUtil.cs create mode 100644 dotnet/framework/Common/version.template create mode 100644 dotnet/framework/ConnectorFramework.sln create mode 100755 dotnet/framework/ConnectorServerService/App.config create mode 100755 dotnet/framework/ConnectorServerService/ConnectorServerService.Designer.cs create mode 100755 dotnet/framework/ConnectorServerService/ConnectorServerService.cs create mode 100755 dotnet/framework/ConnectorServerService/ConnectorServerService.csproj create mode 100755 dotnet/framework/ConnectorServerService/FR_ICF_sq_med.ico create mode 100755 dotnet/framework/ConnectorServerService/Program.cs create mode 100755 dotnet/framework/ConnectorServerService/ProjectInstaller.cs create mode 100755 dotnet/framework/ConnectorServerService/Properties/Resources.Designer.cs create mode 100755 dotnet/framework/ConnectorServerService/Properties/Resources.resx create mode 100755 dotnet/framework/ConnectorServerService/packages.config create mode 100755 dotnet/framework/ConnectorServerService/version.template create mode 100644 dotnet/framework/Framework.targets create mode 100644 dotnet/framework/Framework/Api.cs create mode 100644 dotnet/framework/Framework/ApiOperations.cs create mode 100644 dotnet/framework/Framework/Common.cs create mode 100644 dotnet/framework/Framework/CommonExceptions.cs create mode 100755 dotnet/framework/Framework/CommonObjects.cs create mode 100755 dotnet/framework/Framework/CommonObjectsFilter.cs create mode 100644 dotnet/framework/Framework/CommonSerializer.cs create mode 100644 dotnet/framework/Framework/Framework.csproj create mode 100644 dotnet/framework/Framework/Spi.cs create mode 100644 dotnet/framework/Framework/SpiOperations.cs create mode 100644 dotnet/framework/Framework/version.template create mode 100755 dotnet/framework/FrameworkInternal/Api.cs create mode 100644 dotnet/framework/FrameworkInternal/ApiLocal.cs create mode 100644 dotnet/framework/FrameworkInternal/ApiLocalOperations.cs create mode 100644 dotnet/framework/FrameworkInternal/ApiRemote.cs create mode 100644 dotnet/framework/FrameworkInternal/ApiRemoteMessages.cs create mode 100644 dotnet/framework/FrameworkInternal/ExceptionUtil.cs create mode 100644 dotnet/framework/FrameworkInternal/FrameworkInternal.csproj create mode 100755 dotnet/framework/FrameworkInternal/Resources.resx create mode 100644 dotnet/framework/FrameworkInternal/Security.cs create mode 100755 dotnet/framework/FrameworkInternal/Serializer.cs create mode 100644 dotnet/framework/FrameworkInternal/SerializerBinary.cs create mode 100644 dotnet/framework/FrameworkInternal/SerializerXml.cs create mode 100644 dotnet/framework/FrameworkInternal/Server.cs create mode 100644 dotnet/framework/FrameworkInternal/Test.cs create mode 100644 dotnet/framework/FrameworkInternal/version.template create mode 100755 dotnet/framework/FrameworkProtoBuf/FrameworkProtoBuf.csproj create mode 100755 dotnet/framework/FrameworkProtoBuf/protobuf/CommonObjectMessages.proto create mode 100755 dotnet/framework/FrameworkProtoBuf/protobuf/OperationMessages.proto create mode 100755 dotnet/framework/FrameworkProtoBuf/protobuf/RPCMessages.proto create mode 100755 dotnet/framework/FrameworkProtoBuf/version.template create mode 100755 dotnet/framework/FrameworkRPC/FrameworkRpc.csproj create mode 100755 dotnet/framework/FrameworkRPC/Rpc.cs create mode 100755 dotnet/framework/FrameworkRPC/version.template create mode 100755 dotnet/framework/FrameworkServer/Async.cs create mode 100755 dotnet/framework/FrameworkServer/AsyncImpl.cs create mode 100755 dotnet/framework/FrameworkServer/Client.cs create mode 100755 dotnet/framework/FrameworkServer/ConnectorEventSubscriptionApiOpImpl.cs create mode 100755 dotnet/framework/FrameworkServer/Framework.cs create mode 100755 dotnet/framework/FrameworkServer/FrameworkServer.csproj create mode 100755 dotnet/framework/FrameworkServer/Local.cs create mode 100755 dotnet/framework/FrameworkServer/Remote.cs create mode 100755 dotnet/framework/FrameworkServer/Rpc.cs create mode 100755 dotnet/framework/FrameworkServer/packages.config create mode 100755 dotnet/framework/FrameworkServer/version.template create mode 100755 dotnet/framework/FrameworkServerTests/FrameworkServerTests.csproj create mode 100755 dotnet/framework/FrameworkServerTests/WcfServiceTests.cs create mode 100755 dotnet/framework/FrameworkServerTests/packages.config create mode 100755 dotnet/framework/FrameworkServerTests/version.template create mode 100644 dotnet/framework/FrameworkTests/CollectionUtilTests.cs create mode 100644 dotnet/framework/FrameworkTests/ConnectorAttributeUtilTests.cs create mode 100644 dotnet/framework/FrameworkTests/ConnectorFacadeExceptionTests.cs create mode 100644 dotnet/framework/FrameworkTests/ConnectorFacadeTests.cs create mode 100755 dotnet/framework/FrameworkTests/ConnectorInfoManagerTests.cs create mode 100644 dotnet/framework/FrameworkTests/ExceptionUtilTests.cs create mode 100644 dotnet/framework/FrameworkTests/FilterTranslatorTests.cs create mode 100644 dotnet/framework/FrameworkTests/FrameworkTests.csproj create mode 100644 dotnet/framework/FrameworkTests/GuardedByteArrayTests.cs create mode 100644 dotnet/framework/FrameworkTests/GuardedStringTests.cs create mode 100644 dotnet/framework/FrameworkTests/LocaleTests.cs create mode 100644 dotnet/framework/FrameworkTests/MockConnector.cs create mode 100644 dotnet/framework/FrameworkTests/ObjectClassUtilTests.cs create mode 100644 dotnet/framework/FrameworkTests/ObjectNormalizerFacadeTests.cs create mode 100644 dotnet/framework/FrameworkTests/ObjectPoolTests.cs create mode 100755 dotnet/framework/FrameworkTests/ObjectSerializationTests.cs create mode 100644 dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml create mode 100755 dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/converter/config.xml create mode 100644 dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml create mode 100644 dotnet/framework/FrameworkTests/PropertyBagTests.cs create mode 100644 dotnet/framework/FrameworkTests/ProxyTests.cs create mode 100644 dotnet/framework/FrameworkTests/SafeTypeTest.cs create mode 100644 dotnet/framework/FrameworkTests/ScriptTests.cs create mode 100755 dotnet/framework/FrameworkTests/StringUtilTests.cs create mode 100644 dotnet/framework/FrameworkTests/TestHelperTests.cs create mode 100644 dotnet/framework/FrameworkTests/TestUtil.cs create mode 100644 dotnet/framework/FrameworkTests/UpdateImplTests.cs create mode 100755 dotnet/framework/FrameworkTests/VersionRangeTests.cs create mode 100644 dotnet/framework/FrameworkTests/app.config create mode 100755 dotnet/framework/FrameworkTests/packages.config create mode 100644 dotnet/framework/FrameworkTests/version.template create mode 100644 dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs create mode 100644 dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj create mode 100644 dotnet/framework/PowerShellScriptExecutorFactory/version.template create mode 100755 dotnet/framework/Service/FR_ICF_sq_med.ico create mode 100644 dotnet/framework/Service/Program.cs create mode 100644 dotnet/framework/Service/ProjectInstaller.cs create mode 100755 dotnet/framework/Service/Properties/Resources.Designer.cs create mode 100755 dotnet/framework/Service/Properties/Resources.resx create mode 100644 dotnet/framework/Service/Service.cs create mode 100644 dotnet/framework/Service/Service.csproj create mode 100644 dotnet/framework/Service/app.config create mode 100644 dotnet/framework/Service/version.template create mode 100644 dotnet/framework/ServiceInstall/ExtBuild.proj create mode 100644 dotnet/framework/ServiceInstall/File.bottom create mode 100644 dotnet/framework/ServiceInstall/File.top create mode 100644 dotnet/framework/ServiceInstall/Localize/UILoc_en-us.wxl create mode 100755 dotnet/framework/ServiceInstall/Resources/FR_ICF_sq_med.ico create mode 100644 dotnet/framework/ServiceInstall/Resources/SetupBanner.bmp create mode 100644 dotnet/framework/ServiceInstall/Resources/product_License.rtf create mode 100644 dotnet/framework/ServiceInstall/Resources/setupDialog.bmp create mode 100644 dotnet/framework/ServiceInstall/ServiceInstall.wixproj create mode 100755 dotnet/framework/ServiceInstall/SettingsDlg.wxs create mode 100644 dotnet/framework/ServiceInstall/Setup.wxs create mode 100644 dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs create mode 100644 dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj create mode 100644 dotnet/framework/ShellScriptExecutorFactory/version.template create mode 100644 dotnet/framework/TestBundles/TestBundleV1/AssemblyInfo.cs create mode 100644 dotnet/framework/TestBundles/TestBundleV1/Messages.es-ES.resx create mode 100644 dotnet/framework/TestBundles/TestBundleV1/Messages.es.resx create mode 100644 dotnet/framework/TestBundles/TestBundleV1/Messages.resx create mode 100644 dotnet/framework/TestBundles/TestBundleV1/TestBundleV1.csproj create mode 100755 dotnet/framework/TestBundles/TestBundleV1/TestConnector.cs create mode 100644 dotnet/framework/TestBundles/TestBundleV2/AssemblyInfo.cs create mode 100644 dotnet/framework/TestBundles/TestBundleV2/TestBundleV2.csproj create mode 100644 dotnet/framework/TestBundles/TestBundleV2/TestConnector.cs create mode 100644 dotnet/framework/TestCommon/FrameworkInternalBridge.cs create mode 100644 dotnet/framework/TestCommon/PropertyBag.cs create mode 100644 dotnet/framework/TestCommon/Test.cs create mode 100644 dotnet/framework/TestCommon/TestCommon.csproj create mode 100644 dotnet/framework/TestCommon/TestHelpersSpi.cs create mode 100644 dotnet/framework/TestCommon/config.xsd create mode 100644 dotnet/framework/TestCommon/version.template create mode 100755 dotnet/framework/WcfServiceLibrary/ConnectorServerService.cs create mode 100755 dotnet/framework/WcfServiceLibrary/IWebSocketService.cs create mode 100755 dotnet/framework/WcfServiceLibrary/WcfServiceLibrary.csproj create mode 100755 dotnet/framework/WcfServiceLibrary/version.template create mode 100644 dotnet/framework/legal/CDDLv1.txt create mode 100644 dotnet/framework/legal/ForgeRock_License.txt create mode 100755 dotnet/framework/packages/Google.ProtocolBuffers.3/lib/Google.Protobuf.dll create mode 100755 dotnet/framework/packages/Google.ProtocolBuffers.3/lib/Google.Protobuf.pdb create mode 100755 dotnet/framework/packages/Google.ProtocolBuffers.3/tools/protoc.exe create mode 100644 dotnet/framework/server.pfx diff --git a/dotnet/framework/.gitignore b/dotnet/framework/.gitignore new file mode 100644 index 00000000..76d0819f --- /dev/null +++ b/dotnet/framework/.gitignore @@ -0,0 +1,185 @@ +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# OS generated files # +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings +modulesbin/ +tempbin/ + +# EPiServer Site file (VPP) +AppData/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# vim +*.txt~ +*.swp +*.swo + +# svn +.svn + +# SQL Server files +**/App_Data/*.mdf +**/App_Data/*.ldf +**/App_Data/*.sdf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store + +# SASS Compiler cache +.sass-cache + +# Visual Studio 2014 CTP +**/*.sln.ide + +# OpenICF +**/version.txt +**/AssemblyInfo.cs +Dist/ +FrameworkProtoBuf/*.cs diff --git a/dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.cs new file mode 100644 index 00000000..eb34b10d --- /dev/null +++ b/dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -0,0 +1,89 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System.Reflection; +using System.Collections.Generic; +using Boo.Lang.Interpreter; +using Boo.Lang.Compiler; + +namespace Org.IdentityConnectors.Common.Script.Boo +{ + [ScriptExecutorFactoryClass("Boo")] + public class BooScriptExecutorFactory : ScriptExecutorFactory + { + /// + /// Attempt to trigger an exception if the runtime is not present. + /// + public BooScriptExecutorFactory() + { + new BooScriptExecutor(new Assembly[0], "1").Execute(null); + } + + /// + /// Creates a script executor give the Boo script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly[] referencedAssemblies, string script, bool compile) + { + return new BooScriptExecutor(referencedAssemblies, script); + } + + /// + /// Processes the script. + /// + class BooScriptExecutor : ScriptExecutor + { + private readonly Assembly[] _referencedAssemblies; + private readonly string _script; + private readonly InteractiveInterpreter _engine; + + public BooScriptExecutor(Assembly[] referencedAssemblies, string script) + { + _referencedAssemblies = referencedAssemblies; + _script = script; + _engine = new InteractiveInterpreter(); + _engine.RememberLastValue = true; + foreach (Assembly assembly in referencedAssemblies) + { + _engine.References.Add(assembly); + } + } + public object Execute(IDictionary arguments) + { + // add all the globals + IDictionary args = CollectionUtil.NullAsEmpty(arguments); + foreach (KeyValuePair entry in args) + { + _engine.SetValue(entry.Key, entry.Value); + } + CompilerContext context = _engine.Eval(_script); + if (context.Errors.Count > 0) + { + throw context.Errors[0]; + } + return _engine.LastValue; + } + } + } +} diff --git a/dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj new file mode 100644 index 00000000..d7bddd6f --- /dev/null +++ b/dotnet/framework/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -0,0 +1,100 @@ + + + + + {0747C440-70E4-4E63-9F9D-03B3A010C991} + Debug + AnyCPU + Library + Org.IdentityConnectors.Common.Script.Boo + Boo.ScriptExecutorFactory + Boo ScriptExecutor Factory + v4.5.2 + + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + false + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + true + True + False + false + + + + lib\Boo.Lang.dll + + + lib\Boo.Lang.Compiler.dll + + + lib\Boo.Lang.Interpreter.dll + + + lib\Boo.Lang.Parser.dll + + + lib\Boo.Lang.Useful.dll + + + + 4.0 + + + + 4.0 + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll b/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll new file mode 100644 index 0000000000000000000000000000000000000000..7a4bc09ba3dce42a0b0abf3030063f29df749338 GIT binary patch literal 729088 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
GU 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

T_?v+9kPqlvw@YaP6tl3J0exTmi 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�FL@bc7u8lj-pJ zKjS}iP!6piU=^~LJ}ex7vs8(3kIK-x?Ci_YK&A+dK_=y;NF$EGYy1)Gk;Wf^w;Ci0 zxGq)<$P*Z6KB(>Ttpc(<-8-nlLRlIfUbUP5Zjq2*7b z4q>epGEB0`O{!;A8WEr=N^7P@F5yBvH#|y){DO*YJ_kUSp07ae0P?*XS`_;59~E<29@3 z)zGkVK_q4qqp^n38oMYDXv`u?W0j-?jaI^GJndeqwK)}=G$7PssA(}o)0E^4EwO~t zcm?r8V-+wOXGkW|ctco?n+hu$Ek$UIvH+v8%1Dh_5NtGB0i$tB(vHR|;WS=J2-0{Z zoW_%_Q_-YTqYk4rc3EoDxMifqEJ;%uv4pc6i$a!VSj1_(vh1Z1%SeqGmy<{$8hcbg zDTT^rrO1lYcsAEZifo$Z0m5Ayic+0xLlLPlhon7?I)v4@4AlrRP!o&}a&6g29CS_D zC|FZJfWWB9A0S-Q4~bBkY6zReEy`9&^kP7gVq>{0NxgBTBqu2%U4e$Awj}OIKAjXe zGF;O~^4v(a(`fv~d z_!F5^yrS`8<)or!3aUx!SZ^J*x+YkyXyTW~9+HR>ppNMzR1s_z^&U>9ClvLzoKqx< zT0Oes#5o*hNR~UhYiLr%rCPY=a}5ew=uMl4x?QXevh^?B_nkq#^I~$J<;ZP?Q z1x};7W8HbRF&%C)Xg3_nRcmk5I3>3;XHb`EJ*oPA&?w+d1Z1T)w1*rukk+rxON6U;}2^HH( zO{EUOFk&sS&^BpbFbuf~kXn$#B&8&QnoBWs-(xmaK#Oo@ZU=k;FN__QWVk4&Xr`BTHJLWPdd4&y{m6UiBXJ{Rx z*6^3-tfsyRmOT0-Z`)h3dW7!Y?GD|wQq!F##%i*-K)HH3rHCdoYzM9WTrD(HC~HLY zN;xxqS#2zQ&ds=)zPG2+3}*VXaH(F4mC2{*4KO=$KIt{ik}T*Tm&AfcK!T z$4zJYSoIx~70gm%9T#G%rCBQvtQcYuLr3_C?p`|l@YvoKowvbTQy84n_I7@DPK{0V| zCnO~u=2q2Y8p(Va#!B2cHIqyv`pAIcJVr*V58$niU=Ev8vUE=Or@o>Gsx)Lin3qe9 zOiFij@1h7^j!cE%EhPFRtYGP;M8lM|t(;9Kyu)?ZII>5YUDTQ6OOAOL^G)&`nV-~z z09Hio9Odvm9{TLbxrY$FYNDHM5LZRKC^KYp%iT3D`gk+UEV@L5(uD_VYaT^v5q<|J zR5S48G|p@7V^WcAqAw}Tl7=NWZj>|IF==s9u60)M3ccrgD^~)epooOEsMZZp;c7)B zIooW})JejFYnZ68;v>Z>!wP&6<`uZ2x^2EfNKL&cu10EQL{ya& z@>dEs@m5jJsaTCK@$|$nGgx25s#Q)dJjK)(^#)sgo-wkG2v?g5F(ivJP71K$(@@Pq za3;10&RpemZpd3+`yQ5iT}nhzuNcXoh$;G#y`Ie>$jzO28r`J+)J^kwYyhUhg@umw zo<2XIoo6m-#+(I9HJsDJoF^$>2OyT9`6S+ys)YXOSQDnZrr2jzFXwj5UZyi^NJ41V zQnsO%h4zv6NTp|8mJ;CIfa zw?IXt)L;lXgz%XilxHB8F|?OQ6O#N2_$12$-Y<@69`&}|xYn#x@2?FpmqBH+M!Lpi z&8^)C>x|?rY%zu@Ym9-3H^xhlx;Zm3w%!;rT(4u|-mvyYaaURyeNrE9D<4-Oi8GAV zxSFGe59ckHy0jxg{)&l}RBs5xWJ{_S{1&;@-`B++R|=akr2?~gUN7fE*+%t)m^a>FU-*t(Yr{%kdS^?)4yxIHOwDNB495(_f0Zs17OM?mTbQ79YZ4=B z6cR3|MTad$8Xy^t3{Z8T$&H#?Rxjt3G>I4DcRM8G6S23Qi4g?7*l4x6BjO`;ms4B=& zig)|J&UTec=kgT5HF6h?rRKQ%3&qH$Sty6U5+kd3@_d2qie7bvAJrFHY8iVVKfX_{ zq)VGDRHI9i60d~1LJZ4RTZ>Snq2QJ3)9f(4)w%9+w>vAoERAN7u?tyg%;l@7vA9jA zs!0ts9bi%e#q#`vUOC<-b=-C+Us}V2EfQ}Zwa`+2f_7^w7kMw@&~)ZsI$-mzZywCC zS%rbpKbq&>eEqmw(NvFiujurw5@kjQ&oM=5zCgwzj*93VuMb-<_Br1j$m0{ud}^uF z?Tc1K^E(J%HkT4=*hZzuOlQ9lGAzIxnpzlw(=3QM=q8h8?5@VxwG^uFtA;XHriv9h zY;Ad^K3HI();V0AbV@i^sp8&1hD#_*OLe;kb<2E?>#d@Ogcr0nY9kqUgNQ%b0q7DA z!kvey9!g?25SqAn)TG)(&DqmiakQq}twmqyMe8?)3#2Hji;q3XgaB#hvpyH4d(FRK zDrar=yJ}~5WynH2HV&xJS%x)>u#Mmzh3+2|8#rYobT5o~J#GvMYp3NHysm`OILQDc z$12w}`p&8mS{Ol($3(4JuVb4m&r!&1@S{05g{l35u6s&~>PotW{6r1d?sBz-rQ7$O z^rsE4EGV4jgn*g87=K~B?oQ{(AVK7oP(%VOpO=+(4>D&814fdWPLp*92cm_$?kg#k zat3zeD0n&8GoEX#l|<63fux*YFASte(^btbmZ_BZKFE#M1H`at8K*)_8=Jpxxj(y@ zw1fHyW~QTJI-A$IwfTWOXHjTw=up$CKw}J9~EO8GnH+a0xIc9r$jFj!s=JYfyarK@VfupE<@YbybaL zMi!E9%rGeoI;Q7%QiMpZDk*Ai8I7Bs6uH2NP?wz_rD28%Hc1_$xnbK?n{ zd@-9@r17X1;ivM}l5jyihERoyoIwOJE^~VQsH~Tzn^oPk`2HXuoQ{N36S$L}SHuH_ z{qjzW^0otVp-*N9oDEHutg0|L=MoMW@#2eN`?^c=h7$B66H2Bo_Pe>`shW?hWQS~N zhiR}QS+rqbTIqf7e6ic(Z7KE~jfDlaCBaMSXw*{7)FOgqh#Gw^W<&_{_0JpaV{Nc^ zO9D#XJn$Zw30b^#qU!~`Mh04f#2;>AS+tQ+BugoYV_+wzak@Ygk2+%s+c2pOUBHM~ zTg2Frwzh+jkmGIZ4;kA9IyUJYOs_+4aQwcC&>}A_*q*IWFv=~ZHO8MDC4#mY`8Q&; z0P}|W>oN;T+lxv|R2GBNX0N4kE94Q~bu0s9W}&Pbh%_T{RRRg_c%k3IqCvL!?Ixm@ zXIpW*L#g?M2R0<4RNRDajw0kWnW%q}iWc8W*&{{{YJ6Z<@62XF>X$w+P$=7#8_Gl` z6IRG`K|yh;eJ5EDVKjsi^PW&hJpS~yGy$omIWf*qIJwh%%)a!^JzuV82r41T8WUt< z_@*A4>gYKUTB7$+X-~y z_T~@;Bjl)LsV|B8LhVNPf>z)N;uMb?5fy+D*tDE`? z#zF`Y+M=JWh5@VcTda_k6i*y=R6;EyUMJSEJR;~rf9Elfw2KtQXEh9A8Hr@hnd5TS zA%*Tm`>cBUsGKS5iax!YR!Gd?POS~PhRT7B(`7{&|2%l=F4UbS_L(xLB^!y-1&=sbL zVq+k^?YW7Wat5D<1yRg4)5^|EI2mQ1$ZDBlY}G0ziLqveuQh71@=lvpc!rCgIJI1L zxv6AlBfgPYxn0Hjc%Qx^CGW`Prrxo7jHrVvwca#qQ8%b&=j5kvxkz0{w2F?qs9S9C zWVeplKwwVBjd?agxw6<_?&7g0BvS0#pM5^-+(NEzF>Wb=3UE6tZK>oT-j4LcWO_+&%HM3XT}ob-!Z#yu71^#=_Awiol^&=cFr8WR_tEACPYmxRAI3soA_bga3*gpW0_CG3`<@&+yadZcL z4q)VQi!@lVT7(FvX^6-(g`QP)e*vRW;K!L)*kUu&SxPgJdds1p{1p|Ko%@L7xZ(-+nddqu&a2w7@bl!R^^)~yr5@s*lp%&-Lw3ZUbv6GC6w#b^A9+_AqoPYRl&$VtKiUa3|TYn#-f6(ZW@{eyWtw zp1GFrCJ`00NXD=f+vZORDOD+SA5KAGcS2|XU&ZQ5y3{;*GOHhK$65x(E~d%PuDOzAm$#8!+Y)XTms!B9a_ zX(gDLM6D`VJk9*R!3qP@;|{~r@y7bo^Imthr!h>E*XoNRs_)ZCxD08`+(IOG7Bp2o z;dI(??=5MeByy^&!dp~)nXRMBRHgO@(IKfF+LxgD=kXL4#vJ>#hP{i)yr6Fna~H;@ zRT^2=8MPEpKCHbcgR8n&F1dTBDy>#lbs_F7^bur&o>^p?plRlb4c#G_db;5A3Tp1| zBZ;W`Iyz$@@=&S3wUj5l+cS%DLdGs$`cw48@)vFJ?5bV7h`k&V z%W{2!hfonPrFn>UPNS^KU)Ms-$LpN^RR*6$Ny%Xp?dab;o}B#d$FS~5*2Pqbgn8?) zJ0^2a*^vsKZru`&T=I)P*R@hNoc^s=c@t5>(ASA3SYL^7B}(56^LB>v7;7ILy|B<@ zWO!K(#a{6hOBGyscG*7(tF-0f(}A*Iq^8EKnq94iI|c7=vnV=Q zvl;XFH;kMvlBN!0>~+x+9{V4r$1Fp`c|MEURJh_J%`mdk25XhgW#{MeLaR_o*GWZ? zs1w5-84)O=!2<-RQDNu)y&jM)e{ss*P_Eh8l&EKWP$C)S7sq(ABuK~`$RKpq_M zq$iD&>xc!u5JcL%*JN1ziVO(+$k9l86;IIkAxgM0?b)k05O!ucgw*=HyhC54VGW*q_7 zmu7{N5k1;8n|7;A-;Xb#SxYf0>}f6I_-FUBOmL@6uQml)@58 z!Z8a$`=Z=Y2q)2@I?B#me{zw=I!>8l!`)FrC@c0tsfIpt`bRP|VPCsTSNG@=O$G~* z2_e#*M>zE9wbwc5kOpb}V^f@tN8)+T8Ba?EYvRVPRu4%~z6+uCu){Z>AZeUvH02pl zm|tJctIX%L48jJFtol@C<&Np3(~7tzy8xd~OS91|pg7B6lP;ksoAmjPq)i^>G_HD( zhCM`fwJFZhQb(Y|zyv|cmmETdbRuqztmmwRtO)GPRp7@)!fmhTup#I3dINfEmuimFjY2kJjL0AZ|FMdhRP( zl2$H$@#Jn$1`C}#q}xV-P_C@I6SEMfXPLmMiJZeo&0T8W`_xV>TqHg>A4PNARc)r= zt!9c&s%2QKQi7i8yMAGhDkpT}^Eum-!LArTNes!?#TxeX^VE=CsH=cQ^qtn=Yk$f? zl(}e_bibmkYDuN)#=i9A*-u*?ER=K3S_?)O+$Oino7LPR2w%fk;z(&unWE;?FM1`^ z84|bwl?X*l5=ToJpNC#>05*+)>V6 z2VHw7m1R<%9m{q7>a%2}3+B|qbNI|qlAUFcM2TH1Z$yNcv5uC))h>j?1&2^cj}|-2 z`Lts~4t=Jt>z|~s2ks2JC1uyiau!bRg$cjoVurfKzB_T-iY}IKa)2RWMmt>mvL)B~ zWeK($W=2RB%VEjiust5=wP9zqKLFb^S5q}g^nB|7A0TR zBz!+ZEX;=a#1!A-50S*IQQ!P+daDD zuZM?^63M8>@1tq_Vptf_n3w2Acqo{6h>%Ntj!xZMOl%z?-E|Z*njq8+siLT=zH78y zEi6Wed@l3>LYzI5SdTFC?buE}y=GQI0Tm=by`|_k0D=7?jrW!s^1hSk2_z?xDb$)! z7gkwo)l!!;!eDZ2+`fdEl5lThL;J{$gGKui9Np&RoKUdr5xQROJ?h#4q}f?$2uC$K zh$$Vh8anA^hesY_;Ss|ExQ@|vM0t#TqfVHJ`&gZtq%Z5FX;K?dGBCm-l`W*3!fh6CohNwhs-+rI~QSrz|%-iC7wO$N5vbw<1XX~Z)xVJfW8&xke#8X|? zL^Up_Msf?qdJRkb#nuT~mHPf0ptF3oC>%NpxAUey9MW~V82BITw)t*fZ*`eF9ELOD z%B|#lxNt`*~+<6LccSE$LDJm@%c}ee=q&O?gvQ)jde(fM%q5iF=^b+eI8%Uw((&sV>D||lF z=^5PaedA<7TJZA0HI4RB>jE&`h-YN-9^YL`Yyx#d4S~T~W;^Y5HvxoLNC4L|ig4Fg zjm4^6kY@$uO+Y6wcJ9!%3LapfR)YAw>YQfIY*&!_kB-p9kS+$ z8E_DJY+TYv;W#9WWcWI$>iZ;VAy@9Z`hyq=!ej|{C!zu_k%fA1j zm@mN6lxzxUVlg~^3DQ+1biSn*O@Q`6F`1II`^ac$_{f;Pn-H6Q7M@nr2raG;&KgJz zbo{kwV-SJ=CyOm_o5^7aL1LM;q8dBrX*$DQdxTH*DTy^0%yRG1o;6gm^N&XjY%!4TM%cIlvy7(NaC|E*T_wq zq#|xIK(ZPaw%~WCJr|^Vj)0VpI|<8AJGdOf-*-vk=x#&JN$jGTeEuX^eMNE{&X-CJ zBaM9yu92cm4(zNFNmEs_sN{Jo`&dcumZw0 zzG$r_w@eGYVoeD8C+V9+OhS&6@ejck6e&RiTFnOz7rmCYP16-h_gXaJ86qvrxG!um zU-ZVK>-SOlGbKi$(d@`e%esc7wQY2Ukz}!-{&0XE+y!~!3_*FWg^(R?T2*h0lo^y8XF%?uo=q#O>Y8@2D-5-8P#|LE4>w*k?*KKpzBBS0AgFdB2Vw8EA#wa7LSf>uu zQ<-FOa4U%|ih+P!g-cjbV1y`M)Ui0adv~go9jeA|HITj@6-YjYY2WC?7hN+uJJmf(Cx?uLwcAx(-P@A3^t^;M3UP3^zV~8gz=v`+ zJ#I_q8(Y%M%v_L&Cq+h@ByA;Jygghmaewz<1; zxX*Iq7=<1l%0Hyrv2Kf;f4iBk)_EX(?qqYw+Fm{kJTcXtIIbEZsaVF^E7F1Ln(a;< zq>=YP$Ki8wS$Ky4((h>kJ^05GS;u!c*qz?~Hv$T*$7a+VZ#YeJhpIWiV4c?rjMtdS zfym_racUvz2yA}lvu!i8k$|xAhGfvEx<{M*s|%r3_O;Ggx!u72_;6Q}N+-i}c^HDH zEK2VM8PV&tZWh;I456J;GKJTdpRp#9cRV`jQw@clOpN6enr>>^^+0WNHMz>LUbGwi%b(<&B;eywlmw%h6HN zW?5i({Lk(fa0ktL;((sO%*v^P#1>;-&BS$sZ>Zwj3nmP@f4fD|x^o2+-$jr~;Wjc+ z=5k%I77hE)Ajn2S#4u^_K?)9aifrA$L5NhU6H>f9qxp7#l7Aub5b zUV>DvDr>Xb?)dy??2CV4y%PPc>L{4Ks9pJ*@VaZ6@g(6Zx>R9?gprTiGow>lTI;S2 zn_9RCzSlxq<$;02UEc_M_e@ktNsV37pk-2(*pDpGWl3clR^cQ^hHn(!cZKXc^@wa# zq`4N454g?qKn_e?_Oii*%1pHs(O?r#tQ1@MSfON*@jbC&N4f~fIK_P88fF%;o)W~E zf>@$^N@TSSDnpI^Qh>&quJS(l-uk-I1gfK@s0$ZLNc7Oc+2e;VSbx#hbdRIacjbj~ zq+bfXFhoQlCRGY=NdO`$YAhoZrE z(qNZQu?vTL+oS`pJc;Rv>FGEk<-@#3JKf`>>cTS5X@7jXQ3`g9RZe9?XXrvxP=3p% zWHi<`aMIC*1TERg>0V}Zgk;m^J^?h8g_KDT*?r?;s61%ZlCzXprt$7FCr-kZSw#Qg z?u;7VW*@VhX3Lmhui%``UD@Ul(UlA*m zGrW`XX^RwUG~vV0qU+$z_T<)NEZ$9jAB~CcqPfjm5>uKbvq$d;iYM@ka3@f(UMd-J zeNQD#7f#foIQD>PneL(8fdSVkh0A&;*@ zUg#6B^)BY;{4Ox$~}63}Fi=2WsJoH`ls`ytE`z4k0YUCSBtw$=k`c zFRwNnhCD>bYO^Sq!wT#icG#E61Ab_L0P;yB*fvKe1ZEF}xI< zSTefhHBn!QWp1FT;ovmXs^kqcvoe0)7Y?1WeO#&d91bGWE!aL)pQIwXd9Ix7|wg z_Y&kQB`$O*A>F6e73YsEJcf=+Sk|a&MjIO0Z4i4XV^LmX@YqUJwGWW-#<7PorZJ9O z#SUlGNEKrYW#E@&PhKPQ7>Z<%n4|_aQnZdoigXyN0C9;KPK*_Cn9PViJWVIjhOr(c zDho@3fUjA)xW$0_Og!q<%#+Vjjtr@*jRX}-hNgrnMlt#-L^(gBShoh$#i8{pq3Tn- z6vb<)*j|;ml=ja?f$_C{*AwXyBq^ial66QLUXN>D1JHFs+ZI=+OjSpug=|!FzsIBE1bM?}2FN0cc~Rv9B*T+=niMq8ytdka}L3;Dl~j z&uzgP#+RdyiVChAO>&i+RPxAQ&SPxu9VjnXc+Eu7MoUVUw}IPwhLvnWb#I{exw4pP zy^rRVk1k-|-0rI-jC@2`??T8uC=C=VD*c3jf27XaILv8qBBdH1Fm#%wV^m3i3SV;9 z%C5m2>0q_dT$YWcK8COh1A``4UyE3aTk2jD<3vI?AsfGY$PB@k-Cawr3^`1ws*u-k za3XI&)lrg_abUM8;+Y!c+gpX^P97sn5j^KE&yc$9#O$Jp`cW%E;Zo%&EZO@#CQ$2U z!KyBo)wY-Hb&INKHU|o z>-R$`@8yo4+a)E~LW|&3Jyml%U9=mJV27@QROm?4dJJ;+oq{kvHk@k7BPASlw(oBH zp1kdC?Q{5GVqU;HKIU)?tkbRA(Um{Ra8|s|%iTB&rb)Jq*3%e&@E`-C2>4BSnq6|{ z3Nz!zJ59wCAhEpS^D6_MCggi8ef?S_n>}xoY9wshT=|%)Q?rHMNTLR1plx1a7UAZT zS;wkMR<1GF%I&7M5;7(vX#--cqAj&a6Ri5e@L67XJO+6xl3&RYMtFKBumQIEBhL^ zoHqEid16};OoADe0qrxDq!-N%mn`{W^Ug3vwxCbi>jq42AocY_45mSLS}u9bT*IX; z2Xa0F<38c*_O|75)u7Ig)}AY9Hp5DnIviZgH4uF_Jb&;T!MX5jRjdh4kw&gGypOyWK{ycHm~1u#~N1{ zgI;2WR?Rh|tDO)+2%KuSy?crm?58^GvZlG{@!eIPfW;=Xxl9R7L=aWonq{)z9VkUW zqjm*l2x}ktlD) z>(?F&rwRJm&%jkoQDQ@*QD}m@WZ*`ngiVy~mMXT#{Ko9f?RswbI6yFQ*>VYyy8U(o|Dp~tY9@>z-9Zj|mUOBJ`uNU2~vTX4yZ<;SQc zKiV^$uH016nNYGiFr*X05(%wbb$%yxfWq1-LRdS1VyLevpJE%G&?|VeG{A ztD;XKvTZK(2tIRjwK)5QaqeF0=nLk`nhPF#k_iJ9e8PCVstE1(Y1)^%Ph^9aggfgg>47M-7j|Ks(&+|5JIQU6^`19biv^FoPUt#3+ttnG zF#J%C4O~y2U2r|nXYZ~k*@HS9pIwp%CR}CI=f--6UTTU#VzY#rLN@#%`r+=N(YB7? z&xq(nv_-DxK^lQ9q{c#cVsmGAm4`D5*GbrQFR|V6iW2q>+FJ>RL2vQ_$$0W-#bY}< zGa%_m%hh~TlD75W3jQrZwzv{B2d zGpUOOU_5b@xllZVMADPT>qA%4P2mm;+9%5;gwpG}nTm+G%aN@d8(FuF#61Y4CZ92O zwtaFmCAEmBkAjhg7BMoU%10Pyx6IpmXI+G{k3F$cDiNF*Bsci*h0UbVISO*EvqX-f zlFI05kRpn8ND850`f)L-y6c0Omo`ZDb@91!25&gph$zm~KM=3vZ|$@SuIQ~XbF0w3 z5cZGtR`R}5Os9P!-t_q;%Q6IcVpT-JV+o_cYkoE-Wz^1gB0VR@UQDM#Y}uVBF)crL zn>1{LIgFO+Cm(O~H%&E+CKfWoU&sx=Plywy($JwW1HLQ}sdgV_$g*PMf>gtJkaevi z5ktj+9TDjA3S||Re0$Fl9%&f~F`9ZZ@i*dESmYo&MiyTowp3ErUMpNnAU< za3q1I<5j7bZVVCS8<%1R-pIWhMy>d4+KfnAlHL*ItZ6c32a=t@vamM@D1W zlM(T2v4sdGRLrMFU~<)nEQaM4sX#7Lhhnd8bw&7ymOhq|%^B9@5fiLf_)x(!QG3JS z^HEErX89hyd|^W-#!f-WQ=aCK%By{leD)H7gB?N1J1r?~_>H0Zso}f3taYz<5eD^^ zW}KrM*L@QM(M1G#dWv3Mm#d>;(mPy%SjImyXiknE`V5PepC?M^v}*3q$3oU{~) zrDtnpq-p{5*^=VXn`}19*&yxetwG(ydrzRTAEA|%B~GoPwlM@=xQWeh6F{3#F1Mcz zRozuvN5(5<6lVh@BBAY3;Wv6*Ddx_y_G|w@Rxwnhu>wN+N)SGUkKp}<$Z14(>m5>4 z`#+db^C;Ze_OFtw?JdQ%BMp)Q3W$P-2fH8;Fnyp*PD=JZ{c2VIDsay;nROiY&k2OZry;Xy>-Q#|o` z;1rS4rJtA|!a+Ut|A@+kc-!1ui;}{FAD;i-(EK-t#T`67a2M#9vzR~F;RwfA4|uQ> zJEz>KjL-hY36WOH+o5)2K5qx^s#Y=p18IbtMC#)?Wu$1nQwP~9A*w~RI~SBUD&>2i{;%n%9a24Ep!5l{ z=#wU5$PpeEi<|Eh>nPt)R)6<$nU%DQD?4^vu+;9(%h0tth;=*c+ow{7ofXA$rPIqe zN@2MSH^V{)db3Pd3$$--eE>^x^J|`vchvv7m|CZiAq8Z#So68r@k}9-4-#ZruoI|8 z#&uz#UU8*IaFpRp5<@aH2MJAb%7P3V(jq3Q7Qste=(n)i+~4Z2=y&B^i)~?pAdNMN zR@Dtc0p~1DiaiN)Xg7GhdJtY6^v69?CDf)36k**ZJAW}0nfj-#;?RODk1QGUdQeBD zyAV-lfV@UPZ@1VrNtW8PWRReZ+%$j}LF8gKmajSu+(U@|h)T~Fy>OY7VmcvKAtPUr znw?#ON5~%7=8w1Ea~}+&CdE2?DoAH$-y^-$qa)}BM2J`{9#UND_Tj!Nlqfbj@To!B zelC3GE*hT64IBG_PL>`_=*QFSJR{q&j!JS)q26cZQaf2wRX)YTLv2UQe?$;R6tfGN zTfR}phtNAqu#{c)jXh)%{Z5|*%Ze!#RF$0bcnP6CM^W^-VYTx*Vs znTXxhZllBeaQgn?DUf=PK;m|RTy{|Co0n)hVt`ZlM1dYK!;AE-$z7=!z69Bb8o4uJ}N66g;_0L z+n(D{aB{T+gc!&y>{yp0J2^SjZ`QjLzu_3d0p)}(NoX`AYBI(9#&g6#T)CF1PyC41 zO&&&cxA*(18n*2VBy03X%UtZ)o-G{i4p`!~!%tBKd4?UoFr&lI9!FGN2E?@kOTKN!ip!nCe6=CZXSowAdBs67uC@O3ST#f;mezpD zacsxMgamy|83OBn*S$8$ zoQG2uF@9EE<`P?gaC+|snWYrdX#Z(<$;(#8fP&uoYSRcU1f(S9~-Eb6?I|>o^*$uL;Tvx$A*cS+jV5M$Yp27My+RI{BcX-Bok;2)sFu zVXgP*=Gtv_m+TFHWVK+~HH4B6`Kb8qH^w4pOUJnlu=grS$fD@;3JG6V- zI}{@NJ**v-lEk6M-lh+*Y<2Z>&tM@K`GY9k??qD+IaS(`&tRP~kj*V}wLK7!U|_^> z7~#G_;zZ9dm`W_-l+By+f~{en)}|9CP-#MbR>vu>1$etXtBL0jnY_9@eIjqFzQJP4 zXh|$t^7P#$ZqFEX>l4i(Cn{$zX}QVb34=Z6k=4u=VZWcX8)Sw{FKD7;<#f*RNFEh( z(~iBeUFenL3%?8&r4ssPY86!uOaWVd7GHUl$m)}ZBpp<&tSU5Q!HU_2YN$!8ztJIP zA@WUKUQmMYJXST!zIE28Q%>%X{*!_8JudH3_3ZS)cfRyD-_R1ioLtCNn|r-&Vehl> z$!pG5IqjA8&i9Sv-d*(cW1lK=-DVyJTOGbETM9@ZlEvY!n%Q`EFwZ&lI`f9haummUt+dZc4{{9Ww99qk9jjP}t~ zCz8I`cUVNZ732t1PYI>*n#2qCj(>#pFwhB7kW8joU$EcJCllSPQmAW3o?REJoZ@zh zpM}U|YFy~59t4dmS=!~(1v29;QcOgF@ZE5pH#1W~?=HCrfK1heyfLf%cD| z3Q}e1$R6avvrz*GTfPx9j8@4G!k=4fF00Z%vm-+Ll7sHmnCa!*c<{@UpA%yA^wI7a z5NP8g=7UnIU4BbqBwg;aajUrhc9HgKz)1%hc`KK4)zO_AX6Rm-x4R)9oiKJO>H8`p zd%hX?8~beOn(`>~z>Mtoye!*~nmpZ~yHqldqDEfl^D&W-RgzUReb~xWm{PuVyHKR} z*5m^V$dD#YNSn+!#s?L+gyt#=CvU6cS&uiTlaFfSA`}|{KM`#LX>)yOF;LB1vEhLdg)zt?7M>go_0)Rl z@p9~D-ywct6(fhvQOa@8Y&hJMePprxrM%esKSI8C|7ubN>G02 zf<7S8Te#wpvF4#ZDQ$K!c48QPwBg_=B^#|Ly!4lU|s>bRaH zZ!G*|30rgcY)*`@b=WLb&@R{Q#z=!c+W1nwuH_U@vFNwLNQ=i@?gOgo@ zat=LaimEF|5Wz6aH@4Gs3c{}Yq-}k*wVLxS8_rZ)A_L1eIs5)Q#4C*xq4Qz1EyD058S4V{QiV3{4k+$wB&YN^BF8kXv2{>P?WBzrf50=e5(o^4L3 zy6o0dzbH3DAi`B}7d8s1N2VeTI;b@*2kdDy;U@0Y0H2r6dx;tAC7B$=Ca1BKXRAk- znp`PwEis=4rYDe=%bpt}gVIn){fDsp*I73K@>1ag46Q^b8NLHflQc{yx=2D!}o#NCb zI5mo7G78qa1&{JlNl@_K?eLKzU0_6LDS=A0q~MdVYlewZB8@;>PO>}GJH?@QhX>RK zGYn)L#4t#u0ZO`lsE1vbTA%4d6q#>5e`7=0OSq{xqVBpdQg4jT`r)gH`<>hWHO-J$ zMTKR2rlW#zUzQQ}h5EiU!ps^Q6W#G4;8cP*x;ilAsV)l%^0Gf{IAKZn-vZ zs}rW4wilAs!vrtcdGM*e4o}(H_pK}jJAF0!u}BAUw%M{*(=96}9CmE8fLI_ebAS&m zh*6Qo*tJPL^hq+gz8Xd`Hp&wkYC$mq9S+#ZqRZxDH~ zdBvB527~2bM1W}08lCk~nF#}2vlrHegOT~0y%wLv8CChitwAC#cy%%FYpkI^THkh1 zK7!?pUtxdpwPCV}*jSoRB5&PrG^F!oNk$x)%rvq1Lvx+TiffrBk{cij}TzGa4#>s+bz% zlEVy=KuWqDqQ^X#KHZftFeb5zpeBm!m`zv~-6nxEsqjhIT=i^C`%bmcg_MbEYpsxh zJu)XPH#Lf^<>Hft-eF3_QtS%0nAkee|ABVMfb$Z$*dR2JFSrA>e%8uX)1haw9YX`v zlX|&lLSn-PR3YogNQsMP%3dd7Tai=-4SC5jC~# zGHMnN(XLpi*Pj^sTfgQ_FZD6IRt@SlZzTa?Tz2>hwaoR0QkPwEe%*g2epD7oQn62^ zdae&Kx$k3SEE8j!|JvkbS8T7_hkbplXi(8lYf-Aglj?P({(g2DqUFjJ7uM_Bv&*iy zs9rzbYpk9hofnMCb%Qqg_PN?_R1Y7`m`dKevbBJEG7cU?<&?&vHIQANkY7Vp_lIP) z;Dl$MFT{4jybg5C(VVjh$rw%8AdjQEr{mmNukBj68YIo`3ll&SyQ01f)+X#W!gx8D4#=f25zR4Uni!HV7usd^-Vjd zdU25Hnv%__J;;RP#f{6ZxTMabdty{DILtuuJ?lhmM$&0J=JxboWTbWS!I9A{4@(^LCPzZ@!|QPDyHa!E{s zd%1rfmeyg|+Gntu*ca-`!UXl~qL;NeT2ULf#a55G+s}n0WPG)=;o)j>+ZKD}427E0 zEH3gWnR7}dRA8!SNHVms9nWVohOPlC>Z?^9r%=&&x!r<9Ikq)pS>1tArpV&*Cl7Qi zJ%?&$Rk#YzZ1*!dSbD*vA$lgNI|Us{Tj_u733#-jXlbz$i|-tFnRw=l~FPU=t>bh#b{(AQ$&=c7L9`_d3TduWKxMjJ@jHf zYb-2akhD*?F-H5@U?JPqRMHhQ+eE_Yqa@9A)hslGDFXvJ4Fcg(}c9|32!(ysqjjWF5ynn@W zFPwhzuBj((zu>$}cAo#_^PV*Q#GU6&?bv?Nc^5wU;!DmyfA@t?zG&wKH)cQeQ#Q4q zc;d-VeBu)?UTmDVoils+PPfWAo?zv0LpSl>TJA9RGhtF5kdB2FA?P$3d*mveRP7PmOYsgE^mdQP=`EFPwQcvC^1ygB4Uknsn*JgEZY5a zujOuLe4*y9vy;1WQH0P!MI=T4;#-<=#3@osV=3d(8vGr*`^DTI2QtnF!6p*pS@kW%wD0+T54C!bQS){vZ_M5a@1&NCqrZ%krghB4SMT#uq1s4>G5i` z5fUaHMu`$2hA79?s2Ke$?5KhA%4?&i{GzXe84cR@8QX8@ivn(?)J~q4j}p)`^GvX# zc3LJh2VXa}-AU2wm7Kz)hFq%V6_YV3MwXDiD_;jsc#5cP zr3YE2<6)1=zeY)>^RN!{N5j&EGy9y5g}u;{DLirYa`w=6R$>XQRkGtZ*X*k7I?z-u zK+pQVJJVwztc6*oCtA`BnSJfKb&jyP@V>)6lE_9&(hz-_cT2Lv3iUy)K+!gUTn#N@ zG}dl8Sg;u8myM`7iT*2x(9BkSO%dymzRY**R3KC_)38Ek)YCYKT1 zAH-;j^CZP0Pn%cTAiZvX*tJ>TONYo9NPoy*HzvveJgLkLw1sm6sT^FOr#H?KIn8!+ z7h97Ya-Z(lUzx#kfmv!QISW2Yew3vI6BIQ2G@)<-BJYCp^%n&$6<~XLeAb5 zaoYp#eD5Nyi>GOY2)tjb$zmvZ?PGn!Ayrd{)JGjs32|s0)ZybC@j==&4I;-mn7`)e z98x*ONZr&SRnyo~Gj;el(sAtnR8Oh{VGE8rk96j{C+Z^fi+v%RC7v_I92qq$cq1l; zM`%w8OWc7T9qnnjr^5tCLo^ND5Ynb@+GpVxC*g_vh;!3L8jp_duBt42a9SkQy);rb z7w-GdeeNk7&GpqNZCTT=9PY7J33Vz7x5}HGp^;5ibIY@fB>_sU_Rt9Pa&&;Ge7GXP zwI|D0=Jx-i?R{XYzOp>e>t}3A6%Gg>&x;wF%9)+O4c5%U0<+w zWN{UUrPe(PcOyp}W+ZjJwkSG0k6^@>a4QI7^MLR|i0)TI_&}Spm>1f%S}nOKjAvBN zu!y6sqi6m5p!9w=Uy11SP4ngNDFDsX{AHVYq~CqjWTYu3YL3{6leHKqt6H@|Rdr^_ z=G>Y=uLCqYWsjDMZ=Lt+u;Sr+s_jnSQ7CyYY(_rnl!~}iAi7DJ{X4-7Zp4-9rBbB z&S$7&7(~H|wL$FIiSLa!WSzzq1OHTQ^SV4lwI5u|h)CrT;m&`JVFi`ol;tX0V!bjvd*jbuvLyW6hO;e8+1azgS zs2*W9qSFl;u68J1d=Ko~z0U*zqq?x0!lm{3GtS%e?C)7+hCc+Df?)laxtpI6FRTAox?HwLOrbJvn6J$jLYLgA)T;;G`I>q=16%op6 zl5r_wjQ8PnzGy5C89jaG_(8Zq0h(mRgOCyl3Fd|u6>h-BoYZjCLeKU!zMY^`R}way z!Paavg!L1YednC2kMqNwH2Qt8Qz%`o10>RIH2rwM%ac#P$PUEA1YuLyeaCS;`4h)2 z{)dz);L2~Y+sc-8{pr0)g!dq33QF$U?$R6|B>-^>fk^3>Ys2Xy;}a7n!#Br{5_N)X zgvP{vN|>Ll2gZt}<5P!Fh1#ei;nok=6O6QDg&3X<8l#6|Fd8_lBU!jgQy2oSC|}C* zV6fx5K?+){*YT$&6hMFr#!jcr%t~sQRVJmg5oTkqqnf221jTq(W-Vze306dw#LmY) z>?=Z+iED0m%M&|m^}%wQ`5f*(WdeC zjvbkn##*ms_+k&D)%Tm0mNdJCrK7^TRZbX5A}I=vIf&EqULS+iN0O-2#0()(=-<2} z9b}YRp!>0Gc5x|^F1y%u8<66#v7{mwKYI0pS@;o*kC#o*bpjQlu> zz1gc{N5@k%w|UobTCXxGKI9Q!m9b~dwxtk}Pq(--4~m~mu%Kz7wh4V_YUX2?tzC=E z;FCUM(&H{swXWy)(J7sBn)s^zW-k#ZluP<~m}4fHILIh+TVIUQS93T{yECwl$QfFB z6cam%u5q!gdAKiHU^!r6(n)q14KC8ji zNnYP+Ghybaf>Jc<*|u0NwLYPt%ec8|C{#ZQ0 zf!4MjZX>hGtH+pK}#bpM~4bNgd;Y+Y23=7=|_f@P48$^L(?+SrZ58Y)PDX)1G$l|+b$i&W8@ScOc9wl zyU#p3L8DGLOkKUB{c4T7u5cD1yj-hIB*C^lB<9vbPIzcz22|~ks5;DtM2NaykYQ}I z{WUBccQgE(=VJ%+4ZpgW6tL3&V5WaIV6j>IZh>)($1JEx+x!wgUaRAiaq|uSmvQh_@k{bkWJ!mRWgLPQMrg%{ zw5RE%?}-c8%r+@1C{3o)N+M~AIZz8Jxw)R4m&YuYX}~A(#%==bro^6pk*x#KW2&jl zpl?NLM^&ZRgpKmvlkB>QF$Z%EHSB&QURbwQu-4JH6 z4NpwWaehV=xv;>ZFip>TZ>7^l{#phh4m(%kq{2-x>J(iYnUYw|ynliM?C%d+7YJ;$ zvuejqV__6!v!OK+OB+rthxv}J;K+aS$doVR)SpkkeE2W}seO=UySx4ezlcX@iIXvUY!5Zv=^$dunc%%QoylODQU9bEQ)Ex^b7J#(W7cGh-CR{4uWrAL)pP{eu$B~Ru}~ffH{UK2pstD&IvZbPq82R zU|^d-*cDh`XU}T5b9a9Jy*ntunk{8sW8s^B+>#uwlOS)wAQ_r4ZVO__sQQu8Byg(CsiquQMSmKWMaN z^C{1f&WoLvMyIZn4i6q@WG483&~R?*;HV zBtE!*FZD%-I@f8mj@kR8^!F%lpwL(9FmvDE+xT9x*$9>XYVGr-ik1l~5lWc^!D|>(DdX4EqVL zFVG84HRYeEiP@n>T{ksTn)(~&TepVaqw}qii|@Q^=W^*c=G*pRD1eK=NXk0wfikl= ze%_em^8GXO#U!uxXx>O}YlfKk;8`2ZX6>CzpB7={;TF~k56RSA8ci}G{V8n_R^PsK zv`ddlnlv7=sx3?9mTl-awcB#dwy-f#vIV(VeY-j}dDwirlDTs5;O%P3HXTErHSWmi zZTrB743y)@w`lXJvn{3P>i0aP5E>wJ!TCQo=0DDXuRY0?aYJ9cti3`<$H#%<7N-BME9rh%i5JE z?QPv!iZ}Fr68Vsw-38b1eIPmFAbED8kK2GJj3#VQ58=MqIxz?G1JVKVlTX%Q>;#^0 zRSWEzuS0xbwzX`AnEDg$%X&3WH+e|4QujVbOOp9BVa_t!+|I8^pZDy%Mh` zEJD|_HlEBbzS&BT=5Is8irEst*dJ}Bwb>A)1rO3Aacy1ha`|F~s4>f*ey)}FzYc`D zmy>9hL*n8@cHUIKA+t$ z=+zSMC!NIhXe3QzoR3qQ3C_<#xb@UL9lNl6b{pYd*%J9#^35jDZ8==Hc$7DM1uI(; z=b`g!J!?y3l^Xn$mZ+2DCJQ5%bCy4DcsBTrOS4p<4=vflY~9=zmwp}UoPc_oaRdFn zW{T$W#y48w-dp=*t@X!eEic1id5TzkvH2;1-K|RGA58>Y?-VBen0$~qrrdZk}au>R# zqoiLAo9?~Yr@NZ{sG9H864pDy_kb#2D|C*6JJ@$i-j9Ka-h{>SQWPB~-4f&!cb*8L zCC;u#zPyL0Xt^GL4qc+C#oAibJN5ZP=&s|2bhdJ;89W6`uccRyedoKlOdK#~L1M1<%YR?Lhr2B7RzEO!h{xT$>P-O<+Rh19<1xLuwphW^iNCHbvA_DQEQ10^W|f9OX1G^ zlC4qpNk-(LS-PAS7H6onqZO{$2Sh$5n#F%%z^$rmsQeq2>qDju)=O_4{b78@TEXLX z@N!4%t|v?awK^kfm%)$Nfi(ZD{FeOH+u;vNj#GD?tq&IK1vKU&jC2l8tkAiq5 z2eX#ALtbr)MdiR+k!KlOqvE%qq0azv;I~A@y3d^hY58ns?FQIlE4f+g{ghVB1a!=3=#75G`pNm!+)FC0(1_T1_|quIXdjFObpA zncSBA7D->@UYFd~zuSrq&CcQH3BM&<-Z(dzXA3QPUe90!6+=53{s}4hwH(Qjv+GLh z6Pooh2X85gE$uP?_2{rTGrBGE*L+v5LrWvP56WmJvDfs~@p-I_hn8kTDOPouHs-VL z9=dliT8aVKzPXmhVmq~7W-cNR<@PK50jn=B=BTx#I<5H^Ptlgv>E_W=OCzlq6vvd! z2ky68k>$PM>pJuw8e_*|UX*B2qwSav6;8B5g*@3$JbDWHXoW{!ps#i3l@81pX7Ac; z)jQP+_-TB?d5pzk_fBn>V@sSQ*zyeK(MC(0q^OK6@qBiqwl?>!+k@nZ=g~TJXJ=Eb zMZ*M7Hb7p4W~%dS@o+0VHb0oTGm%GHX=g9Isx^MakRRuI*mPOw)Qna!a@i*Bx@c`y zkf6F=%%wkTJ8Hq(hVxtU{Stgmh#>>}|~MBue{*>N|UD zJBCJ8(&Z}IpSk7`s(o-)>Chqsd9Ip$WPDQgezt;xb`E(&DFfr)>};~$&xYM*cQuze zp7`Rv<~JI5_p#INd^FCiQkrc`D#TmjaNqCP3`<^UeXomV(X;Bzf_%y#Dn6LmvA(4l za$c#?Jj`eH`~@g(t8D|#Fg?=b6b_Fxzm?xUk3D*;o9m66_u`gA`artec7(BKS7R@a zA-f7T#|Yj#cj?+#J~(%o+)=$n`P}hk_w`&D&ts%-(OTYUhA10(^vNrqJNmbvT8$6x zpSxEL91*2G51)DR#>Tl@nv+ZJJeW)Kz#dzT#jT==d7KpP&5zTnY>kg@QAVry_Qlep z`Ec?qN6yzG+ghnDE0?p{*%AzSSCYO)I~g7j9(g0G-2(EsUgTfYZY$?1Tcg|Bxk}_p z-<+j$W$$Zq!JdcY&Dm0(9EH)jTFNV(D|K^HswQ*y&DBPO#631wsd>uVhPmp`DQznb zifH=PGe)8a1~)hd!eHw0mg| z42gzhOVsdU-8Y+`iw!G6jb&_i)ZLA_lJeSKDfVP#Ysh;OZ!(`~!lPyvz9x_VR$m{e zUy`ysjXs~c(#W@1*@Y`_-~Nk07-Rm9l+3HVVw`am5{^_fs_xHaM>hF(>Z3;3J>yyX z{lDWdMCtlIFxB<)^NEV7g>l2A5ehxB??0Nqa+dF{l`GW~1>+hP+&nwBmMyEXb`OLI zhmCDBD@w}CaWciAw-sD*MI~z;X<*PA2SH7C+ z)@0(o#=3LJ{@S@#=<_fQn{QP*U%udtTeczpNWPBxW~=h|->Qv_OgwO_a)Oju#eeWt zWwj=%5zdrpgv4Us+L0E+3|gAIRTJm5a~XxyvT&jE6oboXm0zDUT)ZKqEb$bzR|=nS+denfmhdcGFK= z8GbZD;OEU4{q*flv&`j<9(D(MB=6>SwQJg|*5})|m}`x<%)ia1nMwK!)Npz%>%)JE`rE!-{RS($Loiz16DE2!K>5a(g_1+{=e{hg zMAzKC{j63fvgWHWdAx+SAZFiKCPCCXMB|%W$^S}osbf*|#z@{Q9CD->K5cAi9o-fAp*)40i7Iu>=HX9no~@z& zp=;IVOpmqF)-fBahG^|*EA?}^=>F!f*XH2XxF6s5`UroOOCujv=RwJBFSq2$Je#mG z52pCD_GMje3Hcwj61iB{sJ}L^1)pElBOkJJFGBgSdA_^;oHCLtB1jvarEeZh%8vsy zk7_`--@1k{Y>YjH&Cjy-zB~Gzvab2NM1@(#UotB)_ulF}P6&_4a@@L3?hN&=`VI}j zm0ulqmhg3rQ`=8S^VmnYvoTkz7q;t!*;E=Ed2Dl8y#{-+d46?{GSZp-CtI|8F<~}W zJGp&*I$KlrCBX(O5lf`d*Ssa?>2kY|W zmxYz9vBthEwAorM-;RXmzHAGL{<)+2NEYh8xT2Zmo;>-6dTZxXFSQ&Rq-3sEa_Ks+ znu$NMcsFv}Tx}){b7}KfULR*mrzn&w;SbK%_sI>v`1*A;bwnfsq#6q zd`7Xpb3*(x&$%e0c>8*ZMvh%K-u%r91 zl)_0|pl?4~x19T!!}6<*)ILrB^LWh1-)9`Mn1OTE=2s`~J>ay({BI)#xmoLlEw?It z5=j1)Le968kKL-KxWCcYXgp<$(d3l5x$#sSMDgBPUqW(f+}yP!Avs@9*3O@tYyV?| z%x%2&_dqI}PHhlp+giYeR_e%G2-b>rn=0}p{%n-;+_BoMR&HG!2|j>k);Z(lc8lWw@xH;lQ|+jzSgIkbD=c6Bn8d+>H;o7Q@6i%-sa z&xTulbC}!f&9|$OOWpP^zTo>ll9pC%LZ_dW7#8TP;?TZ^v!I&i*7jz3vJIyr7crOV zs-14!ot@8zZk>PZ`-p)_DM3_9{3p2xozbgb?7U-n-O`-N=7i<)zI|mb#ZE()YAHsh zQ=8KYJ#ae*!=r}%x9I^VKDPfO@_c3(I(_~Z7W{!qQ1Yzq3kG#2bCMRQL!Fw-2HF!SEpY@l5Ak{#1`ArZ+lq@(+T}pHxb-%A+>VQh)ZERoA@P zPltZUFgcYlS(B$&BaeC z1t;so?XWn>7isI^8RhmiS{`ziW~~a(@KoKlh2B9M`J|PB-z`o^&!PJTTJy7)c^A(O zukTA#`7=<`%3CPvjxhXaUK&zfmvwB^An>CGCvg)V9M7?Z3sp-!qtk z{5ye`I!A)F(5WY3fGur5!atR zx2xBJe|mimmUcu!+odNNXK8z_*7Ce!wr@l(^|CTdPDVdTGft@PQc0cceT22#&tB4Z zOh+yHBS`!bAkT`J#9GU>s_42;*4f6i+r9c&Vuj>X^wSrn;#wCg$oul@gU6-)@l}}Mp9&=u_+e+A3dbeM9 zr&VIRtwuzs)txY~);8>gCZe)pK)3hasgJfeiN_ZPT$c-qYwSU2skHQR+ub9k1^8VM z`YXRRom&paE+2|^U`?#a?+V=I(DcT&zr*1T8a7!_>E-x+3OZFfM0N63dx^S|QNdAt`>J6;s}*Wr`HkamZ#7*1Vb&_PRu?=UHDj|JOe>0{YdD{PFS80e@R> zsC$N49G(Inm+a?^@9ACVchOg9tfkI-FIbVwd#EAuB1_Rb)VrsG0HL z^dx?eWq66V;ZE0wyNs*d8j8{>Uox9w&X#|-w7=VmLugEoKlu7)8)t;@8}NzJ-71;Z zc*9bCUnQLub>3I%<6q}$=P%R1JL>O~es&I!KQL&m2%5Or`PIEYf8(=Ves|)&=jf}; zk~E%kkn@(GVWk$sWBH|K&kI}!i>HK(f5p8L?LXHpzpz}8Pclx@wviYAKos&EVe+!| zMe;0fTjQpa%+HV>gHK&VDXL`sYCaub=Qd~cgT;Y zOO8cY-soBw+%Xx-BpM#<5#h2u(LkyH8a}6j4{m)5l~ou%M9%D{!e4rd7F^uiv2+(z zBA0qthvedt#zu^);1zG+Mupe+a622)@Yw(-VT~R5SP4YJg9@xYIX4?atar@?W;+^V6BHd;+hS%YLR8f{n|rqdn=kk6NVD*!(m9ighW=$pnp z42WvUP1|N1T%U%9)!Db%Bgmd+#5B^PjRjyh(zp&sn!(Ngta813sW(ZO(w#tLqSr?v zlXIu%>;Zmq9z(#xY2GLQ~L}!AcT;lzUdM*FAx@wQ0&CnV6!~ z=^CDO-Jmg$qZJm{w@@k+sKXKA(Y@Pjj5Na1=V@FAZR(KqGu+NP9i1&+9h~chP1bNg>@(OBW zyj*V%HuO#he~Vu=F4|`2T1)W0Z`9BMDf-Mym}qJt{!5h6hIrM>!=kA-i!)H}p1YPG z?W^g?Q~a~Z@6zpthn!4dsQ|UY2QS)$<-6piCA6@(zT^?wSHYL!9pRRi21BpYB|U4h z)R+^x^_*O5y{*FsVpo!{uvFveqhVp9yY(!(q^(ZBYm#Wk>Mi&FK1+@!nd5i}VX>xu z3M35?uw1PGbf>ONGFFS)@_FjMoA`cqh$-AX3U+6BchHVZ5yv04dZLmJd6!mZR2g+m z`TR+;PEQuEf`XDxrn-d4V~QguC1F`j5PIDMs0U2)!Xi6tti*H4av(=%nr)@}pA@^b9nDkgl3Ys`HQCi9^m|wojbzfipuJsAI!h-%Oe_EQ#oz!)SYbPafwoqiLURfXy z6uBK&2rF@T=oEFZ<4BNOBgrg$$C3QvlEXkdV)|qpC>{*t2Wq>eOEn^rz%Zc23=eX6 z_)@-}PvyTF+U`=>?Avubn~xR)gbF^oH{-RNLyxj?ITP5GdETaL0fps}(9S`Z)L!Qh z%`tp77VexK@->-W>?JLo324-({Fnafk`?GpZBi?M^uvK@jGG3Vdh7n&~*P)W^`W(CQ&h^3*Kp446b{+!cn!P53;%o%^E$fjEy;(FjTxG zEqTt}W_~N8Og-c|NdL0_oAd;2FLipaN<81Sic;f}?XOF=Wv9j)!lm*r8W#v;Ed*Qe zlD!c3Ib_#q9;8fV6zn!l+$r8huYadL>R!hE?L=GgiUOco2v!tH+FBYcd6+8Kyy)c^ zC>jvHQHS0rXLECwRt#g`x2q&&hJfKJvzcEjrG^ug?bE zE^Ag9?650wSks)UQ#J$oZILBQG4-Sy(!5!tAYGC;vMwJhLmOKQQhset*{M8yF`MDk zZGU)BQlNv{nmr6jsWf_H)JH!eb*q^BQ21)Z`m1L5Tq?B?t|PMJ`1NZ1<>hHyl@7)c zr)_RJNsC8fu9i+DaW3hiGIUvy)eE)$EmjmpG{%#|$H;o#@(4BB+H51bSX?C9yLC0L za9V#mK7WUHJSRboz98J-KeliPNTyE)>Yi|&8(r#Y^DDb)&p9~pRNb?LE;)y?ZiiCh z^VBzKo>_CDuViOU$&1Qt(pw;D)vVer_aVH#l|=gM{3BiLl&#lNiLm%FxX%9%D`uAg z>Q@?PfKMZtzYi@GEB&@Md{q`S7I>BpwKhc*~973XXch>VTxXxKF*`(^&tNq zX8ON{RyOf=eHbR~r`AcVJd}H?={(>hV2%BRympS?t?Q);Fr|B?${Q3j`x|2*tV>E% zu5sx0r7s_(&?O@&*GdfX0?J4@C^x3al}ly6G0Fb7%oDr2G?Jl9O2AW6`f`24V$n0E znU?FlzQhD__aXAr>FK_gC9^ES1XGINOf9%ab>5po3eKJwZgYatw5G1`>dRYF`HkzA zHu+g9?t6)U3RVM$kf0O=fkIQkbxPM1MHEU+HItj?Jp4_1oo2z)7&feMuQ)IbNi&&DQvHZ|e1sfiw&n&`2q37<_(^w`vd z&n9$h7QanR_-tyzXLHkdZEnJEa}$1>oABG*gx}^S{G{Qt_-t;%Z*voVo15@^xCuYm zi!8i{oA7(M2|rs3&WzW?P53?Bgx|wW_&wZ&-@{G#J<^2VBTe`{(uCh5P53?1M88Lx z@Oz{QKiSS~{~u|>?~x|_hMVvkZo+T43BTbc{DzzG8*ajHxCy`ECj5q*@G}da8ILVZ z_-$#zZ%Y$?@)ffE-_nHNmL~kRG~u_U3BN5(_-$#RpR{dO-|9Kbe|pa9TRms-({omz z=sBxT^qiFsJ!i*P&slw|=d6C)05*;IDW^^!JvHLDp$Wf6d!X`-_%+%Cy>G;?(H`i1 zBYutcK-e_m*Juy)zL9zJ1M*JG>hu$~xU!(od`$qaT+7G>NN#IMnQ=zSx8WPvZaX;>`{MT7z8oGhy99oxS8fZTE({E1n{EA0?mQ{v7YO zYqGg7T_pKh-!~Rr$azgk9~0R%PwJn}>3D*F zx<6$;yXf*Py?zo3?Frw2@}lumZj&~~gry3pXqKe2Y_(vdxz0C{+8Cb5kKknZ11E? z8=o8TlWu8z4}M|!g=4?>TR&d%)c<GQvP*QSqtxNt?Ow{3ag zw!W=2(rLHy#dyEs*?sZxgLwZm-aq$yr9a-+$NSEB-yiQ&4raIC;?n_t8lucVTdDFH z_m!EuU@KquwQU22;o7#+1|F-gww1QT`=jwbT3k^&_fcD^dLbsx-HdN`=>92P3k4i_6*yg?qw1p)(W;r9GuR%akD!-4pz}XjxlZ%;;DMdb)hpzOYm* zEbQU-ox;LGJ2JU&uuaG>(5=`p7)nKQ3mrTb7VvcLiWN9_g`EEW1%m~ydZ|mHpQ1f_ z>QPYv;rf|F{H${j#EPXNcdn&kQTJ)!TT@G@3b;0k2$BHwPIq(;f&};Wp-@;bq}Drz z)F8vba)olrB(h$LkA%>bQGBK+gZO|vVsxg z%j89^qWd#?>?`%@w!5@@(R$N)>rsNJyXjC^D7DjOY6pcQ9b!7JZ$Y41k6nukl`i#l z+O4vmisx>c>J$Ci^dL=iQ@XLAm*s0pfqfupCCb-SqI?alx<>C->9R&j%N~c+z))#S ztqt|~L(H4`&sDTlYC55cmnuX*PxesDCj>^8O#4)4NE?UB# z6$fS2(6v-3tte_Vta73BPw0LDCEdx3qLek5jq|>(^iMjt7Q49A+5)yr!t$WXg|?pl z1w(3|Au;_vu0C%XD2=F#~Xn$Qzi0|4+15O?8=~&oX`Gnr}>E%|C z=21ns88Ur~pbQThO@NmSpXI(t;^ z4dw5)+HYty;X=Ow5iEBb#5Yv(ycLoP00VB&U5u-nmpU&{`(m#!!J)KYmrVv}lUm$F zSEug)vrT$&KwX`d%(rnkqw@du(r}3?6nBM7S z@$E8y`$>E&egUn%__nVD!IP-=NGS3DUZz2NSE?MKC}aSMf^M)VkAB5tBE872-Bzsk z3knL!Xdj^AzmTXr6GDHP9dcxFWS3H*1__{QeRS1N*jt^;LXCqZY%i~YPws> zgU3Q!cOkM?chRAC7tj}8(%7neSqvk_MCZ|*^{xD#K|XI-zYo?|4BhvOBE+ZG%oVPr zz1yE!hd&jqP)#syQ2k0Ms@K;B+`5#nq4}W5&kX8ypnhObuY2|fQ7TN=xS*f_D1XDp z7ReEQpIF{Ub~PQPEQy-G_}HKf!9!8pNUGWet8mk<7lgF3RPBOc^9k6fFMx4uKDGL& zJM@#4_>2;tTZzxS#ODUf2D#FqbOq|b?3P?&*Wnszhm9WoP67nWl|QgDci3q6X#%pu zH7jlMphhcwaAv<6t@K%=m42Zmk>anga1lejNJG6S4)UTn$cr?{ND)Y_vg?q6TLZZD z25yamTkrLTok_|U5kEN4rY)6CR&gXUBN8a?G&u$8RvQ!0>odUHB|!#wyAffxDA8vC z(9e=HW7U}DecMN!{Z?n6*lOBIwx3F*Rf(G#M-22*y4Q<5ZxT>G53gdO)kmpue6S=L z8Z*a_guOaq*9p6xG>j){eac#&bW%*!sn4j#K_R7{Oj^aWcAfP8Oxnks3TGUh&wJ0%N_>3@Y|HOk$v1T^zi&MxOXBgq z;qcZ#$%*w=(VpKKD47zkyaVy*2GdHrs>_$H#Crp!A;bDT!`gX4tzaS?_oT?kFl|jg zNZ2@gX=#{J<*Hq;8KDpid-@q<{DBeh2eN!WFugg@FW=R?rF%k5%IGb@{*kMsyOcjr zhbz)#u33IS0+b5eIX&Yn zPdWo*at5rp_mqCn8N|asCX`#!8AK=9D)wkftV)jMED3s2|hrvaG3Ben31@7n~DW&nbdC!&M9?QT?mim^2b{SLi zOa!Y3!%XR7X-t%$p^kPTBfL5m=(13kPF;$+bm>CiEp#l_Wr;4`y7cI>RF`GC^y*U5 zWw|bQ=(0kW-_YewUHWvnOPAl&>%!?Vp@SV;p@TD( zLI=kyg^o44tkq?mE*zf}I#@>t9fb5k2XVa6LGUhg5U~p#gy}-Z!@3YK3>`!XLkFS3 z(7|Uyq2p`1a4u5lcvKfonh70`>9SRqQC%L_Wt%SBb=jfI6S}aeBy{Z5WtT4hoi5+d zWw$O*>hhE>PwTQrmuGZ&R+q9a0Z!`(a9RidhXs=VA4xubzC$T6BeTSU^C_63rlSK44N&vgi$4}`2=t0R^+$7JRsrC;f=oY(C-r8du9K`hW{ z)Zj`DX}4D`N8U?`dR(ce^y_w=GHt18nI-y|!WyKTt_RNDL;pkeNfOjQkhTF#V9Gtu!^LZ9%s>&ZMXD{1X%ma#}#aa05&b zI4J^j19TfP7Y3S+Ktj<{=6n;}N;3!0o1*W5uA!8#PN~i5t4mlD=`H_p zTW|SKN+W2b6{Yf@7D{70qW5U(Uy)iE+F;9(#lTN5QFLvw$Fj^=fzpAlfUj1)PRERT z<=0E)*KOc1df+iG(Kz>oY#u$C`2hGJ^?`<4SwG2U{RHh&{xi{o8-G~FQu*zUg#)D> zO1-NKWlH5g*X1vC`Kd1d2bbwjMC@2kgYtZ78Z`z5#%%=0OOkrQ0dZ0{v z<0x#UOeciECEJQB02)aKRmGIB9*jBU?UXTeD`zEODzOZ!3I3FPEhR29?ER&x-)1nT zyf6AQgg;_k*ot@+3@BoD?q>Jk0xtugzCwDsVNYEe{(lQsNxPNGKW9n_!0H=JTPy{4 zGjGwWcYy?-stf0CVv@zDmZqFUg}+q8qhIBB4dlCG)E?f-!K4Pl$pd);CAlpB0#F0+ zVr8Fe1gV35pQD#QjQ78e_rK9H0NDIe9HzvtNN9u@K$K})X$PFD`zSQA0`D5VDe!iw zigF@B&ZPA6sIabHHsMq6C}`=oEqzJYk2%mWasb*}{@c<48^*u2B+z=xf9D#lxBP24 zO=SGqQR^-L=P1qP|5B>#H`>sh@_Q*&c|N7eAA9Ookib*FOsUGg(zyU8j8L1PR~7)h zM}`{;0PQ^Off2p>P!lvy{!&T0qKSQ%(zS+|-J;hJZdH&^Tj(iGxh5%pXuLGV3w!;G z(#RcRobvT$2`sZFpkqie;2<5NG-o@5$>~}3eihE96~!~GUH}(> z;UkXlVA=G!vdL02>q!}6W1dC)PARo7W3b7Fq_R~MLyw)iW-R|l*7`6mwB!zf8#a8m zI-en#=M-^}&uRqvBdI}!7pl_l<&Onu(6B<)o*U#H7zo3&J)#@!(k+aYYHZczvgEvS zA1JMHtUth12$Pv%dsgRAKHkf%dQnY}(SlXBN`{vXcF;U%@Ltar46UN&KGsvTRp-`d zY`3~|aBfXRtGUNhGovQWCELP=B^@uPND-|MtWr?{K=(YVu+L=wIvrCN`u`#5T zj}S1t%nqy92hevt3LeM=#n;}KnN3FC8K$S`Ah}N(bP-?Pj2Qt^RrDgmB)EDoc)5O35v^)p=4(Fu`&KmAM{}(6dsHQDh^Rp05eFmt2Tg2;~65=8}fA>m-8TCDT>xp z)0b?V$eX@oGK+&fEF2i=YibTDvp+RQqI_+UjQzPQ`iG0Cn~x@=x>YP&8C+z|z#?wR z@tu+wYIvnAQFz_xITCbHh}k%SbTml5_g{L9xEmeI@=%B)3yy|P-gr^vYKB+Go znTj!qxrWWhlPYy2a>JwkCv8&ji=pgyHXMjgGfMvy(x?^kN?9BP|z~3|i31 zJVXttCybbpcPJI*kyQ19Q_SY3%p7P$gQBsg^RHQ>Hz}18K5Vj#6f&C!kn(kCT6_2) z4HiP4eDIhu6svvKK{bw7hgvB1n12n~x||!q55BbdmDvJVq6meZv2;(vVV-LVRs}r~ zSAMV&#};D*8Bmhb>T6bCf=mt&T%;ue*G**9A5>tC^a!WsvFfKux}~p{&RwO1afRfm zgA_b*t*9A_N@x~x%}LjX3A9QumCBedvXtfmsiIPF=4)MngtbKnm>ZHdkwS`(Yuk_UIM03fc?!xhp@1Kj$miizZKUAs|2 z6x3Wnr7f*FkP{}+s(jH>Wgu!`;JAyVQn4$h@>WQpT3~{!X3|J3CHEjCj6I4{tE|c} zPqli8GB`{!fNCE>Daf-hP*c_hK1pRLcEtMP-Hkn#WTA-;(Fk{9_SEbrm_69GNK5`i zRG8*guP*V~HZ;1ag>pQ<8D`T&6slKYsO2PV?!;^efO;HTIAm|V5Zs%Jl+Jx(ZrWLO zjo8ew_d8l)fg3tjU;5unxJVuiaU zBIZTHVoSQB_O7fJ|5<2661>DPhGFD!t|eoCsVWjwM^E{# zFwXF>M}13$n-W}?MZQVTf~GXnMrg2KP3<=jbQ3=jG^(N{aoUuEpV*I}jdqw!Ze7L9 zD-8)qnSHiYQOmcsA5VO^p|pXaEtWPw+YK7os*!X93G#{i5S``1aBL7^>9(l=cb82l z%yGtogCmw$+@_R!&^89HyPZ3F51hSAP}nDR~i!`QAy(R*hU|_HLPV40*T^qmq?NG^1A|oQYF{S zUrf3$EhYV~qZ^YYMzKTXX;Xk|FZ}`S6@l?4TUI4&W&NQSQ|N&F8T;+E(__R;kLnzI zXVmc_HElE+f=1R|sSrz?jN`!C02EParOrt#MHKe|NVvBti}krVU%bUR{@98^>B})u zc|PUbNQu{^U%etd#w9dwq#=3kij}F6s7_!fyJ^ZaP7!exUL0&7K&iT278pFM+r=q~ z+BUtZT*aHP1#}FU4FaA4Te3u6wPf5-y&zI~{}KzZ){ZQ@5@k8hG2z@VWh{Fl{h<^Mh`mimlU8T_h15NG$(q=t(rlb3i}mcQ z_3W!fSpzhW$nKFNxy}aEw3X#sdlY-mioIpXAF{NN7X(~2fH;np7MhBL6-wJh)|mt5 zudu*qT!ZCBr%=S_-j<{hI94wJWxTszC7jAi6{Hon6MwDlGYGY%#U$rpd+w#SC^s&3yZ;?twOQCpQ_X$j0c43J`mB(Qvy=mSF}6> z2~Ew!ntRZmlF+oBO4e+#D+mA*!hCx`Rq5d#NbJV&I+}K%h=pZ!%Z%i86y`wD4DEG| zm`0RlHDWPYSRA}jJ;M{`)uZIgOnB-K7e%K-9f9S&j= zE(KlMbU`-QRf*2@mmUYlRK30NM=x4f=rRd$zvq_MQi`G>t zRWFK{(PGsL%Acs^PYBLt)!W>@kZo}bE!^+X;+faAPfm`2uX|Lrs0O4%3Y$k&@X?ets;p6Ejmjx8x6lMr zDbb>L<|072s)zDfM32gb)DURU`0fO5hrsRVlE*PdP}A(I77b>Wp)ifU-h!~3Stji2 zZ`>iOm1EC>^8}-^T;%}0T#KB`gE4`~eu3=T!=>!QjqJl)*@w$K2^WOzu8^OxKIXRV|a+gPh?@{0HxTFe#U*gy@lmj8_AD(elS%6cfH=E^^{=E^^{a_gM3WGl z7q~*gV;Q`{(GnZM8J7jhQgXCBB@^AVBxJ(WpsZS;09x{2c&MzVnr0P-spcQHWIj{1 z<%5~Qo}gVVnv8(&^d$zbCjt-yDFeMbjg6hYv;r~I66XfH^?0qZMV^bdS53k3naPx@ zP8c@;dggUV;VZ0THvb+cjx01a{~l)?mRka-G?8G7D__;+4UPROUGC819mbz`n4sAb z9%G|Wgl@K+dbUR-M*o;hiZ1wL{y>K`B@^+M;k4B9l}W*REk>A!7_=a@pFb=P*jl>; z4}w;hOV!s+(v9$NmEx&{wUp3Mclt0C?S7LL9zEj+bizi}3HPU~@&U2SZeA<m zKwpQNVPOSn`k+*PRa`t)Iy+F>fOu+oeE;7sfMJZ68m6e<%94r(fSuf#@?vhpVuqr2O;J zGXwp;%Zh{+f|bd(u=u*C^o*?zYiI2rWTk&WVDxV!49?eEd&XO%R}sGY)f3?>9XrHVAB!9vdcu3_6uszYNd+B?ue4C-MO2~Vs(EaL;6fL(2CXf`r+-~nNMJdX)TV;+|=a@g*2pWFq|-=Z&#f$TQu{9)tED04>EHj`1%Lk^{_kJ>`Sl-$eIGq``CoQ#{O6_o{2d^D?;**pA`Y+x&apYhB z@P+?$_n-Ygmk)Qo@Gt(|6J!6^>i^|``jfT)>?i+h*`Hs3;qfE){BQr_;Y0uQFMs`C z|Kp>-^S^(%rGMk7-#B{W|LwYO-GAs}zOZX@ zoTI7v;Ih55ts~lT7 zETEf{i*;&u#rJ7CE1k4&(-%BagAt%-o zR(*!m2f|h@EEN`S*q}oCx%zDehqYJ_c5_^#7gr#Dd%~+P7C{Wo?2*q;L?KPz}bem6@ z?0;+BTm97NHkF_ys=^*p#xNDs(`7e|s7=B>oyFq7J)x+z;-c|eaZMX%q@OskZui*n z!|S%4ICk>r#Q3Roqo*g=J$>xxTmjZI_=kF=Q>zCM1OmL^Y}szV9k;kK}0uC^w} zj~^O8lv{1?{_LGNdhqD+!`U|GF1+v5nQbVLY~i^FVc(gP<0oFsmR;QzR^=n#40UcU zczoi-!I!c?7fRF%Z2n~XLT}|?VNv^;At#`m5aJDyW%V_R64UrD2Xc>GjOo%LWPF2Sm4Eq?K!Eez!ku%X#h`oXsF+xewUaU4C^RDMfa*mT?S+s9u#diUU)}Vl_aUg44s4IgDL%XIw zW~+$WF?lTVK`?FlH^D`{(MQNyvs=%d3E*lPer1^obhZ~{@VMYb^)Kzj&$1U@MTqZW zZzFCc8yx%FSr}wYb=9QsSg|uM8fvCE-rs|*9WQD1kV>W1mRS^t6^rO!v0%Bdf$S_# zogg86RHxtG$!Y$=V9^s2^|BN~Mf zU_{@d%v|^bAC!^E*VnD!Ru$apZ?@WSv4Etm?bM5%g~B3mFrUTC?DX0d)|S9~PhoBj z-9CPBVhocApY5F-!;c<6KFO)RZQ-l=9E!Qhl>4o=urj~gt`{@K<~BXg9iPCr$rPQt zGh=kpqU55D%MQX)}FeUBFf2hzNOq{}&%=8wUB2#i_0bl63IUH_L!Ta{)i9<(U zJUV{rv?iyH!6CJHs8G0r*%TPKo{)(sf9}{Ein``AYz6bx-KZ}RKcZ?!yu=Y7l?i1{ zc-DkxomAFI&pPQ@lggU(tVueG{_O9+1+h*XdU;|zgt-N3+r-%E)7e3uo1<+`@Z#9P z2A-Ikd&Co2z`}g0z$AK+QA)Ey&4)9znu?tJj=3F`_fYtR0$Jd|Bvd}c5&68QtClJ) zV@33yHfDU5#jLMw;UX)uP)3FAxEgaTZfE;|e;36^Q#~?Xp zdzlQ?R${k({%p31j3QHC*<$0&trSs!glH>>lwXbZ$GFsZsQhXq({^jH{F;?}jVXo7 zzt&%v+i;+2PfwnD`5=?DTS@cw@p>sk<-A2Hu37`<3x(f|L+BHE8$ec<_=`B4j|i?2 zf;vw@-MK1)OgYML7^F91@i$zvm49rxKaROS?l(zg<45^|A`KqBFqcK(E?VJ>vGBzR z;*#ZFin*6!?wgkTX3Tvv=DuaQZ^hiV`sG`78>;Wvn|I=ycVhX=mU}toUXHo%8EMYj zyZ7R|_af@=m-rZ4$X>D6SK{j{R#oRQ;80hBy}KIUU3IXPAYLdFeZOV^erAP!77P6> z!oObPfEdBI_>sIDEA~M|?S?(wh`Bdn?njpUk+W5qIQ^%wz(+o6}U4+b9?=H1oC-oj$@I#gx`&=1{ z;MbHYYpl|Gd%ZqZS|2NIvfNEEcT<01?udZfvf9L_?x*lWJEG%o1Tq{!k67+V%pHmS z*&3hmm`!bM*C{RP{!SzCZmYDDIBn;W+3Fr5@fRWNjP2|;E!yWpyF0$z9bfJ-cza^* zp4jLd&->(|X4WJ0=Oao7 ztk8j&dm!c>vfM*4_fX6|V!20R?va=~VYw4AcOvGVwA_<1_hjtqWQo%(MBgcUJr!S1 z#Y$%__iQv4QxuB@&PH84X;NJ&Te)(4S&p@sI#lnEQG}nk{Cg8Hr!}u)bl%b>M}%@?)#?U z!?|P<=DUX*qP$CeW+WLMD0sq?mFbOe&6c6AM3myyY5QjioHvyT#2||wcM+* z#?_d6&2q2B+-ouSXO{c3nENw#4|R40zE3}14RLTG!>Y`3>r9+QA219@a6$*P7F!%> z$1WGxWzpGAJ&h7%RyYXr^E&|D^^Q*4j%mdbv)UaV2< zH^7zstSc~~cl0ft+&19rs0|O(RHk|~5>q}B+$}US!uo$_J0Fr5xhU-HK;VMDs?)le zQ@7P#n)Q7Hug^GQ7^SG-O3y$$(S!le9|o`Cfp*0=_`b`RqJC7W{AX28*~3an#rP6Mo4{*hLpr&su2S|%;hBQ0mLb)i5=WjWu` z>JYhQ%iF}BI<{dAFBJ`8^mJ~6ZFU`_0T%pwOyD+@#u&o_h->(!2Ut;MHJ@v#(kmcr z3g?;iHjtQJ106WhVU23)Xa~D6bxMHGUGerWDT^(hy56wYhl()5 zxsM4tDZ%`W*cg$n9?j=wE|;DG$IEC7!s-J$^;WUF4}P<=i;sVJJG~ml{y;^XL+nU} z>9y?eW<|SqdPDE@=F;?9wkx!A`YPWJYb8xv1NeVOX|$k4FZQ))Hw(Kyj1AqH@3XK} z&oZJ-Q`)+of>vNwXy?*y`qOf>K zD_QuHwdo2Z=% z7E+>H!CR!-N*M!Js}P_B-&CsxeR|X|U}3x+<+~ITjc1ggXnvqH9tTh=G0}fX=K{1? z8~|R3Liz{UZJ(28T_x+PX=gTTRil-K%}m(NeId!KzMfPttK!h8KhlV)Gy`|u7RNat zqx_RpZ08DkiyXRguIx1A5CHgwBS;ipDeWq)_QBezp(+2hu9Y@jr*~M+uk89uyMAcb zzgb3Cr$3{5tjQww0?}e;>^^WMHL?HF;EM;D0sjsAq;!S>g>}%tWpX054FA8eO1v4e z+#x@2f=@om?@DKt|3X7i{w1{0cOd%vu+T}cnR6Q0>C4d%urIqS|0?dEBxQQ%fHr?} zz_dv8E4#&`EGVEYnJ{(vuawV&ZFd%kG1m8aRu*06#$GwW9v~^GY+5HJJ%?sHfF;p* z^C^H;iFL7MvJceij`t-(0(qk&Ued21EKkQDq&c}uVl>;>Ll05oV}#olNu3XK5JPGDN%T^Galp24^p(nA=vx)-N3ies z1TPKPySANzPYvERa#wfCY}i=w5s_F4y{-=#eoF6ZmEIOV@IvBpiG6|6ev#0N@PZ19 z#HOHI{=oPN+am6*uCnk`(QG#avQ3hs?8tQ98Eqd73wtH=iQZ%45AE42zYV=sb}nYm z9w&e3&$v*7RbmVHm;RTUcEG{Ui~Og>7Pf?y ze~uMqP>`4DGj{aC&+!1y_@4%W0_C3@p~^o;OPeyc4w^3pAqvJ_>bbgXM%U<|C&x%G zpZEhE=7x(=A@qv2IKzg5wR34@7r~;Ul!1vUiOwOvor7OQnn{o+IV%p~P zuJ-RiFdBPT?*CYNTY3fe^xatZZLj-ws{6LpeLGv>?b0iQLUI9|2{yWdkoFFZX*VtG zYXos}bGD2rxW9EhqqAmgto~NI*;0SUu`{0fHRr#0F{L1gRCe_;SCh^1e~~Vq-oaS> z&^A!hsb6X+rgx~Kef{*;Jvzij_@KOq6aTeb1v^;A4w?C-g^%PuwONyu1sp=-&c`pQ z`D^G$aB_h*byfE941`~b`cnMAL`7pXIfMp}wt(YL()j5Y`VOl6D~k6{|A>}NWqs&| z=R=_WZD~wi2Xy}s*9g-gdp#uj+q|$exR8#(f5vW{OEb`aH=dp;78F_;gAE&6KJ2O3 zvOXZunr>N91T| zD!nRb)0;omLxB*TSS5p(X~^d82;1qQNt`#+H;oc;+jAIXh( zv>$>Wz%3bywFjJzL-sw30sXX8y=kTzA`4BRmgNMrbB+O=YF=PF28VMlRUgjIF`uhxNI=l5?{Pvs+qa!;1YCZ&l7x2GX9@ zyRGVtKE!0nS_vZoQaH1|(8WYRyWcqd9r-tVyBi%W1v&FRD}&arD=>%Gxd9AQvHD1j zefAfG38n%wcFh&%A2G%yW~s7$vIn&Vit2EwI?N!$t}E`+Eaj>`@2zgd8sVnGhvw{w z27SuX6hMj>rhsqUwPg=f+v3F})z9@oLw@PzM zi=Mk8O~~g7AO(G2!ALV7LQmU4L_Xc%upkZX=eo`eOJ}kif}xEwI^q>w!1Qmp;*?|k zhE9|W(;0AIZ=S}-u@dLYsE=>@(61*DVDFuuv5K`^;pivfF#O6PSzS)BBmp zfa?b|fr)3*`vuxPK#x04jBt`;8+i_RJ~M1?;{b{#U% zGm>ZhP%)87E;ZuGcTj5Mf|$?6P2YmhJ{E-!RUkSO?CDF(JG*q`4ijDu<+U}&^X4B* z+nlw|Yqim02@BD}?LEL^x~%_gNM=63_7V1jQ>Tpmpdqx|>2&K?Y3T#v144(ejk;q( zM8TbfuXZj0EBzr0I$c7-fyi=yvsAf+oD?Lte2qnxC|kbh%??>oe8o`|C12DunWFw~ zLc7A5fUjJA$O_A82Qt%cvSq7BI`3Ad1n?R!eA_QgD8Y>_*mroUuYippS-FO%2LtPq z=~bQGrRtE7HjEwJVM7}By@r^wlgIia#X&G?NsEn$3)-cuJQq{0><(W3Gj6eB9J5$ zH2RK~nfWB7rte+}aR548-xqAgHKGYEx)Tz=4(ggGOYyQ73wD0JR+&PS6g$Xy#}P;6 z=-4PYvkwQ}_McDbNgO(yl!pkRa?Ei_QT*I0OL4>t{Fow6UV7-Okk%**4f*Z~Gi{7C zBrSY}Ffr}gEbddy7$6M_1Z5l|qk375Ik}R&3nW7pEgAQ-{_Duh>}z5UHjJX>CocL?ENKhr(Hd)ZTmMJsi{qm51%10b>f~*~HghmCs9kOqrhN zg#{=|wcP`3bmu28U}U40o8|HwyqCG-R@yC-GlQ)(+_Yi9!OoQFE#IRmQmqesKn_oe z84La3?10LI^+O~u+GEh~L9KeH!Dwsd2-IW&ma;)UxH9?m2hKZZpGD zj#!Oh>uH~}Mqi9#YHAvDgnexECVDhFXc{exsz||5HWpTwdBM_&f&xXQc`2ncj%HO? zgjfM>&KJEBVEB+Kl|la0Z+s=JSz!QwpsQ3}B{|YbT16caF=bZJN+4XO+5zRJ5z?*# zmU+u)aW#r^xLfl8)fBOuGO?d5x!G`)Kb7ma)09@3g>%@2y&K*cR?(#ss>5P3r$BXB z)Sy%7IqMg;X4s6!u!i8KVr%uFfmpt}Dj`VwREhNg!Khi+N6-Tz8!Gr?lj$pDcsWTe zlHsC$RcgY5Ey^p+3`rzp`TE@USiF()7@{hN#;7j4Iv@qXV2lV=p#RKn?^~r`@h5Ha zL}zts81&D&Weh}JJhEE8(BMm0E?ByH&7{ov$Wv0ntl3n};* zFc=CoOHqKei-fEVD<~!k5xNIKL{9XzJMQu>t|;aM!!pre-ZTA63v0&%3#U0jRdMUm z^xw(6Fj7n=$4b+GL%GuQuQY^G;^SKBi}fOJziZ#uX{|)Z8J}ahwY|6u11v6mWynY1 zT~OwOH=cS`PLQe>T{)lo!bV4vlM&Mq5+xMn))YPC002p}_|RDWQle?KPg+=F6Gw13 z5>)IF((`@3ooCi{F>nal0+`?d7KdLxXS(6YG~<(5=*0uB7B>xbly2>Sp4%vK# z&WI6TqZ`O1{(G%2l3hK!w+?5Ufrb5Gdz@EZq75z)WjD(J92fMW%N?Y=R8S0pl{aQ` za#*D%aO2LW1>I9v&bDfT*hD<`o&97GAZ>6l@O34g{MHeot< zB}H2=M!kE{d~Fuy;VeZ6;Bcn?IA4T=nWX=rPDo6Suh~8e*~|>##L$(Su??c5nEWeN zQY$V+W^&Fh`|Z;?j=3$(454(*L&K=^-Q$c&1rD~F-Gd@PG0V8akydrL#x;!qtAl}w z3MJ2RK%1|m{KN>0u>da(E}KJ*skfGl^_}sUrJ;H0mZ^P0Hqz=d!_<|{ z#|({uC>#ur0;;RSr=H%q^`;FNSJV`95ucvzlM{&}6#K8K1vrvdKA%Y9G`Ap=G#lq9 zYv%y_9l_6>#G7o8utUpux0pEbpPI$vz%rsjI+Kj=9PGD1{lJ&%HI$aD=d~CMCAN4? zu~6N6JKH@#st3ApwKhrTJOO?ot8wwujyYTod{9I$=qJ~VCz&*i5wk2TsUZVq+71lK zZCyhMUMa$bDC*Cnhf@Chw|tTWPhKjW#Fh<3I!Bun7kT+=1ch9^M2qKcDmdW<^<)$q zlCCrOy4YBs^Idk;v-%pG6?VUYrRM{2B@7P9+AUNw`vocvoUKEH-WLq}i zz($||#w$p?;J^eN$r66HA7+xV$C(d;JCEm$wUIU*#THStMG8x4h>~K9U5Ubl3#6pj zVhakSG!!dkixd=5lHdQ__iT)jqDaven>QZ6x$nOFaqhY2>wcZ*H@R|e*w_SfTQPat z+sY&GY*_FX(KDrNeI`2>zGTe?w0lQ)Z%QipAeE*;gF?)ufI(xU-HoLAzOPJsq4cpx zCJXUlfsy*dGof>sNE148is#d5g!?r=ke}9cpAD(u`ouzgfWh)W7BrOWiK$ssl(D%) zpT-Bmy$^e0Lxxj7<6}vz#G#e1R ztti_plIRsXe_X%RY6~i51drW+h#xolk$7fi#RqaI022O%rXV(Rt|%*j|2`)-Pko;ROVgKB;P9W!ujU>V%%SCBg>fF3iZ+g+d?9rCq1nmRzrD%5fcl&)+^YQM2c?tG1iG6nbeHy(5~fAmiPW(m3%-Y z_*RomYNehNl&qAYY#GlmnX5_;iTPWHaJItLaJ?yV1LsRAG-F$(+1U`l9HU_K`8URj zBB)XVqKWh&qwy#|TIhuP6Ed&X3on4=j*GT3;*D0&3sd8Iit}k%&<=6r$}b=1n9Ci_|t$xU|G3*^XKn#Ks4HG|D{d;RX&AQkXe(f{ zmdp&Y%tKBh)@!1@*x;ZUk z5mF#NV$!HV2T2gN0MeeeqT62Uv?pG$-C%czvAuBE#10$cR6DGlaG=Kna$m=Hla?Y+ z^48QmJxvev*&K*Ea;uqV)o<$evu|p^W+9duEyecXQYgvxZy~rr0#`|JlDCc^8bOb4 zOkEN5a1$r;~eH$6t2ICZ-m(_{T`W zogi}ltEV-*8WsMBmG30N2 z7$$7MJ}acth}0#$Is!8qF4}eU5Q3tfWGB$Sx9gQa61fY*)WOnNT6bt=0ZR%BgvHKl z8bJ;31EjPFKJBDbwN+l6XWx_!8Q1uGNi#ob%S;mv&&@l0x&r9u;ih>_j-vv=R#jct(-7iBd@&!xgGR{_) zSaQ$B4+`;7zPhn0jDJuajL=LI_@K?Sm>CHh3IJOS8#WwBa8TDUV@WGPwyPA)dOj&a z4y{(tX=kihG%U1!3M4jd9D~2lg)$o`6KN!LRx@{oM}PIP2r}Bjrh?Tj1>$vtlX{~0 zmK5FU`(9~?p=678imfh zp*CBomP<4J!A(x<^6cD+!6E(i2cwNLC&;67i&3MFU!4A60tXwV*&jvA#mdj4#d9;K zPlqG3Bi|c->*T3u=nQXr)Yy7B83M9n-uGFuc|Or_Fvq8SjV!Qy)h_ncH!o`kK>JgL zWVVuK*gCTPn6MHl@YsgEmlGv%8O4fG`L54{3c1YJ$aPaRoZpvody3aDjE*^nAI&_2 zq?}#whag9UKNdx@1le&k=f1i7{B-I9X{=h+2~aEdiA|mUx{$6_N^!NH*c25)mGLB- z>2$AIP-fM(XVaR`3(e;puRargC9Ubo;!*>j2dZiHFc5clIlcE)g@Q>$$Z(;Bfm$jw ze?`QRPBwPx`~b>gp|IMM;9ZU4q(TSY_0VA5J*~z*ga{!m%&L2|V2e9BakWi|wEsc4 z2aj-?8<2MB?#sDv@5aX#nAf9PrBR)Y5=f_CeS|h`)UB|(h#wxGc+E`ZH8yDpOr65l z3F^MlfvXTs#*$ z-ndl_m#)fD)oipR-YDL!R(@H_t^9T{x3ZRS&W&icQ7zS%2PdNHLdl$9)m~ty?br)- z+z!n&>e2s{u@2^n9o^g9JBC9pYm0mO^%IxX82Q6f;3@B?efsx(IL5nl!^qa3r}p8{1sLkQHSz~eye-s03uqg0-o+TGzxb2DyYkKT9dKZ^D|uX=@-)ZC$~o{)l22_J7HAiK*jkYOOhK!$+~0~rP~ z3}hI{Fz|(8Ag33=xL)U0c2=nYf^$Q!h*vt<+mpe5_mH7l`gz!Sir`Im!`Lr*K5F<1ExBu^j6vVSCsS_731D_>+h7C5e{&? zJwkk1^CfDpxmhb`X-7RXz*AwMFu=bc2}pUL6p=wyqBKDsxFzydfI#pr@aN$P3~oWz zAb#Fbs)bc@)ZJaXA9drjT%RGva@#Q7i@1S?^)kYP^MsN(699uu;S>mFRo8dS)cBN<>j7zxnIv%o(uGSjkYfsjV*({|CA3j zWuFWK83r;8WEjXWkYOOhK!$+~0~rRs^cdLhzCs&UM!xh7%^H|tAj3e0feZr~1~Lp} W7|1Y?VIadmhJg$N83z7uG4OBPtaM!f literal 0 HcmV?d00001 diff --git a/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll b/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll new file mode 100644 index 0000000000000000000000000000000000000000..407d01147637f06a2a95f0ad3445cabc552c6683 GIT binary patch literal 86016 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 V1`afEpn(Go9BAM`1OFlo{C^)m7oGqB literal 0 HcmV?d00001 diff --git a/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll b/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll new file mode 100644 index 0000000000000000000000000000000000000000..8bf0313d832a327a3568d7b39af0325641090f04 GIT binary patch literal 421888 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

|?;D`Z! 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 T4zxJX;y{Z7Ee`yD$$|e13;By` literal 0 HcmV?d00001 diff --git a/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll b/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll new file mode 100644 index 0000000000000000000000000000000000000000..73b494556eb679b91547b5e8e616973ae92ae8e1 GIT binary patch literal 81920 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 literal 0 HcmV?d00001 diff --git a/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.dll b/dotnet/framework/BooScriptExecutorFactory/lib/Boo.Lang.dll new file mode 100644 index 0000000000000000000000000000000000000000..83dc5fbc52b26ef9a7916040df05d3b79b5f07ce GIT binary patch literal 110592 zcmeFad0-sXk@)|nXEY;?B-?{z$@stnJ~Vjj!M1$J<}^OQoCe!q8wkkK*jA9$Vn#9; zA%_si0R+N^TuuVS2}uaK?_+ap&P|qNoy~pVko(Rzn`E<_-K_b3s^06Kp0*5p_xSzu z!|-~#>(#4Q?^V63dPlb|-tjV%G{&SzPd;hPhxwI%=jipRFSn9CY4%4anfEt+`Q#5L zF8cDxTlNf8^TQQ?ccpi4zOQ#^$REk?EaxkuL-~QB{JKjw=lA;kFcCI76X}vKQ zC6ea5)0dtf)b@m#baHc|(U@Bk#^fz@=ze$q5Wm8mz^7Qgxl{h-*E~T!@O8^Q+I3EY zKJZ-o)jI*`zjo+eDR*#x-=`9$1=xpQo-i}xc~AA6H-;93{~CD?8wj*CQod<~ z-;oj5u3IhNVF7OP##~pbR4aYlD{t^9Z8I<9H}dZsI;2!75BeYquLhnKPw*rE&N1fP z$m6rO&-TExJ@9M~Jlg}$_Q10}@N5q}+XK(`z_UH@Y!5u!1OGqjflV^6=AX<>J=0(g z|B1Qg1I9Ed)R@8}#=QALV>acPjyu>>{hW2e*WP>HXC7U6-Pb>J&(bO1zVnJFZkhSQ zSNzKrPu}tI%*fw=_G6{@?42?Dhd2HF_0RvzlP4`dWzm*N>lS`_%E^y^Y{4fp(@g65 z{lERqo31?Vq}5Zu+57ufH|+Y{q_cif{qc8x^0~z4U)K5`ix&OhrYj!#;3E$_{^aU+ zzwl#Scg+9$eap{S`RmMEuiN&$4<_ID$or;#!YfQSCQD8|U{>-A5y_WA_Y8Dydnt&C zY`Vi66Y7U-Mn}3cz$nZRCeqDClUV=_TL&BjGz$H7bwu+EprvE^Nz(}raGocqbKTM=@yY4DwO{GURz< zC)0xF%zC&HfGPxSHN{NNLHTLu`3gVY)N#S%#v40@8y}RkKZm>Vxm>(X%`R5SmS-P^ zv*Yu*(3kS#cX9E?3S5sSS{fI5#q(XpshS})$7pY=fH1Nh4reapr1ZluHeyOh(m zIH?OLtn1{wTcq6`vyT=Q@jLtI(S&6#ljGWd{jvaj^>n>1gUN3(#eZIL?vaz@ zjc(2?s%!N3jkeP>vyFGMp;4kE30*#7Ey>k?w{)~JvD%I=Mwhf-blcN)F?1?7`)GBk zFq`(qyZKEg$Ct^Es-EDT0PRWO8BbL+S=cQKY6qJ1t*oz2*8DNFF6A$$w5QUqt`NF{ zS@M#FRXl*YV5yf(6;`_k$$(+>+GW6zK3JW9C2+)1Ck;Aj+H3GdS+CFmbN37s!@JJyif#2lY~<8hz5bbWiOYtsq@no+0@NQ6 z>fA1tIPISmVzw<#nLK!oAK|n8Xd0`PuAU820mUuSg^ov0g=}$W9JQQVSpsu)DbLA+ z{+dV~2~}cSu5hlB@rIIHYe=WtZMr$o(UTcBj zhRVhBJ5W-FK{{(Q(*AlyGe;Vm3LE71`(#?nE4IX=^~DRVEs-&d?K9KDdgo=MwfN^L zsUwZ)fYJhEX-qPcXIUEO9-x(}Dy}mS;O+UX3By03ur*T8H&<$`I|M@Uqiyx+>r^Nk>Av7Y7q3p#Lpc%;I67rf)NE9yRce4EnbtGxv z$?8)w7|Yr8q5`8p*gCL@GD-h3t^wro8pIUa1L)=mRK{7-;KGij8{%&{2A@6*^jd4) z3Wk3L7ZYU+lXDDX$7KUPJcAlJJ`U3uD=RfmG;?Y|jSUP}8O~g+4;p~&b9HSCa{)`+ z=xu{Wh^7Fg3AN6K0)&tTGOgIPj$@N0X2sNs;m4x7u_X{do#%KBtF&GtibOh`%$m`#oeS-tH|Dw%r*dXirqc&)(e%Nv=roh=+(x7jkW689zuKuL zGcab)!EDjOw7*B1kSgGLwAX${KiZ7Ys5Pi*E6hm{8jE==%vD_a6Z0x>Oa?ksk|UmV@nq8c0N6=NdrUgvo&igId%!)MrJFP2Ytn>= zY8GVNrt8M-bAe>6eduWIxeb!^*kWNLQl!ZhX)F-yN<)>iWdX|dH5!Bp(!i!|8QT_1 zq|w!2ibSLnV_3sV;M}>bwJB}ezWN3te5?C3FoPl4ZP z&a8fcw#XlZMffICZalD;`)qO4)8L_{apv~*P10&omb8?FZt5>`(O;3eXh3oCfgvs2 zbHLZ%#g16vz_32&58R-?(+*VhcZ#~YY_ah`RiBA=Zxdr)(hg3hocf?q>E!(dygrJ9 zA@o$B$%Cne!V-n4?g?jB_hPzhHP0({Qn04gGPVjuExt=g)^bK9XQAe3yp59T@H-d? zwC3qtxeirPYdP!nvD50K#9Y!tdF}r&?m^g>Lp)iyjO#egKSdxF34dJtlQkXrS`38@ zCEi1h^O{}!!WZP}oz`BSoFjkz8RU6}t;h$12#+c!J3R2Cr zH&?%dX=!Opjtd4=27#gFJe$ren%sCa{aui%qx75DF@GO7^S!V^waC!TyG}F<*IS;1 z7u=-89t^}mdulx^u~EuPWXbPz{-_*5Bi7l=e|;-Uk~E6(uJ{(c^Vq2?8vfZPnk zjgZEB2AqBPuxF!Du{WMnVz7|nQ>4eLV-!&dUE)=5;a+9neA9w`m|?ZR8bbz_H93yj z+qp^7h(T~VCs2RfGKHaALKO-U3jP5uh39bzROnXj9m4YkY>Guu!mxzZOlM=Lr5-uJ zWb_7_dOT+F$S|8k|Ik|d&rz+@VnNdxPKTXZ91|yaV~Vmvn5BAshzt~Q@ePwwlNw}s zMMn$$oiq$&Gf(?lmvFLPy3k90b9?4!qP1yJvzJL1`T(>bKnZxbF^voqDZ7HQ^KIGd z$jPQzzd+!)OV*r{!tZkJmlU_v+qu0N>Qr4@YlzOjy+R{d1sp|f{vF9@rn*z|kwlO$ zWA@kZKoQmiU?DZ}+R&Zgur~Aoepq=BJZ?JmhK{1SDdjeQ3mN{4;B%Z7r@W5wetwE& zaP)ah9uJPg1;fl1gAgWAAXWqEm~cU8HsvH|oL)<@79{nu7Ic=YS!5&xJGW^y_QBR9 zLxWCyQNV0$2}tb*HJc}%2*#+DMVIu$EmF^M*MNS68IW}!Y4o^UYO;u~`+QH(>ehW2 zm-MkTMd9JPkBnLBRo`Z1aJ+S&jBAcvGias^@ne)`Z5cFb61ONIJ9Tud^lIO=9+S;`DFSmW1-nr27^I8Q{j&2gi7S9 zq*{w9e57fzGWVpNVr!+Xgl}n2uiLem40RcXQ@p|uIZ3^4ulYoK#*guWzWDbE|EL;K zbhGFG4NvzBs6fOXi%v2h2CqPLN35=4hCg&TyTI03Xbv<;rrXBJDH9g&5b=Heu%y@K*nbS`B)yopuKpF0O4d|>{} z65FmmzHqJ;}&ZHo~t(QwXGUnSKcv^)3(;nZBpe<8QcFj}b6zLxezWmriO zyQCX^v@A>6ls`+#2!mfb(w<|n&~n>IPF_%m>;k#l_-=|>eSwB^I`%Juhi>5^X_qS* zpQBXR?4>gWymeZ&I@R7dP(6sa96#a)r zO*2XMgk&mZi7+D(IiqC`K(;&^-3v-d%q8O)z5@H`)NG2ER7wX61dV~cuzNpkihuar zV3nrRe+@kl*oOx5Mrba#ZS9b<7}ph;g@gV<&_v8KJ(OYcJTI9M_!{66McQ6yFl6;x z3v36mix@v<2Vy|sOgH*kEK+lEI)*jV7$Y>p5(=8{NPWX#B4m^Xu@JqLGYu9ib+9l+ z_@>WYugdB{Yf?>JD*b?66R>eB#wnOr-%OcgL9Rz@Ylp~I;w2fI^cQ28g`ieo9tpkD zh)kj~xtBD_r4#s-G@W!3iH8fv}|$iiP>U)dbT)|eUNE0v&HsV+2WLwvc(q9 ze=TIHuj6uaI$VNf)uO_-(c1lZ}B51log*)+SNf6Rb z7*pE4JnN4;3eT4g)JVeileF39b;j~iY7bUg9>kB^ZI;>XHZFTT)mjVTV$3NX?(+qZ zFWknZxmaZIcdA`L{d4IGEgIHkm+Ax34PLtAV6wn~w-JxDS#;jUuSpwrx?K=)nl?;j zF@z3F-<-jIB+akD&%mEIwhQ(qjx}LOV$JvSL%bt++<0TVxtTphwo!}zgRnD>yGaoV z3PN!Q2=eSdBstIJPKwB{H-@866>f)z@$0#3_r~N$Z4<5ZiV|cBWE|TjL8j0n$Xw@0 zBTXeNHVgb$IhHIm+-+2pJSfNPHZrmq0W`t<;0Lu>69qOs(hs*tb;pf0f8}CLY5TZb zh&zc&QsdI5iQ~>6A1CgV)~ObSacBL0qwsOOxYO=83P#v$OdAVIwPqh@zY)>97&WZF z7NZKm1y-lA8D?tJ=%bgrSV-u;ui%4Cy-@on&}pf|I``kzx`uHmvw>3^YmP`55?TLbJWKy)9M!Eu=(T z-ngLV8j|Bu+~gXx=&01H+rSb?EjrOOEt-;RIGX;Qu)Q6QMI>$M;w=F_&Of1=q4@2l zJwuK%U3e+zpkC%smD!d?|4r~FY9#b$5=kw}Nd^BWsK5qllsnOtYm5^#Iol5`3|VKxD=s}j-w@i6ec5DGH83VrXMG$dI*ui-_RDMywP^^arNR) z0m)D=SRGdWQHv-c(F*DRw8lp%k!7G49eO0HdO&*b)<6tVnI&a!Baj(!I5OL(S0e3H z;r^fqI70tYe(`;|>c2?uB0DL8?4$@|U&VT=5sv%pi zc3#dK+fOat7;}VF;WYs^I2nTjk^S{t_1{I@=$K%lV}gou$y2z>nrd$@uCuU8H_>jV zu3@z{p>KkIsLA%1c-{b%6O-~EB85EF*4e_Ofp2r3^dEESrq_e4y3kU* z$s3(af6{~4a+m$OTK4w9dKL}>T-cn{Ur<>q5p0u`WqccYv=Fl=b#priF^;XSDN|r7 z6Ra88Gl^}&qn$_TIhvZyxBCZb1H9^$K|Qtnq@G8#avG6lBs&g{KrSIuorv#9=$lim#5?j4}Gz8>2;B$GH|BtfP}Qm(Zu@i#-48UjomMLQYzg} zW5h(Y7O$>c2w#n3Ql{`$xLepRXHL5(d82qo8BbQk8s&+`JVa4vJ@VEZTg9*>Qq?r{ z3mGmp)T13KnTweVRJL;NhRx`-{|HqfqlaNVQFt#j{kH+BH=EiA5t(2UkjAZgW%`*a z5MD=QYh^-na#d#EI>w{~GLa(4M9Lr&1uB`!#Wk5oCNfD-@_1$(e$u)wruvg~@!V|` zOm#@7Z7;kNhDMq>#+1_MxNR3%U1VkDQIE(8@y2{xLTVv3l4J!~W_+?2{3mMIB=sgW zKFX%_r#WrU2W0f8wXuLn#~?T9uN$p|;;Zw*pLe3q=*ph7lNjrL+_2U!&)eD>bB*~S zoWkNg+*Hox=2U?YBA)(j%5mQh@bp(C^j;87-_L!I9N`y4{P*cUu!uvLw$uM;?*}1i zPePSI>?55hrJiF;Rnd;cXH$HTVPMf7r`#Fj{=F@~2aMC19!ecDJnc#2831krp{K*< z>_N7Rr-QN|l+uOV2;}sC6BNftMF2myM29wmHMQqEJS7`bO@*)0VQuLiX`4R(lYPD; zc>YKGJQ_S7w$J6@nGTZjTZ3l^r|-TR#Q}Iu0Q{x^ud%>l0Q^=2C~eW|za9Z50^m0y zz~2One-9vad_Q>pu6=%t=NTH4wl(FNI+BNSO`Vm!j1q39d0b8mo~EaGD!d;Juxp5B zTCAAlx22LgA82c6XCi0kb(5J-dMt&@yp?$H8se=I+VvDDtKp3jg}ao;BSl6-Ch|Ok zXL-aCK=4Qlk4I<*>F9I`6cfzyjro?>`V+6=a(dC2FL;x_;Z3vmAMz%B&MV5jjFS(6 zc^N7bj1xORIAJ6sgApN7G3RTKI~LNpbbbhH!(vQxGE<;?S*R}dd)jNs!Ut;IdgT$}q3eDGCEWL&e zQxa@1zE|jb9Z)TNgpzEay_g@}^~w}J%5966Z0yL`_hi^!vHJpPa}Ql`rO-_J9|H?t zI_)Y1*^+Bnbe@@RM@DO|wWC5VW2v=MG47U_g{NtiUs~{qw)nIiL5>|@s~~wZ)_k)` zO-t{%zhm(d{Y|F*za?k!9C_;8c7O2P=AIQv_a!(vuDrCG3TD9xlcwq+M&+2z4q^5I zHq;v0CUvw;F3!kJ%1wS58)}-7*1Zv0IGKI4$+^k7NxEGtWxnMp1@`YIsKYi$Ukhl> z=uca!qpg9)TxpupQAW2v(2#zaQs_aIa}7=D`?UevACRZ}yj$;|h!g~h>f(pQWMM97lOY&79C)W0|wfu&~wp^;n_Cnat+RHmmPah&?uwYMav0 zmMfl=o07|kh^0GD$epk#lyZ)0a{Qa3QkJZ*r!(7Io16sG_P3$K?!De*lGAdT9bPV@ zBGQNZbu2xg_nq77vQSlp9K!v!(DnyXxy;Ly7g_(yrQ_K-MBJ99HEuswj#0wi z>YL?7soG%09ek3uwlz9Vn;hp^_uMR7#%Dl^z2qLgVx=yIi8t4MuPt~u9AV_J9IpPRIeR<_z!BK&NwMKYxa$VJ^- zXrdVDR(>%Wu8E7@oNM%Q%~l_>fj$J=+KtM!>x^MKBiG0nX1H1#ioG-2MF-qj6e4JB zLCvtsd|n2DZjO<;&{T+&hOJL$H6>{cQl`_Qxu?RvGA=YS4SWXyEjG|@hTl+<1A@GFmP!V;*`DQU7aF!6YndbdN&J_~e;@1~laJpqJ0k7Gtu$9Qh9=J& z%|skgiZz2SZ**au4=S_}m$VcoSN4XbIBO#Z+GOqRT2;gz28wfNPIk~$ z3bP&})xlgr`qd&@U?w~tXV-+bXnT`7qjyc!IZ!Kg1$oy(l<*?*%61(F3+vb3LP1#NH*2)NI}AM>R;{-Qj9-c=Sct5-fN^cn~RqbK;f>KThDBgmvT9R!`S)~~J=1v!uu#($>a2Ut$h*=bC4fXhu%3I#E zIfZ8Wh&W69DnBZ2l{NC~sE=&`yZ3Bt8_q{rt`i<+PxeaL*^_PW+u_*+Fxw+%nP*Ru z%t^7#N!gxe$!wL()>vk1w&!=WZ+1&~cC%z=Buj0xlBLPBo061hj+JQ6_Fz>gk=3Ie zvs-LFy;?LgJW6^}QtE29G(#>ad7CX+949kQo1L8IY_V^4He_Xvurdi&vaq81X=z!^ zRkIn+%VuZ8`a$LnqIeTZ%OZTc?j4*T(*_8e?CmRZ1LA6?&W;bnL zpMl?3lAcG}Lt@S>=MnV%L*j7Lb03#(q7Jc3(AdyRVwK-B(ZC?yDwl_tg`(`znpz{`cGcji+k&wu#%l zZQ^!so4DQECT{n(iQB#H|D$%}JB}~!?Gv|q`^4?uK5@IZPu%Y96SsSNtlju~!#KgW zJ@xp$5xjnqjP0{X3|&2g;>rCeV<|egzeS^4iWQ)dL5-a+UJK|S$#er+Kol$oumm3TT2xf5F=tzPZnqgxX*Ce_wiJgzj|$8}w*igH z%tFrv8Wo$Kfh{fs8kL;I?Eo4Tp2hv&l;3wJlwayt`2mW{ui;qvffbh@SaJD*^}j5? z?;THmvcY<+{46vsKMRe^PfphJu1idQ7B?-Hy0Pjp7XKiDLjj@EGx@)XI$p^V=!>3@*KV% zK#Z<0aHshd{<5SF(j?M!(g~!KNT-mb9VJq68S^|zi%I{l`^oZ-SQ|vjk-ke8et@5; z@tG%om*)Fq*l*cjTXc%al3%*|1W?{PkTxv@cemn>Led=+0h6i!!ve?_CwtP@bjc4X z6|T!jIp!Y7ZaecsT(?%89Df^~<0whY6{#PXLi};-%}#?X~50#>)vHQchI4uH29< zC$lnp(8tM70FiQHmFmj*w%m2`asr5ylc7{sZrGN)I$llyk#b^$>&o3=%ZW#+<5K{U zax%Q@%Gq_2(QD%61Q03rv!GmX7)-`W4n0k2y<;zs^EQ7(m6bSfInQ?szZLSUDLHEt zEH1IqA+Hkkdyz_kHF*z9oz2Ay*|vHy)r|{Hukv$jXykC80P7C-v3Dz%NX>Q95ed^ zSnlYPJpOa-QQMu=n0Z8L&)%l*cC*s;=Ll(eJ)@XBe%|E5zkuxuxkF5{#>xEGy$f^) zZFTPgp$OrLAHH>W11|y{lAfA6E=b(yNjUwMqx~M?ivaVgj^q|E1ga;H#&742*M5Y6d+kSSa+GEvV~V`lF<_zLYd>099+U}Q`_VO3 zY0j6vRD177oY6k|(z`_}nfSe*Oh>gM zd+PUl1S4!8_KEjC6>{H^6PggI-e;QpJY;wWRd%%`Bz0@!O@>pqVggvlLzln821E6iVBWmK zc!e*APrtGU=5L)x+YfirBpr&k&gZd8l##x*OYSzyU4gq!9m%)KV6VMn2>o&Dl@CV2 zf_&g;q2zzG?o%C=oaaut*4{;2WDAw};W(Vcp%26Fe(76OUUGd5U$%g?md_A*a4p*h z-7@4{{y6zI5Sb%cOKldiNgen#Sq|&@+!7_R;p71LclV*0SRt%ceW5Tv$`I}24Wl2i zappdY zZ>Ub;fsK^3zDZ|ayc?JL+Z)H^r?wpwyuD`OIy&p#PM_@AoI)cUwH7=1#11+gRMZHo z%~W}_d;dSB=aNuQX#-}}y*PmX%RaM+HjQWtJ&adVJ)ust!Um#k3woR#>0KPDo{PQXg`(e~cA>E3ZLs>o0sxCPJf8YS6DGGV1l*OIQlD&T);T;EdfTmE9U<*ZJO6IF1Ys7 z1Sra;(C6|{v;#;*&pf z9G7<5CI*869I?;XZ8?V_V;LKhJzNMpW#R2Sj>u!SG8GTU$5+v$0zqp1D+{$R!*s}3 ziLJl0-^*=Lx?|XI+gbB*D~c20vrTc53dYR}E;D^jgD(iaI3w@$C(Wnf^LD|F?px0` z?GZEA#L1?Vemr|Bzkfr4y6m)(?`IrHj_ze-1tvm_^A>_BR1co62#TKLXjoLo?q}k$Z0S=&Sm$ zVYnm4;@pLwAdho5^3z;g#TU)6JNDaS93p5XmQMQ~kc?gnomAP<;XdghCrFZfjG6rE(fi~vS)M(fcXDQ23mwbfetWKU24|iH^!)F+M&jXz^hmaT zKb)Ay;Dz}<%fCxG&x$vrytg_T2l81)Yqj{+2YcvLeU=h*l}v2t2Z z6WJwP7cWmW=Ur{%Wr^|s+kE|1Sp1*mE0~wq{+yjJ%tcgAY2;lcBq% zh<-EweLONnMV-Wv2%fx1{&IeU$tK6^MAGks|C`BrMP1glVU}mlyN|z0NZ_%AL}4Ke zsRYMg!y^t&Dkxq+wmhrs#$U}H5vi2p&lbqjA@Zdf3)HuC#_yBoX<8oNxYd!OoD!0e z&%k?&Yl5S%N;7^?DakdQ^~!_Qd=7ZkeOlkFnModG_L(z}@tNWWyfJCH3bk%tycIr(E}d+T=QEcdS^BhH zVx4HMOXN!m!JJXQy}wkNAB^c3Pc=U(-JxUOes>>FD}L1V!?^SVZ`Vl|*w%~g=Ae5w zc*d&^p(uqH*3=hUkThQ+pBSUwcX8(EhEVC;VOO4(+x3Pg0^a%^M3QftA9jHgjaWW>CL^K}}nON^n^-sF}c^ znj-Z1UK^+JvqZn453}dgt}`oMY3-}D^Ha3*o@hJ8`Ouu`xU;homr-kHd%SUivC~vJ z+saC#YhXh$&)IruAQPi#16%l})d4W)bCk2@1;0ky8tR;d5REtsQ*1Ix6&VX0o_{+O z^bn?P+jg_$PTMv)XE%+u5wIfTPCZgCM~xrI6fjNJQi&cAvKxXp?IbA`oC_9zz71X( z6!1EkEJEtS6n+aiF8&FETzL>>Zci85cp!jCR#!u{CBqz(SK!jsc0rsfh>iZ~09yGL zN~S=Jg0_91msYGS=9_sJ3WRsQcpnv=HrO9*#_Je|PxmAax<|mRbMH4xwZaK8#arcG zx+4%s;HR5}USZ)ede|=TG3lJB9%|UGK3?K`>*EDShp8ggltXy5=l*AcOWYv3;dmPt z-thCnmgPv~y(N_Q+krcIixC%jOFUY%2S$4c<3;4BfKNXPvLPgV55GY$ie7y@)GM16 zewc4R`tc5My|H&m3HP}^N_@3e!eWfS4}{_}L?}1ok4m9Vtq)a!_n>xEMG$N~+q9G| z;;Il)e$*64>MM2%L&g04@Zg0ESMmF$;w4lpH@4zwuHvGEYSLzEmE53KdvC9{VbuEZ zK!K!xFM(r5G5RCLkE=hYJN^Qn4z4vswBsdf4 zlJIMFX%20`?1(NoKF5VmbV*p`iwwe@V@&K)2ly@qkV%FOARi2@jM%P}o|D5yW0&}} zFF|}lzCIaz+IJ4YK_Z&*LBCg6I5OetH0il~T}{Kn{+7gsTGOCgM^NtoT78sR}C3)}?-wJE0=K z!)$)r=W|JRt{R?16+)( zlW^^kU>!5~y~op)`SBhocm-CFvh+l9e1$xw##i#A^T8nQ-l89`p(*k@wS`F`1pK(jQtGCW_FWn3o>?pBhM~R+&NbM63v(ERryG zZO)T#sp~n90PdECc`Fp3wM2Xtrk9rpljEzkcLUjXSl$G94B-IB*6ZWKYv*gGQ=jUM zE{Gq-jCX=5@CIL&A6`$&`ll-KMxA3r;%i4b8&xe^W!oM=ZPQ+4WNky2S+Y$( z7Y08mIX`5Gye8(Swou@v{2@OhU6vnKBK`rHraSfLzsb*8UIEvb4ZBSL{t7etp#P-L zk&D_g)^;`rURiwV_ka_-A~XB;lI(SMw)A`saFV`YX;&Z}Ej#-7)p3;ylW6{0n@Gjydox+80sF|Mz(ho0-f2AO z3u+Fb6fC6k{O%(&f@O5p&tVrBpRTix^ZTaL9k{Ua>o;3Ite^5zeYy;;3690Ca7sk@ zYc00fHy|;UUdBhyv%S1-TU&?u;gOl{)kj;I&TJ#qg=5~<*KFpL!~}6z)Z;V5SbTA8 z3ohU@^Lop|#I4-Smoa6(+b?4x*e&7+xCG4B23%T;Ta#*o#@8`Kh-TJ1d*JDLt!Vi= z8{&uK!_kxl45eX;ZQT0ZG*&-;Wq}@F7mv_xh3~gm9r5|FPOzk{b~eo8oyT~d#E?Yq z=$5~aVpZt^?6t4WW^2Iy*4AuJ0N?tvq%Uia_3zs@nCr-YMCiHBLD~%d$XnK9(jUnk z+C>su@}lFI^i+pfYsuSTf9#vo?knaRYJM}_SskWIjWq)s*RH&4K{aE+II=di`ax-q zOd9#KV740h+e=c7rLVRU8|cpXDXPW-wXvwh9PN}I9(MLq<%@>AQ@Y_DTn^UNB%j$1 zvr>n!QNmj&3vv$x%t`%>^QHV+8Cn)Qw-w$DisRhEl82b;2>-;I@?*c0ZfT1mzs>4E z;XVptBpEoi9`>4)JBjHwg2(KFaK|oowiNl4hImM@)5;Yuj_a60dOtlf-P&ClO2P0A zmrBCFL1f1hZV=z* zsaMtv?zXaW*_M*%$c!-Wumm&qT<7hO(BU8GXiC3jYW2mcwyjk+L<`HX7Ea{wmQtF> z;wEEwN%>nZR)^~PrlK4crP%Ok;HvsrU7V_sM)TCqo8|@!I#DU6AgEJoPO9x-Ji|gmp0T6ibBjt#(v#ka{>Lg7LBHHeY{5{8*{Rf^1Jv~a$EM_atj6u@UrK!?!9UJeW~z{v@g;&tAM|%Y64^npLB@6P zV!Y~({xPVp*+0rmTRnYeNNGc4Rv?3eHl{j0g>O0vA`d%X3TWhOH0r*_@KlV*H!iQI zjqh=NB7(`Xd>{&t*3WANR^Q1uSd$AYb8YAILAKC#=P4bWo#1*05%jI~J=S+kEgY13 z#W9L}(NEEx_bN|;nu$l_6sPTI{?1C_tpLLP7|}PG!@jSsjA$4J&vOlmsa-R!JJ9_` zbYH&@Y+K>R?T`4|deReoIaR+6nbN=QrhQe@hvv#7p*cd5T@$K9i_0{01?NKcWRXLT3m<=|3Gj2ckSS>*iqUUEo=Y!H_zJMox)J8fE`+kZ^+r~O( zGaYHmXC{bK8#VH9@v1%lrhEsDb`pSZ?Qp&V<)UW9f>Wt|1|g#&n_++Mr|BVAS3UKQ7+ZI+chG zv_Bo;=K^6QsY)|`p}iLs9=}NLJH{{O$1Fh8?XBbOR%jCeNUWiFV?sqG(aqo1P%;)I zx+ur-T~>!9PZ1o3zR#H{MdFW zKk}qFp7|*}6IKUj-8cjAw+OpWs0w7&)=BnuS3kw8{_Z0E^M?6+W#PdZ1;$ zd|YWKD<%RXx!<~5`wk3ZAM!k=AD z3>L%D&gp8MwQiaBiH}e@ngdrLInQ5(HfE1ZnQtnbSReN6N8OGf8vFNWsLf2CVx}+! z)xQaFLQvkmW0S03W3cb!Sv}EFn5?ad9;lp21&2>3tk+qS&jFBZm9rk%TZI$p9R3|* zXn*BP{|8;s{V+4Qd4F}Jytj0Dx$2Kr`pVVt?(%YPf4Q^anGtb$5u8vG%~Qa9MrzKJVHn4*LsE7O>5T4|974L#=CyE`QOxK4afC%l@u>`|@pn{^1$t{QT^7Ye?sk){@qd){{1n&Lf>q z+DN*N)Jxh)>Lc}&%A{SS-K0IF0n)9c=aX(Dy?}H(=?>BhNiQPZNxF;l5a|u1L!>v7 z-b8ws^k&jqNN*!OO8OM(??|5}eTMW|(&tE@Cw+nR80pKTCrCde{fP8q(oaY~CH;)_ zbJA}}za^PpoV_kV@<>Tiiqt?#lQN`9q{*Z_>15I=q&cLyqZEtNEebWB3(?ngmfwCI#Mrb4{3n(T+;QVLDF8*5XmQ<^K0ml&LyoSts|``Z6KXT zI-j(WbSvrkq}xa@Al**7gY-hui%55p?jk)zdIRYY>5Zf}kscAq)o3LS$`Sna?)nf7SdMI6{IUkJ4jcNhDkS&Dx@lDgtU)zBk3id zym7sQRle`06P(Rq7be)7D%xBcNN{|6(R_djp%zWwd* zd1UOL+l_g=RR$tw@+R^3$w?E~07`3%or%JDG*()m31gLi=YCn@u8p8Kd5cP!_-gLMqq^Vj!i zKh5udkbXg$gfSyXFs%ES^GG{Ldr7R{nwOJUNHiZI@vgJ^1HV5YJx02W=d(y0R5H^@ zHx{7o@>G`A~5-!I)LApFe zpO9`M-9<{1P9!ZPolSax^d8d220ku9nnRjG`cGiLB7KYWS&|RzYSN!b8650a`28X2 zt)!2UZX>;dG@U%Y+-rVK`Zj4Z=^WC2(k{}2q`OIdYO!obNLMWdb8O5Hx>sikvEy@v6dPF+j>y9c_I#4rhkQ@j{I^%=uuyAg}EXQ zT4|Vg*JJgVe~W`w=?IOGUqTCHVAg?_m_NrsOHB_$uMVr*+!O~bGw+ClmYZ+KLHq?; zhF=}6m1a#G)MH)~2dy&yP!C!P_rH&WmYCIy+6eijY=_(t2X&i=~?=QXOBbOiw*%Iow|r2Q4wLi-VSO@~n>5a{kV9cO10LTowl{ zHwWXO73L#x5ZuQ>J?1ZQ&?>VDn;2=(3b=oM9JIu|D-K#}z7z*_t4*z=wal!WNkE6m|IXr*bw=GGPLF>B(WRpzF8&`P-fP8_s^qxf~SRx)Z|6$f>jZ^l8(^uR`h ziIuF*FTn2CfmWF7^ZYDy$2fe=|4q9ry6$f>j7JO4( zL555mwA}2BgI1WA$3ZL2C*zOUzIlwAB1V9Mo-o8wV{j zlUR=m8zkhHn=9g=73QsR&`R@o9MoevI%Bj}nYHyGslbnemY6%^prz)MaZtDUMI5xu zWT(~DxCHJmh=W#`2jifX=C^TBk7+zHMhlBs4-#Md_Bd#Xc_I#4YBDV1M0#~87W0NU zXqkCg9JJg#8V9W~kHtaQoEb6lJ!VxLw8}hK4-#MdgE(l3X(u*}u-c8ySsw>=n-|1E z%gju6=<5nDH>={H73PvSXr;L~4(c)QjDuF0f2s$Gul-{jw8Wgj@_dAeWpKYM4(c}V zii4J!ug5{l&2QqM73OhvjO)m+G^gidpdRyzIA|5q$2u(WwO7YMOU!+7&{Ff&IH=on zo)V*luZ@G2o0rBxE6nHOpp|CooCq!9YO(2ygO-?kG$K zvSBlzbFT`J^SJYwi}mt*30Il1Rl(?G0i(=;qpF*`ZRTmjyb7{2E!njcJ{Popb_#uF zZhN^OF&_Zoq&UJw)VGtx5JA{uehNydhO!p$5dX;paRZ1$Pz>gSdv%hy-%LG~wS{<* z5&pE7OcAf7kkFM0;|R43zj3xjw_^nPVzQ;~%RuRqbJ^H1Jdb|REr9h-xgnpcb+yWMh>S=D~>r_=5R7To? zIM>C|Mrr%tF(T{(EyCd@&Qk1FbsW-0>~?ADC2 zlB)SQh37{Li$F!8Wt~A3V>kO81FGytKwVlx-AZP#HnD|57m-BC<0^Sd@fj71cj`G5 zJEK5KfZ8Ejl5bR2fq~omErJ9#9rW%gXYO ztp4n0Z0w?t2u8%QnL;;H`DGMX5G@eUpBVimpqF++L+qTjZeoZ;fux{VfD(e2290o) zet1}Fc)B9}Anv5VZu9svLm#D<>uJ?rKwtcH)Mfnaq=tTFsA{G!qL-eo&>jkiCW^*f zOo75PDKLo8c0Uv9)_PiVaVG_ywhhB{Z`9Dfn?g&UjwR{;UD^U!7nA`xxAY9^l44av zECQ55XHe*A`)h>Wl2u6YTU#mcv@##1j$SQL0sU#aSG>(GUc-46$n&4B;WsMnkAs@H zbWLUV=-%?sNPeK2AM!`?3Yh6Z1CpR(kiAN6MA_5Wo4oqt%i8 z&T_tYC_m6odj@t5lq;pg%1g-X7ap{gRqo1pe`s(&)eQPKl6B*r@=(5yDwSt{=X2q{ zT1w1bvv=pf?oofVny-|1l`G|;zOrR-bsnZliRoL1t{?Jmj8?bWB9s!B+Lq9zz4?8; zgQMmA$o}DSDKAa!>aBL={bBlegzo8sUu6f@N2|ilz{s9RjZzM7E|rqnPD?qIOr<9?GR_ZIg!;&S>3uq3us`qb%8yW9XxSbuCDth`A^-~x3GV6LM&fwxwSr{PlpifQ177Yw>8lc)lk-b{@&q%K`IsP%2&(7sKf}|qiB0~ zBI2FpT|Ugh%YN->*T<#AIZw%V`KEGT9ouZ{4_ncO+@2-<=Q>n6G3>wEP?0X!xYn{* z*(@ZqRl9oo%B4iNlrOtMWm~QqTxIYK?AovW7xq!mMW?QDEjxE~V2~39`5PG-GH5wg zP(f{Ja zzioDYr1yGEfgO#~%R2B1RmS@j(vY2H1QZgW)0O`^3eLQU$QWs2UP84B24{Y705VP; z77N{pG@ilji;!ht4Ip|QSg259PN+cA*xvkJlz=`$`7O!(*wXRD`AB{M)$WzyEaSqC z2@xS2R?0Vw4u}?uLiI`S(7i)>&_@RP0)mdgjbd4hz{&~BCv6Vw-T+;b-|72l@&ky^Iz@Rho7hBsv zb^Z`z@mP$4s$gv#ITeOe>4(111(p(}i+YE4kM`~^=Xc>p#6VU@1_lRpY=#D*+#j;E zRw$SEiP^TDVV$P9i$464df%|A)KouaoB}F)5otR1f`JiQk5b~i^*4!^5UJovtv;(e zccwIOK-Tb~{!X6;DwzEK@}lytUDhcsbfWWzc5yffm#kxWU}#UdGJtGjBV>&*hgbsF zT=Q?eRtsxQbRNEn+^`IRij@*m11Gz;x3A)d{Wrt%5o@vHloD$;2F^nPu7|CzIE@P& zM=8Hvycg}j!87^;6`gcmd59+US?5+toV1}g@H1*6Ll2{TloFN<1bX|$`R%Isdu_F$ zzv$RHWM!hq`J5M!P;5UgQg^7^f!A zxHxLMYvW7|bY-9)1In1`>qQ(gh+HS`8>ozo_70X3bGOt?VrU@T@Xc2dJ1s0FPOhnx zxK`J%^a~+^HnEgA`_drD3E7Vhc*{TWL#Ue(d#n+`u4h6ZKUm&1f{PpKS6!{AfnmVt z<|_lc_e_klR3eW>kru~PD$qn75nY4jA<$=T3IeQ93q|l6#xd5q2CBVX(kdA;RIVd5 z)bxcwTnw$X)_hSVz*VbRO3Vt?*lBAe&o-} z#Ikh_`hC}T;WeC6+nyLfRJ{@>U~PkbB~VcoS7R#k)zN_wHGM8{(B_C6bc##ZbYj@$ z!nBxftJ7pHU#s3g-~&Sh>}sSdWvnswT2wl4lF_iFlvt+Tp>NO^+mj#k{o%Yn3X{8- z7!367k?MCM-s`2=;+6)6MyYI%c;q02twl1Ccm;IfbxnX2am4vTt*Df+9iVn zPm$fJC7EX=7P;5}`>MgMGZl9YYR#nErNTftYWfx*>C&fR;7E1Xs&-2(Tq|RFbl$`o(O&4l z7yUx>6`MJON>)W`TM&#{H*K&1_Nx->MAhlaAW+gUy*ykV>aS9hYyFLTe7aqU)p-b= zyX>Q%0!xWP;GFmO4Kj_x_K6*oc)P#M)YZ;0EEVv|OyMN(M9)L*0pdz0PT6SvrG}tN z5h^2$kw}1W=A|`T9o1KjN94;dA01jGN0iknZ`wGt zkCV-gybDAuXZVD@!^~eTgR3@%K5E#K(C(fuSUBvs1lb*&-B>bU(R!K+Ozj`$~y<$H$e0ENfes?{8+-i$XcuVLOMAc-O#9cG}0vln!<~@eUkr zDZimIAok+yL}^PcDhdpwEKv2*=FrTDhtaWEN^D+7{3s;j15o=3KRk-%6Qf|KnFBiq z1_xxxCK${vgk&^}vUiPji*a9J#aB~i+YoyHsrBoMI&BCi2*G4g)j@byCB(tYTn_tz zLljl*uPGRh*gB_zguSl!cUG`vd&Ee~OsgvMpPdo{)Z|l#ajd$w<4&uikYMUl9t@<` zb#c^q)%SXUsDa|F3HXrto@3%8Dvn!TpM_wl_ zLMJrEt}0!E#6rz>{lwH7t!8mBSYZo#d_xyrgGMtk?W!|O!}&sM?`6r8W$x zXj4}(w4aWQ9Ucp75yWvQcGqTLZed0Sk7#byjzET*M6bcppT@{5uL##(!!9|-L@gSW zWuAID6>9};L&M_gp&}74ja{%tmN{$}sx|2yx}GOAbDM|o%sBh$9h7K{UHrxLyq-N~X4zi!ba zbb`1!H#@5cV=*kOM}4e-e~?D8x4~Q`w6-|)&?5(v3|(Fo!|XzBe3x6h49cNsOGCyP zxVk#2!BD?!FAxwh+awN`KvHBi$np}i`FUMT-OH6@=XoQ02q@5cyUw;JqVTqsaOw}u zeQE`fd$0`@?R>Go`C9Rq?gk#-ijPJxt=*@_IE)c&+^n@Cq9EJOEo=IP^$N1=5Vkx- z5l){tQY=&7sO}3qRjgPd1}pE9(Ir!rpx2|49~3&eUqNu0=*Lv<@3Jl?#IlQzGHZ|_ z8=3ER*%&Rrrxs`8d-GkpSX;tnT5E1yi^D4=&I;PBCR-PMYlb%T)O3ZcPRo2f=Apa0 z=w_LnKeZi>1Tl!{w4hSrpq;2%Fog<75?XG}o!bgKL)@CqWFyPrfxi|V>&25EH&l9M zBB-ynza*CCVoH_r$h5jASm%`~mA?y%C3%HYG57g>{y;y5HUhzhgaG8YPp}F$oH_N_ zA`@Z=iBiI?GP6o>p8)a!#?Mf+eN@xMZ%DKn^+1GS}VPbLIRz|RC6DTy|J#V{O!P;D- zHil~3Si0gLiA;j6Du=6KS7F_#%!6fU)dq8IZGgsGH%OJvH=s@Z`K#czm)Bjdsd+WA zUpl(1EvQj*?F|XdafF;OG)k7qR9(>75%!Y5lO0Ir)^;i7cL`S-mStrpjOb6#*N;!N zVb+ve)NqC+!G?|NZGi3qwDmzqiz(dVW8H;5o^sLJi#DSTn9YG+*7|E?7RqL?9TCzM zwH#f^WC|yS$dUtcJ4!8Oh~x5w{ktk#x3)+4DCF#uIC1;sNs%-Tg$-o>GqmiGsLJ_3oM!; z%Vt=WGGT{K7@aRzZ3iDEV@=+Ro)2)0BrjU5D{v6WS22N(4)fcMx-*Nv(z6)Hs9v}T zvY$vTvZHv3ofn5STZU-#fG!d-(~%7sSyDlcQmb8WVDm#(&fSclKiEGhB`(sL6iP3H z!)|hEP{5Af1U#wu=-OJ7#p*NOWv`YsrUjR?O^i@jfvzpt%LXlF_D1#~r&^2CZDbaG}n#(WQc*%LX`INXOeA*J4!kPzo263M* z4%{dkoizmu^a^Q)hIWquz$%68(Cc!A-1Pghy>_D*YRsV6XS-BHj+SbA!FD-u~WUyDzF|DZ<)Wh_h?N%*KY;HP(?39Ecm#51jcpJ25vTTniDx_%Jjw7lc?_Xzrrx%lmbJ z_;xK*^(rt-Qg2ra9S%hfpH!9JQ8(#G!svmK8f^;jQ~9<<~GQ-K$l?z zJBhVbiI_mpxKpj6D_x?|vYmJKbHGQAwe8msd=Z~x31@pj7(c_siMVgJkDby5nX}g9 zhX;nsgKVRQ0hK+q#kShXiT3)DLVtDT&rY;Tzp1^}LsPj8y(6?v0m@=2(H_hvthPGn zhEPFg*Z@BSCh0(O~V=suldX((U-1(ZMqP zF3ThK#G4ReC2B8zVV5k0v1`V?to;lTAo)XTD^WE$NjLL2?Asa&wRK5V#jv}mc_ASI z78^u4FS=~g`YV__aJDbR5v4~n!$^j>1B*j(xo@jpSxk;W)2jyq^A3vXKL@T49S%EOnQfNgdC?; z@=CL=7?Fgy!#?|kr2LrJl<~kt>r!H6bV&L~y{1Ok%$!&QOH40w3&^v zngz+5JtEdPZ&%N2z7&lhA{_0zKCwiO53-&jb=P!XjSDEf9Cq8=JH(MMam4n6Pl;)q zUz5Q@Mn9c{f#4Xa?CT^>?jNWkR1P(l2jv`S$cNis85l{-RnlVc!jlRbY>LYWjZmUu zPbEw^LT%JyPsZ86-PsIjf)b`*Ib;`Xa)x?1IKW117tHG#R4==qj7xPiQYz6A#x1nF zw#J_5Wb0IV;+TGz;bsX83r__u9*IDjs4tzjKrI&`mM;d{KAg@?RY50t8JG~2qgnaUrG zPn(%~ov+u;sU9G^_5KFEz9h8|$Sd^v2E9J2*N1Yr9^T zHAr218m?$an{VnhotFDjTHyQBSEtkFF1;Se2z+0oaPrzl$$hI{f86+3BcH`+lI!Vu zeN3Rjyyx>s?c%?4P-2%^!1L&!x?YCkR)mHi5rUuWxIAs6A~~Ock8_ zrXHA@Hs{P(HY08RC3y$eM`y_O%QJ-L&-D7FUZ>9#NVi@u((7)$-lo^r>h=A4{hD5X zuh-1115^LQ%;4qRw7Fo`f>~+vT)jRrOZZtZTdrr%79RG@zH)Zj+^&$f%ofZ~>-~zn z;GB_{+WYnX19`!0I9cF5dfj@m^v_%M{+Fjn+4XZI_Z@TP`YF9Wcb?qeH&5v7pD)*s zbP41`1-buyLGIg6mFtyyeU4sp3k2fp^|g9^RIeY^>lgL9^zZZuhsj@x&{6m zz24L59#$2Tp0vT`(=zjpV8UL7a1D3=fC_E-U?k}nvhS6 zK5gcSKiF(0kvoNvFPLfW<^@EA^aL$~$+w)ZaJRTfvj_?p@GTz0Z|5(tpM z4g};H5^jPLAP_Fm1QJLT1Pmbw5DiJl4hmiqK`Je_B77){7OK`uZI8BUwL+yz6|GgY zv|egks1~nPTd}n*wdc3i%)9pvLEGp1&U4Q5{Qm^rwdXgpW@gQr`^@{!z&0Xb-Y#zG zlrNkQ2nyqydlxvkKI7VJ+#$oE9D-W}XfkZmai&%gjif$wuDAsh$Ja+{bvpF}ir|?0 zYQ8xKHwG7h_M`K}-vn{%hfmU!LhA#R3b__ZD2svoKuZNhfjmGBg02yii%-8e_HO`1 za3Saw;(r^7&5K>w0Z-jY!8L6tP3$AS9b z3?>(N(&?W-Bk{GcLFt+$8$(_Qola)}d4N(>7tmxqS2GFvqXctR%HqJX^IkPM}S2)K2l~6IwS9_p~Gl2-`0zoAfEmoXI=K%GA+lAt`MBHXr zbg@MZ!g&=?Dn1Wv6m*?MO%mT$AP?MDD=t|((8PQ29rFJYbcdkX^q?R1z4+YRgU{FA6qJN-$UXRO@O?qU1+~#11wAFGojw-yj-U>l7RT{T#3$q)d^z{2 zpjtue=&yo)CTKmnOy>0A!)y;-PA3G75OfuNZu|+l8eJlDZV+@0jtyqC7@w1RXamj* zWps_8jW`^YkxAcm6cjXRBy(=YfuZcSLeN(!Ea>}!zJ_B@+3jr z>s_D8sqV_$?>VpYtN#5s9QI}KxarrhpFle6gokO+tLU{Xobd_?UxoQiG3WMr8z+g4 z=`{rWFK3>>Sc82zVg9{WvG2UjfS>8-V)pcAa|&ox=e@o6z}I8FM}h8#S<%kE7yFgi zD7vFB+Zg-%`d$^YH#WxA$oW{`;RxZGzQsPs+?OT!tnaIkD!bpU%1xvD4X3KkC}@aw z^!r~3=luRBApf2HAtz4exFvvIy8rE=uFvv!~xg`<|)0Z3L(NJ0m#lhX?l4UMe5SGP9Y}$hmLy zP&%puzS2$1uHC%qzs)D)drN4*%ctKPUVz z3VluJ@2%f>x{MC>SWZRCSe7bFXr9=N8p|?F8q0Fd9P1AsilED)P1%*6BFKQMhrf;f zsHYfQ5rc5f=iyjhxHw)TIFZ=!uw+D!N}c7IA{oeF3GVHxEb`c+U}pN`p3{C{_+I$q`z84=+#)5^(v3% zpg-i;(*v^=x^VeB;K>RU1FaIbjg&8L+bx-IQg89~om^h96-&`|W$ z?C-U}7F9sS7QG2HnyM}OFtAM(QoA57J@u-HHpb9Bm}=i?kUu-=T=XD&1sUy5pu>WW zslud3)dcdRhbKCwCMW$^ok_O{;xLQVB>GkiJqPro82W`en~n=QK&;Oxgw9)}UX;Wr z*P>N|7Fu+vpz8#6(bohW5_BMNXVUw)JL)a#w%527aF|_mhM(Pz3))JrB>fgK+&^xXq?5g0|AMU{`b(1i=g_bi ziok7-Afv%^nGJtWdpeTO(C5;xVw|INIUP^ntPnn@b~uP@$5=hjx;?EP@t>uuV(3Z# zOkG1YW7&dbDZ=b(l|l5C4*MN@z- zq9qp12fCQMgNuZUENBBXB)SBQXbMc0LG%n zQ}*h1+HKKKQ=ZaMI$+ToDbMN->flK*!aSbxqFzf62-<-&eLZR&9kOU>>P`AGsw`$s zQ(i8og)y{7T~2L+OnJG2?v1$>VP3n}Aex@av$t0*nkVSEpf2R}tGbICrWje4r2bA{ zL-$WJ=%Up3^aeUCXe)g^^;5l(PFS~_Qvanl(c}`td2g!UbscqBv@bR6`YQRSv)c~Z z9-QOaLZuen5v+85ofcU12+%j^W{1;%p6doGox%RLq7}Z-wT*I04f=(!S}skIH8o zw=>he?fNdc=NL3E?R%~VXq=!O)Rgw9>mho;q77+JxE`j@Eb2&p!u3PyC^P=Hr#PTrA6R;k=_urjaH`r-t`h?@}epH zT>-b3=^;T|X?yxRuER7^nu;BCd-_MNUsCma!}){szqnqdaSIr2rDxJlxL&7fK|AOV z=`QyhbhAYtrU%@=p?56$GgcyhOWD;XloRP$?xQr(B5!Db`%T(nQF>^&`xp&9-*65O zO?1CUBNrJ|8k*_;Bi&$8O{l{C5goRuA+*5#CpvD?y3k_xCzN@C@ppZw&iyHMSafS> zrTZ^*z@ocCYu$gNBQbQj`yX^7hPvGUr0m5elm|mu?tjreG4vI;QU_vatJ|dtmpGg^ zxjm}fqJ5#;+&;C+qUV4DYL`WS3*F&PR*%NeZSGWc*diUi&7Gz`wkR0>jyt5%mzo%Y z;qSXM)JTh-3q9t}QVT8G2h>}2#N76~`>5S9xBc#ZYH!T#pgUJhyU>KWFLcm7KrOLo zaQH=co|;x`+$My7=^msy1#PA2K!cTg8M|$z>hSN}XQ+jB20a*h-#tuKE;ndp_% zHK*R7yWm!&rZpH8h1+;FvC*Kx;lH{ksgxB4T?16Cj##u6s6;)#(zx9XG*j(fWzY|R z%G8!7gMtXNLT$Xrpr^wwPnCMqqF3QoqprBvxE%vpq^30+^hr3)vs4`yw2i)#_KvGo zU9p&&s+2c&|39@MW19`;<;4KY3DGvkuUQ~&pNfGgV9#{vuBg%GIftd zLo&Ysv}CPun~{0D=Sp>P4Bh3qT0JDl^l_bPy>XL~f2U$!&e+C5JhmxT*H||m+mt7u zC;j(%Hmbd`Fdy(-s}9G|4?Wi@_az*v={LWs9v0L^7iSi$uc{9Pu`Is;I+4J6y{f#F z{dLi`nV-`2>M}vhSp<}_j@>p9_pmprLxQ^Kh2$4JH>&*g#@~&Zzw~TVZwcBYJ@Yp8 zsCC;ia;)B_+OA;E9rSSK>z@BoTLl@t?2y#*c<}MeKX`ViUE=mMJ)8L+&})J=LY88+ zLmlnr@3^32f!}9-2+ou%Ifi5EL!>qy<{#4ErX4C<+&0qj%s+#3lAtaL^JZ0Q{Uv3Q z_hyxO6^GJA;ViHB7IlLlGn?3@b{RMEw@ck4i02kz?=JO@MU#_ydv8-ISg2&34bIB- zep6j1$b_<6Z5On|KP4;QyIVbA5LILi2ij}Vf~?WrJ?gMU3$w-o9k-}9YohmCYD*`_ zu!Cx|rh4yG`CSg0<^8te-Ai_B$SU{Vt&UiVr%lo+c;CcuBFYi8;^L2yvA-?@;nnh1%{mA|`Wbbu zAS2l`YGDkmK`2`c@=xydDIHXI#@ve4!GzlJiT9v-K-`Y1`Mv(&Ij9Z_GIiyk`cROm zD+kqIx`pE3F0w!}@*PwWK}Q2u_xcc_6bpJ<(sxizGn`V7;<0o34yrlg#`Va9P?`jp z6g{V|h@p`0IhFEX93Q)7`+lZ&#ZZ6W^XmQ>+DSiGdj&mBFF}?U)nSV!Ck^twsGi@! zp*&56n5Q0!Y2uY$Lw#}dMy~?jOEI@U^cwH`MGSoiw_iCZHLLe~o>yb2fA8tOI69;E zrxZsMde2F4o74N;1h)$kL-FkFz0h(xZMg}>MckGT^1UwcF?z(m-1qAkdeVQf@3$7Q zmP!=gtHV74`y=ns7<$CN#`k6nJ?X#P_qLjR6Z0C)zoW`xDBJgrS`tH7sdv?`7~14} zPkkn6hyTIQ^}Y{O=FJ=mqnmsmszQtI3hnWItd?064Bzkjv+5MIiJs~GkngzqK+v(k zq2BLfSI51RLoucNQ%AZMeX7P86gb-ZZS45WvFLd34?TZX%PdmaPx=0;Hd&OE zebD!r+AU}kWn{nL`@7m}Q8v&&)Cr4nfKI6VTR8SjG$i|%zJIDM7L5Y>TphRQtnA|G|5bFzIv(_$#i z@6qc7ncC>pcUrf3+1Y-t-f!KwB>QycZ5+ze#HHM)Zx(b+@oJ%89}={Qsb{o#yvOn}>>WMM*lrK{^TeJsqX6Zu~-3MLw z(&P3Re-9y#dh1Gy9tUT(UT2X1K=yQhAAPH!qv|E(dmnwbMSsek?eC|*Z&9evRVqh6 zZ_(I3VQ;Sf*q}gVpSP*Mc7KcGI~w?kplm^1v>*QZ>tzkFJB`1Z&=UVpT_A|- zW}APQo+jv+;?c!$T^n<|)IU;RZQZyhDbP1q#4T-s-fI!}9R>Q3LDH`j=#K<(3>*9f zI&v4sZpJ;M^+=02l+pT8gT&uxeMpdLHAd@KyP>x%C%2KK^$FwVf1vMngfjBm5<`Gn z%tBo%Xp{fPeZT52)Roqa+s`7s%DO$@ca1929oCIo(=mFBbvxE~tAC8X*}8GtI#xem z-MszU^jN*ux^YW8P9L^zL;7vU9_tZ7rjCu*hwo;|w$j{wH~GhF|2+mZ^!vT*Ox+^L zq;{gdM35QtP1G9&?VwxwjrC5{2Q0d?-}l{<^jk4>x%+Ib@0BoR%wDWV4q|jvy_@@q zw^(0c5%;c4SQiRnsjmX( zXJ#!H2y4ye8er7hy9cgHo}vrKoA}xW^+}nkJ1k;Zrs|>N;<8NDTZ~)asX@P?srnAf z$+Aq<_gloWOw~s$CyxuJ>c0vyvP{*xrpDu&ra!QVW%XU-6R zbOF!|eP3xjl$rV=gXmWXWu}hIOmrJ+5S@U(nY!GfzGsZpv-O3u;+$oAl|fW~#%xul zKkDZ8nRSc8Uzv``D%>Wz5$Ig~?3{Qg75cD2^gD!7p|{BznMrMh-enN=8_I42WsS^n z8)*6Z?-xHis7K z2Q1<=EY!O%Gj5!Qh5Ddz3+Per!R>dJlhd$Jzhe=nVWFOLh4_;+EY!7vOd1yI?bnzv z|1|2ap+)*Fi#WbTdRkXJzD4?kaSMzu_z-Rx8;l#rw@Bw$#PKcCHy9-GEz)-jGVv|a z&DR-!hYR#>4 zjtVmIf%B^-%vXw3#$r8DW^QH{vRJ=r{3+JwVqIz60)5B4546&9a(s(*i$xsYV%=dm zx%MyCHwZHEE!IP2Hh5HVd`t93i#UBt^g$a6$G1f9GHw#z68!_q$?+}GKeCA9TcY<{ zPL6MhenXInZ;3wq4U<03zok0+27@@hrMgpQpGPHqOLd`fll)t%XIoB=Z>g@dh~rzT zKen74-%{<1%|5S!8^2n2T`s@jyA9NZw?Fs|-^cp$yF}lFS<#b14+?#(Kig#YV}4`v z>HxMm0;=gvG554NCaxcgYo7>nMnGLu0IKLxp;rn`x@Ah<8YygTH@T) zc*}-e<7Ss=&rONi>zVs!!IzW!oY&n`WJ<+Gj1NpnP_`u8EH>W~$~Vg>xnD-pJ~6)l zs_7*$C#I;U*_11j*TsX`*BnqqY*u))iD{*nnQGCVi@4-K+X*GjgnWf?_Do}93Ev<* zCSGIyorG-69HYjYO&mV!+%f+nfyadJ9>P3qcBs)j$K~pl7d>rmgI|}FF?To4d&Hje z(M@}#q?s^{{ljoI{GM*E9KMt9iDuS`i6zn1MW(bTN}FgC_vN;n51zr{Gmj>t;V(1` zr^(GrA+1v;6U~Y7CYIQqTKaO=FXA^P!Ng)hFs;O>VJy#Cpe~vMs>s-^NH81z7O`0` z^h!`oSA)9n)|KcvW&^Fp%-vmY7ydgy-E<$Q!^t)-YqKhgLw;3UkAiCYgP4uIF`L+p z`F(Nyi*P2Ug3a2d=*!K?!&wrh-TA-B-e@T?%o6cCTf+Gwb0Yu$EA#mxXT8X2WMCVU z?`}G6&pG?=^uYB((;K2gQ-e$^@qySF^ZyO1ZO?1k9Md0}I)3E{meBNx+@5P<8<(}= z_Q7d?OskmKWA+?Zm=@zvwE0d8Y1;pkkz8vyMlGWwr)M|40Q)-CILh>orrk3#myY6a z?giDhUHzik!M^Z*bV9x6HUVQNu{R?wZVfd3M#4PVtjO4WrGR~zcH^HgYYL2J8)Hti z@0VadP|U`4ILwazzsPJjpDE&yxt-Luos64Z^xT-ZB=3vaw6r~A**TUu?-H7*tM7@8 z;s3puO-~)cnu8+KR+}(QzMJtw-0b9OVyQ?h59dlaOtp-fT-`=ZiJXb~5;vb}tYxIF zl#rK6$ScHbe0_PV+adg>1vZ?fe44%>PMxw8mtT=Fo7fG#T;eq$7&DJR6q#~$t+*O$ zN|ezy=V13dH6fc263vM=#-1hARuUtv3GM3=SDZ&lujgt5-({c zwC>cTE1pw|OkNnRJ|^M+C+*lVaB4f(Fzt_NRT4{-F~2Ke-YK&uZcq3P?l?_s6MC9W z88-I+iP^L*iQ$;I5^cW7oG8_)%t?3;djP)biclW@4W@xKjE2B81a?F4Eyi$4!jnrQ z@Ri#L;1O^uqDN^Q&BRlNCzr;<-vroCfcZ@Po<;ld#aa@cTs%QMd3cKO{^?nGa`EuL zJUm4-6?Ri$Hx+0q?54u59CqcfD~DYNL{<2@s0wyfxN)os zc2%&OPb=``;<*S<9-bmxz}A8%7f(B$JUm7CPOb${E}nKgd3cKO9n*z)a`7z3lZU4W z-{J8(c?t2G0GnwC_7XPJL)b&uOpj+@r8ZMG_BJ-tI_wT?rU#fReh+=C(49i}fcE#@ zE9M7-2qe z(SY0B`S{l0gf~xO9Hu)4ybK9YZEgZd*wlB)FV$VV{OL_S4+^RDG2)fPEF=_R@vHalr+#&_Xn6)!~8eA+5R!iQJB~3^+GoY-ELj~2K(=d%|k*Twf2;}UcV?duUMOO z*!&h|mLWTNC%v23A2yE%IOJD)F?A0#G3LWQNzD8fdO&ue_fY1*PqFejWZ>JhUXO+A zdVLn?PMR(@6=HL~&^nPxf?JOO!==U&LO9h_eoJTmn` zG4B%D?iAVb)X~A$q~@s)L3dK;kZ*%tKjdxtiS_lzGX`OI@bk!8cON|gZxe16`8PxU zr%=;*&$x+R7_6}R(pQN517D}6>UL4jN zwEwX6-alChM-6|U)}tM`O08FC4ZkL>kG?W{TUv>lH~eOyw?T5=U)qeE9po#M9o&HI z|8kg5`Kr{_!=Ff7q#g_%Osi6xhaU#Larm!6zcu{#px+<+w-xZyE89=p=bJrb$}SHLjnJ8tz@=+C6GC=qsb{ z@~}ROMV~K)SgUV^_DYxsB+M$6IeKuI^P(WUmH2&wHNw9}m5y!-uXoKKy`9#(mW}T3 zTknbr{ff|U3EeC7SKwKr-W>fl=wC*!3)iVY;d^*%Fr%)q;_3JRM- zlwsG`3pa)Dg=>l0>H2=*w(w5ZzQS9=&x>q_MYcD@{Fay(NxgVbLR;f%8oMZCjcd)= z+KivLy2h>m{p#4?;3i8>=?CKLxO~-kT)uGZf_+P78MM@q`I77Pu^)O~a@FU3=;=Z& zyDalHml}6dCiCpXt~J-_E`-nlo1xTa?vDzdG)%tS)s+zij_b`e>Y|R~NLJ+{;hdbSJ*?%AtEfBlJDceEI?C zP>eJD_#%8C=sL=C`SFha5YSE<0lJY2L9e6npj+rH&>Ltf=zcoj@#9U!XF)#`^9eDN z;&9xGL-s3<3*VCaf>f<``wI}??Y#=a`$Qyyr&m@gN)fyTq; zYt#U`O>AxzdWX>O!2VA98RC7)Juqoq#s<&8q`zgn>G@jVS2|s zG*Tk=LOX@-HsVl{_PP zZt}w9rOBI<_a?uR{8_RqWn4;0N=3@TlxtJ&PkB7$sg!3^UP$>>%CQuk8cgk*IyiM= zYE$YZsn?}`BlVl952YSVeKGa5)FY|KQ~#M73T6k-4$cYA4=xI>46Y7#1iu!%C3tu6 zvEVDgcY_}WPXsB=o7O*VV%oyA3)7m?qG_AcZcDp6?a{Q))3VZsrk|ZYGrc-}d3sCw zW$ByJZ%n@_eOLOP^lztsC;fr+AE*B;{TJyuu3x9WlYTrsC6p2B7aAIx7@83(53LV% zhPH%m3f&(1Zs>=hCqh39y&6ghXNM!&&t|@y`9|iOnIC2TEi*MME31E2e%8pW zF!YmWS=38oF6hB5GJt={m<6SPrh-O52Vpien9?w_N=NG!!fYx` zg_KF-aB>IFpw7hn>nw0h#w=_KT&LnhigKKfVP-pHF`F5W7U@jPRwiL~ayAiFd3nn+ z;7?UJL4!XhrUCxmplA5|fll@h0G;h047x<@ha`O;=1?-b8frrLd*){zq8GAW_7Vkq zy$Nb;%6hTizh|cSi0Q*sSi&uIqX)fO%7I4tcw{=J)0BhxcVkCiP`s z)BD!>AxYmApp|0ZA=JcT=zkLOyZz&mjLhd$8>zm?ynGZ(Yj~b0U`dSmD@8vIJLb+Y zEUmFmG{^Zh^3#PUEIFHjuaz}&EdZK@+|~GkHx;xuau$1h$XAU#$^e}KcTE*=(Kt_| z4`^8R10A4pLC;nLKuc5}=u9;Tv|J4at-yD%8mD~>1FcmfK$odepmq3qRnu}+1X_>p zQ#Da8|QAxXsK)s&3qW5{HK4y$=Yt+q zi$IUzi%3oHs->U-T??9`>p*+!deFYQ5p;lF2|7?Wfu5l+1|5#C2Q}UdYXu#n*MOdh zF9ETqgKz#c-Z8oqbh=)THHjOv3pL~x?LqB1#}xvd?h1gGx{^U>xl%#rxY9tUxqPTm z-*Qa?z0>6a{kH3D(0g3Pp!d0^g5K{c0sWrKgL?NPxK5!*!8wHw?y z3hf8y6nYYzQ|WowPo;z4oJ!BZ*Hn5M{8Q-#a89K|;G9Y?gR`7c@C{Qr1>G)~)7@u- zhVdO!Ic2)1!raSU0-Ejipr((6Yb6Z_XC)1F|4vm>fg5WAR0z&W8UxNs8VAlQn&SQ^ za^Ng*R?%ems-lVRf52uMIICznIIE}>obzdpd$gWU=YVrQm4kCW&2<;SrV^aE604LT@U|&rafwP*L!C6hK;J2FE zz=>5Aa8^?XIBRGfq^hCIz*$3AfU}0Kg5)*S3CO)RA9AfE}nUK zDycv6YY=i`5Oh2U+8YGDV4E0!8FWl!ueQJE+23{e4bzrD7p+JN(*iVyRoL^XVlR^` z!(Xl|wwdr)^NP7su9c-v>;;6$8X8fUXd!;}4u9RW{#U@oqIXl*g4 zRK^`}q+>%<1CACnS1eIH@ELr#Z~}HBi*wAxp^7*(Gl2)^@lx$fzTW_69rF3~IA6A% zOwc|3&E(sM_$rHk%P_xjC638PN@q8A$ki=Q)VNl=vAv#eKcO-{TpxGw&5*m3Iar4& z9&-;-oJ1K3aOQSvGfuLlxi|tBx4!ae*iphw469pNcTRZD)eP})onWlq7uXZ|X$DTH z#DReI7t?usWeA5iLk@bT1t%p0cwReF#|}>TPJ9cHuEw5`RX48dn2$Rp8r$vRohS>j zLLvv3^A%9B!qL+y!5a!%EW2QyQx{#uvO^NS@&$qLQIR<4nfOFZDNTH@`b@bW9dTUH z)RqufDR?-k4JFQ*JbsTpS4k4O@FDv9M(ROl<%_VcQ1kGE15#*$J z&8^^L^l?{cN2$Gq&~oBlh-OI=&c-aTTBEX)D+aEM_1J~PhStsTA9nZ?6yWFn0WH56N861PGl&vHb>~tw!)@Rr!J_1cg4Qg1esu zIAy+(YT9O?2paSGXe#*l-B^QpiZVN&)W6l6>NPw0dD*>%?_pQ{u>H6YHd);!CBE zqJE1`y2tXxGM3^VcXV<%nRnS3s+Fq+SV)?I%bME>hmzxxRw^rru5YPF`9FI-o)Vs0ZYzitq>a@>(& z?t62ZSHv=34iFdjcvf)q$OpOMZ)UUHVIf)Qj16o`aLa*-RE8yJW6JsFWbQq=7v<>0 zf*@XdqcN76i-QfA+v;R?lumB4rTE63<0j;i(_tG=s1R~HNW7kyW`q;L`C?AR7D0{N zoQVlA9yI%y)wHg$!SKZ$xV(#lV>NHZiYQ78d$0xXWG@g?l5N#zvx+@*=S&d9X2q!@ z;$8v$gc%t^w>2#>V@U%pKZ*@4xs8va_in>*CDsf|Ybh;Ut=umMcZ?&^Yt+X^kG6>M z@Q?3kjd@NeZ6~+mCLLTZV#8`=V=RFQKEYy29B0hD_PID`8x<&OH9^&_xD~)OdD9zt zj2#P)Do}71;P7oAn&>4|A7pe6>KxL8(p%c+>HjQDX?V#j(Z zASLyDRR9JMoRRS?oMmmKB*m?T3q?l!Cs zCY0zvY~ZGqNC>+uiZC!cK~#m#!d z1sOf;PAW_>9~HRKXKo`#l;w>Wi8nQnNXAK$`pgqPV;aL#Vwvoe;=V9hFQ{vd^L&BJ zj8%>G7fX=lItiM~7d|xO@`5CB#UW1)*yZ4^e7us|Ap2k=QI^ zv^B41Gsb{Mqv9Y{)tPEJM2K>oHLm(}K7WwU`G481bQNE1F=8H3?=I$Bv0xJ7!o<7#Fv8%nQ(P zV^u){ENxxOvtbO{noyjqT_V8h2?oq~L9LZdRNcO|(VE3*gf=`0HPf_`?8Bzpc2zyw zkyw6nPuA1f4nbz}jc{a!wst0I=KI};gwj^=s8kw2x`Y?0YHPW|arKL}it!mIcRNm5 zE}KX3B`D%sPRz8pWi786@RYl{)zl;mtJb!+qaaW%UntcmBiR|&3WF#_Cs&b@V`tkX zXU)a=jaNiFBgb{yv5_zC)RCgR5+hS6(t&Nby}pAZj8Yq#>b4HLsI{quR@b#r9beZC z6K?oeN4Ph{2yJI9rA^NLAu=A|F#ttzX-jjP`7v}gHQMAhQag`BsNJp%(Pd3-gn>4% zzd5pCtl8XXRwwFl9~|!bZI~z5E*XV%w=#=s7|V3Gu5}{B?e4Pj+8V{M71GP9ngrx5 zu~E!m(-dDri557^Ehx=cC34!hgoTn*F~hll_3Px*mxRl#4%-aeg3AfPAXgT>5T7w{ zK@%H0TP$gj@hcD1;KV=NMe|Th5+*$q$1h8HY%mi98KE$A#*EBpChLw3vlts0)JaGZ zNNnjda;Odq&sh5AY{29T_cWr+#kvHR%nXlt7E*0in=nPi z?0FULL_(j`AjxBk`K_%|T5CD)2sOhPD%WCx6a5W8H7!kR(5T2L3)5Jh)HrrF&rtEC zm!bu&?HzOum-pGWH$_!4w=l;HSP;XFeJz;$aEpST9Npu*70z$m6@yk}1XZ@S@gp5D z+J)8V#!HZ|#^hL-BshztPJ_{{MDhHsz=nCMVkGW1)PLh%f>rbNxPx$Yx1~>6H!NVC zeW4>~Oj(rI(C4+F>plto8*efxU4YE9{^EG1TtXZx6>}ReHMvpNf+cLLvJNOXhK$+C zJaLHSS4qP~xX8_nMWi3JMk!(^6{1)HlEU*Js6U?eFHCG%G@dXNY`Y>}i=u)7OKmf+ zjB_cK3f00RNJdh#E(VXFgeul9Yb#P;na`tXQnA$SxF^;onbo&?XKyu1lTmV{rIz9T zl!n^cLM&dLqSDz7o5b#Ua`lvd-$S4558W^OTZu~I^um*;jH!(c#7+^lGa}%g+YHJq zQ^NjD0gqXE#4&kFQLP=!O`bBYww7-zq}4LlG1YVOl(Dr~uIB-xwOBo6d~IzJe4bfb zdue-J8zy!=TkS7Mhm!_QkLi6mP+QY^_E6T+xW;TdqgUo@{uDU%ov!RshYI z;u5=|VdgYu{6D=8Su{Ighrx`@&APS372D-RF`T&$tvE)vvZ2!?-IjTP9>w z-oV=hyj37Kg7ZcJId@cw3d$RhOSOg60eR-NU=@z{^jooPQQp9pV`AWgH6o;tuT_s_ zkm;(8S9}`PP+wa%4nNqhU5%0J3Qm!ouR)T9uF zAa_^F>hTzw9AEL9vK$SXt^OueT<>|&7(--C3>(O-va`%Ayv;w`{LaBun9L0*L1Ty- z?Py28gW^=)5M9yCT4u`LWxZ7t(9ufuvE?<^IX6IDL8KVjZBe7&cn#);IJWd0m4^5~ zX@*b(C)@TTzJXj|0_S1Hf3t=D@*p|6PQy3ZB<-XobnJg3J9hL{X)F_Lx8)Wf#%u{K5; zCu>;}>s&eYWl?#Lz?NreuhI6H5jVGSQVt+>%mtHtXZtpaZq^usx;Pu#4;X!1#!gNyijv9jwki(hWW^+wC zz%kUuie2ESngRY>(Xd2ern}3zU+P)w(0!VzvZ--BYUPuJPefG92J7a3rUIpA7;<&uYrW?G1rZ5?6W(P7s5(6ZpAq`EeYwKzg7iCoOJ*+vJG z8DT(S#^4Mn2#bJZCeSu=Zb;3r2$9+SaZ|-)n(rtGP1d{>vhosF6QYqBh{SH@x(FLr zxuYfI6*MfRn`Vok91c@PaZpmsTxRC7DEsDJzv@M1?2nHeTPjxG=rt|u^ z`KfL-zYbz+UQ{6+~ld8{x7HE^}eX}C!ri{{Fi z%#1A}yFbo7LtK2PElh=L9b%jxE= zGjPjbXh8=LhHQ02{v${ho~1ddW{=~7$>Z@J%%^AuVyz$wtPK(kL#)zUt24QX%fL3F z|DG(X|NouQlvz&spADa!Fp$>Rc!}qDoNn%f9Wx7Qx?-Cvi3^gans{0^Cwym^#0!Sy z2#d^ShJ71S_Dk(F5^KCXh($v~=i|omM&T9{7Q}dj!b981*n{}*sLXW4|1*_2VI-J5MB^M_Y zRnCf&qG6ZnR=i9{=hxvqtX6CpV_J=NrnGf+T~o^fET&>Ho=4lLle{kBm`g=yd>SB; zam)#glPnIz0&%iyL1X<|6xHzy~v=l?AIHi&MC=l0MHaBOu)R*6E; zv&k>U;QxW32?@8cIH``OxM(aI$i_C)1{;%!0pH&k!+2-#g>+-ifMp-nR1xxq#pHGd z!(bO6;L^IMO2VQQj{$!}QjURXk^kznU>Kz5$+S$)B%qc$gz$vJ}N`M3h&T z`O^b#2r#~~orh@(crb*wv0E^BZswihvXw1(^ABUAMqc&kKq6v0cpTzv^A0G<%VhD9 zHcN@&AY!R&j%`S>Wt2MVE^eI1YjDw3*r_%cKV#-blea;4%Nw`=4qo zD!V~;6gN-C<=w_Y{DyOL^St(%*o9wDQCxJ$b9GUQvl)UC!!0qWQO$)+Y0NcwDGL32iz62pO!=?d}wM)nxYeYguZv=A#z8prpy;=4~toOBn z)+1DQfqkE{ZlRPQ6b=VRTaQYVC2s!((hPFaH51`EdaRRp;;G|MR$5oq!CcX9cOT1< z9;6O8V77vjxyaW{tB@8N#+o$JIjOxEX;=+D)>8!MGjZB+9Gx$M@A8m3{9eJo$TQC4 z>3CWZKJu)0D>PAyFYi|4mo-?A2XW!sPs(K-Vwxs#b-+hEzRJEFUu0j2@2@GVkP2ub zo-y)F!k2%Auw@_dblX&fR!ACH-?j{8ak@CKO)gm5-i>0zrH@lw4gGUIQEC(^XSZg^ zNolK*GwYC^$#@WF2B*3aoDr0|Vq-U&b+8h#wOBq5k#l;8Xe92#hS80G3i!T=pWP5= zh_%!RX<{-aa}8lBaZvjJj+Z%_q+D{DFlC(WP^NI&knxS&%wSEhY=&QIScvU7M>wY? zMAu^E32Ij|ryVQ(h=~R`HRF8P;cFK7;ZRoK;ZjY>rdF&4BG*tn$hAO3cu^JtEQx3r zXZT3@f|fDuhHtK4G;BWn;UzreoT=dv*mD{qh{2ReTc^?z{Yh&=X+Qqf8^2ln{>(j> zFMH0oc8A6r$T#!E^ls5a>S#; z<8s0k!Q32wgqS;A<&IEpjt3-M;f<(pX*e?pKbhiK<8aj^7b?Y8{VB8v-<^ju!ERXK zKfKgGG!~jtL0?3xa9yZTd;M;FrB7~r4o~>l7R1fO#e~0!#W(H#a7DNRGS)yqzxHZh z#HGSDe$Bzwuxx&`Y#NVWWNc#l3O6YYtilz31fe+y_fX~^%KSsYKNS1`;U>1P2tXyP zbvQp<>W^q7F+ZGx5S0gV=45k-r4Tv1CcHMss9+5QT94;SFQ=p;2wNTM>_Ut{%u<60 z0sMZJ7P0$bsluJt`ry`iEgZejkI!aHK$wEC5s@koj6a-@7+IoxM3V`rpmH!Inef=? zBZVkAIt}&;)(d|8egxXN8Tl3_ZB33v1y1p;BRXWc$Xr#VgOEzrjRFKf{wFiDi7gX0e)f*c%nTb;krqLDVL@$4 zf@e6^aH^3j8jybxyS5Zca3Nfzky%Sa&0GviO&NpH7(&ef6uz2p=hjfO$;v8NiM*ZH z$4qc^dqbW7<;JJg)D<_952)V+g zY)B$E3{q(VwHpqW2*50fmP;cXD{a9EmK5Ff_{sPdVW{H}nr1S1{=Jvwk{n!+(Dg zA~q&}D$XD>=3IZSPH`LD2o!z37Qz$ zo7l47pr1Lwi12hU+_?wh27}>T`N?JBbxwFDJLZHt?=mo#edIt*s1_nN4!J<8&HrRp zJdFIntP{+sjv2uT538{AUgRJD;6JqE$pLXQaWU~S@i7T7Nn(=BB!x*TXHcl~yKL5M zh8W*va|)AG4!#r>5qd@|zy{Ow)dZoZ8dFT!9;s5uA^1^24{A<$jTcoIIf!x#$*$*C z59Jg^5}5-crCyr6a8(jXd57j6#wv&>2Mw0?=b&-pR(&v#KPSfr8qN*p!e*Eo zEsHSaaAh@FLgCJbS%@68x;S}21-YOiFYJ#CnGD3qK~e5}2+6~5t}R9t;j)-Xc#ty{ zP8qAnYBnkH)~|(VE~58n`(ZnJrvsiMXic<>2@%NQ<5T zEiP*>h@=L&EzLu6(Dvj>e`Gqh41#LV*|ADs9x7&Tgzpk<=Ry|>y$G@p7%_m6YYf^^ z{>kA6VlTQBP=u2kMAOVh^cg{EM$t8P?nje^cG9$x=pO>oY+CFOuW_eKzG-Moi<%CF zI}aJ{9D?!>C9!tcE}RQRp#pRs;#>v~G7Z1C`5|8pIx@8IoMCW*eEfq9F~6M$lOs+b zod?AcIRM^VRvOAi=OOE3u;b%-1d5s`-q2k@1L4kt_(AUS4+KLv9Hu{q3zsO0b%Z7l zEvdxbc@Sav{qCWVjyup??oXbFXSm{r6YOUzP7ZtEA5kYV2@N0zvJWj__aO{>_=(~C zXXkFpN4f9B;XSNE4FWuWK}iuBYs_{O9qm=Jf8HlAzV^xM_k7xX(Xc(Ad_3;upL;%e z<-K1%c6c*YADQ~-t*K*gESd4cRh_SHSvKo~w(sqGsCCu%HZT8kzwiF#(UrdCJ6@bs zcWl_VZ@F#MH}3sL`eTQePhZvlksT8nE_>?Jf4yEj<%!oO<T>qQ+Vs# zNq;W*VcpAnZfJYahD9kIm?ehh5edIe;5>GRT9Q`uu2&UH)`5!|6U$72!5R zIn7b!fuNxKz~}O*91~uDO7rnJ;A4!;xOVji13FqRQo$e_mm+5|K=S&~IAK%;n;H;| zl6;DT@q0KaO>AEQk-A1He=uF4kLKWj`jab3aeYO@i<+M>jDpbV8Hx*X24$!;A9}ZN zDMT^jtRz;2F{0g30Td7(qoTfYH^m(TA`EqnhdPrZZnQuZP$&aIbFa&j9BOD%raZW=OP@T`i+~Nsbh+Z_r!}kEIP8?9bp&~CKwO!+5DY-t$=VoPP zLft-OEB_+vAZv{&RA$tUGRNg`4mY2$fh;wUB?TTjKa&z!qm!XG2JF=Y>PNuZ1M3RS z<%@@JkT-+;Huah55hENDZPK3Mq}>?|hBMh~R|UL6Mc{^!6Xk`*bNwiSgqFo88Q@1$ zF1A9MhLxMgw>r(oLa@<~5n_eEA6Foe1bOU45=(%G2a`PNRHIG(pDZ+LtmX+J*dW=TOmJGBEMoQN;?sY6G{VMr9n60 z8jb?v)kSGO9`v)22pZaE>n_Skjd*f`h&9}`6is%94-LtqT-3R`gu75{*grhx1W<)4 zc#e`IKK(s-Q@{(^!(Hj82uMl+3xZzN=MFVsfa%xSiZZZ05HUOVktV#Kgr#{L*`I>q z*Oi^a@e04lEs})0a&l7P3Lzn01W|Hvm}z9nPjDK_(vS|1=b|kVH}Y@~Bih^?4!-jt zsl+*)JgHR?<>C<~I^SNtB-Q}31^;=}@o0|r^RUo`dYco1ir6jgfUM0yQFn*Sf+#W( z3@^F)3QCQ>4FwlfSw@V=j+|Wdc$n4Y`uq6Ml4=ZbrP#?YU{0>~5AbEc1rBgxpKM^G zn9;f-!C*wV7*=&Y7|!LU!Nw5u`M7LyR{+AZ81!_(fX{=zeIKqS4in4e_xlVpAZ=Eskp6v!vzTOR*n z^ueQ4{Eu0)&e0O4rlvxC!nN%yS0`=?sx;S2@JvRnXF-I|!%+r7i+M={4IX~ExjzCYR!IH~j*H2*xcWw_#7llgBGO}2b2+WT#;?6;T%ng9waNd*6d zyN(8vII~}ab#6|bq9xsSffp|w8F9`%8X2)4ITVj6DCB=5BX%dGxW#;QF)~t#Z!w$d z`K2>_Cf`!L{LC}!#@COZP&8@mxW>YXlQ1CW2B!!a=|;+tq38hbK_|+;_v5!iQR^u- zyH47*`9CZZ8dnt_CVBq{DN87)?oijeC~JQR7jd1kB}%mSN3L|853svoNX7sFg}v(! zDD447?lRut!LS#_8`B|_Ii4q>=R$&EkUkYZs0t{Vhop{3o5ftHwOmH=A4@av&yDe5 z8Bw4F`B{)d7~}EKg6je|-G>a~n#IW4ppoac9t98af-)Z(DqdN{=vjAeP;_MtpHURS zR#^nE;+Qk-%z0Q5yrtCMw0vzxW27k>X=ubZ(bxxUY$%9CxW~2G^F?2H;EONw$;a&G z*5z_!M#9Nrv3{CYstA1vw8V2$ROcYnC_N2kS1fA~-66{AQ67e)3Lf<-(s)S=Qx?pq zFf6BFLBZ%~d;RF;kUom{yr@9Yu%R=CMaC2sjvWPZW@LVA13sf^jZDXz*o_^nNK0g1 zGj{iy+UszVs-ki58yOX;Yl&d>ltZlZ(WH^Fg{`_(riB-ieNKNR5hc#jBwsH zK^pwQfHe~Q_2+qAweEF3KBAbL@UjuUY-qhSD!lZ1-!XHTmuG}i@f_ZU`|^+x^B0sZ z`1afVvWM=uX!cDn?b6BjT)2aM)StBkqVS9Q(It>`RP>UTCCmBPnkBLPYHL_dGah^RF_2FYb!ZAlplun=A10f;r%uJ+z4V>1%X$_p# zz-bMf*1%~EoYuf;4V>1%|3eKZ+zB!QJ@QcEo-&nG5cnQY??vD|uf=u!Eje3Uk zyzmqv-R9poyw?UDU>j~c{5t%l@|IG)ytmZWEuQ6&rxlZ@QFvGq^WGo7cg0dQAvBhp z-x1_@3RjA}_yF9#{}d6q`Aw*`@NM2E+8`(GX3J^X^z7jqg;Ep#V@wX1QJ1DsD z`)%xN2Bd1kyIlO{)=In=hPU_dp56?+qZdKEth=$$fiay4yZPX6u-vSl=?I12C*-#@ z8RL5`(0K^Ci7tV;32t==vs7fBXMOTpVjR1Z4~+i3>{}%7%sZtG&@8_Z7*91JPyX-a z**M9w?y{Vmb0_E9MDbICyybUFS4$aM4{iNBf1Gcp|4wV*v<6OV;Isx#Yv8m7PHW(_ z22N|>v + /// Description of Assertions. + /// + public static class Assertions + { + private const string NULL_FORMAT = "Parameter '{0}' must not be null."; + private const string BLANK_FORMAT = "Parameter '{0}' must not be blank."; + + /// + /// Throws if the parameter + /// is null. + /// + /// check if the object is null. + /// name of the parameter to check for null. + /// if is null and constructs a + /// message with the name of the parameter. + public static void NullCheck(Object o, String param) + { + Debug.Assert(StringUtil.IsNotBlank(param)); + if (o == null) + { + throw new ArgumentNullException(String.Format(NULL_FORMAT, param)); + } + } + + /// + /// Throws if the parameter + /// is null, otherwise returns its value. + /// + /// the type of the parameter to check for null. Must be a reference type. + /// check if the object is null. + /// name of the parameter to check for null. + /// the value of the parameter . + /// if is null and constructs a + /// message with the name of the parameter. + public static T NullChecked(T o, String param) where T : class + { + // Avoid calling NullCheck() here to reuse code: it deepens the stack trace. + // We want the exception to be thrown as close to the call site as possible. + Debug.Assert(StringUtil.IsNotBlank(param)); + if (o == null) + { + throw new ArgumentNullException(String.Format(NULL_FORMAT, param)); + } + return o; + } + + /// + /// Throws if the parameter + /// is null or blank. + /// + /// value to test for blank. + /// name of the parameter to check. + /// if is null or blank and constructs a + /// message with the name of the parameter. + public static void BlankCheck(String o, String param) + { + Debug.Assert(StringUtil.IsNotBlank(param)); + if (StringUtil.IsBlank(o)) + { + throw new ArgumentException(String.Format(BLANK_FORMAT, param)); + } + } + + /// + /// Throws if the parameter + /// is null or blank, otherwise returns its value. + /// + /// value to test for blank. + /// name of the parameter to check. + /// the value of the parameter . + /// if is null or blank and constructs a + /// message with the name of the parameter. + public static String BlankChecked(String o, String param) + { + // Avoid calling BlankCheck() here to reuse code: it deepens the stack trace. + // We want the exception to be thrown as close to the call site as possible. + Debug.Assert(StringUtil.IsNotBlank(param)); + if (StringUtil.IsBlank(o)) + { + throw new ArgumentException(String.Format(BLANK_FORMAT, param)); + } + return o; + } + } +} diff --git a/dotnet/framework/Common/AsyncHandler.cs b/dotnet/framework/Common/AsyncHandler.cs new file mode 100755 index 00000000..397dfc9a --- /dev/null +++ b/dotnet/framework/Common/AsyncHandler.cs @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +namespace Org.IdentityConnectors.Common +{ + /// + /// A completion handler for consuming errors which occur during the execution of + /// asynchronous tasks. + /// + /// + /// The type of error consumed by the handler. + public interface IFailureHandler + { + /// + /// Invoked when the asynchronous task has failed. + /// + /// + /// The error indicating why the asynchronous task has failed. + void HandleError(TE error); + } + + /// + /// A completion handler for consuming the results of asynchronous tasks. + /// + /// + /// The type of result consumed by the handler. + public interface ISuccessHandler + { + /// + /// Invoked when the asynchronous task has completed successfully. + /// + /// + /// The result of the asynchronous task. + void HandleResult(TV result); + } + +} \ No newline at end of file diff --git a/dotnet/framework/Common/CollectionUtil.cs b/dotnet/framework/Common/CollectionUtil.cs new file mode 100644 index 00000000..941d973a --- /dev/null +++ b/dotnet/framework/Common/CollectionUtil.cs @@ -0,0 +1,1185 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2015 ForgeRock AS. + */ +using System; +using System.Runtime.CompilerServices; +using System.Reflection; +using System.Collections.Generic; +using System.Text; + +namespace Org.IdentityConnectors.Common +{ + internal class IdentityEqualityComparer : IEqualityComparer where T : class + { + public bool Equals(T x, T y) + { + return Object.ReferenceEquals(x, y); + } + public int GetHashCode(T o) + { + return RuntimeHelpers.GetHashCode(o); + } + } + + internal class ReadOnlyCollection : ICollection + { + private readonly ICollection _target; + public ReadOnlyCollection(ICollection target) + { + _target = target; + } + public void Add(T item) + { + throw new NotSupportedException(); + } + public void Clear() + { + throw new NotSupportedException(); + } + public bool Contains(T item) + { + return _target.Contains(item); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _target.GetEnumerator(); + } + public IEnumerator GetEnumerator() + { + return _target.GetEnumerator(); + } + public bool IsReadOnly + { + get + { + return true; + } + } + public int Count + { + get + { + return _target.Count; + } + } + public bool Remove(T item) + { + throw new NotSupportedException(); + } + public void CopyTo(T[] array, + int arrayIndex) + { + _target.CopyTo(array, arrayIndex); + } + + public ICollection GetTarget() + { + return _target; + } + + } + + + internal class ReadOnlyList : ReadOnlyCollection, IList + { + + public ReadOnlyList(IList target) + : base(target) + { + + } + public int IndexOf(T item) + { + return GetTarget().IndexOf(item); + } + public void Insert(int index, T item) + { + throw new NotSupportedException(); + } + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + public T this[int index] + { + get + { + return GetTarget()[index]; + } + set + { + throw new NotSupportedException(); + } + } + + protected new IList GetTarget() + { + return (IList)base.GetTarget(); + } + } + + internal class ReadOnlyDictionary : + ReadOnlyCollection>, + IDictionary + { + public ReadOnlyDictionary(IDictionary target) + : base(target) + { + + } + public void Add(TKey key, TValue val) + { + throw new NotSupportedException(); + } + protected new IDictionary GetTarget() + { + return (IDictionary)base.GetTarget(); + } + public ICollection Keys + { + get + { + return new ReadOnlyCollection(GetTarget().Keys); + } + } + public ICollection Values + { + get + { + return new ReadOnlyCollection(GetTarget().Values); + } + } + public bool Remove(TKey key) + { + throw new NotSupportedException(); + } + public bool ContainsKey(TKey key) + { + return GetTarget().ContainsKey(key); + } + public bool TryGetValue(TKey key, out TValue value) + { + return GetTarget().TryGetValue(key, out value); + } + public TValue this[TKey key] + { + get + { + return GetTarget()[key]; + } + set + { + throw new NotSupportedException(); + } + } + } + + + + + /// + /// Delegate that returns the Key, given a value + /// + public delegate TKey KeyFunction(TValue value); + + + /// + /// Description of CollectionUtil. + /// + public static class CollectionUtil + { + /// + /// Creates a case-insensitive set + /// + /// An empty case-insensitive set + public static ICollection NewCaseInsensitiveSet() + { + HashSet rv = new HashSet(StringComparer.OrdinalIgnoreCase); + return rv; + } + + /// + /// Returns true if the collection is a case-insensitive set + /// + /// The collection. May be null. + /// true if the collection is a case-insensitive set + public static bool IsCaseInsensitiveSet(ICollection collection) + { + if (collection is ReadOnlyCollection) + { + ReadOnlyCollection roc = + (ReadOnlyCollection)collection; + return IsCaseInsensitiveSet(roc.GetTarget()); + } + else if (collection is HashSet) + { + HashSet set = (HashSet)collection; + return StringComparer.OrdinalIgnoreCase.Equals(set.Comparer); + } + else + { + return false; + } + } + /// + /// Creates a case-insensitive map + /// + /// An empty case-insensitive map + public static IDictionary NewCaseInsensitiveDictionary() + { + Dictionary rv = new Dictionary(StringComparer.OrdinalIgnoreCase); + return rv; + } + + /// + /// Returns true if the collection is a case-insensitive map + /// + /// The map. May be null. + /// true if the collection is a case-insensitive map + public static bool IsCaseInsensitiveDictionary(IDictionary map) + { + if (map is ReadOnlyDictionary) + { + ReadOnlyDictionary roc = + (ReadOnlyDictionary)map; + return IsCaseInsensitiveDictionary((IDictionary)roc.GetTarget()); + } + else if (map is Dictionary) + { + Dictionary d = (Dictionary)map; + return StringComparer.OrdinalIgnoreCase.Equals(d.Comparer); + } + else + { + return false; + } + } + + /// + /// Creates a dictionary where the keys are looked up using + /// reference equality + /// + /// + public static IDictionary NewIdentityDictionary() + where K : class + { + IdentityEqualityComparer comp = new IdentityEqualityComparer(); + return new Dictionary(comp); + } + + /// + /// Computes a hashCode over the enumeration. The hashCode is computed + /// such that it doesn't matter what order the elements are listed in. Thus, it + /// is suitable for arrays, lists, sets, and dictionaries + /// + /// The enumerable + /// The hashcode + public static int GetEnumerableHashCode(IEnumerable enum1) + { + if (enum1 == null) + { + return 0; + } + int rv = 0; + foreach (T val1 in enum1) + { + unchecked + { + rv += CollectionUtil.GetHashCode(val1); + } + } + return rv; + } + + /// + /// Computes a hashCode for a key value pair. + /// + /// The pair + /// The hashcode + public static int GetKeyValuePairHashCode(KeyValuePair pair) + { + int rv = 0; + unchecked + { + rv += CollectionUtil.GetHashCode(pair.Key); + rv += CollectionUtil.GetHashCode(pair.Value); + } + return rv; + } + + /// + /// Returns true if the two sets contain the same elements. This is only for + /// sets and dictionaries. Does not work for Lists or Arrays. + /// + /// The first collection + /// The second collection + /// + public static bool SetsEqual(ICollection collection1, + ICollection collection2) + { + return SetsEqual(collection1, collection2, false); + } + + private static bool _SetsEqual(ICollection collection1, + ICollection collection2, Boolean equalsIgnoreCase) + { + return SetsEqual(collection1, collection2, equalsIgnoreCase); + } + + /// + /// Returns true if the two sets contain the same elements. This is only for + /// sets and dictionaries. Does not work for Lists or Arrays. + /// + /// The first collection + /// The second collection + /// + /// + public static bool SetsEqual(ICollection collection1, + ICollection collection2, Boolean equalsIgnoreCase) + { + if (collection1 == null || collection2 == null) + { + return collection1 == null && collection2== null; + } + if (collection1.Count != collection2.Count) + { + return false; + } + foreach (T val1 in collection1) + { + if (!collection2.Contains(val1)) + { + return false; + } + } + return true; + } + + /// + /// Gets the given value from the map or default if not exists. Always + /// use this rather than map[] since map[] throws an exception if not + /// exists. + /// + /// The map + /// The key + /// The default value + /// + public static TValue GetValue(IDictionary map, + TKey key, + TValue def) + { + TValue rv; + bool present = map.TryGetValue(key, out rv); + if (present) + { + return rv; + } + else + { + return def; + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void AddAll(ICollection collection, + IEnumerable enumerable) + where U : T + { + if (enumerable != null) + { + foreach (U obj in enumerable) + { + collection.Add(obj); + } + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// Replace the element value if already stored in the collection. + /// + /// IDictionary key type + /// IDictionary value type + /// Enumeration key type, has to extend IDictionary key type + /// Enumeration value type, has to extend IDictionary value type + /// The collection to add to + /// The enumerable to get from + public static void AddOrReplaceAll(IDictionary collection, + IEnumerable> enumerable) + where UKey : TKey + where UValue : TValue + { + if (enumerable != null) + { + foreach (KeyValuePair obj in enumerable) + { + collection[obj.Key] = obj.Value; + } + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void RemoveAll(ICollection collection, + IEnumerable enumerable) + where U : T + { + if (enumerable != null) + { + foreach (U obj in enumerable) + { + collection.Remove(obj); + } + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void RemovalAll(ICollection collection, + IEnumerable enumerable) + where U : T + { + if (enumerable != null) + { + foreach (U obj in enumerable) + { + collection.Remove(obj); + } + } + } + + + /// + /// Returns c or an empty collection if c is null. + /// + /// The collection + /// c or an empty collection if c is null. + public static ICollection NullAsEmpty(ICollection c) + { + return c ?? new HashSet(); + } + + /// + /// Returns c or an empty collection if c is null. + /// + /// The collection + /// c or an empty collection if c is null. + public static IDictionary NullAsEmpty(IDictionary c) + { + return c ?? new Dictionary(); + } + + /// + /// Returns c or an empty collection if c is null. + /// + /// The collection + /// c or an empty collection if c is null. + public static IList NullAsEmpty(IList c) + { + return c ?? new List(); + } + + /// + /// Returns c or an empty array if c is null. + /// + /// The array + /// c or an empty collection if c is null. + public static T[] NullAsEmpty(T[] c) + { + return c ?? new T[0]; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + TKey k0, + TValue v0) + { + IDictionary rv = new Dictionary(); + rv[k0] = v0; + return rv; + } + + public static IDictionary NewDictionary(T k0, K v0, T k1, K v1) + { + IDictionary map = NewDictionary(k0, v0); + map[k1] = v1; + return map; + } + + public static IDictionary NewDictionary(T k0, K v0, T k1, K v1, T k2, K v2) + { + IDictionary map = NewDictionary(k0, v0, k1, v1); + map[k2] = v2; + return map; + } + + public static IDictionary NewDictionary(T k0, K v0, T k1, K v1, T k2, K v2, T k3, K v3) + { + IDictionary map = NewDictionary(k0, v0, k1, v1, k2, v2); + map[k3] = v3; + return map; + } + + public static IDictionary NewDictionary(T k0, K v0, T k1, K v1, T k2, K v2, T k3, K v3, T k4, K v4) + { + IDictionary map = NewDictionary(k0, v0, k1, v1, k2, v2, k3, v3); + map[k4] = v4; + return map; + } + + public static IDictionary NewDictionary(T k0, K v0, T k1, K v1, T k2, K v2, T k3, K v3, T k4, K v4, T k5, K v5) + { + IDictionary map = NewDictionary(k0, v0, k1, v1, k2, v2, k3, v3, k4, v4); + map[k5] = v5; + return map; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IEnumerable values, + KeyFunction keyFunction) + { + IDictionary rv = new Dictionary(); + if (values != null) + { + foreach (TValue value in values) + { + TKey key = keyFunction(value); + //DONT use Add - it throws exceptions if already there + rv[key] = value; + } + } + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IEnumerable values, + KeyFunction keyFunction) + { + IDictionary rv = + NewDictionary(values, keyFunction); + return new ReadOnlyDictionary(rv); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IDictionary original) + { + return NewDictionary(original); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IDictionary original) + { + IDictionary rv = new Dictionary(); + if (original != null) + { + foreach (KeyValuePair entry in original) + { + //DONT use Add - it throws exceptions if already there + rv[(TKey2)(object)entry.Key] = (TValue2)(object)entry.Value; + } + } + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IDictionary original) + { + return NewReadOnlyDictionary(original); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IDictionary original) + { + IDictionary rv = NewDictionary(original); + return new ReadOnlyDictionary(rv); + } + + + /// + /// Returns a modifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable list, after first copying the collection. + public static IList NewList(IEnumerable collection) + { + return NewList(collection); + } + + /// + /// Returns a modifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable list, after first copying the collection. + public static IList NewList(IEnumerable collection) + { + IList rv = new List(); + if (collection != null) + { + foreach (T element in collection) + { + rv.Add((U)(object)element); + } + } + return rv; + } + + /// + /// Returns a modifiable set, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable set, after first copying the collection. + public static ICollection NewSet(IEnumerable collection) + { + return NewSet(collection); + } + + /// + /// Returns a modifiable set, after first copying the array. + /// + /// An array maybe null. + /// a modifiable set, after first copying the array. + public static ICollection NewSet(params T[] items) + { + return NewSet((IEnumerable)items); + } + + /// + /// Returns a modifiable set, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable set, after first copying the collection. + public static ICollection NewSet(IEnumerable collection) + { + ICollection rv = new HashSet(); + if (collection != null) + { + foreach (T element in collection) + { + rv.Add((TU)(object)element); + } + } + return rv; + } + + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList NewReadOnlyList(ICollection collection) + { + return NewReadOnlyList(collection); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList NewReadOnlyList(ICollection collection) + { + IList list = NewList(collection); + return new ReadOnlyList(list); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static ICollection NewReadOnlySet(ICollection collection) + { + return NewReadOnlySet(collection); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + private static ICollection NewReadOnlySet(ICollection collection) + { + ICollection list = NewSet(collection); + return new ReadOnlyCollection(list); + } + + /// + /// Returns an unmodifiable set, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static ICollection AsReadOnlySet(ICollection collection) + { + ICollection list = NullAsEmpty(collection); + return new ReadOnlyCollection(list); + } + + /// + /// Returns an unmodifiable list, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList AsReadOnlyList(IList collection) + { + IList list = NullAsEmpty(collection); + return new ReadOnlyList(list); + } + + /// + /// Returns an unmodifiable list, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IDictionary AsReadOnlyDictionary(IDictionary d) + { + d = NullAsEmpty(d); + return new ReadOnlyDictionary(d); + } + + /// + /// Creates a new read-only list from an array. + /// + /// + /// + public static IList NewReadOnlyList(params T[] args) + { + return NewReadOnlyList(args); + } + + /// + /// Creates a new read-only list from an array. + /// + /// + /// + private static IList NewReadOnlyList(params T[] args) + { + IList list = CollectionUtil.NewList(args); + return new ReadOnlyList(list); + } + + /// + /// Creates a new read-only set from an array. + /// + /// + /// + public static ICollection NewReadOnlySet(params T[] args) + { + return NewReadOnlySet(args); + } + /// + /// Creates a new read-only set from an array. + /// + /// + /// + private static ICollection NewReadOnlySet(params T[] args) + { + ICollection list = CollectionUtil.NewSet(args); + return new ReadOnlyCollection(list); + } + + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2) + { + return DictionariesEqual(m1, m2, false); + } + + private static bool _DictionariesEqual(IDictionary m1, + IDictionary m2, Boolean equalsIgnoreCase) + { + return DictionariesEqual(m1, m2, equalsIgnoreCase); + } + + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2, Boolean equalsIgnoreCase) + { + if (m1.Count != m2.Count) + { + return false; + } + foreach (KeyValuePair entry1 in m1) + { + TK key1 = entry1.Key; + TV val1 = entry1.Value; + if (!m2.ContainsKey(key1)) + { + return false; + } + Object val2 = m2[key1]; + if (!CollectionUtil.Equals(val1, val2, equalsIgnoreCase)) + { + return false; + } + } + return true; + } + + public static bool ListsEqual(IList v1, + IList v2) + { + return ListsEqual(v1, v2, false); + } + + private static bool _ListsEqual(IList v1, + IList v2, Boolean equalsIgnoreCase) + { + return ListsEqual(v1, v2, equalsIgnoreCase); + } + + public static bool ListsEqual(IList v1, + IList v2, Boolean equalsIgnoreCase) + { + if (v1.Count != v2.Count) + { + return false; + } + for (int i = 0; i < v1.Count; i++) + { + if (!CollectionUtil.Equals(v1[i], v2[i])) + { + return false; + } + } + return true; + } + + /// + /// Forces the compare of two comparable objects and removes any warnings + /// generated by the compiler. + /// + /// the integer value of o1.compareTo(o2). + public static int ForceCompare(object o1, object o2) + { + IComparable t1 = (IComparable)o1; + T t2 = (T)o2; + return t1.CompareTo(t2); + } + + /// + /// hashCode function that properly handles arrays, + /// collections, maps, collections of arrays, and maps of arrays. + /// + /// The object. May be null. + /// the hashCode + public static int GetHashCode(Object o) + { + if (o == null) + { + return 0; + } + else if (o is Array) + { + Array array = (Array)o; + int length = array.Length; + int rv = 0; + for (int i = 0; i < length; i++) + { + Object el = array.GetValue(i); + unchecked + { + rv += GetHashCode(el); + } + } + return rv; + } + else if (ReflectionUtil.IsParentTypeOf(typeof(KeyValuePair<,>), o.GetType())) + { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(KeyValuePair<,>), o.GetType()); + Type[] genericArguments = + parent.GetGenericArguments(); + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("GetKeyValuePairHashCode"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[] { o }); + return (int)rv; + } + else if (ReflectionUtil.IsParentTypeOf(typeof(ICollection<>), o.GetType())) + { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>), o.GetType()); + + Type[] genericArguments = + parent.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("GetEnumerableHashCode"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[] { o }); + return (int)rv; + } + else + { + return o.GetHashCode(); + } + } + + /// + /// Equality function that properly handles arrays, + /// lists, maps, lists of arrays, and maps of arrays. + /// + /// + /// + /// NOTE: For Sets, this relies on the equals method + /// of the Set to do the right thing. This is a reasonable + /// assumption since, in order for Sets to behave + /// properly as Sets, their values must already have + /// a proper implementation of equals. (Or they must + /// be specialized Sets that define a custom comparator that + /// knows how to do the right thing). The same holds true for Map keys. + /// Map values, on the other hand, are compared (so Map values + /// can be arrays). + /// + /// + /// The first object. May be null. + /// The second object. May be null. + /// true if the two objects are equal. + public new static bool Equals(Object o1, Object o2) + { + return Equals(o1, o2, false); + } + + + /// + /// Equality function that properly handles arrays, + /// lists, maps, lists of arrays, and maps of arrays. + /// + /// + /// + /// NOTE: For Sets, this relies on the equals method + /// of the Set to do the right thing. This is a reasonable + /// assumption since, in order for Sets to behave + /// properly as Sets, their values must already have + /// a proper implementation of equals. (Or they must + /// be specialized Sets that define a custom comparator that + /// knows how to do the right thing). The same holds true for Map keys. + /// Map values, on the other hand, are compared (so Map values + /// can be arrays). + /// + /// + /// The first object. May be null. + /// The second object. May be null. + /// If true the String and Character comparison is case-ignore + /// true if the two objects are equal. + public static bool Equals(Object o1, Object o2, Boolean equalsIgnoreCase) + { + if (o1 == o2) + { //same object or both null + return true; + } + else if (o1 == null) + { + return false; + } + else if (o2 == null) + { + return false; + } + else if (o1 is Array) + { + Type clazz1 = o1.GetType(); + Type clazz2 = o2.GetType(); + if (!clazz1.Equals(clazz2)) + { + return false; + } + Array array1 = (Array)o1; + Array array2 = (Array)o2; + int length1 = array1.Length; + int length2 = array2.Length; + if (length1 != length2) + { + return false; + } + for (int i = 0; i < length1; i++) + { + Object el1 = array1.GetValue(i); + Object el2 = array2.GetValue(i); + if (!Equals(el1, el2, equalsIgnoreCase)) + { + return false; + } + } + return true; + } + else if (ReflectionUtil.IsParentTypeOf(typeof(IList<>), o1.GetType())) + { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>), o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>), o2.GetType()); + if (!parent1.Equals(parent2)) + { + return false; + } + Type[] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("_ListsEqual", BindingFlags.Static | BindingFlags.NonPublic); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[] { o1, o2, equalsIgnoreCase }); + return (bool)rv; + } + else if (ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>), o1.GetType())) + { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>), o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>), o2.GetType()); + if (!parent1.Equals(parent2)) + { + return false; + } + Type[] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("_DictionariesEqual", BindingFlags.Static | BindingFlags.NonPublic); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[] { o1, o2 , equalsIgnoreCase}); + return (bool)rv; + } + else if (ReflectionUtil.IsParentTypeOf(typeof(ICollection<>), o1.GetType())) + { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>), o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>), o2.GetType()); + if (!parent1.Equals(parent2)) + { + return false; + } + Type[] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("_SetsEqual", BindingFlags.Static | BindingFlags.NonPublic); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[] { o1, o2, equalsIgnoreCase }); + return (bool)rv; + } + else if (equalsIgnoreCase && o1 is string && o2 is string) + { + return ((string)o1).Equals((string)o2, StringComparison.CurrentCultureIgnoreCase); + } + else if (equalsIgnoreCase && o1 is char && o2 is char) + { + return char.ToLower((char)o1) == char.ToLower((char)o2); + } + else + { + return o1.Equals(o2); + } + } + + public static string Dump(ICollection list) + { + StringBuilder sb = new StringBuilder(); + if (list != null) + { + bool first = true; + foreach (object o in list) + { + if (first) + { + first = false; + } + else + { + sb.Append(", "); + } + sb.Append(o); + } + } + else + { + sb.Append("(null)"); + } + return sb.ToString(); + } + + + } + +} diff --git a/dotnet/framework/Common/Common.csproj b/dotnet/framework/Common/Common.csproj new file mode 100644 index 00000000..955da625 --- /dev/null +++ b/dotnet/framework/Common/Common.csproj @@ -0,0 +1,109 @@ + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Debug + AnyCPU + Library + Org.IdentityConnectors.Common + Common + Open Connectors Common + v4.5.2 + True + False + 4 + false + true + + + + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + bin\Release\ + true + pdbonly + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + false + + + false + + + + + + 4.0 + + + + 4.0 + + + + 4.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/Common/DateTimeUtil.cs b/dotnet/framework/Common/DateTimeUtil.cs new file mode 100644 index 00000000..15b934fd --- /dev/null +++ b/dotnet/framework/Common/DateTimeUtil.cs @@ -0,0 +1,54 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of DateTimeUtil. + /// + public static class DateTimeUtil + { + private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); + + public static DateTime GetDateTimeFromUtcMillis(long dateTime) + { + return DateTime.FromFileTimeUtc(dateTime * 10000); + } + + public static long GetUtcTimeMillis(DateTime dateTime) + { + return dateTime.ToFileTimeUtc() / 10000; + } + + public static long GetCurrentUtcTimeMillis() + { + return GetUtcTimeMillis(DateTime.Now); + } + + public static long GetCurrentUnixTimeMillis() + { + return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds; + } + } +} diff --git a/dotnet/framework/Common/FrameworkInternalBridge.cs b/dotnet/framework/Common/FrameworkInternalBridge.cs new file mode 100644 index 00000000..7f64841e --- /dev/null +++ b/dotnet/framework/Common/FrameworkInternalBridge.cs @@ -0,0 +1,58 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of FrameworkInternalBridge. + /// + internal static class FrameworkInternalBridge + { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + /// + /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static Type LoadType(String typeName) + { + + Assembly assembly; + lock (LOCK) + { + if (_assembly == null) + { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return assembly.GetType(typeName, true); + + } + } +} diff --git a/dotnet/framework/Common/IOUtil.cs b/dotnet/framework/Common/IOUtil.cs new file mode 100644 index 00000000..7d35aa10 --- /dev/null +++ b/dotnet/framework/Common/IOUtil.cs @@ -0,0 +1,446 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Net; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using System.IO; +using System.Data.SqlClient; +using System.Reflection; + +namespace Org.IdentityConnectors.Common +{ + #region IOUtil + /// + /// Description of IOUtil. + /// + public static class IOUtil + { + /// + /// Given an ip address or host, returns the + /// IPAddress + /// + /// + /// + public static IPAddress GetIPAddress(String hostOrIp) + { + if (hostOrIp.Equals("0.0.0.0") || + hostOrIp.Equals("::0")) + { + return IPAddress.Parse(hostOrIp); + } + return Dns.GetHostAddresses(hostOrIp)[0]; + } + + /// + /// Quietly closes the reader. + ///

+ /// This avoids having to handle exceptions, and then inside of the exception + /// handling have a try catch block to close the reader and catch any + /// which may be thrown and ignore it. + ///

+ /// + /// Reader to close + public static void QuietClose(TextReader reader) + { + try + { + if (reader != null) + { + reader.Close(); + } + } + catch (IOException) + { + // ignore + } + } + + /// + /// Quietly closes the writer. + ///

+ /// This avoids having to handle exceptions, and then inside of the exception + /// handling have a try catch block to close the Writer and catch any + /// which may be thrown. + ///

+ /// + /// Writer to close + public static void QuietClose(TextWriter writer) + { + try + { + if (writer != null) + { + writer.Close(); + } + } + catch (IOException) + { + // ignore + } + } + + /// + /// Quietly closes the stream. + ///

+ /// This avoids having to handle exceptions, and then inside of the exception + /// handling have a try catch block to close the stream and catch any + /// which may be thrown. + ///

+ /// + /// Stream to close + public static void QuietClose(Stream stream) + { + try + { + if (stream != null) + { + stream.Close(); + } + } + catch (IOException) + { + // ignore + } + } + + /// + /// Quietly dispose the statement. + ///

+ /// This avoids having to handle exceptions, and then inside of the exception + /// handling have a try catch block to close the statement and catch any + /// which may be thrown. + ///

+ /// + /// Statement to dispose + /// Since 1.3 + public static void QuietClose(SqlCommand stmt) + { + try + { + if (stmt != null) + { + stmt.Dispose(); + } + } + catch (SqlException) + { + // ignore + } + } + + /// + /// Quietly closes the connection. + ///

+ /// This avoids having to handle exceptions, and then inside of the exception + /// handling have a try catch block to close the connection and catch any + /// which may be thrown. + ///

+ /// + /// Connection to close + /// Since 1.3 + public static void QuietClose(SqlConnection conn) + { + try + { + if (conn != null) + { + conn.Close(); + } + } + catch (SqlException) + { + // ignore + } + } + + /// + /// Quietly closes the resultset. + ///

+ /// This avoids having to handle exceptions, and then inside of the exception + /// handling have a try catch block to close the connection and catch any + /// which may be thrown. + ///

+ /// + /// ResultSet to close + /// Since 1.3 + public static void QuietClose(SqlDataReader resultset) + { + try + { + if (resultset != null) + { + resultset.Close(); + } + } + catch (SqlException) + { + // ignore + } + } + + // ======================================================================= + // Resource Utility Methods + // ======================================================================= + /// + /// Returns an Assembly contains the typeName. + /// + /// + /// Returns an Assembly or null if not found + public static Assembly GetAssemblyContainingType(String typeName) + { + foreach (Assembly currentassembly in AppDomain.CurrentDomain.GetAssemblies()) + { + Type t = currentassembly.GetType(typeName, false, true); + if (t != null) + { + return currentassembly; + } + } + return null; + } + + /// + /// Returns an input stream of the resource specified. + /// + /// + /// + /// Returns an InputStream to the resource. + public static Stream GetResourceAsStream(Type clazz, string resourceName) + { + Debug.Assert(clazz != null && StringUtil.IsNotBlank(resourceName)); + return clazz.Assembly.GetManifestResourceStream(resourceName); + } + + /// + /// Get the resource as a byte array. + /// + /// + /// + /// @return + public static byte[] GetResourceAsBytes(Type clazz, string res) + { + Debug.Assert(clazz != null && StringUtil.IsNotBlank(res)); + // copy bytes from the stream to an array.. + Stream ins = GetResourceAsStream(clazz, res); + if (ins == null) + { + throw new InvalidOperationException("Resource not found: " + res); + } + + byte[] buffer = new byte[16 * 1024]; + using (MemoryStream ms = new MemoryStream()) + { + int read; + while ((read = ins.Read(buffer, 0, buffer.Length)) > 0) + { + ms.Write(buffer, 0, read); + } + QuietClose(ins); + return ms.ToArray(); + } + } + + /// + /// Read the entire stream into a String and return it. + /// + /// + /// + /// + public static string GetResourceAsString(Type clazz, string res, Encoding charset) + { + Debug.Assert(clazz != null && StringUtil.IsNotBlank(res)); + string ret = null; + Stream ins = GetResourceAsStream(clazz, res); + if (ins != null) + { + using (StreamReader reader = new StreamReader(ins, charset)) + { + ret = reader.ReadToEnd(); + } + QuietClose(ins); + } + return ret; + } + + /// + /// Read the entire stream into a String and return it. + /// + /// + /// + /// + public static string GetResourceAsString(Type clazz, string res) + { + Debug.Assert(clazz != null && StringUtil.IsNotBlank(res)); + return GetResourceAsString(clazz, res, Encoding.UTF8); + } + + /// + /// Copies a file to a destination. + /// + /// + /// The source must be a file + /// + /// This can be a directory or a file. + /// True if succeeded otherwise false. + public static bool CopyFile(String src, String dest) + { + bool ret = true; + // quick exit if this is bogus + if (src == null || dest == null || !File.Exists(src)) + { + throw new FileNotFoundException(); + } + // check for directory + if (!Directory.Exists(dest)) + { + Directory.CreateDirectory(dest); + } + File.Copy(src, dest, true); + return ret; + } + + /// + /// Copies one file to another. + /// + /// NOTE: does not close streams. + /// + /// + /// + /// + /// + /// total bytes copied. + public static void CopyFile(Stream input, Stream output) + { + byte[] buffer = new byte[32768]; + int read; + while ((read = input.Read(buffer, 0, buffer.Length)) > 0) + { + output.Write(buffer, 0, read); + } + } + + /// + /// Reads the given file as UTF-8 + /// + /// + /// The file to read + /// The contents of the file + /// + /// if there is an issue reading the file. + public static string ReadFileUTF8(String file) + { + string content; + using (StreamReader reader = new StreamReader(file, Encoding.UTF8)) + { + content = reader.ReadToEnd(); + } + return content; + } + + /// + /// Write the contents of the string out to a file in UTF-8 format. + /// + /// + /// the file to write to. + /// + /// the contents of the file to write to. + /// + /// if there is an issue writing the file. + /// + /// if the file parameter is null. + public static void WriteFileUTF8(String file, string contents) + { + using (var sw = new StreamWriter(File.Open(file, FileMode.CreateNew), Encoding.UTF8)) + { + sw.WriteLine(contents); + } + } + + /// + /// + /// + /// Since 1.3 + public static string Join(ICollection collection, char separator) + { + if (collection == null) + { + return null; + } + + return Join(new List(collection).ToArray(), separator, 0, collection.Count); + } + + /// + /// + /// + /// Since 1.3 + public static string Join(object[] array, char separator) + { + if (array == null) + { + return null; + } + + return Join(array, separator, 0, array.Length); + } + + /// + /// + /// + /// + /// + /// + /// Since 1.3 + public static string Join(object[] array, char separator, int startIndex, int endIndex) + { + if (array == null) + { + return null; + } + int noOfItems = endIndex - startIndex; + if (noOfItems <= 0) + { + return String.Empty; + } + + StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) + { + if (i > startIndex) + { + buf.Append(separator); + } + if (array[i] != null) + { + buf.Append(array[i]); + } + } + return buf.ToString(); + } + } + #endregion +} diff --git a/dotnet/framework/Common/Locale.cs b/dotnet/framework/Common/Locale.cs new file mode 100644 index 00000000..342b515e --- /dev/null +++ b/dotnet/framework/Common/Locale.cs @@ -0,0 +1,240 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Globalization; +namespace Org.IdentityConnectors.Common +{ + /// + /// Represents a Java Locale and has facilities for converting to/from + /// C# CultureInfo + /// + public sealed class Locale + { + private static readonly IDictionary + Locale2CultureInfoName = new Dictionary(); + private static readonly IDictionary + CultureInfoName2Locale = new Dictionary(); + + private static void AddMapping(Locale locale, String name) + { + Locale2CultureInfoName[locale] = name; + CultureInfoName2Locale[name] = locale; + } + + /// + /// Special cases + /// + static Locale() + { + AddMapping(new Locale(), ""); + AddMapping(new Locale("iw"), "he"); + AddMapping(new Locale("zh"), "zh-CHS"); + AddMapping(new Locale("iw", "IL"), "he-IL"); + AddMapping(new Locale("no", "NO"), "nb-NO"); + AddMapping(new Locale("no", "NO", "NY"), "nn-NO"); + } + + private String _language; + private String _country; + private String _variant; + + public Locale() + : this("") + { + + } + + public Locale(String language) + : this(language, "") + { + } + public Locale(String language, String country) + : this(language, country, "") + { + } + public Locale(String language, String country, String variant) + { + _language = language ?? ""; + _country = country ?? ""; + _variant = variant ?? ""; + } + + public string Language + { + get + { + return _language; + } + } + + public string Country + { + get + { + return _country; + } + } + + public string Variant + { + get + { + return _variant; + } + } + + public override bool Equals(Object o) + { + Locale other = o as Locale; + if (other != null) + { + if (!Object.Equals(Language, other.Language)) + { + return false; + } + if (!Object.Equals(Country, other.Country)) + { + return false; + } + if (!Object.Equals(Variant, other.Variant)) + { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() + { + unchecked + { + return _language.GetHashCode() + _country.GetHashCode(); + } + } + + public override string ToString() + { + return Language + "_" + Country + "_" + Variant; + } + + /// + /// Creates the closest CultureInfo that maps to this locale + /// + /// The culture info + public CultureInfo ToCultureInfo() + { + CultureInfo rv = null; + String code = CollectionUtil.GetValue(Locale2CultureInfoName, this, null); + if (code != null) + { + rv = TryCultureCode(code); + } + if (rv == null) + { + if (Country.Length > 0) + { + rv = TryCultureCode(Language + "-" + Country); + } + } + if (rv == null) + { + rv = TryCultureCode(Language); + } + if (rv == null) + { + rv = CultureInfo.InvariantCulture; + } + return rv; + } + + public static Locale FindLocale(CultureInfo info) + { + String code = info.Name; + Locale rv = CollectionUtil.GetValue(CultureInfoName2Locale, code, null); + if (rv == null) + { + String[] parts = code.Split(new string[] { "-" }, + StringSplitOptions.RemoveEmptyEntries); + String language = ""; + String country = ""; + if (parts.Length > 0) + { + String l = parts[0]; + if (LooksLikeValidLanguageCode(l)) + { + language = l; + if (parts.Length > 1) + { + String c = parts[1]; + if (LooksLikeValidCountryCode(c)) + { + country = c; + } + } + } + } + rv = new Locale(language, country); + } + return rv; + } + + /// + /// Weeds out some junk + /// + /// + /// + private static bool LooksLikeValidLanguageCode(String l) + { + char[] chars = l.ToCharArray(); + return (chars.Length == 2 && + Char.IsLower(chars[0]) && + Char.IsLower(chars[1])); + } + /// + /// Weeds out some junk + /// + /// + /// + private static bool LooksLikeValidCountryCode(String l) + { + char[] chars = l.ToCharArray(); + return (chars.Length == 2 && + Char.IsUpper(chars[0]) && + Char.IsUpper(chars[1])); + } + + private static CultureInfo TryCultureCode(String code) + { + try + { + return new CultureInfo(code); + } + catch (Exception) + { + return null; + } + } + } +} diff --git a/dotnet/framework/Common/Pair.cs b/dotnet/framework/Common/Pair.cs new file mode 100644 index 00000000..b26b35f4 --- /dev/null +++ b/dotnet/framework/Common/Pair.cs @@ -0,0 +1,103 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS. + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + /// + /// Represents a Pair of objects. + /// + public class Pair + { + + public Pair() + { + } + + /// + /// + /// Obtains an immutable pair of from two objects inferring the generic + /// types. + /// + /// + /// + /// This factory allows the pair to be created using inference to obtain the + /// generic types. + /// + /// + /// @param + /// the left element type + /// @param + /// the right element type + /// + /// the left element, may be null + /// + /// the right element, may be null + /// a pair formed from the two parameters, not null + /// Since 1.4 + public static Pair Of(L left, R right) + { + return new Pair(left, right); + } + + public Pair(T1 first, T2 second) + { + First = first; + Second = second; + } + + public T1 First { get; set; } + public T2 Second { get; set; } + + public override bool Equals(object obj) + { + Pair other = obj as Pair; + if (other != null) + { + return Object.Equals(First, other.First) && + Object.Equals(Second, other.Second); + } + return false; + } + + public override int GetHashCode() + { + int rv = 0; + if (First != null) + { + rv ^= First.GetHashCode(); + } + if (Second != null) + { + rv ^= Second.GetHashCode(); + } + return rv; + } + + public override string ToString() + { + return "( " + First + ", " + Second + " )"; + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Common/Pooling.cs b/dotnet/framework/Common/Pooling.cs new file mode 100644 index 00000000..6ef2db52 --- /dev/null +++ b/dotnet/framework/Common/Pooling.cs @@ -0,0 +1,251 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; + +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Common.Pooling +{ + /// + /// Configuration for pooling objects + /// + public sealed class ObjectPoolConfiguration + { + + /// + /// Max objects (idle+active). + /// + private int _maxObjects = 10; + + /// + /// Max idle objects. + /// + private int _maxIdle = 10; + + /// + /// Max time to wait if the pool is waiting for a free object to become + /// available before failing. + /// + /// + /// Zero means don't wait + /// + private long _maxWait = 150 * 1000; + + /// + /// Minimum time to wait before evicting an idle object. + /// + /// + /// Zero means don't wait + /// + private long _minEvictableIdleTimeMillis = 120 * 1000; + + /// + /// Minimum number of idle objects. + /// + private int _minIdle = 1; + + + /// + /// Get the set number of maximum objects (idle+active) + /// + public int MaxObjects + { + get + { + return _maxObjects; + } + set + { + _maxObjects = value; + } + } + + /// + /// Get the maximum number of idle objects. + /// + public int MaxIdle + { + get + { + return _maxIdle; + } + set + { + _maxIdle = value; + } + } + + /// + /// Max time to wait if the pool is waiting for a free object to become + /// available before failing. + /// + /// + /// Zero means don't wait + /// + public long MaxWait + { + get + { + return _maxWait; + } + set + { + _maxWait = value; + } + } + + /// + /// Minimum time to wait before evicting an idle object. + /// + /// + /// Zero means don't wait + /// + public long MinEvictableIdleTimeMillis + { + get + { + return _minEvictableIdleTimeMillis; + } + set + { + _minEvictableIdleTimeMillis = value; + } + } + + /// + /// Minimum number of idle objects. + /// + public int MinIdle + { + get + { + return _minIdle; + } + set + { + _minIdle = value; + } + } + + // ======================================================================= + // Constructors + // ======================================================================= + + public ObjectPoolConfiguration() + { + } + + public ObjectPoolConfiguration(ObjectPoolConfiguration other) + { + this.MaxObjects = other.MaxObjects; + this.MaxIdle = other.MaxIdle; + this.MaxWait = other.MaxWait; + this.MinEvictableIdleTimeMillis = other.MinEvictableIdleTimeMillis; + this.MinIdle = other.MinIdle; + } + + public void Validate() + { + if (_minIdle < 0) + { + throw new ArgumentException("Min idle is less than zero."); + } + if (_maxObjects < 0) + { + throw new ArgumentException("Max active is less than zero."); + } + if (_maxIdle < 0) + { + throw new ArgumentException("Max idle is less than zero."); + } + if (_maxWait < 0) + { + throw new ArgumentException("Max wait is less than zero."); + } + if (_minEvictableIdleTimeMillis < 0) + { + throw new ArgumentException("Min evictable idle time millis less than zero."); + } + if (_minIdle > _maxIdle) + { + throw new ArgumentException("Min idle is greater than max idle."); + } + if (_maxIdle > _maxObjects) + { + throw new ArgumentException("Max idle is greater than max objects."); + } + } + + public override int GetHashCode() + { + unchecked + { + return (int)(MaxObjects + MaxIdle + MaxWait + MinEvictableIdleTimeMillis + MinIdle); + } + } + + public override bool Equals(Object obj) + { + if (obj is ObjectPoolConfiguration) + { + ObjectPoolConfiguration other = (ObjectPoolConfiguration)obj; + + if (MaxObjects != other.MaxObjects) + { + return false; + } + if (MaxIdle != other.MaxIdle) + { + return false; + } + if (MaxWait != other.MaxWait) + { + return false; + } + if (MinEvictableIdleTimeMillis != other.MinEvictableIdleTimeMillis) + { + return false; + } + if (MinIdle != other.MinIdle) + { + return false; + } + return true; + } + return false; + } + + public override String ToString() + { + // poor man's toString() + IDictionary bld = new Dictionary(); + bld["MaxObjects"] = MaxObjects; + bld["MaxIdle"] = MaxIdle; + bld["MaxWait"] = MaxWait; + bld["MinEvictableIdleTimeMillis"] = MinEvictableIdleTimeMillis; + bld["MinIdle"] = MinIdle; + return bld.ToString(); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Common/Proxy.cs b/dotnet/framework/Common/Proxy.cs new file mode 100644 index 00000000..2b0dec83 --- /dev/null +++ b/dotnet/framework/Common/Proxy.cs @@ -0,0 +1,272 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; +namespace Org.IdentityConnectors.Common.Proxy +{ + /// + /// Similar to java.lang.reflect.InvocationHandler + /// + public interface InvocationHandler + { + Object + Invoke(Object proxy, MethodInfo method, Object[] args); + } + + + /// + /// Similar to java.lang.reflect.Proxy + /// + public static class Proxy + { + private static readonly MethodInfo INVOCATION_HANDLER_METHOD = + typeof(InvocationHandler).GetMethod("Invoke", + new Type[]{typeof(object), + typeof(MethodInfo), + typeof(object[])}); + private static readonly MethodInfo GET_METHOD_FROM_HANDLE_METHOD = + typeof(MethodBase).GetMethod("GetMethodFromHandle", + new Type[] { typeof(RuntimeMethodHandle) }); + + private static readonly object LOCK = new Object(); + + private static IDictionary + _implementationMap = new Dictionary(); + + private static int _count = 0; + + public static Object NewProxyInstance(Type interfaze, + InvocationHandler handler) + { + ConstructorInfo constructor = GetOrCreateConstructorInfo(interfaze); + return constructor.Invoke(new object[] { handler }); + } + + private static ConstructorInfo GetOrCreateConstructorInfo(Type type) + { + lock (LOCK) + { + ConstructorInfo rv = + CollectionUtil.GetValue(_implementationMap, type, null); + if (rv == null) + { + Type impl = GenerateImplementation(type); + rv = + impl.GetConstructor(new Type[] { typeof(InvocationHandler) }); + + _implementationMap[type] = rv; + } + return rv; + } + } + + private static String NextName() + { + int count; + lock (LOCK) + { + count = _count; + count++; + } + return "___proxy" + count; + } + + + + private static Type GenerateImplementation(Type interfaze) + { + if (!interfaze.IsInterface) + { + throw new ArgumentException("Type is not an interface: " + interfaze); + } + if (interfaze.IsGenericType) + { + throw new ArgumentException("Type is a generic type: " + interfaze); + } + + String uniqueName = NextName(); + + AssemblyName assemblyName = new AssemblyName(uniqueName); + AssemblyBuilder assemblyBuilder = + AppDomain.CurrentDomain.DefineDynamicAssembly( + assemblyName, + AssemblyBuilderAccess.RunAndSave); + // For a single-module assembly, the module name is usually + // the assembly name plus an extension. + ModuleBuilder moduleBuilder = + assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); + + TypeBuilder typeBuilder = moduleBuilder.DefineType( + uniqueName, + TypeAttributes.Public); + typeBuilder.AddInterfaceImplementation(interfaze); + + MethodInfo[] methods = interfaze.GetMethods(); + ConstructorBuilder classInitializer = + typeBuilder.DefineTypeInitializer(); + ILGenerator classInitializerCode = + classInitializer.GetILGenerator(); + //generate constructor and _handler field + FieldBuilder handlerField = + typeBuilder.DefineField("_handler", + typeof(InvocationHandler), + FieldAttributes.Private | FieldAttributes.InitOnly); + int count = 0; + foreach (MethodInfo method in methods) + { + GenerateMethod(typeBuilder, method, classInitializerCode, count, + handlerField); + count++; + } + classInitializerCode.Emit(OpCodes.Ret); + + + ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + new Type[] { typeof(InvocationHandler) }); + ILGenerator constructorCode = constructorBuilder.GetILGenerator(); + // For a constructor, argument zero is a reference to the new + // instance. Push it on the stack before calling the base + // class constructor. Specify the default constructor of the + // base class (System.Object) by passing an empty array of + // types (Type.EmptyTypes) to GetConstructor. + constructorCode.Emit(OpCodes.Ldarg_0); + constructorCode.Emit(OpCodes.Call, + typeof(object).GetConstructor(Type.EmptyTypes)); + constructorCode.Emit(OpCodes.Ldarg_0); + constructorCode.Emit(OpCodes.Ldarg_1); + constructorCode.Emit(OpCodes.Stfld, handlerField); + constructorCode.Emit(OpCodes.Ret); + + + Type t = typeBuilder.CreateType(); + + //assemblyBuilder.Save(assemblyName.Name+".dll"); + + return t; + } + + private static void ValidateMethod(MethodInfo info) + { + if (info.GetGenericArguments().Length != 0) + { + throw new ArgumentException( + "Method not supported since it has generic arguments: " + info); + + } + foreach (ParameterInfo parameter in info.GetParameters()) + { + if (parameter.IsOut) + { + throw new ArgumentException( + "Method not supported since it has output paramaters: " + info); + } + if (parameter.IsRetval) + { + throw new ArgumentException( + "Method not supported since it has retval paramaters: " + info); + } + } + } + + private static void GenerateMethod(TypeBuilder typeBuilder, + MethodInfo method, + ILGenerator classInitializerCode, + int methodCount, + FieldBuilder handlerField) + { + ValidateMethod(method); + + FieldBuilder methodField = + typeBuilder.DefineField("METHOD" + methodCount, + typeof(MethodInfo), + FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static); + + + + classInitializerCode.Emit(OpCodes.Ldtoken, + method); + classInitializerCode.Emit(OpCodes.Call, + GET_METHOD_FROM_HANDLE_METHOD); + classInitializerCode.Emit(OpCodes.Castclass, typeof(MethodInfo)); + classInitializerCode.Emit(OpCodes.Stsfld, methodField); + + + Type[] parameterTypes = ReflectionUtil.GetParameterTypes(method); + MethodBuilder methodBuilder = typeBuilder.DefineMethod( + method.Name, + MethodAttributes.Public | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual | + MethodAttributes.Final, + method.CallingConvention, + method.ReturnType, + parameterTypes); + + ILGenerator methodCode = methodBuilder.GetILGenerator(); + methodCode.Emit(OpCodes.Ldarg_0); + methodCode.Emit(OpCodes.Ldfld, handlerField); + methodCode.Emit(OpCodes.Ldarg_0); + methodCode.Emit(OpCodes.Ldsfld, methodField); + methodCode.Emit(OpCodes.Ldc_I4, parameterTypes.Length); + methodCode.Emit(OpCodes.Newarr, typeof(object)); + + for (int index = 0; index < parameterTypes.Length; index++) + { + Type parameterType = parameterTypes[index]; + methodCode.Emit(OpCodes.Dup); + methodCode.Emit(OpCodes.Ldc_I4, index); + methodCode.Emit(OpCodes.Ldarg, index + 1); + if (parameterType.IsValueType) + { + methodCode.Emit(OpCodes.Box, parameterType); + } + methodCode.Emit(OpCodes.Stelem_Ref); + } + + methodCode.Emit(OpCodes.Callvirt, INVOCATION_HANDLER_METHOD); + Type returnType = method.ReturnType; + + if (typeof(void).Equals(returnType)) + { + methodCode.Emit(OpCodes.Pop); + } + else if (returnType.IsValueType) + { + methodCode.Emit(OpCodes.Unbox_Any, returnType); + } + else + { + methodCode.Emit(OpCodes.Castclass, returnType); + } + + methodCode.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodBuilder, method); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Common/ReflectionUtil.cs b/dotnet/framework/Common/ReflectionUtil.cs new file mode 100644 index 00000000..6b7fbe7c --- /dev/null +++ b/dotnet/framework/Common/ReflectionUtil.cs @@ -0,0 +1,171 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +namespace Org.IdentityConnectors.Common +{ + /// + /// Miscellaenous reflection utilities. + /// + public static class ReflectionUtil + { + /// + /// Gets all the interfaces in the hierarchy + /// + /// + /// + public static Type[] GetAllInterfaces(Type type) + { + HashSet temp = new HashSet(); + GetAllInterfaces2(type, temp); + return temp.ToArray(); + } + + private static void GetAllInterfaces2(Type type, HashSet rv) + { + if (type != null) + { + if (type.IsInterface) + { + rv.Add(type); + } + GetAllInterfaces2(type.BaseType, rv); + foreach (Type inter in type.GetInterfaces()) + { + GetAllInterfaces2(inter, rv); + } + } + } + + /// + /// Returns the generic type definition of the given type + /// + /// + /// + public static Type[] GetTypeErasure(Type[] types) + { + Type[] rv = new Type[types.Length]; + for (int i = 0; i < types.Length; i++) + { + rv[i] = GetTypeErasure(types[i]); + } + return rv; + } + /// + /// Returns the generic type definition of the given type + /// + /// + /// + public static Type GetTypeErasure(Type type) + { + if (type.IsGenericType) + { + type = type.GetGenericTypeDefinition(); + } + return type; + } + + /// + /// Returns true iff t1 is a parent type of t2. Unlike + /// Type.isAssignableFrom, this handles generic types as + /// well. + /// + /// + /// + /// + public static bool IsParentTypeOf(Type t1, + Type t2) + { + if (t2 == null) + { + return t1 == null; + } + Type found = FindInHierarchyOf(t1, t2); + return found != null; + } + + /// + /// Finds t1 in the hierarchy of t2 or null if not found. The + /// returned value will have generic parameters applied to it. + /// + /// + /// + /// + public static Type FindInHierarchyOf(Type t1, Type t2) + { + if (t2 == null) + { + return null; + } + if (EqualsIgnoreGeneric(t1, t2)) + { + return t2; + } + Type found1 = FindInHierarchyOf(t1, t2.BaseType); + if (found1 != null) + { + return found1; + } + foreach (Type inter in t2.GetInterfaces()) + { + Type found2 = FindInHierarchyOf(t1, inter); + if (found2 != null) + { + return found2; + } + } + return null; + } + + private static bool EqualsIgnoreGeneric(Type t1, + Type t2) + { + if (t1 == null || t2 == null) + { + return t1 == null && t2 == null; + } + if (t1.IsGenericType) + { + t1 = t1.GetGenericTypeDefinition(); + } + if (t2.IsGenericType) + { + t2 = t2.GetGenericTypeDefinition(); + } + return t1.Equals(t2); + } + + public static Type[] GetParameterTypes(MethodInfo method) + { + ParameterInfo[] parameters = method.GetParameters(); + Type[] rv = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + rv[i] = parameters[i].ParameterType; + } + return rv; + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Common/SafeType.cs b/dotnet/framework/Common/SafeType.cs new file mode 100644 index 00000000..ef953bfd --- /dev/null +++ b/dotnet/framework/Common/SafeType.cs @@ -0,0 +1,143 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + /// + /// The equivalent of java's Class<? extends...%gt; syntax. + /// Allows you to restrict a Type to a certain class hierarchy. + /// + public sealed class SafeType + where T : class + { + private readonly Type _rawType; + + /// + /// Make private so no one can create directly + /// + private SafeType(Type rawType) + { + if (!ReflectionUtil.IsParentTypeOf(typeof(T), rawType)) + { + throw new ArgumentException("Type: " + rawType + " is not a subclass of" + typeof(T)); + } + _rawType = rawType; + } + + /// + /// Returns the SafeType for a given raw type. + /// NOTE: If possible use Get() instead since it is statically + /// checked at compile time. + /// + /// + /// + public static SafeType ForRawType(Type type) + { + return new SafeType(type); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion. + /// + /// The instance of the safe type + public static SafeType Get() + where U : T + { + return new SafeType(typeof(U)); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion from an object. + /// + /// The instance of the safe type + public static SafeType Get(T obj) + { + return new SafeType(obj.GetType()); + } + + /// + /// Returns the generic type definition corresponding to this type. + /// Will return the same type if this is already a generic type. + /// + /// + public SafeType GetTypeErasure() + { + return SafeType.ForRawType(ReflectionUtil.GetTypeErasure(RawType)); + } + + /// + /// Returns the underlying C# type + /// + public Type RawType + { + get + { + return _rawType; + } + } + + /// + /// Creates a new instance of the given type + /// + /// The new instance + public T CreateInstance() + { + return (T)Activator.CreateInstance(RawType); + } + + /// + /// Returns true iff these represent the same underlying type + /// and the SafeType has the same parent type. + /// + /// The other + /// true iff these represent the same underylying type + /// and the TypeGroup has the same parent type + public override bool Equals(object o) + { + if (o is SafeType) + { + SafeType other = (SafeType)o; + return RawType.Equals(other.RawType); + } + return false; + } + /// + /// Returns a hash of the type + /// + /// a hash of the type + public override int GetHashCode() + { + return RawType.GetHashCode(); + } + /// + /// Returns a string representation of the member type + /// + /// a string representation of the member type + public override string ToString() + { + return RawType.ToString(); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Common/Script.cs b/dotnet/framework/Common/Script.cs new file mode 100644 index 00000000..023ef1b3 --- /dev/null +++ b/dotnet/framework/Common/Script.cs @@ -0,0 +1,182 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Common.Script +{ + public interface ScriptExecutor + { + /// + /// Executes the script with the given arguments. + /// + /// key/value set of variables to + /// pass to the script. + /// + object Execute(IDictionary arguments); + } + public abstract class ScriptExecutorFactory + { + + private static readonly object LOCK = new object(); + + /// + /// Loaded w/ all supported languages. + /// + private static IDictionary _supportedLanguages = null; + + /// + /// Load all script executor factory assemblies in the same directory + /// the 'Common' assembly. + /// + private static IDictionary LoadSupportedLanguages() + { + // attempt to process all assemblies.. + IDictionary ret = new Dictionary(); + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = new FileInfo(assembly.Location); + DirectoryInfo directory = thisAssemblyFile.Directory; + // get all *ScriptExecutorFactory assmebly from the current directory + FileInfo[] files = directory.GetFiles("*.ScriptExecutorFactory.dll"); + Type t = typeof(ScriptExecutorFactoryClassAttribute); + foreach (FileInfo file in files) + { + try + { + Assembly lib = Assembly.LoadFrom(file.ToString()); + foreach (Type type in lib.GetTypes()) + { + Object[] attributes = type.GetCustomAttributes(t, false); + if (attributes.Length > 0) + { + ScriptExecutorFactoryClassAttribute attribute = + (ScriptExecutorFactoryClassAttribute)attributes[0]; + // attempt to test assembly.. + Activator.CreateInstance(type); + // if we made it this far its okay + ret[attribute.Language.ToUpper()] = type; + } + } + } + catch (Exception e) + { + TraceUtil.TraceException("Unable to load assembly: " + + assembly.FullName + ". This is a fatal exception: ", e); + throw; + } + } + return ret; + } + + private static IDictionary GetSupportedLanguages() + { + lock (LOCK) + { + if (_supportedLanguages == null) + { + _supportedLanguages = LoadSupportedLanguages(); + } + return _supportedLanguages; + } + } + + /// + /// Returns the set of supported languages. + /// + /// The set of supported languages. + public static ICollection SupportedLanguages + { + get + { + IDictionary map = + GetSupportedLanguages(); + return CollectionUtil.AsReadOnlySet(map.Keys); + } + } + + /// + /// Creates a ScriptExecutorFactory for the given language + /// + /// The name of the language + /// The script executor factory + /// If the given language is not + /// supported. + public static ScriptExecutorFactory NewInstance(String language) + { + if (language == null) + { + throw new ArgumentException("Language must be specified"); + } + Type type = CollectionUtil.GetValue(GetSupportedLanguages(), language.ToUpper(), null); + if (type == null) + { + throw new ArgumentException("Language not supported: " + language); + } + return (ScriptExecutorFactory)Activator.CreateInstance(type); + } + + + /// + /// Creates a script executor for the given script. + /// + /// The classloader that contains the java classes + /// that the script should have access to. + /// The script text. + /// A hint to tell the script executor whether or + /// not to compile the given script. This need not be implemented + /// by all script executors. If true, the caller is saying that + /// they intend to call the script multiple times with different + /// arguments, so compile if possible. + /// A script executor. + public abstract ScriptExecutor NewScriptExecutor( + Assembly[] referencedAssemblies, + String script, + bool compile); + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ScriptExecutorFactoryClassAttribute : System.Attribute + { + private readonly string _lang; + + /// + /// Determine the language supported by the factory. + /// + /// + public ScriptExecutorFactoryClassAttribute(string lang) + { + _lang = lang; + } + + public string Language + { + get + { + return _lang; + } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Common/Security.cs b/dotnet/framework/Common/Security.cs new file mode 100644 index 00000000..23dac12b --- /dev/null +++ b/dotnet/framework/Common/Security.cs @@ -0,0 +1,1008 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ +using System; +using System.Linq; +using System.Security; +using System.Security.Cryptography; +using System.Runtime.InteropServices; +using System.Text; +namespace Org.IdentityConnectors.Common.Security +{ + #region UnmanagedArray + /// + /// Places an array facade on an unmanaged memory data structure that + /// holds senstive data. (In C# placing senstive data in managed + /// memory is considered unsecure since the memory model allows + /// data to be copied around). + /// + public interface UnmanagedArray : IDisposable + { + int Length { get; } + T this[int index] { get; set; } + } + #endregion + + #region GuardedByteArray + /// + /// Secure byte array implementation that solves the problems associated with + /// keeping confidential data as byte[]. + /// + /// + /// That is, anything + /// represented as a byte[] is kept in memory in clear + /// text and stays in memory at least until it is garbage collected. + /// + /// The GuardedByteArray class alleviates this problem by storing the bytes in + /// memory in an encrypted form. The encryption key will be a randomly-generated + /// key. + /// + /// + /// In their serialized form, GuardedByteArray will be encrypted using a known + /// default key. This is to provide a minimum level of protection regardless + /// of the transport. For communications with the Remote Connector Framework + /// it is recommended that deployments enable SSL for true encryption. + /// + /// + /// Applications may also wish to persist GuardedByteArrays. In the case of + /// Identity Manager, it should convert GuardedByteArrays to EncryptedData so + /// that they can be stored and managed using the Manage Encryption features + /// of Identity Manager. Other applications may wish to serialize APIConfiguration + /// as a whole. These applications are responsible for encrypting the APIConfiguration + /// blob for an additional layer of security (beyond the basic default key encryption + /// provided by GuardedByteArray). + /// + /// + public sealed class GuardedByteArray : IDisposable + { + + /// + /// Callback interface for those times that it is necessary to access the + /// clear text of the guarded bytes. + /// + public interface Accessor + { + + /// + /// This method will be called with the clear text of the byte array. + /// + /// + /// After the call the clearBytes array will be automatically zeroed + /// out, thus keeping the window of potential exposure to a bare-minimum. + /// + /// + void Access(UnmanagedArray clearBytes); + } + + public class LambdaAccessor : Accessor + { + private readonly Action> _accessor; + + public LambdaAccessor(Action> accessor) + { + _accessor = accessor; + } + + public void Access(UnmanagedArray clearBytes) + { + _accessor(clearBytes); + } + } + + private SecureString _target; + private String _base64SHA1Hash; + + /// + /// Creates an empty secure byte array. + /// + public GuardedByteArray() + { + _target = new SecureString(); + ComputeHash(); + } + + public GuardedByteArray(UnmanagedArray clearBytes) + { + _target = new SecureString(); + AppendBytes(clearBytes); + } + + private GuardedByteArray(SecureString str) + { + _target = str.Copy(); + ComputeHash(); + } + + + /// + /// Provides access to the clear-text value of the bytes in a controlled fashion. + /// + /// + /// The clear-text bytes will only be available for the duration of the call + /// and automatically zeroed out following the call. + /// + /// NOTE: Callers are encouraged to use + /// where possible if the intended use is merely to verify the contents of + /// the string match an expected hash value. + /// + /// + /// Accessor callback. + /// If the byte array has been disposed + public void Access(Accessor accessor) + { + using (SecureStringToByteArrayAdapter adapter = new SecureStringToByteArrayAdapter(_target)) + { + accessor.Access(adapter); + } + } + + /// + /// Appends a single clear-text byte to the secure byte array. + /// + /// + /// The in-memory data will be decrypted, the character will be + /// appended, and then it will be re-encrypted. + /// + /// The byte to append. + /// If the byte array is read-only + /// If the byte array has been disposed + public void AppendByte(byte b) + { + _target.AppendChar((char)b); + ComputeHash(); + } + + private void AppendBytes(UnmanagedArray clearBytes) + { + for (int i = 0; i < clearBytes.Length; i++) + { + _target.AppendChar((char)clearBytes[i]); + } + ComputeHash(); + } + + /// + /// Clears the in-memory representation of the byte array. + /// + public void Dispose() + { + _target.Dispose(); + } + + /// + /// Returns true iff this byte array has been marked read-only + /// + /// true iff this byte array has been marked read-only + /// If the byte array has been disposed + public bool IsReadOnly() + { + return _target.IsReadOnly(); + } + + /// + /// Mark this byte array as read-only. + /// + /// If the byte array has been disposed + public void MakeReadOnly() + { + _target.MakeReadOnly(); + } + + /// + /// Create a copy of the byte array. + /// + /// + /// If this instance is read-only, + /// the copy will not be read-only. + /// + /// A copy of the byte array. + /// If the byte array has been disposed + public GuardedByteArray Copy() + { + SecureString t2 = _target.Copy(); + GuardedByteArray rv = new GuardedByteArray(t2); + return rv; + } + + /// + /// Verifies that this base-64 encoded SHA1 hash of this byte array + /// matches the given value. + /// + /// The hash to verify against. + /// True if the hash matches the given parameter. + /// If the byte array has been disposed + public bool VerifyBase64SHA1Hash(String hash) + { + CheckNotDisposed(); + return _base64SHA1Hash.Equals(hash); + } + + public string GetBase64SHA1Hash() + { + CheckNotDisposed(); + return _base64SHA1Hash; + } + + + + private void CheckNotDisposed() + { + //this throws if disposed + _target.IsReadOnly(); + } + + + public override bool Equals(Object o) + { + if (o is GuardedByteArray) + { + GuardedByteArray other = (GuardedByteArray)o; + //not the true contract of equals. however, + //due to the high mathematical improbability of + //two unequal strings having the same secure hash, + //this approach feels good. the alternative, + //decrypting for comparison, is simply too + //performance intensive to be used for equals + return _base64SHA1Hash.Equals(other._base64SHA1Hash); + } + return false; + } + + public override int GetHashCode() + { + return _base64SHA1Hash.GetHashCode(); + } + + private void ComputeHash() + { + Access(new LambdaAccessor(array => + { + _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); + })); + } + + } + #endregion + + #region GuardedString + /// + /// Secure string implementation that solves the problems associated with + /// keeping passwords as java.lang.String. + /// + /// + /// That is, anything + /// represented as a String is kept in memory as a clear + /// text password and stays in memory at least until it is garbage collected. + /// + /// The GuardedString class alleviates this problem by storing the characters in + /// memory in an encrypted form. The encryption key will be a randomly-generated + /// key. + /// + /// + /// In their serialized form, GuardedString will be encrypted using a known + /// default key. This is to provide a minimum level of protection regardless + /// of the transport. For communications with the Remote Connector Framework + /// it is recommended that deployments enable SSL for true encryption. + /// + /// + /// Applications may also wish to persist GuardedStrings. In the case of + /// Identity Manager, it should convert GuardedStrings to EncryptedData so + /// that they can be stored and managed using the Manage Encryption features + /// of Identity Manager. Other applications may wish to serialize APIConfiguration + /// as a whole. These applications are responsible for encrypting the APIConfiguration + /// blob for an additional layer of security (beyond the basic default key encryption + /// provided by GuardedString). + /// + /// + public sealed class GuardedString : IDisposable + { + + /// + /// Callback interface for those times that it is necessary to access the + /// clear text of the secure string. + /// + public interface Accessor + { + + /// + /// This method will be called with the clear text of the string. + /// + /// + /// After the call the clearChars array will be automatically zeroed + /// out, thus keeping the window of potential exposure to a bare-minimum. + /// + /// + void Access(UnmanagedArray clearChars); + } + + public class LambdaAccessor : Accessor + { + private readonly Action> _accessor; + + public LambdaAccessor(Action> accessor) + { + _accessor = accessor; + } + + public void Access(UnmanagedArray clearBytes) + { + _accessor(clearBytes); + } + } + + private SecureString _target; + private String _base64SHA1Hash; + + /// + /// Creates an empty secure string + /// + public GuardedString() + { + _target = new SecureString(); + ComputeHash(); + } + + public GuardedString(SecureString str) + { + _target = str.Copy(); + ComputeHash(); + } + + + /// + /// Provides access to the clear-text value of the string in a controlled fashion. + /// + /// + /// The clear-text characters will only be available for the duration of the call + /// and automatically zeroed out following the call. + /// + /// NOTE: Callers are encouraged to use + /// where possible if the intended use is merely to verify the contents of + /// the string match an expected hash value. + /// + /// + /// Accessor callback. + /// If the string has been disposed + public void Access(Accessor accessor) + { + using (SecureStringAdapter adapter = new SecureStringAdapter(_target)) + { + accessor.Access(adapter); + } + } + + /// + /// Appends a single clear-text character to the secure string. + /// + /// + /// The in-memory data will be decrypted, the character will be + /// appended, and then it will be re-encrypted. + /// + /// The character to append. + /// If the string is read-only + /// If the string has been disposed + public void AppendChar(char c) + { + _target.AppendChar(c); + ComputeHash(); + } + + /// + /// Clears the in-memory representation of the string. + /// + public void Dispose() + { + _target.Dispose(); + } + + /// + /// Returns true iff this string has been marked read-only + /// + /// true iff this string has been marked read-only + /// If the string has been disposed + public bool IsReadOnly() + { + return _target.IsReadOnly(); + } + + /// + /// Mark this string as read-only. + /// + /// If the string has been disposed + public void MakeReadOnly() + { + _target.MakeReadOnly(); + } + + /// + /// Create a copy of the string. + /// + /// + /// If this instance is read-only, + /// the copy will not be read-only. + /// + /// A copy of the string. + /// If the string has been disposed + public GuardedString Copy() + { + SecureString t2 = _target.Copy(); + GuardedString rv = new GuardedString(t2); + return rv; + } + + /// + /// Verifies that this base-64 encoded SHA1 hash of this string + /// matches the given value. + /// + /// The hash to verify against. + /// True if the hash matches the given parameter. + /// If the string has been disposed + public bool VerifyBase64SHA1Hash(String hash) + { + CheckNotDisposed(); + return _base64SHA1Hash.Equals(hash); + } + + public string GetBase64SHA1Hash() + { + CheckNotDisposed(); + return _base64SHA1Hash; + } + + + + private void CheckNotDisposed() + { + //this throws if disposed + _target.IsReadOnly(); + } + + + public override bool Equals(Object o) + { + if (o is GuardedString) + { + GuardedString other = (GuardedString)o; + //not the true contract of equals. however, + //due to the high mathematical improbability of + //two unequal strings having the same secure hash, + //this approach feels good. the alternative, + //decrypting for comparison, is simply too + //performance intensive to be used for equals + return _base64SHA1Hash.Equals(other._base64SHA1Hash); + } + return false; + } + + public override int GetHashCode() + { + return _base64SHA1Hash.GetHashCode(); + } + + public SecureString ToSecureString() + { + return _target.Copy(); + } + + private void ComputeHash() + { + Access(new LambdaAccessor(array => + { + _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); + })); + } + + } + #endregion + + #region AbstractUnmanagedArray + public abstract class AbstractUnmanagedArray : UnmanagedArray + { + private readonly int _length; + private bool _disposed; + public AbstractUnmanagedArray(int length) + { + if (length < 0) + { + throw new ArgumentException("Invalid length: " + length); + } + _length = length; + } + public int Length + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("UnmanagedArray"); + } + return _length; + } + } + public T this[int index] + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("UnmanagedArray"); + } + if (index < 0 || index >= Length) + { + throw new IndexOutOfRangeException(); + } + return GetValue(index); + } + set + { + if (_disposed) + { + throw new ObjectDisposedException("SecureStringAdapter"); + } + if (index < 0 || index >= Length) + { + throw new IndexOutOfRangeException(); + } + SetValue(index, value); + } + } + public void Dispose() + { + if (!_disposed) + { + for (int i = 0; i < Length; i++) + { + this[i] = default(T); + } + _disposed = true; + FreeMemory(); + } + } + + abstract protected T GetValue(int index); + abstract protected void SetValue(int index, T val); + abstract protected void FreeMemory(); + } + #endregion + + #region SecureStringAdapter + internal class SecureStringAdapter : AbstractUnmanagedArray + { + private IntPtr _bstrPtr; + public SecureStringAdapter(SecureString secureString) + : base(secureString.Length) + { + Assertions.NullCheck(secureString, "secureString"); + _bstrPtr = Marshal.SecureStringToBSTR(secureString); + } + protected override char GetValue(int index) + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + return *(charPtr + index); + } + } + protected override void SetValue(int index, char c) + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + *(charPtr + index) = c; + } + } + protected override void FreeMemory() + { + Marshal.ZeroFreeBSTR(_bstrPtr); + } + } + #endregion + + #region SecureStringToByteArrayAdapter + internal class SecureStringToByteArrayAdapter : AbstractUnmanagedArray + { + private IntPtr _bstrPtr; + public SecureStringToByteArrayAdapter(SecureString secureString) + : base(secureString.Length) + { + Assertions.NullCheck(secureString, "secureString"); + _bstrPtr = Marshal.SecureStringToBSTR(secureString); + } + protected override byte GetValue(int index) + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + return (byte)*(charPtr + index); + } + } + protected override void SetValue(int index, byte b) + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + *(charPtr + index) = (char)b; + } + } + protected override void FreeMemory() + { + Marshal.ZeroFreeBSTR(_bstrPtr); + } + } + #endregion + + #region UnmanagedCharArray + public class UnmanagedCharArray : AbstractUnmanagedArray + { + private IntPtr _ptr; + public UnmanagedCharArray(int length) + : base(length) + { + unsafe + { + _ptr = Marshal.AllocHGlobal(length * sizeof(char)); + } + } + protected override char GetValue(int index) + { + unsafe + { + char* charPtr = (char*)_ptr; + return *(charPtr + index); + } + } + protected override void SetValue(int index, char c) + { + unsafe + { + char* charPtr = (char*)_ptr; + *(charPtr + index) = c; + } + } + protected override void FreeMemory() + { + Marshal.FreeHGlobal(_ptr); + } + } + #endregion + + #region UnmanagedByteArray + public class UnmanagedByteArray : AbstractUnmanagedArray + { + private IntPtr _ptr; + public UnmanagedByteArray(int length) + : base(length) + { + unsafe + { + _ptr = Marshal.AllocHGlobal(length * sizeof(byte)); + } + } + protected override byte GetValue(int index) + { + unsafe + { + byte* charPtr = (byte*)_ptr; + return *(charPtr + index); + } + } + protected override void SetValue(int index, byte c) + { + unsafe + { + byte* charPtr = (byte*)_ptr; + *(charPtr + index) = c; + } + } + protected override void FreeMemory() + { + Marshal.FreeHGlobal(_ptr); + } + } + #endregion + + #region SecurityUtil + /// + /// Description of SecurityUtil. + /// + public static class SecurityUtil + { + public static string[] LowerHexArray = Enumerable.Range(0, 256).Select(v => v.ToString("x2")).ToArray(); + public static string[] UpperHexArray = Enumerable.Range(0, 256).Select(v => v.ToString("X2")).ToArray(); + + /// + /// Converts chars to bytes without using any external functions + /// that might allocate additional buffers for the potentially + /// sensitive data. + /// + /// + /// This guarantees the caller that they only + /// need to cleanup the input and result. + /// + /// The chars + /// The bytes + public static UnmanagedArray CharsToBytes(UnmanagedArray chars) + { + UnmanagedByteArray bytes = new UnmanagedByteArray(chars.Length * 2); + + for (int i = 0; i < chars.Length; i++) + { + char v = chars[i]; + bytes[i * 2] = (byte)(0xff & (v >> 8)); + bytes[i * 2 + 1] = (byte)(0xff & (v)); + } + return bytes; + } + + /// + /// Converts bytes to chars without using any external functions + /// that might allocate additional buffers for the potentially + /// sensitive data. + /// + /// + /// This guarantees the caller that they only + /// need to cleanup the input and result. + /// + /// The bytes + /// The chars + public static UnmanagedArray BytesToChars(UnmanagedArray bytes) + { + UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length / 2); + for (int i = 0; i < chars.Length; i++) + { + char v = (char)((bytes[i * 2] << 8) | bytes[i * 2 + 1]); + chars[i] = v; + } + return chars; + } + + + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) + { + using (UnmanagedArray bytes = SecurityUtil.CharsToBytes(input)) + { + return ComputeBase64SHA1Hash(bytes); + } + } + + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) + { + byte[] managedBytes = new byte[input.Length]; + fixed (byte* dummy = managedBytes) + { //pin it + try + { + //populate it in pinned block + SecurityUtil.UnmanagedBytesToManagedBytes(input, managedBytes); + SHA1 hasher = SHA1.Create(); + byte[] data = hasher.ComputeHash(managedBytes); + return Convert.ToBase64String(data); + } + finally + { + //clear it before we leave pinned block + SecurityUtil.Clear(managedBytes); + } + } + } + + /// + /// Computes the Hex encoded SHA1 hash of the input. + /// + /// + /// The input bytes. + /// + /// {@code true} converts to lowercase or {@code false} to + /// uppercase + /// the hash (computed from the input bytes). + /// since 1.5 + public static string ComputeHexSHA1Hash(byte[] bytes, bool toLowerCase) + { + SHA1 hasher = SHA1.Create(); + byte[] data = hasher.ComputeHash(bytes); + return BytesToHex(data, toLowerCase); + } + + /// + /// Computes the Hex encoded input. + /// + /// + /// The input bytes to convert to Hex characters + /// + /// {@code true} converts to lowercase or {@code false} to + /// uppercase + /// A String containing hexadecimal characters + /// since 1.5 + public static string BytesToHex(byte[] bytes, bool toLowerCase) + { + StringBuilder hexChars = new StringBuilder(bytes.Length * 2); + string[] hexArray = toLowerCase ? LowerHexArray : UpperHexArray; + foreach (var v in bytes) + hexChars.Append(hexArray[v]); + return hexChars.ToString(); + } + + /// + /// Copies an unmanaged byte array into a managed byte array. + /// + /// + /// NOTE: it is imperative for security reasons that this only + /// be done in a context where the byte array in question is pinned. + /// moreover, the byte array must be cleared prior to leaving the + /// pinned block + /// + public static void UnmanagedBytesToManagedBytes(UnmanagedArray array, + byte[] bytes) + { + for (int i = 0; i < array.Length; i++) + { + bytes[i] = array[i]; + } + } + + /// + /// Clears an array of potentially sensitive bytes + /// + /// The bytes. May be null. + /// NOTE: because this is C#, this alone is not enough. The + /// array must be pinned during the interval it is in-use or + /// it could be copied out from under you. + public static void Clear(byte[] bytes) + { + if (bytes != null) + { + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = 0; + } + } + } + + /// + /// Clears an array of potentially sensitive chars + /// + /// The characters. May be null. + /// NOTE: because this is C#, this alone is not enough. The + /// array must be pinned during the interval it is in-use or + /// it could be copied out from under you. + public static void Clear(char[] chars) + { + if (chars != null) + { + for (int i = 0; i < chars.Length; i++) + { + chars[i] = (char)0; + } + } + } + + public static bool VerifyBase64SHA1Hash(UnmanagedArray input, string hash) + { + string inputHash = ComputeBase64SHA1Hash(input); + return inputHash.Equals(hash); + } + + /// + /// Decrypts the value of a . + /// + /// + /// the guarded string value. + /// the clear string value. + /// Since 1.4 + public static string Decrypt(GuardedString guardedString) + { + StringBuilder buf = new StringBuilder(); + guardedString.Access(new GuardedString.LambdaAccessor( + array => + { + for (int i = 0; i < array.Length; i++) + { + buf.Append(array[i]); + } + })); + return buf.ToString(); + } + + /// + /// Decrypts the value of a . + /// + /// + /// the guarded byte array value. + /// the clear byte array value. + /// Since 1.4 + public static byte[] Decrypt(GuardedByteArray guardedByteArray) + { + byte[] result = null; + guardedByteArray.Access(new GuardedByteArray.LambdaAccessor( + array => + { + result = new byte[array.Length]; + for (int i = 0; i < array.Length; i++) + { + result[i] = array[i]; + } + })); + return result; + } + } + #endregion + + #region Encryptor + /// + /// Responsible for encrypting/decrypting bytes. + /// + /// + /// Implementations + /// are intended to be thread-safe. + /// + public interface Encryptor + { + /// + /// Decrypts the given byte array + /// + /// The encrypted bytes + /// The decrypted bytes + UnmanagedArray Decrypt(byte[] bytes); + + /// + /// Encrypts the given byte array + /// + /// The clear bytes + /// The ecnrypted bytes + byte[] Encrypt(UnmanagedArray bytes); + } + #endregion + + #region EncryptorFactory + public abstract class EncryptorFactory + { + private static readonly object LOCK = new object(); + + // At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME = "Org.IdentityConnectors.Common.Security.Impl.EncryptorFactoryImpl"; + + private static EncryptorFactory _instance; + + /// + /// Get the singleton instance of the . + /// + public static EncryptorFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { + Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = (EncryptorFactory)Activator.CreateInstance(type); + } + return _instance; + } + } + + /// + /// Default encryptor that encrypts/descrypts using a default key + /// + public abstract Encryptor GetDefaultEncryptor(); + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/Common/StringUtil.cs b/dotnet/framework/Common/StringUtil.cs new file mode 100644 index 00000000..008cb735 --- /dev/null +++ b/dotnet/framework/Common/StringUtil.cs @@ -0,0 +1,645 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Specialized; +using System.Text; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Collections.Generic; +using System.Linq; +using Org.IdentityConnectors.Common.Security; + +namespace Org.IdentityConnectors.Common +{ + public static class StringUtil + { + + /// + /// Finds the index of the first digit and starts from the index specified. + /// + /// + /// String to search for a digit. + /// + /// Starting index from which to search + /// -1 if not found otherwise the index. + public static int IndexOfDigit(string str, int startidx) + { + int ret = -1; + if (str != null) + { + for (int i = startidx; i < str.Length; i++) + { + // get the first digit.. + if (char.IsDigit(str[i])) + { + ret = i; + break; + } + } + } + return ret; + } + + /// + /// Finds the index of the first digit. + /// + /// + /// String to seach for a digit. + /// -1 if not found otherwise the index. + public static int IndexOfDigit(string str) + { + return IndexOfDigit(str, 0); + } + + /// + /// Finds the index of the first non digit and starts from the index + /// specified. + /// + /// + /// String to seach for a non digit. + /// + /// Starting index from which to search. + /// -1 if not found otherwise the index. + public static int IndexOfNonDigit(string str, int startidx) + { + int ret = -1; + if (str != null) + { + for (int i = startidx; i < str.Length; i++) + { + // get the first digit.. + if (!char.IsDigit(str[i])) + { + ret = i; + break; + } + } + } + return ret; + } + + /// + /// Finds the index of the first non digit. + /// + /// + /// String to seach for a non digit. + /// -1 if not found otherwise the index. + public static int IndexOfNonDigit(string str) + { + return IndexOfNonDigit(str, 0); + } + + /// + /// Return the string of digits from string. + /// + /// + /// Source string to search. + public static string SubDigitString(string str) + { + return SubDigitString(str, 0); + } + + + /// + /// Return the string of digits from string. + /// + /// + /// Source string to search. + /// + /// Start index from which to search. + public static string SubDigitString(string str, int idx) + { + string ret = null; + int sidx = IndexOfDigit(str, idx); + if (sidx != -1) + { + int eidx = IndexOfNonDigit(str, sidx); + ret = (eidx == -1) ? str.Substring(sidx) : str.Substring(sidx, eidx - sidx); + } + return ret; + } + + /// + /// Removes the attribute from the source string and returns. + /// + public static string StripXmlAttribute(string src, string attrName) + { + string ret = null; + // quick exit.. + if (src == null) + { + return null; + } + // find the attribute and remove all occurances of it.. + char[] quote = new char[] { '\'', '"' }; + ret = src; + while (true) + { + int start = ret.IndexOf(attrName); + // no more attributes + if (start == -1) + { + break; + } + // find the end of the attribute + int openQuote = IndexOf(ret, quote, start); + // there a problem because there's no open quote.. + if (openQuote == -1) + { + break; + } + // look for the closed quote + int closeQuote = IndexOf(ret, quote, openQuote + 1); + if (closeQuote == -1) + { + break; + } + // remove the space either before or after the attribute + if (start - 1 >= 0 && ret[start - 1] == ' ') + { + start -= 1; + } + else if (closeQuote + 1 < ret.Length && ret[closeQuote + 1] == ' ') + { + closeQuote += 1; + } + // construct new string from parts.. + StringBuilder builder = new StringBuilder(); + builder.Append(ret.Substring(0, start)); + builder.Append(ret.Substring(closeQuote + 1)); + ret = builder.ToString(); + } + return ret; + } + + /// + /// Removes newline characters (0x0a and 0x0d) from a string. + /// + public static string StripNewlines(string src) + { + String dest = null; + if (src != null) + { + StringBuilder b = new StringBuilder(); + int max = src.Length; + for (int i = 0; i < max; i++) + { + char c = src[i]; + if (c != 0x0a && c != 0x0d) + { + b.Append(c); + } + } + dest = b.ToString(); + } + return dest; + } + + /// + /// Finds the start index of the comparison string regards of case. + /// + /// + /// String to search. + /// + /// Comparsion string to find. + /// -1 if not found otherwise the index of the starting character. + public static int IndexOfIgnoreCase(string src, string cmp) + { + // quick check exit... + if (src == null || cmp == null) + { + return -1; + } + string isrc = src.ToUpper(); + string icmp = cmp.ToUpper(); + return isrc.IndexOf(icmp); + } + + + + private const string END_XMLCOMMENT = "-->"; + private const string START_XMLCOMMENT = " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/ConnectorServerService/ConnectorServerService.Designer.cs b/dotnet/framework/ConnectorServerService/ConnectorServerService.Designer.cs new file mode 100755 index 00000000..fd399166 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/ConnectorServerService.Designer.cs @@ -0,0 +1,37 @@ +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + partial class ConnectorServerService + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + this.ServiceName = "OpenICFWebsocketService"; + } + + #endregion + } +} diff --git a/dotnet/framework/ConnectorServerService/ConnectorServerService.cs b/dotnet/framework/ConnectorServerService/ConnectorServerService.cs new file mode 100755 index 00000000..c66d71b7 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/ConnectorServerService.cs @@ -0,0 +1,621 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http.Headers; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; +using System.ServiceModel.Configuration; +using System.ServiceProcess; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Org.ForgeRock.OpenICF.Common.ProtoBuf; +using Org.ForgeRock.OpenICF.Framework.Remote; +using Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using vtortola.WebSockets; + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + + #region OpenICFWebsocketService + + public partial class ConnectorServerService : ServiceBase + { + public const string PropKey = "connectorserver.key"; + public const string PropCertificateThumbprint = "connectorserver.certificateThumbprint"; + public const string PropFacadeLifetime = "connectorserver.maxFacadeLifeTime"; + + private Action _closeAction; + + public ConnectorServerService() + { + InitializeComponent(); + } + + public static void InitializeConnectors(AsyncLocalConnectorInfoManager manager) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = new FileInfo(assembly.Location); + DirectoryInfo directory = thisAssemblyFile.Directory; + if (directory == null) throw new ArgumentNullException("directory"); + Environment.CurrentDirectory = directory.FullName; + Trace.TraceInformation("Starting connector server from: " + Environment.CurrentDirectory); + + FileInfo[] files = directory.GetFiles("*.Connector.dll"); + foreach (FileInfo file in files) + { + Assembly lib = Assembly.LoadFrom(file.ToString()); + manager.AddConnectorAssembly(lib); + } + + // also handle connector DLL file names with a version + FileInfo[] versionedFiles = directory.GetFiles("*.Connector-*.dll"); + foreach (FileInfo versionedFile in versionedFiles) + { + Assembly lib = Assembly.LoadFrom(versionedFile.ToString()); + manager.AddConnectorAssembly(lib); + } + } + + protected virtual Uri[] ExtractEndPoint() + { + ServicesSection servicesSection = + ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection; + + if (servicesSection != null) + { + foreach (ServiceElement service in servicesSection.Services) + { + foreach (ServiceEndpointElement endpoint in service.Endpoints) + { + if (String.Equals(typeof (IWebSocketService).FullName, endpoint.Contract)) + { + return + (from BaseAddressElement baseAddress in service.Host.BaseAddresses + select new Uri(baseAddress.BaseAddress)).ToArray(); + } + } + } + } + throw new Exception("No BaseAddress //TODO fix message"); + } + + public void StartService(string[] args) + { + OnStart(args); + } + + protected override void OnStart(string[] args) + { + try + { + ConnectorFramework connectorFramework = new ConnectorFramework(); + InitializeConnectors(connectorFramework.LocalManager); + + NameValueCollection settings = ConfigurationManager.AppSettings; + + String keyHash = settings.Get(PropKey); + if (keyHash == null) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "Missing required configuration property: " + PropKey); + } + + ClientAuthenticationValidator validator = new ClientAuthenticationValidator(); + validator.Add(new SingleTenantPrincipal(connectorFramework), keyHash); + + String facadeLifeTimeStr = settings.Get(PropFacadeLifetime); + if (facadeLifeTimeStr != null) + { + // _server.MaxFacadeLifeTime = Int32.Parse(facedeLifeTimeStr); + } + + String disableWcf = settings.Get("disableWcf"); + OperatingSystem os = Environment.OSVersion; + if (disableWcf == null && (os.Platform == PlatformID.Win32NT) && + ((os.Version.Major > 6) || ((os.Version.Major == 6) && (os.Version.Minor >= 2)))) + { + ServiceHost host = new ConnectorServiceHost(validator); + host.Open(); + + _closeAction = () => host.Close(); + Trace.TraceInformation("Started WCF connector server"); + } + else + { + Uri[] endpointUri = ExtractEndPoint(); + if (endpointUri == null) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "Missing required baseAddress"); + } + VtortConnectorServiceHost host = new VtortConnectorServiceHost(validator, endpointUri); + host.Open(); + + _closeAction = () => host.Close(); + Trace.TraceInformation("Started WebSocketListener connector server"); + } + } + catch (Exception e) + { + TraceUtil.TraceException("Exception occured starting connector server", e); + throw; + } + } + + public void StopService() + { + OnStop(); + } + + protected override void OnStop() + { + try + { + Trace.TraceInformation("Stopping connector server"); + if (_closeAction != null) + { + _closeAction(); + _closeAction = null; + } + Trace.TraceInformation("Stopped connector server"); + } + catch (Exception e) + { + TraceUtil.TraceException("Exception occured stopping connector server", e); + } + } + } + + #endregion + + #region VtortConnectorServiceHost + + public class VtortConnectorServiceHost + { + private WebSocketListener _listener; + private readonly ClientAuthenticationValidator _validator; + private readonly Uri _endPointUri; + + public VtortConnectorServiceHost(ClientAuthenticationValidator validator, params Uri[] baseAddresses) + { + _validator = validator; + _endPointUri = baseAddresses[0]; + } + + public void Open() + { + IPAddress ipAddress = IPAddress.Any; + if (_endPointUri.IsLoopback) + { + ipAddress = IPAddress.Loopback; + } + else if (!"0.0.0.0".Equals(_endPointUri.DnsSafeHost)) + { + ipAddress = IOUtil.GetIPAddress(_endPointUri.DnsSafeHost); + } + + var options = new WebSocketListenerOptions() + { + NegotiationQueueCapacity = 128, + ParallelNegotiations = 16, + PingTimeout = Timeout.InfiniteTimeSpan, + SubProtocols = new[] {"v1.openicf.forgerock.org"}, + OnHttpNegotiation = (request, response) => + { + var authHeader = request.Headers["Authorization"]; + if (authHeader != null) + { + var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader); + + // RFC 2617 sec 1.2, "scheme" name is case-insensitive + if (authHeaderVal.Scheme.Equals("basic", + StringComparison.OrdinalIgnoreCase) && + authHeaderVal.Parameter != null) + { + var encoding = Encoding.GetEncoding("iso-8859-1"); + var credentials = encoding.GetString(Convert.FromBase64String(authHeaderVal.Parameter)); + + int separator = credentials.IndexOf(':'); + if (separator != -1) + { + string name = credentials.Substring(0, separator); + string password = credentials.Substring(separator + 1); + + var pair = _validator.FindPrincipal(name); + if (null != pair) + { + if (ClientAuthenticationValidator.Verify(pair.Second, password)) + { + request.Items["ConnectionPrincipal"] = pair.First; + } + else + { + Trace.TraceWarning("Incorrect password - username: {0}", name); + response.Status = HttpStatusCode.Forbidden; + } + } + else + { + Trace.TraceWarning("Unknown username: {0}", name); + response.Status = HttpStatusCode.Forbidden; + } + } + else + { + Trace.TraceWarning("Invalid Basic Authorization : {0}", credentials); + response.Status = HttpStatusCode.BadRequest; + } + } + else + { + Trace.TraceWarning("Basic Authorization header expected but found{0}", authHeader); + response.Status = HttpStatusCode.BadRequest; + } + } + else + { + //401 + Realm + response.Status = HttpStatusCode.Unauthorized; + } + } + }; + _listener = new WebSocketListener(new IPEndPoint(ipAddress, _endPointUri.Port), options); + + bool useSsl = String.Equals("https", _endPointUri.Scheme, StringComparison.OrdinalIgnoreCase) || + String.Equals("wss", _endPointUri.Scheme, StringComparison.OrdinalIgnoreCase); + if (useSsl) + { + _listener.ConnectionExtensions.RegisterExtension( + new WebSocketSecureConnectionExtension(GetCertificate())); + } + + var rfc6455 = new vtortola.WebSockets.Rfc6455.WebSocketFactoryRfc6455(_listener); + _listener.Standards.RegisterStandard(rfc6455); + _listener.Start(); + Task.Run((Func) ListenAsync); + } + + public void Close() + { + if (null != _listener) + { + _listener.Stop(); + _validator.Dispose(); + } + } + + protected X509Certificate2 GetCertificate() + { + NameValueCollection settings = ConfigurationManager.AppSettings; + String certificateThumbprint = settings.Get(ConnectorServerService.PropCertificateThumbprint); + if (String.IsNullOrWhiteSpace(certificateThumbprint)) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "Missing required configuration setting: " + ConnectorServerService.PropCertificateThumbprint); + } + + X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); + try + { + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + + X509Certificate2 certificate = + store.Certificates.Cast() + .FirstOrDefault( + certificate1 => + String.Equals(certificate1.Thumbprint, certificateThumbprint, + StringComparison.CurrentCultureIgnoreCase)); + if (certificate == null) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "The Certificate can not be found with thumbprint: " + certificateThumbprint); + } + return certificate; + } + finally + { + store.Close(); + } + } + + private async Task ListenAsync() + { + while (_listener.IsStarted) + { + try + { + var websocket = await _listener.AcceptWebSocketAsync(CancellationToken.None) + .ConfigureAwait(false); + if (websocket != null) + { + object value; + websocket.HttpRequest.Items.TryGetValue("ConnectionPrincipal", out value); + ConnectionPrincipal connectionPrincipal = value as ConnectionPrincipal; + if (null != connectionPrincipal) + { + Task.Run(() => HandleWebSocketAsync(websocket, connectionPrincipal)).ConfigureAwait(false); + } + else + { + Trace.TraceInformation("Unidentified WebSocket from {0}", websocket.RemoteEndpoint); + websocket.Close(); + } + } + else + { + Trace.TraceInformation("Stopping Connector Server!?"); + } + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to Accept WebSocket", e); + } + } + } + + private async Task HandleWebSocketAsync(WebSocket websocket, ConnectionPrincipal connectionPrincipal) + { + VtortWebsocket soWebsocket = new VtortWebsocket(websocket, connectionPrincipal); + try + { + Trace.TraceInformation("Server onConnect()"); + connectionPrincipal.OperationMessageListener.OnConnect(soWebsocket); + + while (websocket.IsConnected) + { + var message = await websocket.ReadMessageAsync(CancellationToken.None) + .ConfigureAwait(false); + if (message != null) + { + switch (message.MessageType) + { + case WebSocketMessageType.Text: + using (var sr = new StreamReader(message, Encoding.UTF8)) + { + String msgContent = await sr.ReadToEndAsync(); + connectionPrincipal.OperationMessageListener.OnMessage(soWebsocket, msgContent); + } + break; + case WebSocketMessageType.Binary: + using (var ms = new MemoryStream()) + { + await message.CopyToAsync(ms); + if (ms.Length > 0) + { + connectionPrincipal.OperationMessageListener.OnMessage(soWebsocket, ms.ToArray()); + } + } + break; + } + } + } + } + catch (Exception ex) + { + connectionPrincipal.OperationMessageListener.OnError(ex); + } + finally + { + connectionPrincipal.OperationMessageListener.OnClose(soWebsocket, 1000, ""); + //Dispose before onClose is complete ??? + websocket.Dispose(); + } + } + } + + #endregion + + #region VtortWebsocket + + public class VtortWebsocket : WebSocketConnectionHolder + { + private readonly WebSocket _webSocket; + private RemoteOperationContext _context; + private readonly ConnectionPrincipal _principal; + + public VtortWebsocket(WebSocket webSocket, ConnectionPrincipal principal) + { + _webSocket = webSocket; + _principal = principal; + Task.Factory.StartNew(WriteMessageAsync); + } + + protected override async Task WriteMessageAsync(byte[] entry, + System.Net.WebSockets.WebSocketMessageType messageType) + { + try + { + switch (messageType) + { + case System.Net.WebSockets.WebSocketMessageType.Binary: + using (var writer = _webSocket.CreateMessageWriter(WebSocketMessageType.Binary)) + { + await writer.WriteAsync(entry, 0, entry.Length); + } + break; + case System.Net.WebSockets.WebSocketMessageType.Text: + using (var writer = _webSocket.CreateMessageWriter(WebSocketMessageType.Text)) + { + await writer.WriteAsync(entry, 0, entry.Length); + } + break; + default: + throw new InvalidAttributeValueException("Unsupported WebSocketMessageType: " + messageType); + } + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to write", e); + } + } + + protected override void Handshake(HandshakeMessage message) + { + _context = _principal.Handshake(this, message); + if (null != _context) + { + Trace.TraceInformation("Client Connection Handshake succeeded"); + } + else + { + Trace.TraceError("Client Connection Handshake failed - Close Connection"); + TryClose(); + } + } + + protected override void TryClose() + { + _webSocket.Close(); + } + + public override bool Operational + { + get { return _webSocket.IsConnected; } + } + + public override RemoteOperationContext RemoteConnectionContext + { + get { return _context; } + } + } + + #endregion + + /* + #region BasicAuthHttpModule + + public abstract class BasicAuthHttpModule : IHttpModule + { + private const string Realm = "OpenICF"; + + public void Init(HttpApplication context) + { + // Register event handlers + context.AuthenticateRequest += OnApplicationAuthenticateRequest; + context.EndRequest += OnApplicationEndRequest; + } + + private static void SetPrincipal(IPrincipal principal) + { + Thread.CurrentPrincipal = principal; + if (HttpContext.Current != null) + { + HttpContext.Current.User = principal; + } + } + + protected abstract ConnectionPrincipal CheckPassword(SecurityToken token); + + private void AuthenticateUser(string credentials) + { + try + { + var encoding = Encoding.GetEncoding("iso-8859-1"); + credentials = encoding.GetString(Convert.FromBase64String(credentials)); + + int separator = credentials.IndexOf(':'); + if (separator != -1) + { + string name = credentials.Substring(0, separator); + string password = credentials.Substring(separator + 1); + + UserNameSecurityToken token = new UserNameSecurityToken(name, password); + ConnectionPrincipal principal = CheckPassword(token); + + if (null != principal) + { + SetPrincipal(principal); + } + else + { + // Invalid username or password. + HttpContext.Current.Response.StatusCode = 403; + } + } + else + { + //Bad Request + HttpContext.Current.Response.StatusCode = 400; + } + } + catch (FormatException) + { + // Credentials were not formatted correctly. + HttpContext.Current.Response.StatusCode = 401; + } + } + + private void OnApplicationAuthenticateRequest(object sender, EventArgs e) + { + var request = HttpContext.Current.Request; + var authHeader = request.Headers["Authorization"]; + if (authHeader != null) + { + var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader); + + // RFC 2617 sec 1.2, "scheme" name is case-insensitive + if (authHeaderVal.Scheme.Equals("basic", + StringComparison.OrdinalIgnoreCase) && + authHeaderVal.Parameter != null) + { + AuthenticateUser(authHeaderVal.Parameter); + } + } + else + { + HttpContext.Current.Response.StatusCode = 401; + } + } + + // If the request was unauthorized, add the WWW-Authenticate header + // to the response. + private static void OnApplicationEndRequest(object sender, EventArgs e) + { + var response = HttpContext.Current.Response; + if (response.StatusCode == 401) + { + response.Headers.Add("WWW-Authenticate", + string.Format("Basic realm=\"{0}\"", Realm)); + } + } + + public void Dispose() + { + } + } + + #endregion + */ +} diff --git a/dotnet/framework/ConnectorServerService/ConnectorServerService.csproj b/dotnet/framework/ConnectorServerService/ConnectorServerService.csproj new file mode 100755 index 00000000..e96bfbc9 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/ConnectorServerService.csproj @@ -0,0 +1,154 @@ + + + + + + Debug + AnyCPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} + Exe + Properties + Org.ForgeRock.OpenICF.Framework.ConnectorServerService + ConnectorServerService + OpenICF Framework - Connector Server Service + v4.5.2 + 512 + + True + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + FR_ICF_sq_med.ico + + + + + ..\packages\Google.ProtocolBuffers.3\lib\Google.Protobuf.dll + + + + + + + + + + False + ..\packages\vtortola.WebSocketListener.2.1.9.0\lib\net45\vtortola.WebSockets.dll + + + False + ..\packages\vtortola.WebSocketListener.2.1.9.0\lib\net45\vtortola.WebSockets.Rfc6455.dll + + + False + ..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll + + + + + Component + + + ConnectorServerService.cs + + + + Component + + + + True + True + Resources.resx + + + + + Designer + + + + + + {f140e8da-52b4-4159-992a-9da10ea8eefb} + Common + + + {8b24461b-456a-4032-89a1-cd418f7b5b62} + Framework + + + {5a9e8c5b-4d41-4e3e-9680-6c195bfad47a} + FrameworkProtoBuf + + + {b85c5a35-e3a2-4b04-9693-795e57d66de2} + FrameworkRpc + + + {5b47befd-c60b-4e80-943e-a7151ceea568} + FrameworkServer + + + {D1771E11-C7D3-43FD-9D87-46F1231846F1} + WcfServiceLibrary + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/ConnectorServerService/FR_ICF_sq_med.ico b/dotnet/framework/ConnectorServerService/FR_ICF_sq_med.ico new file mode 100755 index 0000000000000000000000000000000000000000..10240f4759865dd5741f62a4b15549b1e2e712c1 GIT binary patch literal 32038 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 I>*ay}189e!r~m)} literal 0 HcmV?d00001 diff --git a/dotnet/framework/ConnectorServerService/Program.cs b/dotnet/framework/ConnectorServerService/Program.cs new file mode 100755 index 00000000..27d8a4bc --- /dev/null +++ b/dotnet/framework/ConnectorServerService/Program.cs @@ -0,0 +1,274 @@ +using System; +using System.Configuration; +using System.Configuration.Install; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.ServiceProcess; +using Org.ForgeRock.OpenICF.Framework.ConnectorServerService.Properties; +using Org.IdentityConnectors.Common.Security; + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + internal static class Program + { + private const string Debug = "debug"; + + private static void Usage() + { + Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); + Console.WriteLine(" /install [/serviceName ] - Installs the service."); + Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); + Console.WriteLine(" /run - Runs the service from the console."); + Console.WriteLine(" /setKey [] - Sets the connector server key."); + Console.WriteLine(" /setCertificate - Sets secure server certificate thumbprint"); + Console.WriteLine(" /setDefaults - Sets default app.config"); + } + + /// + /// The main entry point for the application. + /// + private static void Main(string[] args) + { + if (args.Length == 0) + { + Usage(); + } + else + { + String cmd = args[0].ToLower(); + if (cmd.Equals("/setkey", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 2) + { + Usage(); + return; + } + DoSetKey(args.Length > 1 ? args[1] : null); + return; + } + if (cmd.Equals("/setCertificate", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 1) + { + Usage(); + return; + } + DoSetCertificate(); + return; + } + if (cmd.Equals("/setDefaults", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 1) + { + Usage(); + return; + } + using ( + var file = new StreamWriter(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false)) + { + file.WriteLine(Resources.ResourceManager.GetString("DefaultConfig")); + Console.WriteLine(@"Default configuration successfully restored."); + } + return; + } + if ("/install".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoInstall(); + } + else if ("/uninstall".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoUninstall(); + } + else if ("/run".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 1 && Debug.Equals(args[1], StringComparison.InvariantCultureIgnoreCase)) + { + Process currentProcess = Process.GetCurrentProcess(); + Console.WriteLine( + @"It's time to attach with debugger to process:{0} and press any key to continue.", + currentProcess.Id); + Console.ReadKey(); + } + DoRun(); + } + else if ("/service".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + ServiceBase.Run(new ServiceBase[] {new ConnectorServerService()}); + } + else + { + Usage(); + } + } + } + + private static void DoInstall() + { + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = + { + Assembly.GetExecutingAssembly().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("install.log", + cmdline); + ti.Context = ctx; + ti.Install(new System.Collections.Hashtable()); + } + + private static void DoUninstall() + { + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = + { + Assembly.GetExecutingAssembly().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("uninstall.log", + cmdline); + ti.Context = ctx; + ti.Uninstall(null); + } + + private static void DoRun() + { + ConnectorServerService svc = new ConnectorServerService(); + + svc.StartService(new String[0]); + + Console.WriteLine(@"Press q to shutdown."); + + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(); + if (info.KeyChar == 'q') + { + break; + } + } + svc.StopService(); + } + + private static GuardedString ReadPassword() + { + GuardedString rv = new GuardedString(); + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(true); + if (info.Key == ConsoleKey.Enter) + { + Console.WriteLine(); + rv.MakeReadOnly(); + return rv; + } + else + { + Console.Write("*"); + rv.AppendChar(info.KeyChar); + } + } + } + + private static void DoSetCertificate() + { + X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); + try + { + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + X509Certificate2Collection certificates = store.Certificates; + int i = 0; + if (certificates.Count > 0) + { + Console.WriteLine(@"Select certificate you want to use:"); + Console.WriteLine(@"Index Issued To Thumbprint"); + Console.WriteLine(@"----- --------- -------------------------"); + Console.WriteLine(); + foreach (var cerItem in certificates) + { + Console.WriteLine(@"{0,4}) {1,-25} {2}", i++, + cerItem.GetNameInfo(X509NameType.SimpleName, false), + cerItem.Thumbprint); + } + string line; + Console.WriteLine(); + do + { + line = Console.ReadLine(); + if (!String.IsNullOrWhiteSpace(line)) + { + try + { + int inputIndex = Convert.ToInt32(line); + if (inputIndex >= 0 && inputIndex < certificates.Count) + { + X509Certificate2 certificate = store.Certificates[inputIndex]; + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(ConnectorServerService.PropCertificateThumbprint); + config.AppSettings.Settings.Add(ConnectorServerService.PropCertificateThumbprint, + certificate.Thumbprint); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine(@"Certificate Thumbprint has been successfully updated to {0}.", + certificate.Thumbprint); + break; + } + } + catch (FormatException) + { + } + Console.WriteLine(@"Invalid input: {0}", line); + } + } while (!String.IsNullOrWhiteSpace(line)); + } + else + { + Console.WriteLine(@"No certificate was found in 'LocalMachine:My' store"); + } + } + finally + { + store.Close(); + } + } + + private static void DoSetKey(string key) + { + GuardedString str; + if (key == null) + { + Console.Write("Please enter the new key: "); + GuardedString v1 = ReadPassword(); + Console.Write("Please confirm the new key: "); + GuardedString v2 = ReadPassword(); + if (!v1.Equals(v2)) + { + Console.WriteLine("Error: Key mismatch."); + return; + } + str = v2; + } + else + { + str = new GuardedString(); + foreach (char c in key) + { + str.AppendChar(c); + } + } + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(ConnectorServerService.PropKey); + config.AppSettings.Settings.Add(ConnectorServerService.PropKey, str.GetBase64SHA1Hash()); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine("Key has been successfully updated."); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/ConnectorServerService/ProjectInstaller.cs b/dotnet/framework/ConnectorServerService/ProjectInstaller.cs new file mode 100755 index 00000000..cf7b2df8 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/ProjectInstaller.cs @@ -0,0 +1,93 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ + +using System; +using System.ComponentModel; +using System.Configuration.Install; +using System.Diagnostics; +using System.IO; +using System.ServiceProcess; + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + [RunInstaller(true)] + public class ProjectInstaller : Installer + { + public static string ServiceName { get; set; } + public static string DisplayName { get; set; } + public static string Description { get; set; } + + static ProjectInstaller() + { + ServiceName = "ConnectorServerService"; + DisplayName = "OpenICF Connector Server"; + Description = "OpenICF Connector Server"; + } + + public ProjectInstaller() + { + Process pc = Process.GetCurrentProcess(); + Directory.SetCurrentDirectory + (pc.MainModule.FileName.Substring(0, + pc.MainModule.FileName.LastIndexOf(@"\", + StringComparison.CurrentCulture) + )); + + var serviceProcessInstaller = new ServiceProcessInstaller(); + var serviceInstaller = new ServiceInstaller(); + + // Here you can set properties on serviceProcessInstaller or register event handlers + serviceProcessInstaller.Account = ServiceAccount.LocalSystem; + + serviceInstaller.ServiceName = ServiceName; + serviceInstaller.Description = Description; + serviceInstaller.DisplayName = DisplayName; + serviceInstaller.StartType = ServiceStartMode.Automatic; + + Installers.AddRange(new Installer[] {serviceProcessInstaller, serviceInstaller}); + } + + protected string AppendPathParameter(string path, string parameter) + { + if (path.Length > 0 && path[0] != '"') + { + path = "\"" + path + "\""; + } + path += " " + parameter; + return path; + } + + protected override void OnBeforeInstall(System.Collections.IDictionary savedState) + { + Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); + base.OnBeforeInstall(savedState); + } + + protected override void OnBeforeUninstall(System.Collections.IDictionary savedState) + { + Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); + base.OnBeforeUninstall(savedState); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/ConnectorServerService/Properties/Resources.Designer.cs b/dotnet/framework/ConnectorServerService/Properties/Resources.Designer.cs new file mode 100755 index 00000000..8c5177ed --- /dev/null +++ b/dotnet/framework/ConnectorServerService/Properties/Resources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34209 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("Org.ForgeRock.OpenICF.Framework.ConnectorServerService.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<configuration> + /// <appSettings> + /// <!-- Access these values via the property: + /// System.Configuration.ConfigurationManager.AppSettings[key] + /// --> + /// <add key="connectorserver.certificateThumbprint" value="Use certutil and copy: Cert Hash(sha1) Example:1b0889cdf9e0cee904646bb8a3d0aa4f72035056" /> + /// <add key="connectorserver.maxFacadeLifeTime" value="0" /> + /// <add key="connectorserver.key" value="lmA6bMfENJGlIDbfrVtklXFK32s=" /> + /// <!-- Enable/ [rest of string was truncated]";. + /// + internal static string DefaultConfig { + get { + return ResourceManager.GetString("DefaultConfig", resourceCulture); + } + } + } +} diff --git a/dotnet/framework/ConnectorServerService/Properties/Resources.resx b/dotnet/framework/ConnectorServerService/Properties/Resources.resx new file mode 100755 index 00000000..e65057e9 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/Properties/Resources.resx @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version="1.0" encoding="utf-8"?> +<configuration> + <appSettings> + <!-- Access these values via the property: + System.Configuration.ConfigurationManager.AppSettings[key] + --> + <add key="connectorserver.certificateThumbprint" value="Use certutil and copy: Cert Hash(sha1) Example:1b0889cdf9e0cee904646bb8a3d0aa4f72035056" /> + <add key="connectorserver.maxFacadeLifeTime" value="0" /> + <add key="connectorserver.key" value="lmA6bMfENJGlIDbfrVtklXFK32s=" /> + <!-- Enable/Disable the logging proxy for all operations. --> + <add key="logging.proxy" value="false" /> + <add key="disableWcf" value="true" /> + </appSettings> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> + </startup> + <system.serviceModel> + <services> + <service name="Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary.WcfWebsocket"> + <endpoint address="" binding="customBinding" bindingConfiguration="customWebSocket" + contract="Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary.IWebSocketService"> + <identity> + <dns value="localhost" /> + </identity> + </endpoint> + <host> + <baseAddresses> + <!-- Remove trailing '/' otherwise response: 405 Method Not Allowed --> + <add baseAddress="http://0.0.0.0:8759/openicf" /> + </baseAddresses> + </host> + </service> + </services> + <bindings> + <customBinding> + <binding name="customWebSocket"> + <byteStreamMessageEncoding /> + <httpTransport authenticationScheme="Basic" realm="OpenICF"> + <webSocketSettings transportUsage="Always" createNotificationOnConnection="true" + subProtocol="v1.openicf.forgerock.org" /> + </httpTransport> + </binding> + </customBinding> + </bindings> + <behaviors> + <serviceBehaviors> + <behavior> + <!-- To avoid disclosing metadata information, set the values below to false before deployment --> + <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" /> + <!-- To receive exception details in faults for debugging purposes, set the value below to true. + Set to false before deployment to avoid disclosing exception information --> + <serviceDebug includeExceptionDetailInFaults="false" /> + <!--Specify the Certificate- -> + <serviceCertificate findValue="ConnectorServerSSLCertificate" + storeLocation="LocalMachine" + x509FindType="FindBySubjectName" + storeName="ConnectorServerSSLCertificate" /--> + </behavior> + </serviceBehaviors> + </behaviors> + <!--serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /--> + </system.serviceModel> + <system.diagnostics> + <trace autoflush="true" indentsize="4"> + <listeners> + <remove name="Default" /> + <add name="console" /> + <add name="file" /> + </listeners> + </trace> + <sources> + <source name="ConnectorServer" switchName="switch1"> + <listeners> + <remove name="Default" /> + <add name="file" /> + </listeners> + </source> + </sources> + <switches> + <add name="switch1" value="Information" /> + </switches> + <sharedListeners> + <add name="console" type="System.Diagnostics.ConsoleTraceListener" /> + <add name="file" type="System.Diagnostics.TextWriterTraceListener" initializeData="logs\ConnectorServerService.log" + traceOutputOptions="DateTime"> + <filter type="System.Diagnostics.EventTypeFilter" initializeData="Information" /> + </add> + </sharedListeners> + </system.diagnostics> +</configuration> + + \ No newline at end of file diff --git a/dotnet/framework/ConnectorServerService/packages.config b/dotnet/framework/ConnectorServerService/packages.config new file mode 100755 index 00000000..40276061 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/dotnet/framework/ConnectorServerService/version.template b/dotnet/framework/ConnectorServerService/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/ConnectorServerService/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/Framework.targets b/dotnet/framework/Framework.targets new file mode 100644 index 00000000..a23074b5 --- /dev/null +++ b/dotnet/framework/Framework.targets @@ -0,0 +1,105 @@ + + + + $(MSBuildProjectDirectory)\version.template + $(MSBuildProjectDirectory)\version.txt + ForgeRock + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + $(MSBuildProjectDirectory)\..\Dist + + $(MSBuildProjectDirectory) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FrameworkBeforeBuild; + $(BuildDependsOn); + FrameworkAfterBuild + + + $(CleanDependsOn); + FrameworkClean + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/Framework/Api.cs b/dotnet/framework/Framework/Api.cs new file mode 100644 index 00000000..ec44e054 --- /dev/null +++ b/dotnet/framework/Framework/Api.cs @@ -0,0 +1,940 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.Net.Security; +using System.Text; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Framework.Api +{ + public static class APIConstants + { + public const int NO_TIMEOUT = -1; + } + + #region APIConfiguration + /// + /// Interface to show the configuration properties from both the SPI/API based on + /// the makeup. + /// + /// Before this is passed into the one must call + /// and configure accordingly. + /// + public interface APIConfiguration + { + + /// + /// Gets instance of the configuration properties. + /// + /// These are initialized to their default values based on meta information. + /// Caller can then modify the properties as needed. + /// + ConfigurationProperties ConfigurationProperties { get; } + + /// + /// Add a configuration change listener callback handler. + /// + /// This callback handler will be notified when connector push an event back + /// to application to notify the initial configuration has to be changed + /// before next time creating a new Connectorfacade in order to continue + /// operate properly. + /// + /// + /// the callback handler to receive the change event. + /// a closeable to unregister the change listener. + IConfigurationPropertyChangeListener ChangeListener { get; set; } + + /// + /// Determines if this uses the framework's connector + /// pooling. + /// + /// true if the uses the framework's connector + /// pooling feature. + bool IsConnectorPoolingSupported { get; } + + /// + /// Gets the connector pooling configuration. + /// + /// This is initialized to the default values. Caller can then modify the + /// properties as needed. + /// + ObjectPoolConfiguration ConnectorPoolConfiguration { get; } + + // ======================================================================= + // Operational Support Set + // ======================================================================= + /// + /// Get the set of operations that this will support. + /// + ICollection> SupportedOperations { get; } + + // ======================================================================= + // Framework Configuration.. + // ======================================================================= + /// + /// Sets the timeout value for the operation provided. + /// + /// + /// particular operation that requires a timeout. + /// + /// milliseconds that the operation will wait in order to + /// complete. Values less than or equal to zero are considered to + /// disable the timeout property. + void SetTimeout(SafeType operation, int timeout); + + /// + /// Gets the timeout in milliseconds based on the operation provided. + /// + /// + /// particular operation to get a timeout for. + /// milliseconds to wait for an operation to complete before throwing + /// an error. + int GetTimeout(SafeType operation); + + /// + /// Sets the size of the buffer for the support + /// and what the results of the producer buffered. + /// + /// + /// default is 100, if size is set to zero or less will disable + /// buffering. + int ProducerBufferSize { get; set; } + + + /// + /// Get the configuration of the ResultsHandler chain of the Search + /// operation. + /// + ResultsHandlerConfiguration ResultsHandlerConfiguration { get; } + + } + #endregion + + #region ConfigurationProperties + /// + /// Configuration properties encapsulates the and uses + /// to determine the properties available for manipulation. + /// + public interface ConfigurationProperties + { + /// + /// Get the list of properties names for this . + /// + /// get the list of properties names. + IList PropertyNames { get; } + + /// + /// Get a particular by name. + /// + /// the unique name of the property. + /// a if it exists otherwise null. + ConfigurationProperty GetProperty(string name); + + /// + /// Set the value of the property by name. + /// + /// Name of the property to set the value against. + /// Value to set on the configuration property. + /// iff the property name does not exist. + void SetPropertyValue(string name, Object value); + + } + #endregion + + #region ConfigurationProperty + /// + /// Translation from at the SPI layer to the API. + /// + public interface ConfigurationProperty + { + int Order { get; } + + /// + /// Get the unique name of the configuration property. + /// + string Name { get; } + + /// + /// Get the help message from the message catalog. + /// + string GetHelpMessage(string def); + + /// + /// Get the display name for this configuration property. + /// + string GetDisplayName(string def); + + /// + /// Get name of the group for this configuration property. + /// + string GetGroup(string def); + + /// + /// Get the value from the property. + /// + /// + /// This should be the default value. + /// + object Value { get; set; } + + /// + /// Get the type of the property. + /// + Type ValueType { get; } + + /// + /// Is this a confidential property whose value should be encrypted by + /// the application when persisted? + /// + bool IsConfidential { get; } + + /// + /// Is this a required property + /// + /// True if the property is required + bool IsRequired { get; } + + /// + /// Set of operations for which this property must be specified. + /// + /// + /// This is used for the case where a connector may or may not + /// implement certain operations depending in the configuration. + /// The default value of "empty array" is special in that + /// it means that this property is applicable to all operations. + /// + /// + ICollection> Operations { get; } + } + #endregion + + #region IConfigurationPropertyChangeListener + /// + /// A ConfigurationPropertyChangeListener receives the change of + /// ConfigurationProperty. + /// + /// since 1.5 + public interface IConfigurationPropertyChangeListener + { + + /// + /// Receives notification that an ConfigurationProperty has been changed to + /// connector. + /// + /// + /// containing the property name and value of the + /// ConfigurationProperty that was changed. + void ConfigurationPropertyChange(IList changes); + + } + #endregion + + #region ConnectorFacade + /// + /// Main interface through which an application invokes Connector operations. + /// Represents at the API level a specific instance of a Connector that has been + /// configured in a specific way. + /// + /// + public interface ConnectorFacade : CreateApiOp, IConnectorEventSubscriptionApiOp, DeleteApiOp, + SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, ResolveUsernameApiOp, + GetApiOp, ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, + ISyncEventSubscriptionApiOp, SyncApiOp + { + + /// + /// Gets the unique generated identifier of this ConnectorFacade. + /// + /// It's not guarantied that the equivalent configuration will generate the + /// same configuration key. Always use the generated value and maintain it in + /// the external application. + /// + /// identifier of this ConnectorFacade instance. + /// Since 1.4 + string ConnectorFacadeKey { get; } + + /// + /// Get the set of operations that this will support. + /// + ICollection> SupportedOperations { get; } + + /// + /// Get an instance of an operation that this facade supports. + /// + APIOperation GetOperation(SafeType type); + + } + #endregion + + #region ConnectorFacadeFactory + /// + /// Manages a pool of connectors for use by a provisioner. + /// + public abstract class ConnectorFacadeFactory + { + // At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Api.ConnectorFacadeFactoryImpl"; + + private const string IMPL_NAME_MANAGED = + "Org.IdentityConnectors.Framework.Impl.Api.ManagedConnectorFacadeFactoryImpl"; + + private static ConnectorFacadeFactory _instance; + private static ConnectorFacadeFactory _managedInstance; + private static object LOCK = new Object(); + + /// + /// Get the singleton instance of the . + /// + public static ConnectorFacadeFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = t.CreateInstance(); + } + } + return _instance; + } + + /// + /// Get the singleton instance of the stateful . + /// + /// Since 1.4 + public static ConnectorFacadeFactory GetManagedInstance() + { + lock (LOCK) + { + if (_managedInstance == null) + { + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME_MANAGED); + _managedInstance = t.CreateInstance(); + } + return _managedInstance; + } + + } + + /// + /// Get a new instance of . + /// + /// all the configuration that the framework, connector, and + /// pooling needs. + /// + /// to call API operations against. + /// + public abstract ConnectorFacade NewInstance(APIConfiguration config); + + /// + /// Get a new instance of . + /// + /// TODO add doc later + /// all the configuration that the framework, connector, and pooling needs. It's a Base64 serialised APIConfiguration instance. + /// + /// to call API operations against. + /// + /// since 1.4 + public abstract ConnectorFacade NewInstance(ConnectorInfo connectorInfo, String config); + + /// + /// Dispose of all connection pools, resources, etc. + /// + public abstract void Dispose(); + } + #endregion + + #region ConnectorInfo + /// + /// The connector meta-data for a given connector. + /// + public interface ConnectorInfo + { + /// + /// Returns a friendly name suitable for display in the UI. + /// + /// The friendly name + string GetConnectorDisplayName(); + + ConnectorMessages Messages { get; } + + ConnectorKey ConnectorKey { get; } + + /// + /// Loads the and class in order to + /// determine the proper default configuration parameters. + /// + APIConfiguration CreateDefaultAPIConfiguration(); + } + #endregion + + #region ConnectorInfoManager + /// + /// Class responsible for maintaing a list of ConnectorInfo + /// associated with a set of connector bundles. + /// + public interface ConnectorInfoManager + { + /// + /// Returns the list of ConnectorInfo + /// + /// the list of ConnectorInfo + IList ConnectorInfos { get; } + + /// + /// Given a connectorName and connectorVersion, returns the + /// associated ConnectorInfo. + /// + /// The connector key. + /// The ConnectorInfo or null if it couldn't + /// be found. + ConnectorInfo FindConnectorInfo(ConnectorKey key); + } + #endregion + + #region ConnectorInfoManagerFactory + /// + /// The main entry point into connectors. + /// + /// + /// This allows you + /// to load the connector classes from a set of bundles. + /// + public abstract class ConnectorInfoManagerFactory + { + //At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Api.ConnectorInfoManagerFactoryImpl"; + private static ConnectorInfoManagerFactory _instance; + private static object LOCK = new Object(); + /// + /// Singleton pattern for getting an instance of the + /// ConnectorInfoManagerFactory. + /// + /// + public static ConnectorInfoManagerFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { + SafeType t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = t.CreateInstance(); + } + } + return _instance; + } + public abstract ConnectorInfoManager GetLocalManager(); + public abstract ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info); + + /// + /// Clears the bundle manager cache. + /// + /// + /// Generally intended for unit testing + /// + public abstract void ClearRemoteCache(); + } + #endregion + + #region ConnectorKey + /// + /// Uniquely identifies a connector within an installation. + /// + /// + /// Consists of the triple (bundleName, bundleVersion, connectorName) + /// + public sealed class ConnectorKey + { + private readonly string _bundleName; + private readonly string _bundleVersion; + private readonly string _connectorName; + + public ConnectorKey(String bundleName, + String bundleVersion, + String connectorName) + { + if (bundleName == null) + { + throw new ArgumentException("bundleName may not be null"); + } + if (bundleVersion == null) + { + throw new ArgumentException("bundleVersion may not be null"); + } + if (connectorName == null) + { + throw new ArgumentException("connectorName may not be null"); + } + _bundleName = bundleName; + _bundleVersion = bundleVersion; + _connectorName = connectorName; + } + + public string BundleName + { + get + { + return _bundleName; + } + } + + public string BundleVersion + { + get + { + return _bundleVersion; + } + } + + public string ConnectorName + { + get + { + return _connectorName; + } + } + + public override bool Equals(object o) + { + if (o is ConnectorKey) + { + ConnectorKey other = (ConnectorKey)o; + if (!_bundleName.Equals(other._bundleName)) + { + return false; + } + if (!_bundleVersion.Equals(other._bundleVersion)) + { + return false; + } + if (!_connectorName.Equals(other._connectorName)) + { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() + { + int rv = 0; + rv ^= _connectorName.GetHashCode(); + return rv; + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.Append("ConnectorKey("); + builder.Append(" bundleName=").Append(_bundleName); + builder.Append(" bundleVersion=").Append(_bundleVersion); + builder.Append(" connectorName=").Append(_connectorName); + builder.Append(" )"); + return builder.ToString(); + } + } + #endregion + + #region RemoteFrameworkConnectionInfo + public sealed class RemoteFrameworkConnectionInfo + { + private readonly String _host; + private readonly int _port; + private readonly GuardedString _key; + private readonly bool _useSSL; + private readonly RemoteCertificateValidationCallback _certificateValidationCallback; + private readonly int _timeout; + + /// + /// Creates a new instance of RemoteFrameworkConnectionInfo, using + /// a clear (non-ssl) connection and a 60-second timeout. + /// + /// The host to connect to + /// The port to connect to + public RemoteFrameworkConnectionInfo(String host, + int port, + GuardedString key) + : this(host, port, key, false, null, 60 * 1000) + { + } + + /// + /// Creates a new instance of RemoteFrameworkConnectionInfo. + /// + /// The host to connect to + /// The port to connect to + /// Set to true if we are to connect via SSL. + /// to use + /// for establising the SSL connection. May be null or empty, + /// in which case the default installed providers for the JVM will + /// be used. Ignored if 'useSSL' is false. + /// The timeout to use (in milliseconds). A value of 0 + /// means infinite timeout; + public RemoteFrameworkConnectionInfo(String host, + int port, + GuardedString key, + bool useSSL, + RemoteCertificateValidationCallback certificateValidationCallback, + int timeout) + { + + if (host == null) + { + throw new ArgumentException("Parameter 'host' is null."); + } + if (key == null) + { + throw new ArgumentException("Parameter 'key' is null."); + } + + _host = host; + _port = port; + _key = key; + _useSSL = useSSL; + _certificateValidationCallback = certificateValidationCallback; + _timeout = timeout; + } + + /// + /// Returns the host to connect to. + /// + /// The host to connect to. + public String Host + { + get + { + return _host; + } + } + + /// + /// Returns the port to connect to + /// + /// The port to connect to + public int Port + { + get + { + return _port; + } + } + + public GuardedString Key + { + get + { + return _key; + } + } + + /// + /// Returns true iff we are to use SSL to connect. + /// + /// true iff we are to use SSL to connect. + public bool UseSSL + { + get + { + return _useSSL; + } + } + + /// + /// Returns the list of 's. + /// + /// + /// to use when establishing + /// the connection. + /// + /// The list of 's. + public RemoteCertificateValidationCallback CertificateValidationCallback + { + get + { + return _certificateValidationCallback; + } + } + + /// + /// Returns the timeout (in milliseconds) to use for the connection. + /// + /// + /// A value of zero means infinite timeout. + /// + /// the timeout (in milliseconds) to use for the connection. + public int Timeout + { + get + { + return _timeout; + } + } + + public override bool Equals(Object o) + { + if (o is RemoteFrameworkConnectionInfo) + { + RemoteFrameworkConnectionInfo other = + (RemoteFrameworkConnectionInfo)o; + if (!Object.Equals(Host, other.Host)) + { + return false; + } + if (Port != other.Port) + { + return false; + } + if (UseSSL != other.UseSSL) + { + return false; + } + if (CertificateValidationCallback == null || + other.CertificateValidationCallback == null) + { + if (CertificateValidationCallback != null || + other.CertificateValidationCallback != null) + { + return false; + } + } + else + { + if (!CertificateValidationCallback.Equals + (other.CertificateValidationCallback)) + { + return false; + } + } + + if (!Key.Equals(other.Key)) + { + return false; + } + + if (Timeout != other.Timeout) + { + return false; + } + + return true; + } + return false; + } + + public override int GetHashCode() + { + return _host.GetHashCode() ^ _port; + } + + public override String ToString() + { + return "{host=" + _host + ", port=" + _port + "}"; + } + } + #endregion + + #region ResultsHandlerConfiguration + /// + /// Configuration for result handler chain + /// + public sealed class ResultsHandlerConfiguration + { + + /// + /// Enables the {@link NormalizingResultsHandler} in the handler chain. + /// + private bool _enableNormalizingResultsHandler = true; + + /// + /// Enables the {@link FilteredResultsHandler} in the handler chain. + /// + private bool _enableFilteredResultsHandler = true; + + /// + /// Enables the case insensitive filtering. + /// + /// + /// + /// + private bool _enableCaseInsensitiveFilter = false; + + /// + /// Enables the {@link AttributesToGetSearchResultsHandler} in the handler chain. + /// + /// + /// + /// + private bool _enableAttributesToGetSearchResultsHandler = true; + + /// + /// Default empty constructor. + /// + public ResultsHandlerConfiguration() + { + } + + /// + /// Copy constructor + /// + /// configuration that copied to. + public ResultsHandlerConfiguration(ResultsHandlerConfiguration source) + { + this.EnableNormalizingResultsHandler = source.EnableNormalizingResultsHandler; + this.EnableFilteredResultsHandler = source.EnableFilteredResultsHandler; + this.EnableCaseInsensitiveFilter = source.EnableCaseInsensitiveFilter; + this.EnableAttributesToGetSearchResultsHandler = source.EnableAttributesToGetSearchResultsHandler; + } + + /// + /// Get the set number of maximum objects (idle+active) + /// + public bool EnableNormalizingResultsHandler + { + get + { + return _enableNormalizingResultsHandler; + } + set + { + _enableNormalizingResultsHandler = value; + } + } + + /// + /// Get the maximum number of idle objects. + /// + public bool EnableFilteredResultsHandler + { + get + { + return _enableFilteredResultsHandler; + } + set + { + _enableFilteredResultsHandler = value; + } + } + + /// + /// Max time to wait if the pool is waiting for a free object to become + /// available before failing. + /// + /// + /// Zero means don't wait + /// + public bool EnableCaseInsensitiveFilter + { + get + { + return _enableCaseInsensitiveFilter; + } + set + { + _enableCaseInsensitiveFilter = value; + } + } + + /// + /// Minimum time to wait before evicting an idle object. + /// + /// + /// Zero means don't wait + /// + public bool EnableAttributesToGetSearchResultsHandler + { + get + { + return _enableAttributesToGetSearchResultsHandler; + } + set + { + _enableAttributesToGetSearchResultsHandler = value; + } + } + + public override int GetHashCode() + { + unchecked + { + int hash = 3; + hash = 79 * hash + (EnableNormalizingResultsHandler ? 1 : 0); + hash = 79 * hash + (EnableFilteredResultsHandler ? 1 : 0); + hash = 79 * hash + (EnableCaseInsensitiveFilter ? 1 : 0); + hash = 79 * hash + (EnableAttributesToGetSearchResultsHandler ? 1 : 0); + return hash; + } + } + + public override bool Equals(Object obj) + { + if (obj is ResultsHandlerConfiguration) + { + ResultsHandlerConfiguration other = (ResultsHandlerConfiguration)obj; + + if (EnableNormalizingResultsHandler != other.EnableNormalizingResultsHandler) + { + return false; + } + if (EnableFilteredResultsHandler != other.EnableFilteredResultsHandler) + { + return false; + } + if (EnableCaseInsensitiveFilter != other.EnableCaseInsensitiveFilter) + { + return false; + } + if (EnableAttributesToGetSearchResultsHandler != other.EnableAttributesToGetSearchResultsHandler) + { + return false; + } + return true; + } + return false; + } + + public override String ToString() + { + // poor man's toString() + IDictionary bld = new Dictionary(); + bld["EnableNormalizingResultsHandler"] = EnableNormalizingResultsHandler; + bld["EnableFilteredResultsHandler"] = EnableFilteredResultsHandler; + bld["EnableCaseInsensitiveFilter"] = EnableCaseInsensitiveFilter; + bld["EnableAttributesToGetSearchResultsHandler"] = EnableAttributesToGetSearchResultsHandler; + return bld.ToString(); + } + } + #endregion +} diff --git a/dotnet/framework/Framework/ApiOperations.cs b/dotnet/framework/Framework/ApiOperations.cs new file mode 100644 index 00000000..fa93a059 --- /dev/null +++ b/dotnet/framework/Framework/ApiOperations.cs @@ -0,0 +1,560 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Collections.Generic; + +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Api.Operations +{ + /// + /// Base interface for all API operations. + /// + public interface APIOperation + { + } + + public interface AuthenticationApiOp : APIOperation + { + /// + /// Most basic authentication available. + /// + /// + /// string that represents the account or user id. + /// string that represents the password for the account or user. + /// + /// iff the credentials do not pass authentication otherwise + /// nothing. + Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); + } + + #region IConnectorEventSubscriptionApiOp + /// + /// since 1.5 + public interface IConnectorEventSubscriptionApiOp : APIOperation + { + ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, OperationOptions operationOptions); + } + #endregion + + public interface ResolveUsernameApiOp : APIOperation + { + /// + /// Resolve the given username + /// to the corresponding . + /// + /// + /// The Uid is the one + /// that would return + /// in case of a successful authentication. + /// + /// The object class to use for authenticate. + /// Will typically be an account. Must not be null. + /// string that represents the account or user id. + /// additional options that impact the way this operation is run. + /// May be null. + /// Uid The uid of the account that would be used to authenticate. + /// iff the username could not be resolved. + Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options); + } + + /// + /// Operation to create connector objects based on the attributes provided. + /// + public interface CreateApiOp : APIOperation + { + /// + /// Creates a user based on the ConnectorAttributes provide. The only + /// required attribute is the ObjectClass and those required by the + /// Connector. The API will validate the existence of the + /// ObjectClass attribute and that there are no duplicate name'd + /// attributes. + /// + /// + /// ConnectorAttribtes to create the object. + /// Unique id for the created object. + Uid Create(ObjectClass objectClass, ICollection attrs, OperationOptions options); + } + + /// + /// Deletes an object with the specified Uid and ObjectClass on the + /// resource. + /// + public interface DeleteApiOp : APIOperation + { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// The type of object to delete. + /// The unique identitfier for the object to delete. + /// Throws UnknowUid if the object does not exist. + void Delete(ObjectClass objectClass, Uid uid, OperationOptions options); + } + + /// + /// Get a particular based on the . + /// + public interface GetApiOp : APIOperation + { + /// + /// Get a particular based on the . + /// + /// the unique id of the object that to get. + /// + /// based on the provided. + ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options); + } + + /// + /// Get the schema from the . + /// + public interface SchemaApiOp : APIOperation + { + /// + /// Retrieve the basic schema of this . + /// + Schema Schema(); + } + + + public interface SearchApiOp : APIOperation + { + /// + /// Search the resource for all objects that match the object class and + /// filter. + /// + /// + /// reduces the number of entries to only those that match the + /// provided. + /// + /// Reduces the number of entries to only those that match the + /// provided, if any. May be null. + /// + /// class responsible for working with the objects returned from + /// the search. + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// The query result or {@code null}. + /// + /// if there is problem during the processing of the results. + SearchResult Search(ObjectClass objectClass, Filter filter, ResultsHandler handler, OperationOptions options); + + } + + /// + /// Runs a script in the same JVM or .Net Runtime as the Connector. + /// + /// + /// That is, if you are using a local framework, the script will be + /// run in your JVM. If you are connected to a remote framework, the + /// script will be run in the remote JVM or .Net Runtime. + /// + /// This API allows an application to run a script in the context + /// of any connector. (A connector need not implement any particular interface + /// in order to enable this.) The minimum contract to which each connector + /// must adhere is as follows: + /// + /// + /// Script will run in the same classloader/execution environment + /// as the connector, so the script will have access to all the classes + /// to which the connector has access. + /// + /// + /// + /// Script will have access to a "connector" variable + /// that is equivalent to an initialized instance of a connector. + /// Thus, at a minimum the script will be able to access + /// . + /// + /// + /// + /// Script will have access to any + /// + /// passed in by the application. + /// + /// + /// + /// + /// + /// A connector that implements + /// may provide more variables than what is described above. + /// A connector also may perform special processing + /// for specific to that connector. + /// Consult the javadoc of each particular connector to find out what + /// additional capabilities, if any, that connector exposes for use in scripts. + /// + /// + /// NOTE: A caller who wants to execute scripts on a connector + /// should assume that a script must not use any method of the connector + /// beyond the minimum contract described above, + /// unless the connector explicitly documents that method as + /// "for use by connector script". The primary function of a connector + /// is to implement the SPI in the context of the Connector framework. + /// In general, no caller should invoke Connector methods directly + /// --whether by a script or by other means. + /// + /// + public interface ScriptOnConnectorApiOp : APIOperation + { + /// + /// Runs the script. + /// + /// - The script and arguments to run. + /// - Additional options that control how the script is + /// run. The framework does not currently recognize any options + /// but specific connectors might. Consult the documentation + /// for each connector to identify supported options. + /// The result of the script. The return type must be + /// a type that the framework supports for serialization. + /// + Object RunScriptOnConnector(ScriptContext request, + OperationOptions options); + } + /// + /// Runs a script on the target resource that a connector manages. + /// + /// + /// This API operation is supported only for a connector that implements + /// . + /// + /// The contract here at the API level is intentionally very loose. + /// Each connector decides what script languages it supports, + /// what running a script on a target resource actually means, + /// and what script options (if any) that connector supports. + /// Refer to the javadoc of each particular connector for more information. + /// + /// + public interface ScriptOnResourceApiOp : APIOperation + { + /// + /// Runs a script on a specific target resource. + /// + /// The script and arguments to run. + /// Additional options which control how the script is + /// run. Please refer to the connector documentation for supported + /// options. + /// The result of the script. The return type must be + /// a type that the connector framework supports for serialization. + /// See for a list of supported return types. + Object RunScriptOnResource(ScriptContext request, + OperationOptions options); + } + /// + /// Poll for synchronization events--i.e., native changes to target objects. + /// + /// + /// This will be supported by + /// connectors that implement . + /// + /// + public interface SyncApiOp : APIOperation + { + /// + /// Request synchronization events--i.e., native changes to target objects. + /// + /// This method will call the specified + /// once to pass back each + /// matching . Once this method + /// returns, this method will no longer invoke the specified handler. + /// + /// + /// Each {@link SyncDelta#getToken() synchronization event contains a + /// token} that can be used to resume reading events starting from that + /// point in the event stream. In typical usage, a client will save the + /// token from the final synchronization event that was received from one + /// invocation of this {@code sync()} method and then pass that token into + /// that client's next call to this {@code sync()} method. This allows a + /// client to "pick up where he left off" in receiving synchronization + /// events. However, a client can pass the token from any + /// synchronization event into a subsequent invocation of this {@code sync()} + /// method. This will return synchronization events (that represent native + /// changes that occurred) immediately subsequent to the event from which the + /// client obtained the token. + /// + /// + /// A client that wants to read synchronization events "starting now" can + /// call and then pass that token into this + /// {@code sync()} method. + /// + /// + /// + /// + /// The class of object for which to return synchronization + /// events. Must not be null. + /// + /// The token representing the last token from the previous sync. + /// The {@code SyncResultsHandler} will return any number of + /// objects, each of which contains a + /// token. Should be {@code null} if this is the client's first + /// call to the {@code sync()} method for this connector. + /// + /// The result handler. Must not be null. + /// + /// Options that affect the way this operation is run. May be + /// null. + /// The sync token or {@code null}. + /// + /// if {@code objectClass} or {@code handler} is null or if any + /// argument is invalid. + SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options); + + + /// + /// Returns the token corresponding to the most recent synchronization event + /// for any instance of the specified object class. + /// + /// An application that wants to receive synchronization events + /// "starting now" --i.e., wants to receive only native changes that occur + /// after this method is called-- should call this method and then pass the + /// resulting token into . + /// + /// + /// + /// + /// This is to support applications that may wish to sync starting + /// "now". + /// + /// + /// the class of object for which to find the most recent + /// synchronization event (if any). + /// A token if synchronization events exist; otherwise {@code null}. + SyncToken GetLatestSyncToken(ObjectClass objectClass); + } + + #region ISyncEventSubscriptionApiOp + /// + /// since 1.5 + public interface ISyncEventSubscriptionApiOp : APIOperation + { + /// + /// Create a subscription to a given sync topic. + /// + /// + /// + /// + /// + /// @return + /// + /// when the operation failed to create subscription. + ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions); + } + #endregion + + /// + /// Updates a . + /// + /// + /// This operation + /// is supported for those connectors that implement + /// either or the more advanced + /// . + /// + public interface UpdateApiOp : APIOperation + { + /// + /// Update the object specified by the and , + /// replacing the current values of each attribute with the values + /// provided. + /// + /// + /// + /// For each input attribute, replace + /// all of the current values of that attribute in the target object with + /// the values of that attribute. + /// + /// + /// If the target object does not currently contain an attribute that the + /// input set contains, then add this + /// attribute (along with the provided values) to the target object. + /// + /// + /// If the value of an attribute in the input set is + /// null, then do one of the following, depending on + /// which is most appropriate for the target: + /// + /// + /// If possible, remove that attribute from the target + /// object entirely. + /// + /// + /// + /// Otherwise, replace all of the current values of that + /// attribute in the target object with a single value of + /// null. + /// + /// + /// + /// + /// + /// the type of object to modify. Must not be null. + /// the uid of the object to modify. Must not be null. + /// set of new . the values in this set + /// represent the new, merged values to be applied to the object. + /// This set may also include . + /// Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. + Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options); + + /// + /// Update the object specified by the and , + /// adding to the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, add to + /// the current values of that attribute in the target object all of the + /// values of that attribute in the input set. + /// + /// + /// NOTE that this does not specify how to handle duplicate values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute may contain duplicates. + /// Therefore, in general simply append the provided values + /// to the current value for each attribute. + /// + /// + /// IMPLEMENTATION NOTE: for connectors that merely implement + /// and not this method will be simulated by + /// fetching, merging, and calling + /// . Therefore, + /// connector implementations are encourage to implement + /// from a performance and atomicity standpoint. + /// + /// + /// the type of object to modify. Must not be null. + /// the uid of the object to modify. Must not be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to add to attributes in the object. + /// merged. This set must not include . + /// Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. + Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options); + + /// + /// Update the object specified by the and , + /// removing from the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, + /// remove from the current values of that attribute in the target object + /// any value that matches one of the values of the attribute from the input set. + /// + /// + /// NOTE that this does not specify how to handle unmatched values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute are merely representational state. + /// Therefore, the implementer should simply ignore any provided value + /// that does not match a current value of that attribute in the target + /// object. Deleting an unmatched value should always succeed. + /// + /// + /// IMPLEMENTATION NOTE: for connectors that merely implement + /// and not this method will be simulated by + /// fetching, merging, and calling + /// . Therefore, + /// connector implementations are encourage to implement + /// from a performance and atomicity standpoint. + /// + /// + /// the type of object to modify. Must not be null. + /// the uid of the object to modify. Must not be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to remove from attributes in the object. + /// merged. This set must not include . + /// Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. + Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options); + + } + + /// + /// Validates the . + /// + /// A valid configuration is one that is ready to be used by the connector: + /// it is complete (all the required properties have been given values) + /// and the property values are well-formed (are in the expected range, + /// have the expected format, etc.) + /// + public interface ValidateApiOp : APIOperation + { + /// + /// Validates the . + /// + /// iff the configuration is not valid. + void Validate(); + } + + /// + /// Tests the with the connector. + /// + /// Unlike validation performed by , testing a configuration should + /// check that any pieces of environment referred by the configuration are available. + /// For example the connector could make a physical connection to a host specified + /// in the configuration to check that it exists and that the credentials + /// specified in the configuration are usable. + /// + /// + /// Since this operation may connect to the resource, it may be slow. Clients are + /// advised not to invoke this operation often, such as before every provisioning operation. + /// This operation is not intended to check that the connector is alive + /// (i.e., its physical connection to the resource has not timed out). + /// + /// + /// This operation may be invoked before the configuration has been validated. + /// + /// + public interface TestApiOp : APIOperation + { + /// + /// Tests the current with the connector. + /// + /// iff the configuration is not valid or the test failed. + void Test(); + } +} \ No newline at end of file diff --git a/dotnet/framework/Framework/Common.cs b/dotnet/framework/Framework/Common.cs new file mode 100644 index 00000000..fc37a0a6 --- /dev/null +++ b/dotnet/framework/Framework/Common.cs @@ -0,0 +1,992 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ +using System; +using System.Collections; +using System.Reflection; +using System.Text; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Common +{ + + #region ConnectorKeyRange + + /// + /// A ConnectorKeyRange identifies a range of ConnectorKeys. + /// + /// The uniquely identifies one connector with exact + /// meanwhile this class has a + /// which can match multiple. + /// + /// + /// since 1.5 + public sealed class ConnectorKeyRange + { + private readonly String _bundleName; + private readonly VersionRange _bundleVersionRange; + private readonly String _exactVersion; + private readonly String _connectorName; + + public string BundleName + { + get { return _bundleName; } + } + + public VersionRange BundleVersionRange + { + get { return _bundleVersionRange; } + } + + public string ConnectorName + { + get { return _connectorName; } + } + + public bool IsInRange(ConnectorKey connectorKey) + { + return !_bundleVersionRange.Empty && _bundleName.Equals(connectorKey.BundleName) && + _connectorName.Equals(connectorKey.ConnectorName) && + _bundleVersionRange.IsInRange(Version.Parse(connectorKey.BundleVersion)); + } + + public ConnectorKey ExactConnectorKey + { + get + { + if (_bundleVersionRange.Exact) + { + return new ConnectorKey(_bundleName, _exactVersion, _connectorName); + } + throw new ArgumentException("BundleVersion is not exact version"); + } + } + + private ConnectorKeyRange(String bundleName, String bundleVersion, String connectorName) + { + _bundleName = bundleName; + _exactVersion = bundleVersion; + _bundleVersionRange = VersionRange.Parse(_exactVersion); + _connectorName = connectorName; + } + + public override bool Equals(object o) + { + if (this == o) + { + return true; + } + if (!(o is ConnectorKeyRange)) + { + return false; + } + + ConnectorKeyRange that = (ConnectorKeyRange) o; + + return _bundleName.Equals(that._bundleName) && _bundleVersionRange.Equals(that._bundleVersionRange) && + _connectorName.Equals(that._connectorName); + } + + public override int GetHashCode() + { + int result = _bundleName.GetHashCode(); + result = 31*result + _bundleVersionRange.GetHashCode(); + result = 31*result + _connectorName.GetHashCode(); + return result; + } + + public static Builder NewBuilder() + { + return new Builder(); + } + + public class Builder + { + private string _bundleName; + private string _bundleVersion; + private string _connectorName; + + public virtual Builder SetBundleName(string bundleName) + { + _bundleName = Assertions.BlankChecked(bundleName, "bundleName"); + return this; + } + + public virtual Builder SetBundleVersion(string bundleVersion) + { + _bundleVersion = Assertions.BlankChecked(bundleVersion, "bundleVersion"); + return this; + } + + public virtual Builder SetConnectorName(string connectorName) + { + _connectorName = Assertions.BlankChecked(connectorName, "connectorName"); + return this; + } + + public virtual ConnectorKeyRange Build() + { + return new ConnectorKeyRange(_bundleName, _bundleVersion, _connectorName); + } + } + } + + #endregion + + #region FrameworkInternalBridge + internal static class FrameworkInternalBridge + { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + /// + /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static SafeType LoadType(String typeName) where T : class + { + + Assembly assembly; + lock (LOCK) + { + if (_assembly == null) + { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return SafeType.ForRawType(assembly.GetType(typeName, true)); + + } + } + #endregion + + #region FrameworkUtil + public static class FrameworkUtil + { + private static readonly IDictionary, SafeType> SPI_TO_API; + private static readonly ICollection CONFIG_SUPPORTED_TYPES; + private static readonly ICollection ATTR_SUPPORTED_TYPES; + + static FrameworkUtil() + { + IDictionary, SafeType> temp = + new Dictionary, SafeType>(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.ForRawType(typeof(SearchOp<>))] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); + + CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet + ( + typeof(string), + typeof(long), + typeof(long?), + typeof(char), + typeof(char?), + typeof(double), + typeof(double?), + typeof(float), + typeof(float?), + typeof(int), + typeof(int?), + typeof(bool), + typeof(bool?), + typeof(Uri), + typeof(FileName), + typeof(GuardedByteArray), + typeof(GuardedString), + typeof(Script) + ); + ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet + ( + typeof(string), + typeof(long), + typeof(long?), + typeof(char), + typeof(char?), + typeof(double), + typeof(double?), + typeof(float), + typeof(float?), + typeof(int), + typeof(int?), + typeof(bool), + typeof(bool?), + typeof(byte), + typeof(byte?), + typeof(byte[]), + typeof(BigDecimal), + typeof(BigInteger), + typeof(GuardedByteArray), + typeof(GuardedString), + typeof(IDictionary) + ); + + } + + + /// + /// Determines if the class is a supported attribute type. + /// + /// + /// If not it throws + /// an . + /// + /// + /// string + /// + /// + /// + /// long + /// + /// + /// + /// long? + /// + /// + /// + /// char + /// + /// + /// + /// char? + /// + /// + /// + /// double + /// + /// + /// + /// double? + /// + /// + /// + /// float + /// + /// + /// + /// float? + /// + /// + /// + /// int + /// + /// + /// + /// int? + /// + /// + /// + /// bool + /// + /// + /// + /// bool? + /// + /// + /// + /// byte + /// + /// + /// + /// byte? + /// + /// + /// + /// byte[] + /// + /// + /// + /// BigDecimal + /// + /// + /// + /// BigInteger + /// + /// + /// + /// IDictionary + /// + /// + /// + /// + /// type to check against the support list of types. + /// iff the type is not on the supported list. + public static void CheckAttributeType(Type type) + { + if (!FrameworkUtil.IsSupportedAttributeType(type)) + { + String MSG = "Attribute type ''" + type + "'' is not supported."; + throw new ArgumentException(MSG); + } + } + /// + /// Determines if the class of the object is a supported attribute type. If + /// not it throws an . + /// + /// + /// The value to check or null. + /// + /// If the class of the object is a supported attribute type. + public static void CheckAttributeValue(Object value) + { + if (value != null) + { + CheckAttributeValue((String)null, value); + } + } + /// + /// Determines if the class of the object is a supported attribute type. If + /// not it throws an . + /// + /// + /// The name of the attribute to check + /// + /// The value to check or null. + /// + /// If the class of the object is a supported attribute type. + public static void CheckAttributeValue(String name, Object value) + { + + if (value != null) + { + if (value is IDictionary) + { + CheckAttributeValue(new StringBuilder(name == null ? "?" : name), value); + } + else + { + if (name == null) + { + CheckAttributeType(value.GetType()); + } + else if (!IsSupportedAttributeType(value.GetType())) + { + throw new ArgumentException("Attribute ''" + name + "'' type ''" + value.GetType() + "'' is not supported."); + } + } + } + } + private static void CheckAttributeValue(StringBuilder name, Object value) + { + if (value != null) + { + IDictionary dictionary = value as IDictionary; + if (dictionary != null) + { + foreach (DictionaryEntry entry in dictionary) + { + object key = entry.Key; + Object entryValue = entry.Value; + if (key is string) + { + StringBuilder nameBuilder = (new StringBuilder(name.ToString())).Append('/').Append(key); + if (entryValue is IList) + { + nameBuilder.Append("[*]"); + foreach (Object item in ((IList)entryValue)) + { + CheckAttributeValue(nameBuilder, item); + } + } + else + { + CheckAttributeValue(nameBuilder, entryValue); + } + } + else + { + throw new ArgumentException( + "Map Attribute ''" + name + "'' must have String key, type ''" + key.GetType() + "'' is not supported."); + } + } + } + else + { + if (!IsSupportedAttributeType(value.GetType())) + { + throw new ArgumentException("Attribute ''" + name + "'' type ''" + value.GetType() + "'' is not supported."); + } + } + } + } + public static ICollection> Spi2Apis(SafeType type) + { + type = type.GetTypeErasure(); + HashSet> set = new HashSet>(); + set.Add(SPI_TO_API[type]); + // add GetApiOp if search is available.. + + if (type.RawType.Equals(typeof(SearchOp<>))) + { + set.Add(SafeType.Get()); + } + return set; + } + public static ICollection> AllSPIOperations() + { + return SPI_TO_API.Keys; + } + public static ICollection> AllAPIOperations() + { + ICollection> set = + new HashSet>(); + CollectionUtil.AddAll(set, + SPI_TO_API.Values); + // add Get because it doesn't have a corresponding SPI. + set.Add(SafeType.Get()); + set.Add(SafeType.Get()); + return CollectionUtil.AsReadOnlySet(set); + } + public static ICollection> GetDefaultSupportedOperations(SafeType connector) + { + ICollection> rv = + new HashSet>(); + ICollection interfaces = + ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector.RawType)); + foreach (SafeType spi in AllSPIOperations()) + { + if (interfaces.Contains(spi.RawType)) + { + CollectionUtil.AddAll(rv, Spi2Apis(spi)); + } + } + //finally add unconditionally supported ops + CollectionUtil.AddAll(rv, GetUnconditionallySupportedOperations()); + return CollectionUtil.AsReadOnlySet(rv); + } + public static ICollection> GetUnconditionallySupportedOperations() + { + HashSet> ret; + ret = new HashSet>(); + //add validate api op always + ret.Add(SafeType.Get()); + //add ScriptOnConnectorApiOp always + ret.Add(SafeType.Get()); + return ret; + } + public static ICollection GetAllSupportedConfigTypes() + { + return CONFIG_SUPPORTED_TYPES; + } + public static bool IsSupportedConfigurationType(Type type) + { + if (type.IsArray) + { + return IsSupportedConfigurationType(type.GetElementType()); + } + else + { + return CONFIG_SUPPORTED_TYPES.Contains(type); + } + } + public static ICollection GetAllSupportedAttributeTypes() + { + return ATTR_SUPPORTED_TYPES; + } + public static bool IsSupportedAttributeType(Type clazz) + { + return ATTR_SUPPORTED_TYPES.Contains(clazz) || null != ReflectionUtil.FindInHierarchyOf + (typeof(IDictionary<,>), clazz); + } + + /// + /// Determines if the class is a supported type for an OperationOption. + /// + /// + /// If not it throws + /// an . + /// + /// type to check against the support list of types. + /// iff the type is not on the supported list. + public static void CheckOperationOptionType(Type clazz) + { + //the set of supported operation option types + //is the same as that for configuration beans plus Name, + //ObjectClass, Uid, and QualifiedUid + + if (clazz.IsArray) + { + CheckOperationOptionType(clazz.GetElementType()); + return; + } + + if (FrameworkUtil.IsSupportedConfigurationType(clazz)) + { + return; //ok + } + + if (typeof(ObjectClass).IsAssignableFrom(clazz)) + { + return; //ok + } + + if (typeof(Uid).IsAssignableFrom(clazz)) + { + return; //ok + } + + if (typeof(QualifiedUid).IsAssignableFrom(clazz)) + { + return; //ok + } + + if (typeof(SortKey).IsAssignableFrom(clazz)) + { + return; //ok + } + + String MSG = "ConfigurationOption type '+" + clazz.Name + "+' is not supported."; + throw new ArgumentException(MSG); + } + /// + /// Determines if the class of the object is a supported attribute type. + /// + /// + /// If not it throws an . + /// + /// The value to check or null. + public static void CheckOperationOptionValue(Object val) + { + if (val != null) + { + CheckOperationOptionType(val.GetType()); + } + } + + /// + /// Check if + /// was invoked from + /// or + /// . + /// + /// + /// the query parameter of + /// + /// {@code null} if invoked from + /// + /// or value if invoked + /// + /// + /// since 1.5 + public static Uid GetUidIfGetOperation(Filter filter) + { + Uid uidToGet = null; + var equalsFilter = filter as EqualsFilter; + if (equalsFilter != null) + { + if (equalsFilter.GetAttribute() is Uid) + { + uidToGet = (Uid)equalsFilter.GetAttribute(); + } + else if (equalsFilter.GetAttribute().@Is(Uid.NAME)) + { + uidToGet = new Uid(ConnectorAttributeUtil.GetStringValue(equalsFilter.GetAttribute())); + } + } + return uidToGet; + } + + /// + /// Returns the version of the framework. + /// + /// the framework version; never null. + public static Version GetFrameworkVersion() + { + return Assembly.GetExecutingAssembly().GetName().Version; + } + } + #endregion + + #region VersionRange + /// + /// A version range is an interval describing a set of . + ///

+ /// A range has a left (lower) endpoint and a right (upper) endpoint. Each + /// endpoint can be open (excluded from the set) or closed (included in the set). + /// + ///

+ /// {@code VersionRange} objects are immutable. + /// + /// @author Laszlo Hordos + /// @Immutable + ///

+ public class VersionRange + { + + /// + /// The left endpoint is open and is excluded from the range. + ///

+ /// The value of {@code LEFT_OPEN} is {@code '('}. + ///

+ public const char LEFT_OPEN = '('; + /// + /// The left endpoint is closed and is included in the range. + ///

+ /// The value of {@code LEFT_CLOSED} is {@code '['}. + ///

+ public const char LEFT_CLOSED = '['; + /// + /// The right endpoint is open and is excluded from the range. + ///

+ /// The value of {@code RIGHT_OPEN} is {@code ')'}. + ///

+ public const char RIGHT_OPEN = ')'; + /// + /// The right endpoint is closed and is included in the range. + ///

+ /// The value of {@code RIGHT_CLOSED} is {@code ']'}. + ///

+ public const char RIGHT_CLOSED = ']'; + + private const string ENDPOINT_DELIMITER = ","; + + private readonly Version floorVersion; + private readonly bool isFloorInclusive; + private readonly Version ceilingVersion; + private readonly bool isCeilingInclusive; + private readonly bool empty; + + /// + /// Parse version component into a Version. + /// + /// + /// version component string + /// + /// Complete range string for exception message, if any + /// Version + private static Version parseVersion(string version, string range) + { + try + { + return Version.Parse(version); + } + catch (System.ArgumentException e) + { + throw new System.ArgumentException("invalid range \"" + range + "\": " + e.Message, e); + } + } + + /// + /// Creates a version range from the specified string. + /// + ///

+ /// Version range string grammar: + /// + ///

+        /// range ::= interval | at least
+        /// interval ::= ( '[' | '(' ) left ',' right ( ']' | ')' )
+        /// left ::= version
+        /// right ::= version
+        /// at least ::= version
+        /// 
+ ///
+ /// + /// String representation of the version range. The versions in + /// the range must contain no whitespace. Other whitespace in the + /// range string is ignored. + /// + /// If {@code range} is improperly formatted. + public static VersionRange Parse(string range) + { + Assertions.BlankCheck(range, "range"); + int idx = range.IndexOf(ENDPOINT_DELIMITER); + // Check if the version is an interval. + if (idx > 1 && idx == range.LastIndexOf(ENDPOINT_DELIMITER)) + { + string vlo = range.Substring(0, idx).Trim(); + string vhi = range.Substring(idx + 1).Trim(); + + bool isLowInclusive = true; + bool isHighInclusive = true; + if (vlo[0] == LEFT_OPEN) + { + isLowInclusive = false; + } + else if (vlo[0] != LEFT_CLOSED) + { + throw new System.ArgumentException("invalid range \"" + range + "\": invalid format"); + } + vlo = vlo.Substring(1).Trim(); + + if (vhi[vhi.Length - 1] == RIGHT_OPEN) + { + isHighInclusive = false; + } + else if (vhi[vhi.Length - 1] != RIGHT_CLOSED) + { + throw new System.ArgumentException("invalid range \"" + range + "\": invalid format"); + } + vhi = vhi.Substring(0, vhi.Length - 1).Trim(); + + return new VersionRange(parseVersion(vlo, range), isLowInclusive, parseVersion(vhi, range), isHighInclusive); + } + else if (idx == -1) + { + return new VersionRange(VersionRange.parseVersion(range.Trim(), range), true, null, false); + } + else + { + throw new System.ArgumentException("invalid range \"" + range + "\": invalid format"); + } + } + + public VersionRange(Version low, bool isLowInclusive, Version high, bool isHighInclusive) + { + Assertions.NullCheck(low, "floorVersion"); + floorVersion = low; + isFloorInclusive = isLowInclusive; + ceilingVersion = high; + isCeilingInclusive = isHighInclusive; + empty = Empty0; + } + + public Version Floor + { + get + { + return floorVersion; + } + } + + public bool FloorInclusive + { + get + { + return isFloorInclusive; + } + } + + public Version Ceiling + { + get + { + return ceilingVersion; + } + } + + public bool CeilingInclusive + { + get + { + return isCeilingInclusive; + } + } + + public bool IsInRange(Version version) + { + if (empty) + { + return false; + } + int c = floorVersion.CompareTo(version); + if (c == 0 && isFloorInclusive) + { + return true; + } + if (c < 0 && ceilingVersion != null) + { + return ceilingVersion.CompareTo(version) >= (isCeilingInclusive ? 0 : 1); + } + return false; + } + + /// + /// Returns whether this version range contains only a single version. + /// + /// {@code true} if this version range contains only a single + /// version; {@code false} otherwise. + public bool Exact + { + get + { + if (empty) + { + return false; + } + else if (ceilingVersion == null) + { + return true; + } + if (isFloorInclusive) + { + if (isCeilingInclusive) + { + // [f,c]: exact if f == c + return floorVersion.Equals(ceilingVersion); + } + else + { + // [f,c): exact if f++ >= c + Version adjacent1 = new Version(floorVersion.Major, floorVersion.Minor, floorVersion.Build, floorVersion.Revision + 1); + return adjacent1.CompareTo(ceilingVersion) >= 0; + } + } + else + { + if (isCeilingInclusive) + { + // (f,c] is equivalent to [f++,c]: exact if f++ == c + Version adjacent1 = new Version(floorVersion.Major, floorVersion.Minor, floorVersion.Build, floorVersion.Revision + 1); + return adjacent1.Equals(ceilingVersion); + } + else + { + // (f,c) is equivalent to [f++,c): exact if (f++)++ >=c + Version adjacent2 = new Version(floorVersion.Major, floorVersion.Minor, floorVersion.Build, floorVersion.Revision + 2); + return adjacent2.CompareTo(ceilingVersion) >= 0; + } + } + } + } + + /// + /// Returns whether this version range is empty. A version range is empty if + /// the set of versions defined by the interval is empty. + /// + /// {@code true} if this version range is empty; {@code false} + /// otherwise. + public bool Empty + { + get + { + return empty; + } + } + + /// + /// Internal isEmpty behavior. + /// + /// {@code true} if this version range is empty; {@code false} + /// otherwise. + private bool Empty0 + { + get + { + if (ceilingVersion == null) // infinity + { + return false; + } + int comparison = floorVersion.CompareTo(ceilingVersion); + if (comparison == 0) // endpoints equal + { + return !isFloorInclusive || !isCeilingInclusive; + } + return comparison > 0; // true if left > right + } + } + + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (this.GetType() != obj.GetType()) + { + return false; + } + VersionRange other = (VersionRange)obj; + if (floorVersion != other.floorVersion && (floorVersion == null || !floorVersion.Equals(other.floorVersion))) + { + return false; + } + if (isFloorInclusive != other.isFloorInclusive) + { + return false; + } + if (ceilingVersion != other.ceilingVersion && (ceilingVersion == null || !ceilingVersion.Equals(other.ceilingVersion))) + { + return false; + } + if (isCeilingInclusive != other.isCeilingInclusive) + { + return false; + } + return true; + } + + public override int GetHashCode() + { + int result = floorVersion.GetHashCode(); + result = 31 * result + (isFloorInclusive ? 1 : 0); + result = 31 * result + (ceilingVersion != null ? ceilingVersion.GetHashCode() : 0); + result = 31 * result + (isCeilingInclusive ? 1 : 0); + return result; + } + + public override string ToString() + { + if (ceilingVersion != null) + { + StringBuilder sb = new StringBuilder(); + sb.Append(isFloorInclusive ? LEFT_CLOSED : LEFT_OPEN); + sb.Append(floorVersion.ToString()).Append(ENDPOINT_DELIMITER).Append(ceilingVersion.ToString()); + sb.Append(isCeilingInclusive ? RIGHT_CLOSED : RIGHT_OPEN); + return sb.ToString(); + } + else + { + return floorVersion.ToString(); + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/Framework/CommonExceptions.cs b/dotnet/framework/Framework/CommonExceptions.cs new file mode 100644 index 00000000..f7432952 --- /dev/null +++ b/dotnet/framework/Framework/CommonExceptions.cs @@ -0,0 +1,841 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS. + */ +using System; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Common; + +namespace Org.IdentityConnectors.Framework.Common.Exceptions +{ + #region AlreadyExistsException + /// + /// AlreadyExistsException is thrown to indicate if + /// attempts + /// to create an object that exists prior to the method execution or + /// attempts + /// to rename an object to that exists prior to the method execution. + /// + public class AlreadyExistsException : ConnectorException + { + [NonSerialized] + private Uid _uid; + + /// + public AlreadyExistsException() + : base() + { + } + + /// + public AlreadyExistsException(String message) + : base(message) + { + + } + + /// + public AlreadyExistsException(Exception ex) + : base(ex) + { + } + + /// + public AlreadyExistsException(String message, Exception ex) + : base(message, ex) + { + } + + public Uid Uid + { + get + { + return _uid; + } + } + + /// + /// Sets the Uid of existing Object. + /// + /// Connectors who throw this exception from their + /// or + /// should + /// set the object's Uid if available. + /// + /// + /// The uid. + /// A reference to this. + public AlreadyExistsException InitUid(Uid uid) + { + this._uid = uid; + return this; + } + } + #endregion + + #region ConfigurationException + public class ConfigurationException : ConnectorException + { + public ConfigurationException() + : base() + { + } + + public ConfigurationException(String message) + : base(message) + { + } + + public ConfigurationException(Exception ex) + : base(ex) + { + } + + public ConfigurationException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region ConnectionBrokenException + /// + /// ConnectionBrokenException is thrown when a connection to a target resource + /// instance fails during an operation. + /// + /// An instance of ConnectionBrokenException generally wraps the + /// native exception (or describes the native error) returned by the target + /// resource. + /// + public class ConnectionBrokenException : ConnectorIOException + { + + /// + public ConnectionBrokenException() + : base() + { + } + + /// + public ConnectionBrokenException(String msg) + : base(msg) + { + } + + /// + public ConnectionBrokenException(Exception ex) + : base(ex) + { + } + + /// + public ConnectionBrokenException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region ConnectionFailedException + /// + /// ConnectionFailedException is thrown when a Connector cannot reach the target + /// resource instance. + /// + /// An instance of ConnectionFailedException generally wraps the + /// native exception (or describes the native error) returned by the target + /// resource. + /// + public class ConnectionFailedException : ConnectorIOException + { + + /// + public ConnectionFailedException() + : base() + { + } + + /// + public ConnectionFailedException(String msg) + : base(msg) + { + } + + /// + public ConnectionFailedException(Exception ex) + : base(ex) + { + } + + /// + public ConnectionFailedException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region ConnectorException + public class ConnectorException : ApplicationException + { + public ConnectorException() + : base() + { + } + + /// + /// Sets a message for the . + /// + /// passed to the message. + public ConnectorException(String message) + : base(message) + { + } + + /// + /// Sets the stack trace to the original exception, so this exception can + /// masquerade as the original only be a . + /// + /// the original exception adapted to . + public ConnectorException(Exception ex) + : base(ex.Message, ex) + { + } + + /// + /// Sets the stack trace to the original exception, so this exception can + /// masquerade as the original only be a . + /// + /// + /// the original exception adapted to . + public ConnectorException(String message, Exception originalException) + : base(message, originalException) + { + } + + } + #endregion + + #region ConnectorIOException + public class ConnectorIOException : ConnectorException + { + public ConnectorIOException() + : base() + { + } + + public ConnectorIOException(String msg) + : base(msg) + { + } + + public ConnectorIOException(Exception ex) + : base(ex) + { + } + + public ConnectorIOException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region ConnectorSecurityException + public class ConnectorSecurityException : ConnectorException + { + public ConnectorSecurityException() + : base() + { + } + + public ConnectorSecurityException(String message) + : base(message) + { + } + + public ConnectorSecurityException(Exception ex) + : base(ex) + { + } + + public ConnectorSecurityException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region InvalidAttributeValueException + /// + /// InvalidAttributeValueException is thrown when an attempt is made to add to an + /// attribute a value that conflicts with the attribute's schema definition. + /// + /// This could happen, for example, if attempting to add an attribute with no + /// value when the attribute is required to have at least one value, or if + /// attempting to add more than one value to a single valued-attribute, or if + /// attempting to add a value that conflicts with the type of the attribute or if + /// attempting to add a value that conflicts with the syntax of the attribute. + /// + /// Since 1.4 + public class InvalidAttributeValueException : ConnectorException + { + /// + /// Constructs a new InvalidAttributeValueException exception with + /// null as its detail message. The cause is not initialized, + /// and may subsequently be initialized by a call to . + /// + public InvalidAttributeValueException() + : base() + { + } + + /// + /// Constructs a new InvalidAttributeValueException exception with the specified + /// detail message. The cause is not initialized, and may subsequently be + /// initialized by a call to . + /// + /// + /// the detail message. The detail message is is a String that + /// describes this particular exception and saved for later + /// retrieval by the method. + public InvalidAttributeValueException(string message) + : base(message) + { + } + + /// + /// Constructs a new InvalidAttributeValueException exception with the specified + /// cause and a detail message of + /// (cause==null ? null : cause.toString()) (which typically + /// contains the class and detail message of cause). This + /// constructor is useful for InvalidAccountException exceptions that are + /// little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public InvalidAttributeValueException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new InvalidAttributeValueException exception with the specified + /// detail message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public InvalidAttributeValueException(string message, Exception cause) + : base(message, cause) + { + } + } + #endregion + + #region InvalidCredentialException + /// + /// InvalidCredentialException signals that user authentication failed. + ///

+ /// This exception is thrown by Connector if authentication failed. For example, + /// a Connector throws this exception if the user entered an + /// incorrect password. + ///

+ public class InvalidCredentialException : ConnectorSecurityException + { + + /// + public InvalidCredentialException() + : base() + { + } + + /// + public InvalidCredentialException(String message) + : base(message) + { + } + + /// + public InvalidCredentialException(Exception ex) + : base(ex) + { + } + + /// + public InvalidCredentialException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region InvalidPasswordException + public class InvalidPasswordException : InvalidCredentialException + { + public InvalidPasswordException() + : base() + { + } + + public InvalidPasswordException(String message) + : base(message) + { + } + + public InvalidPasswordException(Exception ex) + : base(ex) + { + } + + public InvalidPasswordException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region OperationTimeoutException + public class OperationTimeoutException : ConnectorException + { + public OperationTimeoutException() + : base() + { + } + + public OperationTimeoutException(String msg) + : base(msg) + { + } + + public OperationTimeoutException(Exception e) + : base(e) + { + } + + public OperationTimeoutException(String msg, Exception e) + : base(msg, e) + { + } + + } + #endregion + + #region PasswordExpiredException + /// + /// PasswordExpiredException signals that a user password has expired. + /// + /// This exception is thrown by Connector when they determine that a password has + /// expired. For example, a Connector, after successfully + /// authenticating a user, may determine that the user's password has expired. In + /// this case the Connector throws this exception to notify the + /// application. The application can then take the appropriate steps to notify + /// the user. + /// + /// + /// + public class PasswordExpiredException : InvalidPasswordException + { + [NonSerialized] + private Uid _uid; + + /// + public PasswordExpiredException() + : base() + { + } + + /// + public PasswordExpiredException(String message) + : base(message) + { + } + + /// + public PasswordExpiredException(Exception ex) + : base(ex) + { + } + + /// + public PasswordExpiredException(String message, Exception ex) + : base(message, ex) + { + } + + public Uid Uid + { + get + { + return _uid; + } + /// + /// Sets the Uid. Connectors who throw this exception from their + /// should set the account Uid if available. + /// + /// + /// The uid. + /// A reference to this. + set + { + _uid = value; + } + } + } + #endregion + + #region PermissionDeniedException + public class PermissionDeniedException : ConnectorSecurityException + { + public PermissionDeniedException() + : base() + { + } + + public PermissionDeniedException(String message) + : base(message) + { + } + + public PermissionDeniedException(Exception ex) + : base(ex) + { + } + + public PermissionDeniedException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion + + #region PreconditionFailedException + + + /// + /// PreconditionFailedException is thrown to indicate that a resource's current + /// version does not match the version provided. + /// + /// Equivalent to HTTP status: 412 Precondition Failed. + /// + /// Since 1.4 + public class PreconditionFailedException : ConnectorException + { + /// + /// Constructs a new PreconditionFailedException exception with + /// null as its detail message. The cause is not initialized, + /// and may subsequently be initialized by a call to . + /// + public PreconditionFailedException() + : base() + { + } + + /// + /// Constructs a new PreconditionFailedException exception with the specified + /// detail message. The cause is not initialized, and may subsequently be + /// initialized by a call to . + /// + /// + /// the detail message. The detail message is is a String that + /// describes this particular exception and saved for later + /// retrieval by the method. + public PreconditionFailedException(string message) + : base(message) + { + } + + /// + /// Constructs a new PreconditionFailedException exception with the specified + /// cause and a detail message of + /// (cause==null ? null : cause.toString()) (which typically + /// contains the class and detail message of cause). This + /// constructor is useful for InvalidAccountException exceptions that are + /// little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionFailedException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new PreconditionFailedException exception with the specified + /// detail message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionFailedException(string message, Exception cause) + : base(message, cause) + { + } + } + #endregion + + #region PreconditionRequiredException + /// + /// PreconditionRequiredException is thrown to indicate that a resource requires + /// a version, but no version was supplied in the request. + /// + /// Equivalent to draft-nottingham-http-new-status-03 HTTP status: 428 + /// Precondition Required. + /// + /// Since 1.4 + public class PreconditionRequiredException : ConnectorException + { + /// + /// Constructs a new PreconditionRequiredException exception with + /// null as its detail message. The cause is not initialized, + /// and may subsequently be initialized by a call to . + /// + public PreconditionRequiredException() + : base() + { + } + + /// + /// Constructs a new PreconditionRequiredException exception with the + /// specified detail message. The cause is not initialized, and may + /// subsequently be initialized by a call to . + /// + /// + /// the detail message. The detail message is is a String that + /// describes this particular exception and saved for later + /// retrieval by the method. + public PreconditionRequiredException(string message) + : base(message) + { + } + + /// + /// Constructs a new PreconditionRequiredException exception with the + /// specified cause and a detail message of + /// (cause==null ? null : cause.toString()) (which typically + /// contains the class and detail message of cause). This + /// constructor is useful for InvalidAccountException exceptions that are + /// little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionRequiredException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new PreconditionRequiredException exception with the + /// specified detail message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionRequiredException(string message, Exception cause) + : base(message, cause) + { + } + } + #endregion + + #region RetryableException + /// + /// RetryableException indicates that a failure may be temporary, and that + /// retrying the same request may be able to succeed in the future. + /// + /// Since 1.4 + public class RetryableException : ConnectorException + { + /// + /// Constructs a new RetryableException exception with the specified cause + /// and a detail message of (cause==null ? null : cause.toString()) + /// (which typically contains the class and detail message of cause + /// ). This constructor is useful for InvalidAccountException exceptions that + /// are little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + private RetryableException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new RetryableException exception with the specified detail + /// message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + private RetryableException(string message, Exception cause) + : base(message, cause) + { + } + + /// + /// If parameter passed in is a + /// it is simply returned. Otherwise the is wrapped in a + /// RetryableException and returned. + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// Exception to wrap or cast and return. + /// a RuntimeException that either is the + /// specified exception or contains the specified exception. + public static RetryableException Wrap(string message, Exception cause) + { + // don't bother to wrap a exception that is already a + // RetryableException. + if (cause is RetryableException) + { + return (RetryableException)cause; + } + + if (null != message) + { + return new RetryableException(message, cause); + } + else + { + return new RetryableException(cause); + } + } + + /// + /// Constructs a new RetryableException which signals partial success of + /// create operation. + /// + /// This should be called inside + /// + /// implementation to signal that the create was not completed but the object + /// was created with Uid and Application should call the + /// + /// method now. + ///

+ /// Use this only if the created object can not be deleted. The best-practice + /// should always be the Connector implementation reverts the changes if the + /// operation failed. + ///

+ /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// + /// the new object's Uid. + /// a RetryableException that either is the + /// specified exception or contains the specified exception. + public static RetryableException Wrap(string message, Uid uid) + { + return new RetryableException(message, new AlreadyExistsException().InitUid(Assertions.NullChecked(uid, "Uid"))); + } + } + #endregion + + #region UnknownUidException + public class UnknownUidException : InvalidCredentialException + { + const string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; + + public UnknownUidException() + : base() + { + } + + public UnknownUidException(Uid uid, ObjectClass objclass) : + base(String.Format(MSG, uid, objclass)) + { + } + + public UnknownUidException(String message) + : base(message) + { + } + + public UnknownUidException(Exception ex) + : base(ex) + { + } + + public UnknownUidException(String message, Exception ex) + : base(message, ex) + { + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/Framework/CommonObjects.cs b/dotnet/framework/Framework/CommonObjects.cs new file mode 100755 index 00000000..30c3787d --- /dev/null +++ b/dotnet/framework/Framework/CommonObjects.cs @@ -0,0 +1,6256 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ +using System; +using System.Linq; +using System.Security; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using System.Threading; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; + +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Serializer; + +namespace Org.IdentityConnectors.Framework.Common.Objects +{ + #region NameUtil + internal static class NameUtil + { + public static bool IsSpecialName(String name) + { + return (name.StartsWith("__") && name.EndsWith("__")); + } + + public static string CreateSpecialName(string name) + { + if (StringUtil.IsBlank(name)) + { + const string ERR = "Name parameter must not be blank!"; + throw new ArgumentException(ERR); + } + return "__" + name + "__"; + } + + public static bool NamesEqual(string name1, string name2) + { + return name1.ToUpper(CultureInfoCache.Instance).Equals( + name2.ToUpper(CultureInfoCache.Instance)); + } + + public static int GetNameHashCode(string name) + { + return name.ToUpper(CultureInfoCache.Instance).GetHashCode(); + } + } + #endregion + + #region ConnectorAttributeUtil + public static class ConnectorAttributeUtil + { + /// + /// Gets the string value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the string value from. + /// null if the value is null otherwise the string value for the + /// attribute. + /// if the object in the attribute is not an string. + /// if the attribute is a multi valued instead of single valued. + public static string GetStringValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (string)obj : null; + } + + /// + /// Gets the character value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the character value from. + /// null if the value is null otherwise the character value for the + /// attribute. + /// if the object in the attribute is not an string. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static char? GetCharacterValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (char?)obj : null; + } + + /// + /// Gets the string value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the string value from. + /// null if the value is null otherwise the string value for the + /// attribute. + /// if the attribute is a multi valued instead of single valued. + public static string GetAsStringValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? obj.ToString() : null; + } + + /// + /// Gets the guarded string value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the guarded string value from. + /// null if the value is null otherwise the guarded string value for the + /// attribute. + /// if the object in the attribute is not a guarded string. + /// if the attribute is a multi valued instead of single valued. + public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (GuardedString)obj : null; + } + + /// + /// Gets the guarded byte array value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the guarded byte array value from. + /// null if the value is null otherwise the guarded byte array value for the + /// attribute. + /// if the object in the attribute is not a guarded byte array. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static GuardedByteArray GetGuardedByteArrayValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (GuardedByteArray)obj : null; + } + + /// + /// Gets the integer value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the integer value from. + /// null if the value is null otherwise the integer value for the + /// attribute. + /// if the object in the attribute is not an integer. + /// if the attribute is a multi valued instead of single valued. + public static int? GetIntegerValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (int?)obj : null; + } + + /// + /// Gets the long value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the long value from. + /// null if the value is null otherwise the long value for the + /// attribute. + /// if the object in the attribute is not an long. + /// if the attribute is a multi valued instead of single valued. + public static long? GetLongValue(ConnectorAttribute attr) + { + Object obj = GetSingleValue(attr); + return obj != null ? (long?)obj : null; + } + + /// + /// Gets the date value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the date value from. + /// null if the value is null otherwise the date value for the + /// attribute. + /// if the object in the attribute is not an long. + /// if the attribute is a multi valued instead of single valued. + public static DateTime? GetDateTimeValue(ConnectorAttribute attr) + { + long? val = GetLongValue(attr); + if (val != null) + { + return DateTimeUtil.GetDateTimeFromUtcMillis(val.Value); + } + return null; + } + + /// + /// Gets the double value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the double value from. + /// null if the value is null otherwise the double value for the + /// attribute. + /// if the object in the attribute is not a double. + /// if the attribute is a multi valued instead of single valued. + public static double? GetDoubleValue(ConnectorAttribute attr) + { + Object obj = GetSingleValue(attr); + return obj != null ? (double?)obj : null; + } + + /// + /// Gets the float value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the float value from. + /// null if the value is null otherwise the float value for the + /// attribute. + /// if the object in the attribute is not a float. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static float? GetFloatValue(ConnectorAttribute attr) + { + Object obj = GetSingleValue(attr); + return obj != null ? (float?)obj : null; + } + + /// + /// Gets the byte value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the byte value from. + /// null if the value is null otherwise the byte value for the + /// attribute. + /// if the object in the attribute is not a byte. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static byte? GetByteValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (byte?)obj : null; + } + + /// + /// Gets the byte value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the byte value from. + /// null if the value is null otherwise the byte value for the + /// attribute. + /// if the object in the attribute is not a byte. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static byte[] GetByteArrayValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (byte[])obj : null; + } + + /// + /// Gets the boolean value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the boolean value from. + /// null if the value is null otherwise the boolean value for the + /// attribute. + /// if the object in the attribute is not a boolean. + /// if the attribute is a multi valued instead of single valued. + public static bool? GetBooleanValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (bool?)obj : null; + } + + /// + /// Gets the big decimal value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the big decimal value from. + /// null if the value is null otherwise the big decimal value for the + /// attribute. + /// if the object in the attribute is not a big decimal. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static BigDecimal GetBigDecimalValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (BigDecimal)obj : null; + } + + /// + /// Gets the big integer value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the big integer value from. + /// null if the value is null otherwise the big integer value for the + /// attribute. + /// if the object in the attribute is not a big integer. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static BigInteger GetBigIntegerValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (BigInteger)obj : null; + } + + /// + /// Gets the dictionary value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the dictionary value from. + /// null if the value is null otherwise the dictionary value for the + /// attribute. + /// if the object in the attribute is not a dictionary. + /// if the attribute is a multi valued instead of single valued. + /// Since 1.4 + public static IDictionary GetDictionaryValue(ConnectorAttribute attr) + { + object obj = GetSingleValue(attr); + return obj != null ? (IDictionary)obj : null; + } + + /// + /// Get the single value from the ConnectorAttribute. + /// + /// + /// Return + /// null if the attribute's list of values is null or empty. + /// + public static object GetSingleValue(ConnectorAttribute attr) + { + Object ret = null; + IList val = attr.Value; + if (val != null && val.Count > 0) + { + // make sure this only called for single value.. + if (val.Count > 1) + { + const string MSG = "The method is only for single value attributes."; + throw new ArgumentException(MSG); + } + ret = val[0]; + } + return ret; + } + + /// + /// Transform a Collection of ConnectorAttribute} instances into a {@link Map}. + /// The key to each element in the map is the name of an ConnectorAttribute. + /// The value of each element in the map is the ConnectorAttribute instance with that name. + /// + /// + /// + public static IDictionary ToMap( + ICollection attributes) + { + IDictionary ret = + new Dictionary( + StringComparer.OrdinalIgnoreCase); + foreach (ConnectorAttribute attr in attributes) + { + ret[attr.Name] = attr; + } + return ret; + } + + /// + /// Get the from the attribute set. + /// + /// set of s that may contain a . + /// null if the set does not contain a object the first + /// one found. + public static Uid GetUidAttribute(ICollection attrs) + { + return (Uid)Find(Uid.NAME, attrs); + } + + /// + /// Filters out all special attributes from the set. + /// + /// + /// These special attributes + /// include , etc.. + /// + /// set of s to filter out the operational and + /// default attributes. + /// a set that only contains plain attributes or empty. + public static ICollection GetBasicAttributes(ICollection attrs) + { + ICollection ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) + { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + if (!IsSpecial(attr)) + { + ret.Add(attr); + } + } + return ret; + } + + /// + /// Filter out any basic attributes from the specified set, leaving only + /// special attributes. + /// + /// + /// Special attributes include , , + /// and . + /// + /// set of s to filter out the basic attributes + /// a set that only contains special attributes or an empty set if + /// there are none. + public static ICollection GetSpecialAttributes(ICollection attrs) + { + ICollection ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) + { + if (IsSpecial(attr)) + { + ret.Add(attr); + } + } + return ret; + } + + /// + /// Returns a mutable copy of the original set with the uid attribute removed. + /// + /// The original set. Must not be null. + /// A mutable copy of the original set with the uid attribute removed. + public static ICollection FilterUid(ICollection attrs) + { + Assertions.NullCheck(attrs, "attrs"); + HashSet ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) + { + if (!(attr is Uid)) + { + ret.Add(attr); + } + } + return ret; + } + + /// + /// Returns a mutable copy of the original set with the uid attribute added. + /// + /// The original set. Must not be null. + /// The uid. Must not be null. + /// A mutable copy of the original set with the uid attribute added. + public static ICollection AddUid(ICollection attrs, Uid uid) + { + Assertions.NullCheck(attrs, "attrs"); + Assertions.NullCheck(uid, "uid"); + HashSet ret = new HashSet(attrs); + ret.Add(uid); + return ret; + } + + /// + /// Determines if this attribute is a special attribute. + /// + /// + /// to test for against. + /// true if the attribute value is a , + /// , , or + /// . + /// if the attribute parameter is null. + public static bool IsSpecial(ConnectorAttribute attr) + { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + String name = attr.Name; + return IsSpecialName(name); + } + + /// + /// Determines if this attribute is a special attribute. + /// + /// + /// to test for against. + /// true if the attribute value is a , + /// , , or + /// . + /// if the attribute parameter is null. + public static bool IsSpecial(ConnectorAttributeInfo attr) + { + String name = attr.Name; + return IsSpecialName(name); + } + + /// + /// Determines whether the specified attribute name is special in the + /// sense of . + /// + /// the name of the attribute to test + /// true if the attribute name is special + public static bool IsSpecialName(String name) + { + return NameUtil.IsSpecialName(name); + } + + /// + /// Creates the special naming for operational type attributes. + /// + /// string to make special + /// name constructed for use as an operational attribute. + public static string CreateSpecialName(string name) + { + return NameUtil.CreateSpecialName(name); + } + + /// + /// Compares two attribute names for equality. + /// + /// the first attribute name + /// the second attribute name + /// true if the two attribute names are equal + public static bool NamesEqual(string name1, string name2) + { + return NameUtil.NamesEqual(name2, name2); + } + + /// + /// Gets the 'Name' attribute from a set of ConnectorAttributes. + /// + /// set of attributes to search against. + /// the 'Name' attribute it if exsist otherwisenull + public static Name GetNameFromAttributes(ICollection attrs) + { + return (Name)Find(Name.NAME, attrs); + } + + + /// + /// Find the of the given name in the . + /// + /// + /// 's name to search for. + /// + /// of attribute to search. + /// + /// with the specified otherwise null. + public static ConnectorAttribute Find(string name, ICollection attrs) + { + Assertions.NullCheck(name, "name"); + ICollection attributes = CollectionUtil.NullAsEmpty(attrs); + foreach (ConnectorAttribute attr in attributes) + { + if (attr.Is(name)) + { + return attr; + } + } + return null; + } + /// + /// Get the password value from the provided set of s. + /// + public static GuardedString GetPasswordValue(ICollection attrs) + { + ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + + /// + /// Get the current password value from the provided set of s. + /// + /// Set of s that may contain the current password + /// + /// . + /// + /// null if it does not exist in the else + /// the value. + public static GuardedString GetCurrentPasswordValue(ICollection attrs) + { + ConnectorAttribute pwd = Find(OperationalAttributes.CURRENT_PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + /// + /// Determine if the is locked out. + /// + /// + /// By getting the + /// value of the . + /// + /// + /// object to inspect. + /// if the parameter 'obj' is null. + /// + /// null if the attribute does not exist otherwise to + /// value of the . + public static bool? IsLockedOut(ConnectorObject obj) + { + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.LOCK_OUT_NAME); + return (attr == null) ? null : GetBooleanValue(attr); + } + + /// + /// Determine if the is enable. + /// + /// + /// By getting the value + /// of the . + /// + /// + /// object to inspect. + /// if the object does not contain attribute in question. + /// if the parameter 'obj' is null. + /// + /// null if the attribute does not exist otherwise to + /// value of the . + public static bool? IsEnabled(ConnectorObject obj) + { + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.ENABLE_NAME); + return (attr == null) ? null : GetBooleanValue(attr); + } + + /// + /// Retrieve the password expiration date from the . + /// + /// + /// object to inspect. + /// if the object does not contain attribute in question. + /// if the parameter 'obj' is null. + /// + /// null if the does not exist + /// otherwise the value of the . + public static DateTime? GetPasswordExpirationDate(ConnectorObject obj) + { + DateTime? ret = null; + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); + if (attr != null) + { + long? date = GetLongValue(attr); + if (date != null) + { + ret = DateTime.FromFileTimeUtc(date.Value); + } + } + return ret; + } + /// + /// Get the password expired attribute from a of + /// s. + /// + /// set of attribute to find the expired password + /// . + /// + /// null if the attribute does not exist and the value + /// of the if it does. + public static bool? GetPasswordExpired(ICollection attrs) + { + ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_EXPIRED_NAME, attrs); + return (pwd == null) ? null : GetBooleanValue(pwd); + } + + /// + /// Determine if the password is expired for this object. + /// + /// + /// that should contain a password expired + /// attribute. + /// + /// null if the attribute does not exist and the value + /// of the if it does. + public static bool? IsPasswordExpired(ConnectorObject obj) + { + ConnectorAttribute pwd = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRED_NAME); + return (pwd == null) ? null : GetBooleanValue(pwd); + } + + /// + /// Get the enable date from the set of attributes. + /// + /// set of attribute to find the enable date + /// . + /// + /// null if the attribute does not exist and the value + /// of the if it does. + public static DateTime? GetEnableDate(ICollection attrs) + { + ConnectorAttribute attr = Find(OperationalAttributes.ENABLE_DATE_NAME, attrs); + return (attr == null) ? null : GetDateTimeValue(attr); + } + } + #endregion + + #region ConnectorAttributeInfoUtil + public static class ConnectorAttributeInfoUtil + { + /// + /// Transform a Collection of instances into + /// a . + /// + /// + /// The key to each element in the map is the name of + /// an AttributeInfo. The value of each element in the map is the + /// AttributeInfo instance with that name. + /// + /// set of AttributeInfo to transform to a map. + /// a map of string and AttributeInfo. + /// if the parameter attributes is + /// null. + public static IDictionary ToMap( + ICollection attributes) + { + IDictionary + ret = new Dictionary( + StringComparer.OrdinalIgnoreCase); + foreach (ConnectorAttributeInfo attr in attributes) + { + ret[attr.Name] = attr; + } + return ret; + } + + /// + /// Find the of the given name in the . + /// + /// + /// 's name to search for. + /// + /// of AttributeInfo to search. + /// + /// with the specified otherwise null. + public static ConnectorAttributeInfo Find(string name, ICollection attrs) + { + Assertions.NullCheck(name, "name"); + ICollection attributes = CollectionUtil.NullAsEmpty(attrs); + foreach (ConnectorAttributeInfo attr in attributes) + { + if (attr.Is(name)) + { + return attr; + } + } + return null; + } + } + #endregion + + #region BigDecimal + /// + /// Placeholder since C# doesn't have a BigInteger + /// + public sealed class BigDecimal + { + private BigInteger _unscaledVal; + private int _scale; + public BigDecimal(BigInteger unscaledVal, + int scale) + { + if (unscaledVal == null) + { + throw new ArgumentNullException(); + } + _unscaledVal = unscaledVal; + _scale = scale; + } + public BigInteger UnscaledValue + { + get + { + return _unscaledVal; + } + } + public int Scale + { + get + { + return _scale; + } + } + public override bool Equals(object o) + { + BigDecimal other = o as BigDecimal; + if (other != null) + { + return UnscaledValue.Equals(other.UnscaledValue) && + Scale == other.Scale; + } + return false; + } + public override int GetHashCode() + { + return _unscaledVal.GetHashCode(); + } + + public override string ToString() + { + return UnscaledValue.ToString(); + } + } + #endregion + + #region BigInteger + /// + /// Placeholder since C# doesn't have a BigInteger + /// + public sealed class BigInteger + { + private string _value; + public BigInteger(string val) + { + if (val == null) + { + throw new ArgumentNullException(); + } + _value = val; + } + public string Value + { + get + { + return _value; + } + } + public override bool Equals(object o) + { + BigInteger other = o as BigInteger; + if (other != null) + { + return Value.Equals(other.Value); + } + return false; + } + public override int GetHashCode() + { + return _value.GetHashCode(); + } + public override string ToString() + { + return _value; + } + } + #endregion + + #region ConnectorAttribute + /// + /// Represents a named collection of values within a resource object, + /// although the simplest case is a name-value pair (e.g., email, + /// employeeID). Values can be empty, null, or set with various types. + /// Empty and null are supported because it makes a difference on some + /// resources (in particular database resources). The developer of a + /// Connector will use an builder to construct an instance of + /// ConnectorAttribute. + /// + public class ConnectorAttribute + { + private readonly string _name; + private readonly IList _value; + + internal ConnectorAttribute(string name, IList val) + { + if (StringUtil.IsBlank(name)) + { + throw new ArgumentException("Name must not be blank!"); + } + if (OperationalAttributes.PASSWORD_NAME.Equals(name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) + { + // check the value.. + if (val == null || val.Count != 1) + { + String MSG = "Must be a single value."; + throw new ArgumentException(MSG); + } + if (!(val[0] is GuardedString)) + { + const string MSG = "Password value must be an instance of GuardedString."; + throw new ArgumentException(MSG); + } + } + _name = name; + // copy to prevent corruption preserve null + _value = (val == null) ? null : CollectionUtil.NewReadOnlyList(val); + } + + public string Name + { + get + { + return _name; + } + } + + public IList Value + { + get + { + return _value; + } + } + + public bool Is(string name) + { + return NameUtil.NamesEqual(_name, name); + } + + public sealed override bool Equals(Object obj) + { + // test identity + if (this == obj) + { + return true; + } + // test for null.. + if (obj == null) + { + return false; + } + // test that the exact class matches + if (!(GetType().Equals(obj.GetType()))) + { + return false; + } + // test name field.. + ConnectorAttribute other = (ConnectorAttribute)obj; + if (!Is(other._name)) + { + return false; + } + + if (!CollectionUtil.Equals(_value, other._value)) + { + return false; + } + return true; + } + + public sealed override int GetHashCode() + { + return NameUtil.GetNameHashCode(_name); + } + + + public override string ToString() + { + // poor man's consistent toString impl.. + StringBuilder bld = new StringBuilder(); + bld.Append("ConnectorAttribute: "); + bld.Append(Name).Append(" = ").Append(CollectionUtil.Dump(Value)); + return bld.ToString(); + } + + public string GetDetails() + { + StringBuilder bld = new StringBuilder(); + bld.Append("ConnectorAttribute: Name='").Append(Name).Append("', Value(s)='").Append(CollectionUtil.Dump(Value)).Append("'"); + return bld.ToString(); + } + } + #endregion + + #region ConnectorAttributeBuilder + public sealed class ConnectorAttributeBuilder + { + private const String NAME_ERROR = "Name must not be blank!"; + + private string _name; + private IList _value; + + public ConnectorAttributeBuilder() + { + } + public static ConnectorAttribute Build(String name) + { + return new ConnectorAttributeBuilder() { Name = name }.Build(); + } + public static ConnectorAttribute Build(String name, + params Object[] args) + { + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.Name = name; + bld.AddValue(args); + return bld.Build(); + } + public static ConnectorAttribute Build(String name, + ICollection val) + { + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.Name = name; + bld.AddValue(val); + return bld.Build(); + } + + public string Name + { + get + { + return _name; + } + set + { + if (StringUtil.IsBlank(value)) + { + throw new ArgumentException(NAME_ERROR); + } + _name = value; + } + } + + public IList Value + { + get + { + return _value == null ? null : CollectionUtil.AsReadOnlyList(_value); + } + } + + public ConnectorAttributeBuilder AddValue(params Object[] args) + { + AddValuesInternal(args); + return this; + } + public ConnectorAttributeBuilder AddValue(ICollection values) + { + AddValuesInternal(values); + return this; + } + + public ConnectorAttribute Build() + { + if (StringUtil.IsBlank(Name)) + { + throw new ArgumentException(NAME_ERROR); + } + if (Uid.NAME.Equals(_name)) + { + return new Uid(GetSingleStringValue()); + } + else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) + { + return new Name(GetSingleStringValue()); + } + return new ConnectorAttribute(Name, _value); + } + private void CheckSingleValue() + { + if (_value == null || _value.Count != 1) + { + const String MSG = "Must be a single value."; + throw new ArgumentException(MSG); + } + } + private String GetSingleStringValue() + { + CheckSingleValue(); + if (!(_value[0] is String)) + { + const String MSG = "Must be single string value."; + throw new ArgumentException(MSG); + } + return (String)_value[0]; + } + private void AddValuesInternal(IEnumerable values) + { + if (values != null) + { + // make sure the list is ready to receive values. + if (_value == null) + { + _value = new List(); + } + // add each value checking to make sure its correct + foreach (Object v in values) + { + FrameworkUtil.CheckAttributeValue(Name, v); + _value.Add(v); + } + } + } + + // ======================================================================= + // Operational Attributes + // ======================================================================= + /// + /// Builds an password expiration date . + /// + /// + /// This + /// represents the date/time a password will expire on a + /// resource. + /// + /// UTC time in milliseconds. + /// an built with the pre-defined name for password + /// expiration date. + public static ConnectorAttribute BuildPasswordExpirationDate(DateTime dateTime) + { + return BuildPasswordExpirationDate(DateTimeUtil.GetUtcTimeMillis(dateTime)); + } + + /// + /// Builds an password expiration date . + /// + /// + /// This + /// represents the date/time a password will expire on a + /// resource. + /// + /// UTC time in milliseconds. + /// an built with the pre-defined name for password + /// expiration date. + public static ConnectorAttribute BuildPasswordExpirationDate(long dateTime) + { + return Build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, + dateTime); + } + + /// + /// Builds the operational attribute password. + /// + /// the string that represents a password. + /// an attribute that represents a password. + public static ConnectorAttribute BuildPassword(GuardedString password) + { + return Build(OperationalAttributes.PASSWORD_NAME, password); + } + + /// + /// Builds the operational attribute current password. + /// + /// + /// The current password + /// indicates this a password change by the account owner and not an + /// administrator. The use case is that an administrator password change may + /// not keep history or validate against policy. + /// + /// the string that represents a password. + /// an attribute that represents a password. + public static ConnectorAttribute BuildCurrentPassword(GuardedString password) + { + return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); + } + + public static ConnectorAttribute BuildPassword(SecureString password) + { + return Build(OperationalAttributes.PASSWORD_NAME, new GuardedString(password)); + } + public static ConnectorAttribute BuildCurrentPassword(SecureString password) + { + return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, new GuardedString(password)); + } + /// + /// Builds ant operational attribute that either represents the object is + /// enabled or sets in disabled depending on where its used for instance on + /// it could be used to create a disabled account. + /// + /// + /// In + /// it would show the object is enabled or disabled. + /// + /// true indicates the object is enabled otherwise false. + /// + /// that determines the enable/disable state of an + /// object. + public static ConnectorAttribute BuildEnabled(bool val) + { + return Build(OperationalAttributes.ENABLE_NAME, val); + } + + /// + /// Builds out an operational that determines the enable + /// date for an object. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildEnableDate(DateTime date) + { + return BuildEnableDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /// + /// Builds out an operational that determines the enable + /// date for an object. + /// + /// + /// The time parameter is UTC in milliseconds. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildEnableDate(long date) + { + return Build(OperationalAttributes.ENABLE_DATE_NAME, date); + } + + /// + /// Builds out an operational that determines the disable + /// date for an object. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildDisableDate(DateTime date) + { + return BuildDisableDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /// + /// Builds out an operational that determines the disable + /// date for an object. + /// + /// + /// The time parameter is UTC in milliseconds. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildDisableDate(long date) + { + return Build(OperationalAttributes.DISABLE_DATE_NAME, date); + } + + /// + /// Builds the lock attribute that determines if an object is locked out. + /// + /// true if the object is locked otherwise false. + /// + /// that represents the lock state of an object. + public static ConnectorAttribute BuildLockOut(bool lck) + { + return Build(OperationalAttributes.LOCK_OUT_NAME, lck); + } + + /// + /// Builds out an operational that determines if a password + /// is expired or expires a password. + /// + /// from the API true expires and from the SPI its shows its + /// either expired or not. + /// + /// + /// + public static ConnectorAttribute BuildPasswordExpired(bool expired) + { + return Build(OperationalAttributes.PASSWORD_EXPIRED_NAME, expired); + } + + // ======================================================================= + // Pre-defined Attributes + // ======================================================================= + + /// + /// Builds out a pre-defined that determines the last login + /// date for an object. + /// + /// The date and time of the last login. + /// + /// + /// + public static ConnectorAttribute BuildLastLoginDate(DateTime date) + { + return BuildLastLoginDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /// + /// Builds out a pre-defined that determines the last login + /// date for an object. + /// + /// + /// The time parameter is UTC in milliseconds. + /// + /// The date and time of the last login. + /// + /// + /// + public static ConnectorAttribute BuildLastLoginDate(long date) + { + return Build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); + } + + /// + /// Builds out a pre-defined that determines the last + /// password change date for an object. + /// + /// The date and time the password was changed. + /// + /// + /// + public static ConnectorAttribute BuildLastPasswordChangeDate(DateTime date) + { + return BuildLastPasswordChangeDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /// + /// Builds out a pre-defined that determines the last + /// password change date for an object. + /// + /// The date and time the password was changed. + /// + /// + /// + public static ConnectorAttribute BuildLastPasswordChangeDate(long date) + { + return Build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date); + } + + /// + /// Common password policy attribute where the password must be changed every + /// so often. + /// + /// + /// The value for this attribute is milliseconds since its the + /// lowest common denominator. + /// + public static ConnectorAttribute BuildPasswordChangeInterval(long val) + { + return Build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, val); + } + } + #endregion + + #region ConnectorMessages + /// + /// Message catalog for a given connector. + /// + public interface ConnectorMessages + { + /// + /// Formats the given message key in the current UI culture. + /// + /// The message key to format. + /// The default message if key is not found. If null, defaults + /// to key. + /// Parameters with which to format the message. + /// The formatted string. + String Format(String key, String dflt, params object[] args); + } + #endregion + + #region ConnectorObject + public sealed class ConnectorObject + { + private readonly ObjectClass _objectClass; + private readonly IDictionary _attrs; + public ConnectorObject(ObjectClass objectClass, ICollection attrs) + { + if (objectClass == null) + { + throw new ArgumentException("ObjectClass may not be null"); + } + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.ArgumentException("Connector object class can not be type of __ALL__"); + } + if (attrs == null || attrs.Count == 0) + { + throw new ArgumentException("attrs cannot be empty or null."); + } + _objectClass = objectClass; + _attrs = + CollectionUtil.NewReadOnlyDictionary(attrs, + value => { return value.Name; }); + if (!_attrs.ContainsKey(Uid.NAME)) + { + const String MSG = "The ConnectorAttribute set must contain a Uid."; + throw new ArgumentException(MSG); + } + if (!_attrs.ContainsKey(Name.NAME)) + { + const string MSG = "The ConnectorAttribute set must contain a Name."; + throw new ArgumentException(MSG); + } + } + public ICollection GetAttributes() + { + return _attrs.Values; + } + public ConnectorAttribute GetAttributeByName(string name) + { + return CollectionUtil.GetValue(_attrs, name, null); + } + public Uid Uid + { + get + { + return (Uid)GetAttributeByName(Uid.NAME); + } + } + public Name Name + { + get + { + return (Name)GetAttributeByName(Name.NAME); + } + } + public ObjectClass ObjectClass + { + get + { + return _objectClass; + } + } + public override int GetHashCode() + { + return CollectionUtil.GetHashCode(_attrs); + } + public override bool Equals(Object o) + { + ConnectorObject other = o as ConnectorObject; + if (other != null) + { + if (!_objectClass.Equals(other.ObjectClass)) + { + return false; + } + return CollectionUtil.Equals(_attrs, other._attrs); + } + return false; + } + } + #endregion + + #region ConnectorObjectBuilder + public sealed class ConnectorObjectBuilder + { + private ObjectClass _objectClass; + private IDictionary _attributes; + public ConnectorObjectBuilder() + { + _attributes = new Dictionary(); + // default always add the account object class.. + ObjectClass = ObjectClass.ACCOUNT; + } + + public void SetUid(string uid) + { + AddAttribute(new Uid(uid)); + } + + public void SetUid(Uid uid) + { + AddAttribute(uid); + } + + public void SetName(string name) + { + AddAttribute(new Name(name)); + } + + public void SetName(Name name) + { + AddAttribute(name); + } + + public ObjectClass ObjectClass + { + get + { + return _objectClass; + } + set + { + if (ObjectClass.ALL.Equals(value)) + { + throw new System.ArgumentException("Connector object class can not be type of __ALL__"); + } + _objectClass = value; + } + } + + + + // ======================================================================= + // Clone basically.. + // ======================================================================= + /// + /// Takes all the attribute from a and add/overwrite + /// the current attributes. + /// + public ConnectorObjectBuilder Add(ConnectorObject obj) + { + // simply add all the attributes it will include (Uid, ObjectClass..) + foreach (ConnectorAttribute attr in obj.GetAttributes()) + { + AddAttribute(attr); + } + ObjectClass = obj.ObjectClass; + return this; + } + + public ConnectorObjectBuilder AddAttribute(params ConnectorAttribute[] attrs) + { + ValidateParameter(attrs, "attrs"); + foreach (ConnectorAttribute a in attrs) + { + //DONT use Add - it throws exceptions if already there + _attributes[a.Name] = a; + } + return this; + } + public ConnectorObjectBuilder AddAttributes(ICollection attrs) + { + ValidateParameter(attrs, "attrs"); + foreach (ConnectorAttribute a in attrs) + { + _attributes[a.Name] = a; + } + return this; + } + /// + /// Adds values to the attribute. + /// + public ConnectorObjectBuilder AddAttribute(String name, params object[] objs) + { + AddAttribute(ConnectorAttributeBuilder.Build(name, objs)); + return this; + } + + /// + /// Adds each object in the collection. + /// + public ConnectorObjectBuilder AddAttribute(String name, ICollection obj) + { + AddAttribute(ConnectorAttributeBuilder.Build(name, obj)); + return this; + } + public ConnectorObject Build() + { + // check that there are attributes to return.. + if (_attributes.Count == 0) + { + throw new InvalidOperationException("No attributes set!"); + } + return new ConnectorObject(ObjectClass, _attributes.Values); + } + private static void ValidateParameter(Object param, String paramName) + { + if (param == null) + { + String FORMAT = "Parameter " + param + " must not be null!"; + throw new NullReferenceException(FORMAT); + } + } + } + #endregion + + #region ConnectorAttributeInfo + public sealed class ConnectorAttributeInfo + { + private readonly string _name; + private readonly Type _type; + private readonly Flags _flags; + + /// + /// Enum of modifier flags to use for attributes. + /// + /// + /// Note that + /// this enum is designed for configuration by exception such that + /// an empty set of flags are the defaults: + /// + /// + /// updateable + /// + /// + /// + /// creatable + /// + /// + /// + /// returned by default + /// + /// + /// + /// readable + /// + /// + /// + /// single-valued + /// + /// + /// + /// optional + /// + /// + /// + /// + [FlagsAttribute] + public enum Flags + { + NONE = 0, + REQUIRED = 1, + MULTIVALUED = 2, + NOT_CREATABLE = 4, + NOT_UPDATEABLE = 8, + NOT_READABLE = 16, + NOT_RETURNED_BY_DEFAULT = 32 + } + + internal ConnectorAttributeInfo(string name, Type type, + Flags flags) + { + if (StringUtil.IsBlank(name)) + { + throw new ArgumentException("Name must not be blank!"); + } + if ((OperationalAttributes.PASSWORD_NAME.Equals(name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) && + !typeof(GuardedString).Equals(type)) + { + String MSG = "Password based attributes must be of type GuardedString."; + throw new ArgumentException(MSG); + } + // check the type.. + FrameworkUtil.CheckAttributeType(type); + _name = name; + _type = type; + _flags = flags; + if (!IsReadable && IsReturnedByDefault) + { + throw new ArgumentException("Attribute " + name + " is flagged as not-readable, so it should also be as not-returned-by-default."); + } + } + + + /// + /// The native name of the attribute. + /// + /// the native name of the attribute its describing. + public string Name + { + get + { + return _name; + } + } + + /// + /// The basic type associated with this attribute. + /// + /// + /// All primitives are + /// supported. + /// + /// the native type if uses. + public Type ValueType + { + get + { + return _type; + } + } + + /// + /// Returns the set of flags associated with the attribute. + /// + /// the set of flags associated with the attribute + public Flags InfoFlags + { + get + { + return _flags; + } + } + + + public bool Is(string name) + { + return NameUtil.NamesEqual(_name, name); + } + + /// + /// Determines if the attribute is readable. + /// + /// true if the attribute is readable else false. + public bool IsReadable + { + get + { + return (_flags & Flags.NOT_READABLE) == 0; + } + } + + /// + /// Determines if the attribute is writable on create. + /// + /// true if the attribute is writable on create else false. + public bool IsCreatable + { + get + { + return (_flags & Flags.NOT_CREATABLE) == 0; + } + } + + /// + /// Determines if the attribute is writable on update. + /// + /// true if the attribute is writable on update else false. + public bool IsUpdateable + { + get + { + return (_flags & Flags.NOT_UPDATEABLE) == 0; + } + } + + /// + /// Determines whether this attribute is required for creates. + /// + /// true if the attribute is required for an object else false. + public bool IsRequired + { + get + { + return (_flags & Flags.REQUIRED) != 0; + } + } + + /// + /// Determines if this attribute can handle multiple values. + /// + /// + /// There is a + /// special case with byte[] since in most instances this denotes a single + /// object. + /// + /// true if the attribute is multi-value otherwise false. + public bool IsMultiValued + { + get + { + return (_flags & Flags.MULTIVALUED) != 0; + } + } + + /// + /// Determines if the attribute is returned by default. + /// + /// + /// Indicates if an + /// will be returned during or + /// inside a by default. The default + /// value is true. + /// + /// false if the attribute should not be returned by default. + public bool IsReturnedByDefault + { + get + { + return (_flags & Flags.NOT_RETURNED_BY_DEFAULT) == 0; + } + } + + public override bool Equals(Object o) + { + ConnectorAttributeInfo other = o as ConnectorAttributeInfo; + if (other != null) + { + if (!Is(other.Name)) + { + return false; + } + if (!ValueType.Equals(other.ValueType)) + { + return false; + } + if (_flags != other._flags) + { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() + { + return NameUtil.GetNameHashCode(_name); + } + + public override string ToString() + { + return SerializerUtil.SerializeXmlObject(this, false); + } + } + #endregion + + #region ConnectorAttributeInfoBuilder + /// + /// Simplifies the process of building 'AttributeInfo' objects. + /// + /// + /// This class is + /// responsible for providing a default implementation of . + /// + /// AttributeInfoBuilder bld = new AttributeInfoBuilder("email"); + /// bld.setRequired(true); + /// AttributeInfo info = bld.build(); + /// + /// + /// Will Droste + /// $Revision: 1.9 $ + /// 1.0 + public sealed class ConnectorAttributeInfoBuilder + { + + private String _name; + private Type _type; + private ConnectorAttributeInfo.Flags _flags; + + /// + /// Creates an builder with all the defaults set. + /// + /// + /// The name must be set before + /// the 'build' method is called otherwise an + /// is thrown. + ///
+        /// Name: <not set>
+        /// Readable: true
+        /// Writeable: true
+        /// Required: false
+        /// Type: string
+        /// MultiValue: false
+        /// 
+ ///
+ public ConnectorAttributeInfoBuilder() + { + ValueType = (typeof(String)); + _flags = ConnectorAttributeInfo.Flags.NONE; + } + + /// + /// Creates an builder with all the defaults set. + /// + /// + /// The name must be set before + /// the 'build' method is called otherwise an + /// is thrown. + ///
+        /// Name: <not set>
+        /// Readable: true
+        /// Writeable: true
+        /// Required: false
+        /// Type: string
+        /// MultiValue: false
+        /// 
+ ///
+ public ConnectorAttributeInfoBuilder(String name) + : this(name, typeof(String)) + { + } + + /// + /// Creates an builder with all the defaults set. + /// + /// + /// The name must be set before + /// the 'build' method is called otherwise an + /// is thrown. + ///
+        /// Name: <not set>
+        /// Readable: true
+        /// Writeable: true
+        /// Required: false
+        /// Type: string
+        /// MultiValue: false
+        /// 
+ ///
+ public ConnectorAttributeInfoBuilder(String name, Type type) + { + Name = (name); + ValueType = (type); + //noneOf means the defaults + _flags = ConnectorAttributeInfo.Flags.NONE; + } + + /// + /// Builds an object based on the properties set. + /// + /// + /// based on the properties set. + public ConnectorAttributeInfo Build() + { + return new ConnectorAttributeInfo(_name, _type, _flags); + } + + /// + /// Sets the unique name of the object. + /// + /// unique name of the object. + public String Name + { + set + { + if (StringUtil.IsBlank(value)) + { + throw new ArgumentException("Argument must not be blank."); + } + _name = value; + } + } + + /// + /// Sets the unique name of the object. + /// + /// unique name of the object. + public ConnectorAttributeInfoBuilder SetName(string name) + { + if (StringUtil.IsBlank(name)) + { + throw new ArgumentException("Argument must not be blank."); + } + _name = name; + return this; + } + + /// + /// Please see for the + /// definitive list of supported types. + /// + /// type for an 's value. + /// if the Class is not a supported type. + public Type ValueType + { + set + { + FrameworkUtil.CheckAttributeType(value); + _type = value; + } + } + + /// + /// Please see for the + /// definitive list of supported types. + /// + /// type for an 's value. + /// if the Class is not a supported type. + public ConnectorAttributeInfoBuilder SetValueType(Type type) + { + FrameworkUtil.CheckAttributeType(type); + _type = type; + return this; + } + + /// + /// Determines if the attribute is readable. + /// + public bool Readable + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE, !value); + } + } + + /// + /// Determines if the attribute is readable. + /// + public ConnectorAttributeInfoBuilder SetReadable(bool value) + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE, !value); + return this; + } + + /// + /// Determines if the attribute is writable. + /// + public bool Creatable + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE, !value); + } + } + + /// + /// Determines if the attribute is writable. + /// + public ConnectorAttributeInfoBuilder SetCreatable(bool value) + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE, !value); + return this; + } + + /// + /// Determines if this attribute is required. + /// + public bool Required + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.REQUIRED, value); + } + } + + /// + /// Determines if this attribute is required. + /// + public ConnectorAttributeInfoBuilder SetRequired(bool value) + { + SetFlag(ConnectorAttributeInfo.Flags.REQUIRED, value); + return this; + } + + /// + /// Determines if this attribute supports multivalue. + /// + public bool MultiValued + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED, value); + } + } + + /// + /// Determines if this attribute supports multivalue. + /// + public ConnectorAttributeInfoBuilder SetMultiValued(bool value) + { + SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED, value); + return this; + } + + /// + /// Determines if this attribute writable during update. + /// + public bool Updateable + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE, !value); + } + } + + /// + /// Determines if this attribute writable during update. + /// + public ConnectorAttributeInfoBuilder SetUpdateable(bool value) + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE, !value); + return this; + } + + public bool ReturnedByDefault + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT, !value); + } + } + + public ConnectorAttributeInfoBuilder SetReturnedByDefault(bool value) + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT, !value); + return this; + } + + /// + /// Sets all of the flags for this builder. + /// + /// The set of attribute info flags. Null means clear all flags. + /// + /// NOTE: EnumSet.noneOf(AttributeInfo.Flags.class) results in + /// an attribute with the default behavior: + /// + /// + /// updateable + /// + /// + /// + /// creatable + /// + /// + /// + /// returned by default + /// + /// + /// + /// readable + /// + /// + /// + /// single-valued + /// + /// + /// + /// optional + /// + /// + /// + /// + /// + public ConnectorAttributeInfo.Flags InfoFlags + { + set + { + _flags = value; + } + } + + private void SetFlag(ConnectorAttributeInfo.Flags flag, bool value) + { + if (value) + { + _flags = _flags | flag; + } + else + { + _flags = _flags & ~flag; + } + } + + /// + /// Convenience method to create an AttributeInfo. + /// + /// + /// Equivalent to + /// + /// new AttributeInfoBuilder(name,type).setFlags(flags).build() + /// + /// + /// The name of the attribute + /// The type of the attribute + /// The flags for the attribute. Null means clear all flags + /// The attribute info + public static ConnectorAttributeInfo Build(String name, Type type, + ConnectorAttributeInfo.Flags flags) + { + return new ConnectorAttributeInfoBuilder(name, type) { InfoFlags = flags }.Build(); + } + /// + /// Convenience method to create an AttributeInfo. + /// + /// + /// Equivalent to + /// + /// AttributeInfoBuilder.build(name,type,null) + /// + /// + /// The name of the attribute + /// The type of the attribute + /// The attribute info + public static ConnectorAttributeInfo Build(String name, Type type) + { + return Build(name, type, ConnectorAttributeInfo.Flags.NONE); + } + + /// + /// Convenience method to create an AttributeInfo. + /// + /// + /// Equivalent to + /// + /// AttributeInfoBuilder.build(name,type) + /// + /// + /// The name of the attribute + /// The attribute info + public static ConnectorAttributeInfo Build(String name) + { + return Build(name, typeof(String)); + } + + /// + /// Convenience method to create a new AttributeInfoBuilder. + /// + /// Equivalent to: new AttributeInfoBuilder(name, String.class) + /// + /// + /// The name of the attribute + /// The attribute info builder with predefined name and type value. + /// Since 1.4 + public static ConnectorAttributeInfoBuilder Define(string name) + { + return new ConnectorAttributeInfoBuilder(name, typeof(string)); + } + + /// + /// Convenience method to create a new AttributeInfoBuilder. + /// + /// Equivalent to: new AttributeInfoBuilder(name, type) + /// + /// + /// The name of the attribute + /// + /// The type of the attribute + /// The attribute info builder with predefined name and type value. + /// Since 1.4 + public static ConnectorAttributeInfoBuilder Define(string name, Type type) + { + return new ConnectorAttributeInfoBuilder(name, type); + } + } + #endregion + + #region FileName + /// + /// Placeholder for java.io.File since C#'s + /// FileInfo class throws exceptions if the + /// file doesn't exist. + /// + public sealed class FileName + { + private string _path; + public FileName(string path) + { + if (path == null) + { + throw new ArgumentNullException(); + } + _path = path; + } + public string Path + { + get + { + return _path; + } + } + public override bool Equals(object o) + { + FileName other = o as FileName; + if (other != null) + { + return Path.Equals(other.Path); + } + return false; + } + public override int GetHashCode() + { + return _path.GetHashCode(); + } + public override string ToString() + { + return _path; + } + } + #endregion + + #region Name + public sealed class Name : ConnectorAttribute + { + public readonly static string NAME = ConnectorAttributeUtil.CreateSpecialName("NAME"); + public readonly static ConnectorAttributeInfo INFO = + new ConnectorAttributeInfoBuilder(NAME) { Required = true }.Build(); + + public Name(String value) + : base(NAME, CollectionUtil.NewReadOnlyList(value)) + { + } + + /// + /// The single value of the attribute that is the unique id of an object. + /// + /// value that identifies an object. + public String GetNameValue() + { + return ConnectorAttributeUtil.GetStringValue(this); + } + } + #endregion + + #region ObjectClassUtil + public static class ObjectClassUtil + { + /// + /// Determines if this object class is a special object class. + /// Special object classes include the predefined ones, such as + /// and . + /// + /// the object class to test + /// true if the object class is special + /// if the object class parameter is null + public static bool IsSpecial(ObjectClass oclass) + { + String name = oclass.GetObjectClassValue(); + return IsSpecialName(name); + } + + /// + /// Determines whether the specified object class name is special in the + /// sense of . + /// + /// the name of the object class to test + /// true if the object class name is special + public static bool IsSpecialName(String name) + { + return NameUtil.IsSpecialName(name); + } + + /// + /// Create a special name from the specified name. Add the __ + /// string as both prefix and suffix. This indicates that a name + /// identifies a special object class such as a predefined one. + /// + /// object class name to make special + /// name constructed for use as a special name + public static string CreateSpecialName(string name) + { + return NameUtil.CreateSpecialName(name); + } + + /// + /// Compares two object class names for equality. + /// + /// the first object class name + /// the second object class name + /// true if the two object class names are equal + public static bool NamesEqual(string name1, string name2) + { + return NameUtil.NamesEqual(name2, name2); + } + } + #endregion + + #region ObjectClass + public sealed class ObjectClass + { + /// + /// This constant defines a specific {@link #getObjectClassValue value + /// of ObjectClass} that is reserved for . + /// + public static readonly String ACCOUNT_NAME = ObjectClassUtil.CreateSpecialName("ACCOUNT"); + + /// + /// This constant defines a specific {@link #getObjectClassValue value + /// of ObjectClass} that is reserved for . + /// + public static readonly String GROUP_NAME = ObjectClassUtil.CreateSpecialName("GROUP"); + /// + /// This constant defines a specific {@link #getObjectClassValue value + /// of ObjectClass} that is reserved for . + /// + public static readonly String ALL_NAME = ObjectClassUtil.CreateSpecialName("ALL"); + /// + /// Denotes an account based object. + /// + public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); + /// + /// Denotes a group based object. + /// + public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); + /// + /// Represents all collections that contains any object. + /// + /// This constant allowed to use in operation + /// + /// and + /// + /// any other operation throws + /// + /// + public static readonly ObjectClass ALL = new ObjectClass(ALL_NAME); + + private readonly String _type; + + public ObjectClass(String type) + { + if (type == null) + { + throw new ArgumentException("Type cannot be null."); + } + _type = type; + } + public String GetObjectClassValue() + { + return _type; + } + + public String Type + { + get { return _type; } + } + + /// + /// Convenience method to build the display name key for + /// an object class. + /// + /// The display name key. + public String GetDisplayNameKey() + { + return "MESSAGE_OBJECT_CLASS_" + _type.ToUpper(CultureInfo.GetCultureInfo("en-US")); + } + + /// + /// Determines if the 'name' matches this . + /// + /// case-insensitive string representation of the ObjectClass's + /// type. + /// + /// true if the case-insensitive name is equal to + /// that of the one in this . + public bool Is(String name) + { + return NameUtil.NamesEqual(_type, name); + } + + public override int GetHashCode() + { + return NameUtil.GetNameHashCode(_type); + } + + public override bool Equals(object obj) + { + // test identity + if (this == obj) + { + return true; + } + + // test for null.. + if (obj == null) + { + return false; + } + + // test that the exact class matches + if (!(GetType() == obj.GetType())) + { + return false; + } + + ObjectClass other = (ObjectClass)obj; + + if (!Is(other._type)) + { + return false; + } + + return true; + } + + public override string ToString() + { + return "ObjectClass: " + _type; + } + } + #endregion + + #region ObjectClassInfo + public sealed class ObjectClassInfo + { + private readonly String _type; + private readonly ICollection _info; + private readonly bool _isContainer; + + public ObjectClassInfo(String type, + ICollection attrInfo, + bool isContainer) + { + Assertions.NullCheck(type, "type"); + _type = type; + _info = CollectionUtil.NewReadOnlySet(attrInfo); + _isContainer = isContainer; + // check to make sure name exists + IDictionary dict + = ConnectorAttributeInfoUtil.ToMap(attrInfo); + if (!dict.ContainsKey(Name.NAME)) + { + const string MSG = "Missing 'Name' connector attribute info."; + throw new ArgumentException(MSG); + } + } + + public ICollection ConnectorAttributeInfos + { + get + { + return this._info; + } + } + + public String ObjectType + { + get + { + return this._type; + } + } + + /// + /// Determines if the 'name' matches this . + /// + /// case-insensitive string representation of the ObjectClassInfo's + /// type. + /// + /// true if the case insensitive type is equal to + /// that of the one in this . + public bool Is(String name) + { + return NameUtil.NamesEqual(_type, name); + } + + public bool IsContainer + { + get + { + return this._isContainer; + } + } + + public override int GetHashCode() + { + return NameUtil.GetNameHashCode(_type); + } + + public override bool Equals(Object obj) + { + // test identity + if (this == obj) + { + return true; + } + + // test for null.. + if (obj == null) + { + return false; + } + + if (!obj.GetType().Equals(this.GetType())) + { + return false; + } + + ObjectClassInfo other = obj as ObjectClassInfo; + + if (!Is(other.ObjectType)) + { + return false; + } + + if (!CollectionUtil.Equals(ConnectorAttributeInfos, + other.ConnectorAttributeInfos)) + { + return false; + } + + if (_isContainer != other._isContainer) + { + return false; + } + + return true; + } + + public override string ToString() + { + return SerializerUtil.SerializeXmlObject(this, false); + } + } + #endregion + + #region ObjectClassInfoBuilder + /// + /// Used to help facilitate the building of objects. + /// + public sealed class ObjectClassInfoBuilder + { + private bool _isContainer; + private IDictionary _info; + + public ObjectClassInfoBuilder() + { + _info = new Dictionary(); + ObjectType = ObjectClass.ACCOUNT_NAME; + } + + public string ObjectType { get; set; } + + /// + /// Add each object to the . + /// + public ObjectClassInfoBuilder AddAttributeInfo(ConnectorAttributeInfo info) + { + if (_info.ContainsKey(info.Name)) + { + const string MSG = "ConnectorAttributeInfo of name ''{0}'' already exists!"; + throw new ArgumentException(String.Format(MSG, info.Name)); + } + _info[info.Name] = info; + return this; + } + + public ObjectClassInfoBuilder AddAllAttributeInfo(ICollection info) + { + foreach (ConnectorAttributeInfo cainfo in info) + { + AddAttributeInfo(cainfo); + } + return this; + } + + public bool IsContainer + { + get + { + return _isContainer; + } + set + { + _isContainer = value; + } + } + + public ObjectClassInfo Build() + { + // determine if name is missing and add it by default + if (!_info.ContainsKey(Name.NAME)) + { + _info[Name.NAME] = Name.INFO; + } + return new ObjectClassInfo(ObjectType, _info.Values, _isContainer); + } + } + #endregion + + #region OperationalAttributeInfos + /// + /// for each operational attribute. + /// + public static class OperationalAttributeInfos + { + /// + /// Gets/sets the enable status of an object. + /// + public static readonly ConnectorAttributeInfo ENABLE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.ENABLE_NAME, typeof(bool)); + + /// + /// Gets/sets the enable date for an object. + /// + public static readonly ConnectorAttributeInfo ENABLE_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.ENABLE_DATE_NAME, typeof(long)); + + /// + /// Gets/sets the disable date for an object. + /// + public static readonly ConnectorAttributeInfo DISABLE_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.DISABLE_DATE_NAME, typeof(long)); + + /// + /// Gets/sets the lock out attribute for an object. + /// + public static readonly ConnectorAttributeInfo LOCK_OUT = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.LOCK_OUT_NAME, typeof(bool)); + + /// + /// Gets/sets the password expiration date for an object. + /// + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRATION_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, typeof(long)); + + /// + /// Normally this is a write-only attribute. + /// + /// + /// Sets the password for an object. + /// + public static readonly ConnectorAttributeInfo PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + /// + /// Used in conjunction with password to do an account level password change. + /// + /// + /// This is for a non-administrator change of the password and therefore + /// requires the current password. + /// + public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + /// + /// Used to determine if a password is expired or to expire a password. + /// + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRED = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_EXPIRED_NAME, typeof(bool)); + + } + #endregion + + #region OperationalAttributes + /// + /// Operational attributes have special meaning and cannot be represented by pure + /// operations. + /// + /// + /// For instance some administrators would like to create an account + /// in the disabled state. The do not want this to be a two operation process + /// since this can leave the door open to abuse. Therefore special attributes + /// that can perform operations were introduced. The + /// attribute could be added to the set of + /// attribute sent to a Connector for the operation. To tell the + /// to create the account with it in the disabled state whether + /// the target resource itself has an attribute or an additional method must be + /// called. + /// + public static class OperationalAttributes + { + /// + /// Gets/sets the enable status of an object. + /// + public static readonly string ENABLE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE"); + /// + /// Gets/sets the enable date for an object. + /// + public static readonly string ENABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE_DATE"); + /// + /// Gets/sets the disable date for an object. + /// + public static readonly string DISABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("DISABLE_DATE"); + /// + /// Gets/sets the lock out attribute for an object. + /// + public static readonly string LOCK_OUT_NAME = ConnectorAttributeUtil.CreateSpecialName("LOCK_OUT"); + /// + /// Gets/sets the password expiration date for an object. + /// + public static readonly string PASSWORD_EXPIRATION_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRATION_DATE"); + /// + /// Gets/sets the password expired for an object. + /// + public static readonly string PASSWORD_EXPIRED_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRED"); + /// + /// Normally this is a write-only attribute. + /// + /// + /// Sets the password for an object. + /// + public static readonly string PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD"); + /// + /// Used in conjunction with password to do an account level password change. + /// + /// + /// This is for a non-administrator change of the password and therefore + /// requires the current password. + /// + public static readonly string CURRENT_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("CURRENT_PASSWORD"); + + // ======================================================================= + // Helper Methods.. + // ======================================================================= + public static readonly ICollection OPERATIONAL_ATTRIBUTE_NAMES = + CollectionUtil.NewReadOnlySet( + LOCK_OUT_NAME, + ENABLE_NAME, + ENABLE_DATE_NAME, + DISABLE_DATE_NAME, + PASSWORD_EXPIRATION_DATE_NAME, + PASSWORD_NAME, + CURRENT_PASSWORD_NAME, + PASSWORD_EXPIRED_NAME + ); + + public static ICollection GetOperationalAttributeNames() + { + return CollectionUtil.NewReadOnlySet(OPERATIONAL_ATTRIBUTE_NAMES); + } + public static bool IsOperationalAttribute(ConnectorAttribute attr) + { + string name = (attr != null) ? attr.Name : null; + return OPERATIONAL_ATTRIBUTE_NAMES.Contains(name); + } + } + #endregion + + #region PredefinedAttributes + /// + /// List of well known or pre-defined attributes. + /// + /// + /// Common attributes that most + /// resources have that are not operational in nature. + /// + public static class PredefinedAttributes + { + /// + /// Attribute that should hold a reasonable value to + /// display for the value of an object. + /// + /// + /// If this is not present, then the + /// application will have to use the NAME to show the value. + /// + public static readonly String SHORT_NAME = ConnectorAttributeUtil.CreateSpecialName("SHORT_NAME"); + + /// + /// Attribute that should hold the value of the object's description, + /// if one is available. + /// + public static readonly String DESCRIPTION = ConnectorAttributeUtil.CreateSpecialName("DESCRIPTION"); + + /// + /// Read-only attribute that shows the last date/time the password was + /// changed. + /// + public static readonly string LAST_PASSWORD_CHANGE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_PASSWORD_CHANGE_DATE"); + + /// + /// Common password policy attribute where the password must be changed every + /// so often. + /// + /// + /// The value for this attribute is milliseconds since its the + /// lowest common denominator. + /// + public static readonly string PASSWORD_CHANGE_INTERVAL_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_CHANGE_INTERVAL"); + + /// + /// Last login date for an account. + /// + /// + /// This is usually used to determine inactivity. + /// + public static readonly string LAST_LOGIN_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_LOGIN_DATE"); + + /// + /// Groups an account object belongs to. + /// + public static readonly string GROUPS_NAME = ConnectorAttributeUtil.CreateSpecialName("GROUPS"); + } + #endregion + + #region PredefinedAttributeInfos + public static class PredefinedAttributeInfos + { + /// + /// Attribute that should hold a reasonable value to + /// display for the value of an object. + /// + /// + /// If this is not present, then the + /// application will have to use the NAME to show the value. + /// + public static readonly ConnectorAttributeInfo SHORT_NAME = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.SHORT_NAME); + + /// + /// Attribute that should hold the value of the object's description, + /// if one is available. + /// + public static readonly ConnectorAttributeInfo DESCRIPTION = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.DESCRIPTION); + /// + /// Read-only attribute that shows the last date/time the password was + /// changed. + /// + public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, + typeof(long), + ConnectorAttributeInfo.Flags.NOT_CREATABLE | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); + + /// + /// Common password policy attribute where the password must be changed every + /// so often. + /// + /// + /// The value for this attribute is milliseconds since its the + /// lowest common denominator. + /// + public static readonly ConnectorAttributeInfo PASSWORD_CHANGE_INTERVAL = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, typeof(long)); + + /// + /// Last login date for an account. + /// + /// + /// This is usually used to determine + /// inactivity. + /// + public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.LAST_LOGIN_DATE_NAME, + typeof(long), + ConnectorAttributeInfo.Flags.NOT_CREATABLE | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); + + /// + /// Groups that an account or person belong to. + /// + /// + /// The Attribute values are the + /// UID value of each group that an account has membership in. + /// + public static readonly ConnectorAttributeInfo GROUPS = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.GROUPS_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.MULTIVALUED | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + } + #endregion + + #region OperationOptions + /// + /// Arbitrary options to be passed into various operations. + /// + /// + /// This serves + /// as a catch-all for extra options. + /// + public sealed class OperationOptions + { + /// + /// An option to use with that specified the scope + /// under which to perform the search. + /// + /// + /// To be used in conjunction with + /// . Must be one of the following values + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public const String OP_SCOPE = "SCOPE"; + public const String SCOPE_OBJECT = "object"; + public const String SCOPE_ONE_LEVEL = "onelevel"; + public const String SCOPE_SUBTREE = "subtree"; + + /// + /// An option to use with that specified the container + /// under which to perform the search. + /// + /// + /// Must be of type . + /// Should be implemented for those object classes whose + /// returns true. + /// + public const String OP_CONTAINER = "CONTAINER"; + + /// + /// An option to use with and possibly others + /// that specifies an account under which to execute the script/operation. + /// + /// + /// The specified account will appear to have performed any action that the + /// script/operation performs. + /// + /// Check the javadoc for a particular connector to see whether that + /// connector supports this option. + /// + /// + public static readonly string OP_RUN_AS_USER = "RUN_AS_USER"; + + /// + /// An option to use with and possibly others + /// that specifies a password under which to execute the script/operation. + /// + public static readonly string OP_RUN_WITH_PASSWORD = "RUN_WITH_PASSWORD"; + + /// + /// Determines the attributes to retrieve during and + /// . + /// + /// This option overrides the default behavior, which is for the connector to + /// return exactly the set of attributes that are identified as + /// in the + /// schema for that connector. + /// + /// + /// This option allows a client application to request additional + /// attributes that would not otherwise not be returned (generally + /// because such attributes are more expensive for a connector to fetch and + /// to format) and/or to request only a subset of the attributes that + /// would normally be returned. + /// + /// + public static readonly string OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; + + /// + /// An option to use with that specifies an opaque cookie + /// which is used by the connector to track its position in the set of query + /// results. + /// + public static readonly string OP_PAGED_RESULTS_COOKIE = "PAGED_RESULTS_COOKIE"; + + /// + /// An option to use with that specifies the policy used for calculating + /// the total number of paged results. + /// + public const string OP_TOTAL_PAGED_RESULTS_POLICY = "TOTAL_PAGED_RESULTS_POLICY"; + + /// + /// An option to use with that specifies the index within + /// the result set of the first result which should be returned. + /// + public static readonly string OP_PAGED_RESULTS_OFFSET = "PAGED_RESULTS_OFFSET"; + + /// + /// An option to use with that specifies the requested + /// page results page size. + /// + public static readonly string OP_PAGE_SIZE = "PAGE_SIZE"; + + /// + /// An option to use with that specifies the sort keys + /// which should be used for ordering the returned by + /// search request. + /// + public static readonly string OP_SORT_KEYS = "SORT_KEYS"; + + private readonly IDictionary _operationOptions; + + /// + /// Public only for serialization; please use . + /// + /// The options. + public OperationOptions(IDictionary operationOptions) + { + foreach (Object val in operationOptions.Values) + { + FrameworkUtil.CheckOperationOptionValue(val); + } + //clone options to do a deep copy in case anything + //is an array + IDictionary operationOptionsClone = (IDictionary)SerializerUtil.CloneObject(operationOptions); + _operationOptions = CollectionUtil.NewReadOnlyDictionary(operationOptionsClone); + } + + /// + /// Returns a map of options. + /// + /// + /// Each value in the map + /// must be of a type that the framework can serialize. + /// See for a list of supported types. + /// + /// A map of options. + public IDictionary Options + { + get + { + return _operationOptions; + } + } + + /// + /// Convenience method that returns . + /// + /// The value for . + public String Scope + { + get + { + return (String)CollectionUtil.GetValue(_operationOptions, OP_SCOPE, null); + } + } + + /// + /// Convenience method that returns . + /// + /// The value for . + public QualifiedUid getContainer + { + get + { + return (QualifiedUid)CollectionUtil.GetValue(_operationOptions, OP_CONTAINER, null); + } + } + + /// + /// Get the string array of attribute names to return in the object. + /// + public string[] AttributesToGet + { + get + { + return (string[])CollectionUtil.GetValue( + _operationOptions, OP_ATTRIBUTES_TO_GET, null); + } + } + + /// + /// Get the account to run the operation as.. + /// + public string RunAsUser + { + get + { + return (string)CollectionUtil.GetValue( + _operationOptions, OP_RUN_AS_USER, null); + } + } + + /// + /// Get the password to run the operation as.. + /// + public GuardedString RunWithPassword + { + get + { + return (GuardedString)CollectionUtil.GetValue( + _operationOptions, OP_RUN_WITH_PASSWORD, null); + } + } + + /// + /// Returns the opaque cookie which is used by the Connector to track its + /// position in the set of query results. Paged results will be enabled if + /// and only if the page size is non-zero. + /// + /// The cookie must be {@code null} in the initial search request sent by the + /// client. For subsequent search requests the client must include the cookie + /// returned with the previous search result, until the resource provider + /// returns a {@code null} cookie indicating that the final page of results + /// has been returned. + /// + /// + /// + /// The opaque cookie which is used by the Connector to track its + /// position in the set of search results, or {@code null} if paged + /// results are not requested (when the page size is 0), or if the + /// first page of results is being requested (when the page size is + /// non-zero). + /// + /// + /// Since 1.4 + public string PagedResultsCookie + { + get + { + return (string)CollectionUtil.GetValue( + _operationOptions, OP_PAGED_RESULTS_COOKIE, null); + } + } + + /// + /// Returns the used to calculate + /// . + /// + /// The count policy. + /// + /// Since 1.5 + public SearchResult.CountPolicy TotalPagedResultsPolicy + { + get + { + return (SearchResult.CountPolicy)CollectionUtil.GetValue( + _operationOptions, OP_TOTAL_PAGED_RESULTS_POLICY, SearchResult.CountPolicy.NONE); + } + } + + /// + /// Returns the index within the result set of the first result which should + /// be returned. Paged results will be enabled if and only if the page size + /// is non-zero. If the parameter is not present or a value less than 1 is + /// specified then the page following the previous page returned will be + /// returned. A value equal to or greater than 1 indicates that a specific + /// page should be returned starting from the position specified. + /// + /// The index within the result set of the first result which should + /// be returned. + /// + /// + /// Since 1.4 + public int? PagedResultsOffset + { + get + { + return (int?)CollectionUtil.GetValue( + _operationOptions, OP_PAGED_RESULTS_OFFSET, null); + } + } + + /// + /// Returns the requested page results page size or {@code 0} if paged + /// results are not required. For all paged result requests other than the + /// initial request, a cookie should be provided with the search request. See + /// for more information. + /// + /// The requested page results page size or {@code 0} if paged + /// results are not required. + /// + /// + /// Since 1.4 + public int? PageSize + { + get + { + return (int?)CollectionUtil.GetValue( + _operationOptions, OP_PAGE_SIZE, null); + } + } + + /// + /// Returns the sort keys which should be used for ordering the + /// s returned by this search request. + /// + /// The sort keys which should be used for ordering the + /// s returned by this search request (never + /// {@code null}). + /// Since 1.4 + public SortKey[] SortKeys + { + get + { + return (SortKey[])CollectionUtil.GetValue( + _operationOptions, OP_SORT_KEYS, null); + } + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("OperationOptions: ").Append(Options); + return bld.ToString(); + } + } + #endregion + + #region OperationOptionsBuilder + /// + /// Builder for . + /// + public sealed class OperationOptionsBuilder + { + private readonly IDictionary _options; + + /// + /// Create a builder with an empty set of options. + /// + /// new builder + /// Since 1.5 + public static OperationOptionsBuilder Create() + { + return new OperationOptionsBuilder(); + } + + /// + /// Create a builder with an empty set of options. + /// + public OperationOptionsBuilder() + { + _options = new Dictionary(); + } + + /// + /// Create a builder from an existing set of options. + /// + /// The existing set of options. Must not be null. + public OperationOptionsBuilder(OperationOptions options) + { + Assertions.NullCheck(options, "options"); + // clone options to do a deep copy in case anything + // is an array + IDictionary operationOptionsClone = (IDictionary)SerializerUtil + .CloneObject(options.Options); + _options = CollectionUtil.NewDictionary(operationOptionsClone); + } + + /// + /// Sets a given option and a value for that option. + /// + /// The name of the option + /// The value of the option. Must be one of the types that + /// we can serialize. + /// See for a list of supported types. + public OperationOptionsBuilder SetOption(String name, Object value) + { + if (name == null) + { + throw new ArgumentException("Argument 'value' cannot be null."); + } + //don't validate value here - we do that implicitly when + //we clone in the constructor of OperationOptions + _options[name] = value; + return this; + } + + /// + /// Returns a mutable reference of the options map. + /// + /// A mutable reference of the options map. + public IDictionary Options + { + get + { + //might as well be mutable since it's the builder and + //we don't want to deep copy anyway + return _options; + } + } + + /// + /// Creates the OperationOptions. + /// + /// The newly-created OperationOptions + public OperationOptions Build() + { + return new OperationOptions(_options); + } + + /// + /// Sets the option. + /// + /// list of names. + public string[] AttributesToGet + { + set + { + Assertions.NullCheck(value, "AttributesToGet"); + // don't validate value here - we do that in + // the constructor of OperationOptions - that's + // really the only place we can truly enforce this + _options[OperationOptions.OP_ATTRIBUTES_TO_GET] = value; + } + } + + /// + /// Set the run with password option. + /// + public GuardedString RunWithPassword + { + set + { + Assertions.NullCheck(value, "RunWithPassword"); + _options[OperationOptions.OP_RUN_WITH_PASSWORD] = value; + } + } + + /// + /// Set the run as user option. + /// + public string RunAsUser + { + set + { + Assertions.NullCheck(value, "RunAsUser"); + _options[OperationOptions.OP_RUN_AS_USER] = value; + } + } + /// + /// Convenience method to set + /// + /// The scope. May not be null. + /// A this reference to allow chaining + public string Scope + { + set + { + Assertions.NullCheck(value, "Scope"); + _options[OperationOptions.OP_SCOPE] = value; + } + } + + /// + /// Convenience method to set + /// + /// The container. May not be null. + /// A this reference to allow chaining + public QualifiedUid Container + { + set + { + Assertions.NullCheck(value, "Container"); + _options[OperationOptions.OP_CONTAINER] = value; + } + } + + + /// + /// Convenience method to set + /// + /// + /// + /// The pagedResultsCookie. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public string PagedResultsCookie + { + set + { + Assertions.NullCheck(value, "pagedResultsCookie"); + _options[OperationOptions.OP_PAGED_RESULTS_COOKIE] = value; + } + } + + /// + /// Sets the policy for calculating the total number of paged results. If no + /// count policy is supplied or paged results are not requested a default of + /// will be used. This will result in no count being + /// performed and no overhead incurred. + /// + /// Since 1.5 + public SearchResult.CountPolicy TotalPagedResultsPolicy + { + set + { + Assertions.NullCheck(value, "totalPagedResultsPolicy"); + _options[OperationOptions.OP_TOTAL_PAGED_RESULTS_POLICY] = value; + } + } + + /// + /// Convenience method to set + /// + /// + /// + /// The pagedResultsOffset. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public int? PagedResultsOffset + { + set + { + Assertions.NullCheck(value, "pagedResultsOffset"); + _options[OperationOptions.OP_PAGED_RESULTS_OFFSET] = value; + } + } + + /// + /// Convenience method to set + /// + /// + /// The pageSize. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public int? PageSize + { + set + { + Assertions.NullCheck(value, "pageSize"); + _options[OperationOptions.OP_PAGE_SIZE] = value; + } + } + + /// + /// Convenience method to set + /// + /// + /// The sort keys. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public IList SortKeys + { + set + { + Assertions.NullCheck(value, "sortKeys"); + SortKey[] array = new SortKey[((IList)value).Count]; + ((IList)value).CopyTo(array, 0); + _options[OperationOptions.OP_SORT_KEYS] = array; + } + } + + /// + /// Convenience method to set + /// + /// + /// The sort keys. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public OperationOptionsBuilder SetSortKeys(params SortKey[] sortKeys) + { + + Assertions.NullCheck(sortKeys, "sortKeys"); + _options[OperationOptions.OP_SORT_KEYS] = sortKeys; + return this; + } + + } + #endregion + + #region OperationOptionInfo + public sealed class OperationOptionInfo + { + private String _name; + private Type _type; + + public OperationOptionInfo(String name, + Type type) + { + Assertions.NullCheck(name, "name"); + Assertions.NullCheck(type, "type"); + FrameworkUtil.CheckOperationOptionType(type); + _name = name; + _type = type; + } + + public String Name + { + get + { + return _name; + } + } + + public Type OptionType + { + get + { + return _type; + } + } + + public override bool Equals(Object o) + { + if (o is OperationOptionInfo) + { + OperationOptionInfo other = + (OperationOptionInfo)o; + if (!_name.Equals(other._name)) + { + return false; + } + if (!_type.Equals(other._type)) + { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() + { + return _name.GetHashCode(); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("OperationOptionInfo("); + bld.Append(_name); + bld.Append(_type.ToString()); + bld.Append(')'); + return bld.ToString(); + } + + } + #endregion + + #region OperationOptionInfoBuilder + public sealed class OperationOptionInfoBuilder + { + private String _name; + private Type _type; + + public OperationOptionInfoBuilder() + { + } + + public OperationOptionInfoBuilder(String name, + Type type) + { + _name = name; + _type = type; + } + + public String Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + + public Type OptionType + { + get + { + return _type; + } + set + { + _type = value; + } + } + + public OperationOptionInfo Build() + { + return new OperationOptionInfo(_name, _type); + } + + public static OperationOptionInfo Build(String name, Type type) + { + return new OperationOptionInfoBuilder(name, type).Build(); + } + + public static OperationOptionInfo Build(String name) + { + return Build(name, typeof(string)); + } + + public static OperationOptionInfo BuildAttributesToGet() + { + return Build(OperationOptions.OP_ATTRIBUTES_TO_GET, typeof(string[])); + } + + public static OperationOptionInfo BuildRunWithPassword() + { + return Build(OperationOptions.OP_RUN_WITH_PASSWORD, typeof(GuardedString)); + } + + public static OperationOptionInfo BuildRunAsUser() + { + return Build(OperationOptions.OP_RUN_AS_USER); + } + public static OperationOptionInfo BuildScope() + { + return Build(OperationOptions.OP_SCOPE); + } + + public static OperationOptionInfo BuildContainer() + { + return Build(OperationOptions.OP_CONTAINER, typeof(QualifiedUid)); + } + + public static OperationOptionInfo BuildPagedResultsCookie() + { + return Build(OperationOptions.OP_PAGED_RESULTS_COOKIE); + } + + public static OperationOptionInfo BuildPagedResultsOffset() + { + return Build(OperationOptions.OP_PAGED_RESULTS_OFFSET, typeof(int?)); + } + + public static OperationOptionInfo BuildPageSize() + { + return Build(OperationOptions.OP_PAGE_SIZE, typeof(int?)); + } + + public static OperationOptionInfo BuildSortKeys() + { + return Build(OperationOptions.OP_SORT_KEYS, typeof(SortKey)); + } + } + #endregion + + #region QualifiedUid + /// + /// A fully-qualified uid. + /// + /// + /// That is, a pair of and + /// . + /// + public sealed class QualifiedUid + { + private readonly ObjectClass _objectClass; + private readonly Uid _uid; + + /// + /// Create a QualifiedUid. + /// + /// The object class. May not be null. + /// The uid. May not be null. + public QualifiedUid(ObjectClass objectClass, + Uid uid) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(uid, "uid"); + _objectClass = objectClass; + _uid = uid; + } + + /// + /// Returns the object class. + /// + /// The object class. + public ObjectClass ObjectClass + { + get + { + return _objectClass; + } + } + + /// + /// Returns the uid. + /// + /// The uid. + public Uid Uid + { + get + { + return _uid; + } + } + + /// + /// Returns true if o is a QualifiedUid and the object class and uid match. + /// + public override bool Equals(Object o) + { + if (o is QualifiedUid) + { + QualifiedUid other = (QualifiedUid)o; + return (_objectClass.Equals(other._objectClass) && + _uid.Equals(other._uid)); + } + return false; + } + + /// + /// Returns a hash code based on uid + /// + public override int GetHashCode() + { + return _uid.GetHashCode(); + } + + /// + /// Returns a string representation acceptible for debugging. + /// + public override String ToString() + { + return SerializerUtil.SerializeXmlObject(this, false); + } + + } + #endregion + + #region ResultsHandler + public class ResultsHandler + { + /// + /// Invoked each time a matching is returned from a + /// query request. + /// + /// + /// The matching ConnectorObject. + /// {@code true} if this handler should continue to be notified of + /// any remaining matching ConnectorObjects, or {@code false} if the + /// remaining ConnectorObjects should be skipped for some reason + /// (e.g. a client side size limit has been reached or the failed to + /// handle the last item). If returns {@code false} the last items + /// should be considers unhandled and in next page request it should + /// be the first item. + /// + /// + /// the implementor should throw a that + /// wraps any native exception (or that describes any other + /// problem during execution) that is serious enough to stop the + /// iteration. + public Func Handle; + } + #endregion + + #region Schema + /// + /// Determines the objects supported by a + /// . + /// + /// + /// The object is used to represent the basic objects that a + /// connector supports. This does not prevent a connector from supporting more. + /// Rather, this is informational for the caller of the connector to understand + /// a minimum support level. + /// The schema defines 4 primary data structures + /// + /// + /// Declared ObjectClasses (). + /// + /// + /// + /// Declared OperationOptionInfo (). + /// + /// + /// + /// Supported ObjectClasses by operation (). + /// + /// + /// + /// Supported OperationOptionInfo by operation(). + /// + /// + /// + /// TODO: add more to describe and what is expected from this call and how it is + /// used.. based on OperationalAttribute etc.. + /// + public sealed class Schema + { + private readonly ICollection _declaredObjectClasses; + private readonly ICollection _declaredOperationOptions; + private readonly IDictionary, ICollection> + _supportedObjectClassesByOperation; + private readonly IDictionary, ICollection> + _supportedOptionsByOperation; + + /// + /// Public only for serialization; please use + /// SchemaBuilder instead. + /// + /// + /// + public Schema(ICollection info, + ICollection options, + IDictionary, ICollection> supportedObjectClassesByOperation, + IDictionary, ICollection> supportedOptionsByOperation) + { + _declaredObjectClasses = CollectionUtil.NewReadOnlySet(info); + _declaredOperationOptions = CollectionUtil.NewReadOnlySet(options); + + //make read-only + { + IDictionary, ICollection> temp = + new Dictionary, ICollection>(); + foreach (KeyValuePair, ICollection> entry in + supportedObjectClassesByOperation) + { + SafeType op = + entry.Key; + ICollection resolvedClasses = + CollectionUtil.NewReadOnlySet(entry.Value); + temp[op] = resolvedClasses; + } + _supportedObjectClassesByOperation = CollectionUtil.AsReadOnlyDictionary(temp); + } + //make read-only + { + IDictionary, ICollection> temp = + new Dictionary, ICollection>(); + foreach (KeyValuePair, ICollection> entry in + supportedOptionsByOperation) + { + SafeType op = + entry.Key; + ICollection resolvedClasses = + CollectionUtil.NewReadOnlySet(entry.Value); + temp[op] = resolvedClasses; + } + _supportedOptionsByOperation = CollectionUtil.AsReadOnlyDictionary(temp); + } + } + + /// + /// Returns the set of object classes that are defined in the schema, regardless + /// of which operations support them. + /// + public ICollection ObjectClassInfo + { + get + { + return _declaredObjectClasses; + } + } + + /// + /// Returns the ObjectClassInfo for the given type. + /// + /// The type to find. + /// the ObjectClassInfo for the given type or null if not found. + public ObjectClassInfo FindObjectClassInfo(String type) + { + foreach (ObjectClassInfo info in _declaredObjectClasses) + { + if (info.Is(type)) + { + return info; + } + } + return null; + } + + /// + /// Returns the set of operation options that are defined in the schema, regardless + /// of which operations support them. + /// + /// The options defined in this schema. + public ICollection OperationOptionInfo + { + get + { + return _declaredOperationOptions; + } + } + + /// + /// Returns the OperationOptionInfo for the given name. + /// + /// The name to find. + /// the OperationOptionInfo for the given name or null if not found. + public OperationOptionInfo FindOperationOptionInfo(String name) + { + Assertions.NullCheck(name, "name"); + foreach (OperationOptionInfo info in _declaredOperationOptions) + { + if (info.Name.Equals(name)) + { + return info; + } + } + return null; + } + + /// + /// Returns the supported object classes for the given operation. + /// + /// The operation. + /// the supported object classes for the given operation. + public ICollection GetSupportedObjectClassesByOperation(SafeType apiop) + { + ICollection rv = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, apiop, null); + if (rv == null) + { + ICollection empty = + CollectionUtil.NewReadOnlySet(); + + return empty; + } + else + { + return rv; + } + } + + /// + /// Returns the supported options for the given operation. + /// + /// The operation. + /// the supported options for the given operation. + public ICollection GetSupportedOptionsByOperation(SafeType apiop) + { + ICollection rv = + CollectionUtil.GetValue(_supportedOptionsByOperation, apiop, null); + if (rv == null) + { + ICollection empty = + CollectionUtil.NewReadOnlySet(); + return empty; + } + else + { + return rv; + } + } + + /// + /// Returns the set of object classes that apply to a particular operation. + /// + /// the set of object classes that apply to a particular operation. + public IDictionary, ICollection> SupportedObjectClassesByOperation + { + get + { + return _supportedObjectClassesByOperation; + } + } + /// + /// Returns the set of operation options that apply to a particular operation. + /// + /// the set of operation options that apply to a particular operation. + public IDictionary, ICollection> SupportedOptionsByOperation + { + get + { + return _supportedOptionsByOperation; + } + } + + + + public override int GetHashCode() + { + return CollectionUtil.GetHashCode(_declaredObjectClasses); + } + + public override bool Equals(object o) + { + Schema other = o as Schema; + if (other != null) + { + if (!CollectionUtil.Equals(ObjectClassInfo, other.ObjectClassInfo)) + { + return false; + } + if (!CollectionUtil.Equals(OperationOptionInfo, other.OperationOptionInfo)) + { + return false; + } + if (!CollectionUtil.Equals(_supportedObjectClassesByOperation, + other._supportedObjectClassesByOperation)) + { + return false; + } + if (!CollectionUtil.Equals(_supportedOptionsByOperation, + other._supportedOptionsByOperation)) + { + return false; + } + return true; + } + return false; + } + + public override string ToString() + { + return SerializerUtil.SerializeXmlObject(this, false); + } + } + #endregion + + #region SchemaBuilder + /// + /// Simple builder class to help facilitate creating a object. + /// + public sealed class SchemaBuilder + { + private readonly ICollection _declaredObjectClasses + = new HashSet(); + private readonly ICollection _declaredOperationOptions + = new HashSet(); + + private readonly IDictionary, ICollection> + _supportedObjectClassesByOperation = + new Dictionary, ICollection>(); + private readonly IDictionary, ICollection> + _supportedOptionsByOperation = + new Dictionary, ICollection>(); + + private readonly ICollection> _defaultSupportedOperations; + + /// + /// + public SchemaBuilder(SafeType connectorClass) + { + Assertions.NullCheck(connectorClass, "connectorClass"); + _defaultSupportedOperations = FrameworkUtil.GetDefaultSupportedOperations(connectorClass); + } + + private bool ObjectClassOperation(SafeType op) + { + if (typeof(AuthenticationApiOp) == op.RawType || + typeof(CreateApiOp) == op.RawType || + typeof(DeleteApiOp) == op.RawType || + typeof(GetApiOp) == op.RawType || + typeof(ResolveUsernameApiOp) == op.RawType || + typeof(SearchApiOp) == op.RawType || + typeof(SyncApiOp) == op.RawType || + typeof(UpdateApiOp) == op.RawType) + { + return true; + } + return false; + } + + private bool OperationOptionOperation(SafeType op) + { + if (typeof(AuthenticationApiOp) == op.RawType || + typeof(CreateApiOp) == op.RawType || + typeof(DeleteApiOp) == op.RawType || + typeof(GetApiOp) == op.RawType || + typeof(ResolveUsernameApiOp) == op.RawType || + typeof(ScriptOnConnectorApiOp) == op.RawType || + typeof(ScriptOnResourceApiOp) == op.RawType || + typeof(SearchApiOp) == op.RawType || + typeof(SyncApiOp) == op.RawType || + typeof(UpdateApiOp) == op.RawType) + { + return true; + } + return false; + } + + /// + /// Adds another ObjectClassInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported classes for every operation defined by + /// the Connector. + /// + /// + /// If already defined + public void DefineObjectClass(ObjectClassInfo info) + { + Assertions.NullCheck(info, "info"); + if (_declaredObjectClasses.Contains(info)) + { + throw new InvalidOperationException("ObjectClass already defined: " + + info.ObjectType); + } + _declaredObjectClasses.Add(info); + foreach (SafeType op in _defaultSupportedOperations) + { + if (ObjectClassOperation(op)) + { + ICollection oclasses = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, op, null); + if (oclasses == null) + { + oclasses = new HashSet(); + _supportedObjectClassesByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + } + /// + /// Adds another ObjectClassInfo to the schema. + /// + /// Also, adds this to the set of supported classes for every operation + /// defined by the Connector. + /// + /// + /// + /// The SPI operation which use supports this + /// objectClassInfo + /// + /// + /// If already defined + public void DefineObjectClass(ObjectClassInfo info, params SafeType[] operations) + { + if (operations.Length > 0) + { + Assertions.NullCheck(info, "objectClassInfo"); + if (_declaredObjectClasses.Contains(info)) + { + throw new InvalidOperationException("ObjectClass already defined: " + info.ObjectType); + } + _declaredObjectClasses.Add(info); + foreach (SafeType spi in operations) + { + if (typeof(SchemaOp) == spi.RawType || + typeof(ScriptOnConnectorOp) == spi.RawType || + typeof(ScriptOnResourceOp) == spi.RawType || + typeof(TestOp) == spi.RawType) + { + continue; + } + IEnumerable> apiOperations = FrameworkUtil.Spi2Apis(spi).Intersect(_defaultSupportedOperations); + foreach (SafeType op in apiOperations) + { + if (ObjectClassOperation(op)) + { + ICollection oclasses = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, op, null); + if (oclasses == null) + { + oclasses = new HashSet(); + _supportedObjectClassesByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + } + } + else + { + DefineObjectClass(info); + } + } + /// + /// Adds another OperationOptionInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported options for every operation defined by + /// the Connector. + /// + public void DefineOperationOption(OperationOptionInfo info) + { + Assertions.NullCheck(info, "info"); + if (_declaredOperationOptions.Contains(info)) + { + throw new InvalidOperationException("OperationOption already defined: " + + info.Name); + } + _declaredOperationOptions.Add(info); + foreach (SafeType op in _defaultSupportedOperations) + { + if (OperationOptionOperation(op)) + { + ICollection oclasses = + CollectionUtil.GetValue(_supportedOptionsByOperation, op, null); + if (oclasses == null) + { + oclasses = new HashSet(); + _supportedOptionsByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + } + /// + /// Adds another OperationOptionInfo to the schema. Also, adds this to the + /// set of supported options for operation defined. + /// + /// + /// + /// + /// + /// If already defined + public void DefineOperationOption(OperationOptionInfo info, params SafeType[] operations) + { + if (operations.Length > 0) + { + Assertions.NullCheck(info, "info"); + if (_declaredOperationOptions.Contains(info)) + { + throw new InvalidOperationException("OperationOption already defined: " + info.Name); + } + _declaredOperationOptions.Add(info); + foreach (SafeType spi in operations) + { + if (typeof(SchemaOp) == spi.RawType || + typeof(TestOp) == spi.RawType) + { + continue; + } + IEnumerable> apiOperations = FrameworkUtil.Spi2Apis(spi).Intersect(_defaultSupportedOperations); + foreach (SafeType op in apiOperations) + { + if (OperationOptionOperation(op)) + { + ICollection oclasses = + CollectionUtil.GetValue(_supportedOptionsByOperation, op, null); + if (oclasses == null) + { + oclasses = new HashSet(); + _supportedOptionsByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + } + } + else + { + DefineOperationOption(info); + } + } + + /// + /// Adds another ObjectClassInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported classes for every operation defined by + /// the Connector. + /// + /// If already defined + public void DefineObjectClass(String type, ICollection attrInfo) + { + ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); + bld.ObjectType = type; + bld.AddAllAttributeInfo(attrInfo); + ObjectClassInfo obj = bld.Build(); + DefineObjectClass(obj); + } + + /// + /// Adds another OperationOptionInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported options for every operation defined by + /// the Connector. + /// + /// If already defined + public void DefineOperationOption(String optionName, Type type) + { + OperationOptionInfoBuilder bld = new OperationOptionInfoBuilder(); + bld.Name = (optionName); + bld.OptionType = (type); + OperationOptionInfo info = bld.Build(); + DefineOperationOption(info); + } + + /// + /// Adds the given ObjectClassInfo as a supported ObjectClass for + /// the given operation. + /// + /// The SPI operation + /// The ObjectClassInfo + /// If the given ObjectClassInfo was + /// not already defined using . + public void AddSupportedObjectClass(SafeType op, + ObjectClassInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + IEnumerable> apis = + FrameworkUtil.Spi2Apis(op).Intersect(_defaultSupportedOperations); + if (!_declaredObjectClasses.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + + " not defined in schema."); + } + foreach (SafeType api in apis) + { + if (ObjectClassOperation(api)) + { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, api, null); + if (infos == null) + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); + } + if (infos.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + + " already supported for operation " + op); + } + infos.Add(def); + } + } + } + + /// + /// Removes the given ObjectClassInfo as a supported ObjectClass for + /// the given operation. + /// + /// The SPI operation + /// The ObjectClassInfo + /// If the given ObjectClassInfo was + /// not already defined using . + public void RemoveSupportedObjectClass(SafeType op, + ObjectClassInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection> apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredObjectClasses.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + + " not defined in schema."); + } + foreach (SafeType api in apis) + { + if (ObjectClassOperation(api)) + { + if (_defaultSupportedOperations.Contains(api)) + { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, api, null); + if (infos == null || !infos.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + + " already removed for operation " + op); + } + infos.Remove(def); + } + else + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); + } + } + } + } + /// + /// Adds the given OperationOptionInfo as a supported option for + /// the given operation. + /// + /// The SPI operation + /// The OperationOptionInfo + /// If the given OperationOptionInfo was + /// not already defined using . + public void AddSupportedOperationOption(SafeType op, + OperationOptionInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + IEnumerable> apis = + FrameworkUtil.Spi2Apis(op).Intersect(_defaultSupportedOperations); + if (!_declaredOperationOptions.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + + " not defined in schema."); + } + foreach (SafeType api in apis) + { + if (OperationOptionOperation(api)) + { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation, api, null); + if (infos == null) + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); + } + if (infos.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + + " already supported for operation " + op); + } + infos.Add(def); + } + } + } + + /// + /// Removes the given OperationOptionInfo as a supported option for + /// the given operation. + /// + /// The SPI operation + /// The OperationOptionInfo + /// If the given OperationOptionInfo was + /// not already defined using . + public void RemoveSupportedOperationOption(SafeType op, + OperationOptionInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection> apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredOperationOptions.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + + " not defined in schema."); + } + foreach (SafeType api in apis) + { + if (OperationOptionOperation(api)) + { + if (_defaultSupportedOperations.Contains(api)) + { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation, api, null); + if (infos == null || !infos.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + + " already removed for operation " + op); + } + infos.Remove(def); + } + else + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); + } + } + } + } + + /// + /// Clears the operation-specific supported classes. + /// + /// + /// Normally, when + /// you add an ObjectClass, using , + /// it is added to all operations. You may then remove those that you need + /// using . You + /// may wish, as an alternative to clear everything out and instead add using + /// . + /// + public void ClearSupportedObjectClassesByOperation() + { + foreach (ICollection values in + _supportedObjectClassesByOperation.Values) + { + values.Clear(); + } + } + /// + /// Clears the operation-specific supported options. + /// + /// + /// Normally, when + /// you add an OperationOptionInfo, using , + /// it is added to all operations. You may then remove those that you need + /// using . You + /// may wish, as an alternative to clear everything out and instead add using + /// . + /// + public void ClearSupportedOptionsByOperation() + { + foreach (ICollection values in + _supportedOptionsByOperation.Values) + { + values.Clear(); + } + } + + /// + /// Builds the object based on the s + /// added so far. + /// + /// new Schema object based on the info provided. + public Schema Build() + { + if (_declaredObjectClasses.Count == 0) + { + String ERR = "Must be at least one ObjectClassInfo object!"; + throw new InvalidOperationException(ERR); + } + return new Schema(_declaredObjectClasses, + _declaredOperationOptions, + _supportedObjectClassesByOperation, + _supportedOptionsByOperation); + } + } + #endregion + + #region Script + /// + /// Represents a script in a scripting language. + /// + /// 1.1 + public sealed class Script + { + + private readonly string scriptLanguage; + private readonly string scriptText; + + internal Script(string scriptLanguage, string scriptText) + { + Assertions.BlankCheck(scriptLanguage, "scriptLanguage"); + Assertions.NullCheck(scriptText, "scriptText"); // Allow empty text. + this.scriptLanguage = scriptLanguage; + this.scriptText = scriptText; + } + + /// + /// Returns the language of this script. + /// + /// the script language; never null. + public string ScriptLanguage + { + get + { + return scriptLanguage; + } + } + + /// + /// Returns the text of this script. + /// + /// the script text; never null. + public string ScriptText + { + get + { + return scriptText; + } + } + + public override int GetHashCode() + { + return scriptLanguage.GetHashCode() ^ scriptText.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (obj is Script) + { + Script other = (Script)obj; + if (!scriptLanguage.Equals(other.scriptLanguage)) + { + return false; + } + if (!scriptText.Equals(other.scriptText)) + { + return false; + } + return true; + } + return false; + } + + public override string ToString() + { + // Text can be large, probably should not be included. + return "Script: " + scriptLanguage; + } + } + #endregion + + #region ScriptBuilder + /// + /// Builder for . + /// + public class ScriptBuilder + { + /// + /// Creates a new ScriptBuilder. + /// + public ScriptBuilder() + { + } + + /// + /// Gets/sets the language of the script. + /// + public string ScriptLanguage + { + get; + set; + } + + /// + /// Gets/sets the text of the script. + /// + public string ScriptText + { + get; + set; + } + + /// + /// Creates a Script. + /// + /// + /// Prior to calling this method the language + /// and the text should have been set. + /// + /// a new script; never null. + public Script Build() + { + return new Script(ScriptLanguage, ScriptText); + } + } + #endregion + + #region ScriptContext + /// + /// Encapsulates a script and all of its parameters. + /// + /// + /// + public sealed class ScriptContext + { + private readonly String _scriptLanguage; + private readonly String _scriptText; + private readonly IDictionary _scriptArguments; + + /// + /// Public only for serialization; please use . + /// + /// The script language. Must not be null. + /// The script text. Must not be null. + /// The script arguments. May be null. + public ScriptContext(String scriptLanguage, + String scriptText, + IDictionary scriptArguments) + { + + if (StringUtil.IsBlank(scriptLanguage)) + { + throw new ArgumentException("Argument 'scriptLanguage' must be specified"); + } + if (StringUtil.IsBlank(scriptText)) + { + throw new ArgumentException("Argument 'scriptText' must be specified"); + } + //clone script arguments and options - this serves two purposes + //1)makes sure everthing is serializable + //2)does a deep copy + IDictionary scriptArgumentsClone = (IDictionary)SerializerUtil.CloneObject(scriptArguments); + _scriptLanguage = scriptLanguage; + _scriptText = scriptText; + _scriptArguments = CollectionUtil.NewReadOnlyDictionary(scriptArgumentsClone); + } + + /// + /// Identifies the language in which the script is written + /// (e.g., bash, csh, + /// Perl4 or Python). + /// + /// The script language. + public String ScriptLanguage + { + get + { + return _scriptLanguage; + } + } + + /// + /// Returns the text (i.e., actual characters) of the script. + /// + /// The text of the script. + public String ScriptText + { + get + { + return _scriptText; + } + } + + /// + /// Returns a map of arguments to be passed to the script. + /// + /// + /// Values must be types that the framework can serialize. + /// See for a list of supported types. + /// + /// A map of arguments to be passed to the script. + public IDictionary ScriptArguments + { + get + { + return _scriptArguments; + } + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("ScriptContext: "); + // poor man's to string method. + IDictionary map = new Dictionary(); + map["Language"] = ScriptLanguage; + map["Text"] = ScriptText; + map["Arguments"] = ScriptArguments; + bld.Append(map.ToString()); + return bld.ToString(); + } + + } + #endregion + + #region ScriptContextBuilder + /// + /// Builds an . + /// + public sealed class ScriptContextBuilder + { + private String _scriptLanguage; + private String _scriptText; + private readonly IDictionary _scriptArguments = new + Dictionary(); + + /// + /// Creates an empty builder. + /// + public ScriptContextBuilder() + { + + } + + /// + /// Creates a builder with the required parameters specified. + /// + /// a string that identifies the language + /// in which the script is written + /// (e.g., bash, csh, + /// Perl4 or Python). + /// The text (i.e., actual characters) of the script. + public ScriptContextBuilder(String scriptLanguage, + String scriptText) + { + _scriptLanguage = scriptLanguage; + _scriptText = scriptText; + } + + /// + /// Identifies the language in which the script is written + /// (e.g., bash, csh, + /// Perl4 or Python). + /// + /// The script language. + public String ScriptLanguage + { + get + { + return _scriptLanguage; + } + set + { + _scriptLanguage = value; + } + } + + /// + /// Returns the actual characters of the script. + /// + /// the actual characters of the script. + public String ScriptText + { + get + { + return _scriptText; + } + set + { + _scriptText = value; + } + } + + /// + /// Adds or sets an argument to pass to the script. + /// + /// The name of the argument. Must not be null. + /// The value of the argument. Must be one of + /// type types that the framework can serialize. + /// + public ScriptContextBuilder AddScriptArgument(String name, Object value) + { + if (name == null) + { + throw new ArgumentException("Argument 'name' cannot be null."); + } + //don't validate value here - we do that implicitly when + //we clone in the constructor of ScriptRequest + _scriptArguments[name] = value; + return this; + } + + /// + /// Removes the given script argument. + /// + /// The name of the argument. Must not be null. + public ScriptContextBuilder RemoveScriptArgument(String name) + { + if (name == null) + { + throw new ArgumentException("Argument 'name' cannot be null."); + } + _scriptArguments.Remove(name); + return this; + } + + /// + /// Returns a mutable reference of the script arguments map. + /// + /// A mutable reference of the script arguments map. + public IDictionary ScriptArguments + { + get + { + //might as well be mutable since it's the builder and + //we don't want to deep copy anyway + return _scriptArguments; + } + } + + /// + /// Creates a ScriptContext. + /// + /// + /// The scriptLanguage and scriptText + /// must be set prior to calling this. + /// + /// The ScriptContext. + public ScriptContext Build() + { + return new ScriptContext(_scriptLanguage, + _scriptText, + _scriptArguments); + } + } + #endregion + + #region SearchResult + /// + /// The final result of a query request returned after all connector objects + /// matching the request have been returned. In addition to indicating that no + /// more objects are to be returned by the search, the search result will contain + /// page results state information if result paging has been enabled for the + /// search. + /// + /// Since 1.4 + public sealed class SearchResult + { + + /// + /// An enum of count policy types. + /// + /// + /// + public enum CountPolicy + { + /// + /// There should be no count returned. No overhead should be incurred. + /// + NONE, + + /// + /// Estimated count may be used. If no estimation is available it is up + /// to the implementor whether to return an count or + /// . It should be known to the client which was used as in + /// + /// + ESTIMATE, + + /// + /// Exact count is required. + /// + EXACT + } + + /// + /// The value provided when no count is known or can reasonably be supplied. + /// + public const int NoCount = -1; + + private readonly string _pagedResultsCookie; + private readonly CountPolicy _totalPagedResultsPolicy; + private readonly int _totalPagedResults; + private readonly int _remainingPagedResults; + + /// + /// Creates a new search result with a {@code null} paged results cookie and + /// no estimate of the total number of remaining results. + /// + public SearchResult() + : this(null, CountPolicy.NONE, NoCount, NoCount) + { + } + + /// + /// Creates a new search result with the provided paged results cookie and + /// estimate of the total number of remaining results. + /// + /// + /// The opaque cookie which should be used with the next paged + /// results search request, or {@code null} if paged results were + /// not requested, or if there are not more pages to be returned. + /// + /// An estimate of the total number of remaining results to be + /// returned in subsequent paged results search requests, or + /// {@code -1} if paged results were not requested, or if the + /// total number of remaining results is unknown. + public SearchResult(string pagedResultsCookie, int remainingPagedResults) + : this(pagedResultsCookie, CountPolicy.NONE, NoCount, remainingPagedResults) + { + } + + /// + /// Creates a new query response with the provided paged results cookie and a + /// count of the total number of resources according to + /// . + /// + /// + /// The opaque cookie which should be used with the next paged + /// results query request, or {@code null} if paged results were + /// not requested, or if there are not more pages to be returned. + /// + /// The policy that was used to calculate + /// . If none is specified ({@code null} + /// ), then is assumed. + /// + /// The total number of paged results requested in adherence to + /// the in + /// the request, or if paged results were not + /// requested, the count policy is {@code NONE}, or if the total + /// number of results is unknown. + /// + /// An estimate of the total number of remaining results to be + /// returned in subsequent paged results query requests, or + /// {@code -1} if paged results were not requested, or if the + /// total number of remaining results is unknown. + /// Since 1.5 + public SearchResult(string pagedResultsCookie, CountPolicy totalPagedResultsPolicy, int totalPagedResults, int remainingPagedResults) + { + _pagedResultsCookie = pagedResultsCookie; + _totalPagedResultsPolicy = totalPagedResultsPolicy; + _totalPagedResults = totalPagedResults; + _remainingPagedResults = remainingPagedResults; + } + + /// + /// Returns the opaque cookie which should be used with the next paged + /// results search request. + /// + /// The opaque cookie which should be used with the next paged + /// results search request, or {@code null} if paged results were not + /// requested, or if there are not more pages to be returned. + public string PagedResultsCookie + { + get + { + return _pagedResultsCookie; + } + } + + /// + /// Returns the policy that was used to calculate the + /// {@literal totalPagedResults}. + /// + /// The count policy. + /// Since 1.5 + public CountPolicy TotalPagedResultsPolicy + { + get + { + return _totalPagedResultsPolicy; + } + } + + /// + /// Returns the total number of paged results in adherence with the + /// in the request or + /// if paged results were not requested, the count policy + /// is {@code NONE}, or the total number of paged results is unknown. + /// + /// A count of the total number of paged results to be returned in + /// subsequent paged results query requests, or if + /// paged results were not requested, or if the total number of paged + /// results is unknown. + /// Since 1.5 + public int TotalPagedResults + { + get + { + return _totalPagedResults; + } + } + + /// + /// Returns an estimate of the total number of remaining results to be + /// returned in subsequent paged results search requests. + /// + /// An estimate of the total number of remaining results to be + /// returned in subsequent paged results search requests, or + /// {@code -1} if paged results were not requested, or if the total + /// number of remaining results is unknown. + public int RemainingPagedResults + { + get + { + return _remainingPagedResults; + } + } + + } + #endregion + + #region SortKey + /// + /// A sort key which can be used to specify the order in which connector objects + /// should be included in the results of a search request. + /// + /// + /// Since 1.4 + public sealed class SortKey + { + + private readonly string _field; + private readonly bool _isAscendingOrder; + + public SortKey(string field, bool isAscendingOrder) + { + this._field = Assertions.BlankChecked(field, "field"); + this._isAscendingOrder = isAscendingOrder; + } + + /// + /// Returns the sort key field. + /// + /// The sort key field. + public string Field + { + get + { + return _field; + } + } + + /// + /// Returns {@code true} if this sort key is in ascending order, or + /// {@code false} if it is in descending order. + /// + /// + /// {@code true} if this sort key is in ascending order, or + /// {@code false} if it is in descending ord)er. + /// + public bool IsAscendingOrder() + { + return _isAscendingOrder; + } + + /// + /// Creates a new ascending-order sort key for the provided field. + /// + /// + /// The sort key field. + /// + /// A new ascending-order sort key. + /// + /// If {@code field} is not a valid attribute name. + /// + public static SortKey AscendingOrder(string field) + { + return new SortKey(field, true); + } + + /// + /// Creates a new descending-order sort key for the provided field. + /// + /// + /// The sort key field. + /// + /// A new descending-order sort key. + /// + /// If {@code field} is not a valid attribute name. + /// + public static SortKey DescendingOrder(string field) + { + return new SortKey(field, false); + } + + /// + /// Creates a new sort key having the same field as the provided key, but in + /// reverse sort order. + /// + /// + /// The sort key to be reversed. + /// + /// The reversed sort key. + public static SortKey ReverseOrder(SortKey key) + { + return new SortKey(key._field, !key._isAscendingOrder); + } + } + #endregion + + #region ISubscription + /// + /// A SubscriptionHandler represents a subscription to an asynchronous event + /// channel. + /// + /// since 1.5 + public interface ISubscription : IDisposable + { + /// + /// Indicates whether this Subscription is currently unsubscribed. + /// + /// true if this Subscription is currently + /// unsubscribed, false otherwise + bool Unsubscribed { get; } + } + + public class CancellationSubscription : ISubscription + { + private readonly CancellationTokenSource _cts; + + /// + /// Gets the used by this CancellationSubscription. + /// + /// + public CancellationToken Token + { + get + { + return _cts.Token; + } + } + + /// + /// Gets a value that indicates whether the object is disposed. + /// + /// + public bool IsDisposed + { + get + { + return _cts.IsCancellationRequested; + } + } + + /// + /// Initializes a new instance of the + /// class that uses an existing . + /// + /// + /// used for cancellation. + /// is null. + public CancellationSubscription(CancellationTokenSource cts) + { + if (cts == null) + throw new ArgumentNullException("cts"); + _cts = cts; + } + + /// + /// Initializes a new instance of the class that uses a + /// new . + /// + /// + public CancellationSubscription() + : this(new CancellationTokenSource()) + { + } + + /// + /// Cancels the underlying . + /// + /// + public void Dispose() + { + _cts.Cancel(); + } + + /// + /// Indicates whether this Subscription is currently unsubscribed. + /// + /// true if this Subscription is currently + /// unsubscribed, false otherwise + public bool Unsubscribed + { + get { return IsDisposed; } + } + } + + #endregion + + #region SyncDelta + /// + /// Represents a change to an object in a resource. + /// + /// + /// + public sealed class SyncDelta + { + private readonly SyncToken _token; + private readonly SyncDeltaType _deltaType; + private readonly Uid _previousUid; + private readonly ObjectClass _objectClass; + private readonly Uid _uid; + private readonly ConnectorObject _object; + + /// + /// Creates a SyncDelata + /// + /// The token. Must not be null. + /// The delta. Must not be null. + /// The previousUid. Can be null. + /// The objectClass. Can be null. + /// The uid. Must not be null. + /// The object that has changed. May be null for delete. + internal SyncDelta(SyncToken token, SyncDeltaType deltaType, + Uid previousUid, ObjectClass objectClass, Uid uid, + ConnectorObject obj) + { + Assertions.NullCheck(token, "token"); + Assertions.NullCheck(deltaType, "deltaType"); + Assertions.NullCheck(uid, "uid"); + + //do not allow previous Uid for anything else than create or update + if (previousUid != null && (deltaType == SyncDeltaType.DELETE || deltaType == SyncDeltaType.CREATE)) + { + throw new ArgumentException("The previous Uid can only be specified for create_or_update or udate."); + } + + //only allow null object for delete + if (obj == null && + deltaType != SyncDeltaType.DELETE) + { + throw new ArgumentException("ConnectorObject must be specified for anything other than delete."); + } + + //if object not null, make sure its Uid + //matches + if (obj != null) + { + if (!uid.Equals(obj.Uid)) + { + throw new ArgumentException("Uid does not match that of the object."); + } + if (!objectClass.Equals(obj.ObjectClass)) + { + throw new ArgumentException("ObjectClass does not match that of the object."); + } + } + + _token = token; + _deltaType = deltaType; + _previousUid = previousUid; + _objectClass = objectClass; + _uid = uid; + _object = obj; + + } + + /// + /// If the change described by this SyncDelta modified the + /// object's Uid, this method returns the Uid before the change. + /// + /// + /// Not + /// all resources can determine the previous Uid, so this method can + /// return null. + /// + /// the previous Uid or null if it could not be determined + /// or the change did not modify the Uid. + public Uid PreviousUid + { + get + { + return _previousUid; + } + } + + /// + /// If the change described by this SyncDelta.DELETE and the + /// deleted object value is null, this method returns the + /// ObjectClass of the deleted object. If operation syncs + /// + /// this must be set, otherwise this method can return null. + /// + /// the ObjectClass of the deleted object. + public ObjectClass ObjectClass + { + get + { + return _objectClass; + } + } + + /// + /// Returns the Uid of the object that changed. + /// + /// the Uid of the object that changed. + public Uid Uid + { + get + { + return _uid; + } + } + + /// + /// Returns the connector object that changed. + /// + /// + /// This + /// may be null in the case of delete. + /// + /// The object or possibly null if this + /// represents a delete. + public ConnectorObject Object + { + get + { + return _object; + } + } + + /// + /// Returns the SyncToken of the object that changed. + /// + /// the SyncToken of the object that changed. + public SyncToken Token + { + get + { + return _token; + } + } + + /// + /// Returns the type of the change the occured. + /// + /// The type of change that occured. + public SyncDeltaType DeltaType + { + get + { + return _deltaType; + } + } + + + public override String ToString() + { + IDictionary values = new Dictionary(); + values["Token"] = _token; + values["DeltaType"] = _deltaType; + values["PreviousUid"] = _previousUid; + values["ObjectClass"] = _objectClass; + values["Uid"] = _uid; + values["Object"] = _object; + return values.ToString(); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = (_token != null ? _token.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (int)_deltaType; + hashCode = (hashCode * 397) ^ (_previousUid != null ? _previousUid.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (_objectClass != null ? _objectClass.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (_uid != null ? _uid.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (_object != null ? _object.GetHashCode() : 0); + return hashCode; + } + } + + private bool Equals(SyncDelta other) + { + return Equals(_token, other._token) && _deltaType == other._deltaType && Equals(_previousUid, other._previousUid) && Equals(_objectClass, other._objectClass) && Equals(_uid, other._uid) && Equals(_object, other._object); + } + + public override bool Equals(Object o) + { + if (o is SyncDelta) + { + SyncDelta other = (SyncDelta)o; + if (!_token.Equals(other._token)) + { + return false; + } + if (!_deltaType.Equals(other._deltaType)) + { + return false; + } + if (_previousUid == null) + { + if (other._previousUid != null) + { + return false; + } + } + else if (!_previousUid.Equals(other._previousUid)) + { + return false; + } + if (_objectClass == null) + { + if (other._objectClass != null) + { + return false; + } + } + else if (!_objectClass.Equals(other._objectClass)) + { + return false; + } + if (!_uid.Equals(other._uid)) + { + return false; + } + if (_object == null) + { + if (other._object != null) + { + return false; + } + } + else if (!_object.Equals(other._object)) + { + return false; + } + return true; + } + return false; + } + } + #endregion + + #region SyncDeltaBuilder + /// + /// Builder for . + /// + public sealed class SyncDeltaBuilder + { + private SyncToken _token; + private SyncDeltaType _deltaType; + private Uid _previousUid; + private ObjectClass _objectClass; + private Uid _uid; + private ConnectorObject _object; + + /// + /// Create a new SyncDeltaBuilder + /// + public SyncDeltaBuilder() + { + + } + + /// + /// Creates a new SyncDeltaBuilder whose + /// values are initialized to those of the delta. + /// + /// The original delta. + public SyncDeltaBuilder(SyncDelta delta) + { + _token = delta.Token; + _deltaType = delta.DeltaType; + _previousUid = delta.PreviousUid; + _objectClass = delta.ObjectClass; + _uid = delta.Uid; + _object = delta.Object; + } + + /// + /// Returns the SyncToken of the object that changed. + /// + /// the SyncToken of the object that changed. + public SyncToken Token + { + get + { + return _token; + } + set + { + _token = value; + } + } + + /// + /// Returns the type of the change that occurred. + /// + /// The type of change that occurred. + public SyncDeltaType DeltaType + { + get + { + return _deltaType; + } + set + { + _deltaType = value; + } + } + + /// + /// Returns the Uid before the change. + /// + /// the Uid before the change. + public Uid PreviousUid + { + get + { + return _previousUid; + } + set + { + _previousUid = value; + } + } + + /// + /// Returns the ObjectClass of Deleted object. + /// + /// the ObjectClass of Deleted object. + public ObjectClass ObjectClass + { + get + { + return _objectClass; + } + set + { + _objectClass = value; + } + } + + + /// + /// Returns the Uid of the object that changed. + /// + /// + /// Note that this is implicitly set when you call + /// . + /// + /// the Uid of the object that changed. + public Uid Uid + { + get + { + return _uid; + } + set + { + _uid = value; + } + } + + /// + /// Returns the object that changed. + /// + /// + /// Sets the object that changed and implicitly + /// sets Uid if object is not null. + /// + /// The object that changed. May be null for + /// deletes. + public ConnectorObject Object + { + get + { + return _object; + } + set + { + _object = value; + if (value != null) + { + _uid = value.Uid; + _objectClass = value.ObjectClass; + } + } + } + + /// + /// Creates a SyncDelta. + /// + /// + /// Prior to calling the following must be specified: + /// + /// + /// (for anything other than delete) + /// + /// + /// + /// (this is implictly set when calling ) + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SyncDelta Build() + { + return new SyncDelta(_token, _deltaType, _previousUid, _objectClass, _uid, _object); + } + } + #endregion + + #region SyncDeltaType + /// + /// The type of change. + /// + public enum SyncDeltaType + { + /// + /// The change represents either a create or an update in + /// the resource. + /// + /// + /// These are combined into a single value because: + /// + /// + /// Many resources will not be able to distinguish a create from an update. + /// Those that have an audit log will be able to. However, many implementations + /// will only have the current record and a modification timestamp. + /// + /// + /// + /// Regardless of whether or not the resource can distinguish the two cases, + /// the application needs to distinguish. + /// + /// + /// + /// + CREATE_OR_UPDATE, + + /// + /// The change represents a DELETE in the resource + /// + DELETE, + + /// + /// The change represents a CREATE in the resource + /// + /// + /// Experimental type to support better event mechanism where it's possible. + /// @see #CREATE_OR_UPDATE + /// + CREATE, + + /// + /// The change represents a UPDATE in the resource + /// + /// + /// Experimental type to support better event mechanism where it's possible. + /// @see #CREATE_OR_UPDATE + /// + UPDATE + } + #endregion + + #region SyncResultsHandler + public class SyncResultsHandler + { + /// + /// Called to handle a delta in the stream. + /// + /// + /// Will be called multiple times, + /// once for each result. Although a callback, this is still invoked + /// synchronously. That is, it is guaranteed that following a call to + /// no + /// more invocations to will be performed. + /// + /// The change + /// True if the application wants to continue processing more + /// results. + /// If the application encounters an exception. This will stop + /// the interation and the exception will be propogated back to + /// the application. + public Func Handle; + } + #endregion + + #region SyncToken + /// + /// Abstract "place-holder" for synchronization. + /// + /// + /// The application must not make + /// any attempt to interpret the value of the token. From the standpoint of the + /// application the token is merely a black-box. The application may only persist + /// the value of the token for use on subsequent synchronization requests. + /// + /// What this token represents is entirely connector-specific. On some connectors + /// this might be a last-modified value. On others, it might be a unique ID of a + /// log table entry. On others such as JMS, this might be a dummy value since + /// JMS itself keeps track of the state of the sync. + /// + /// + public sealed class SyncToken + { + + private Object _value; + + /// + /// Creates a new + /// + /// May not be null. TODO: define set of allowed value types + /// (currently same as set of allowed attribute values). + public SyncToken(Object value) + { + Assertions.NullCheck(value, "value"); + FrameworkUtil.CheckAttributeValue(value); + _value = value; + } + + /// + /// Returns the value for the token. + /// + /// The value for the token. + public Object Value + { + get + { + return _value; + } + } + + public override String ToString() + { + return "SyncToken: " + _value.ToString(); + } + + public override int GetHashCode() + { + return CollectionUtil.GetHashCode(_value); + } + + public override bool Equals(Object o) + { + if (o is SyncToken) + { + SyncToken other = (SyncToken)o; + return CollectionUtil.Equals(_value, other._value); + } + return false; + } + + + } + #endregion + + #region Uid + public sealed class Uid : ConnectorAttribute + { + + public static readonly string NAME = ConnectorAttributeUtil.CreateSpecialName("UID"); + + private readonly String _revision; + + public Uid(String value) + : base(NAME, CollectionUtil.NewReadOnlyList(Check(value))) + { + _revision = null; + } + + public Uid(String value, string revision) + : base(NAME, CollectionUtil.NewReadOnlyList(Check(value))) + { + if (StringUtil.IsBlank(revision)) + { + throw new System.ArgumentException("Revision value must not be blank!"); + } + this._revision = revision; + } + + /// + /// Throws an if the value passed in blank. + /// + private static String Check(String value) + { + if (StringUtil.IsBlank(value)) + { + String ERR = "Uid value must not be blank!"; + throw new ArgumentException(ERR); + } + return value; + } + + /// + /// Obtain a string representation of the value of this attribute, which + /// value uniquely identifies a on the target + /// resource. + /// + /// value that uniquely identifies an object. + public String GetUidValue() + { + return ConnectorAttributeUtil.GetStringValue(this); + } + + /// + /// Return the string representation of the revision value of the + ///

+ /// The revision number specifies a given version ot the + /// identified by the + /// + ///

+ /// null if the connector does not support the MVCC and does not set + /// this value otherwise return the revision number of the object. + public string Revision + { + get + { + return _revision; + } + } + } + #endregion + + #region ConnectorAttributesAccessor + /// + /// Attributes Accessor convenience methods for accessing attributes. + /// + /// + /// This class wraps a set of attributes to make lookup faster than the + /// method, since that method must + /// re-create the map each time. + /// + /// Warren Strange + public class ConnectorAttributesAccessor + { + + IDictionary _attrMap; + + public ConnectorAttributesAccessor(ICollection attrs) + { + _attrMap = ConnectorAttributeUtil.ToMap(attrs); + } + + /// + /// Find the named attribute + /// + /// - + /// the attribute name to search for + /// the Attribute, or null if not found. + public ConnectorAttribute Find(String name) + { + return CollectionUtil.GetValue(_attrMap, name, null); + } + + /// + /// Get the attribute from the set of attributes. + /// + /// the attribute in the set. + public Name GetName() + { + return (Name)Find(Name.NAME); + } + + /// + /// Get the attribute from the set of attributes. + /// + /// the attribute in the set. + public Uid GetUid() + { + return (Uid)Find(Uid.NAME); + } + + /// + /// Return the enabled status of the account. + /// + /// + /// If the ENABLE operational attribute is present, it's value takes + /// precedence over the current value. If it is missing, the currentlyEnabled + /// status is returned instead. + /// + /// the default state if enable is not found. + /// true if the account is enabled, false otherwise + public bool GetEnabled(bool dflt) + { + bool e = dflt; + ConnectorAttribute enable = Find(OperationalAttributes.ENABLE_NAME); + if (enable != null) + { + e = ConnectorAttributeUtil.GetBooleanValue(enable).Value; + } + return e; + } + + /// + /// Get the password as a GuardeString + /// + /// the password as a guarded String + public GuardedString GetPassword() + { + ConnectorAttribute a = Find(OperationalAttributes.PASSWORD_NAME); + return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); + } + + /// + /// Return a list of attributes + /// + /// - + /// name of attribute to search for. + /// The List (generic object) if it exists otherwise null. + public IList FindList(String name) + { + ConnectorAttribute a = Find(name); + return (a == null) ? null : a.Value; + } + + /// + /// Return the multivalued attribute as a list of strings. + /// + /// + /// This will throw a + /// ClassCastException if the underlying attribute list is not of type + /// String. + /// + /// the name of the attribute to search for + /// a List of String values for the attribute + public IList FindStringList(String name) + { + IList l = FindList(name); + if (l != null) + { + IList ret = new List(l.Count); + foreach (object o in l) + { + ret.Add((String)o); + } + return ret; + } + return null; + } + + /// + /// Get the name of attributes this Accessor was created with. + /// + /// new Case Insensitive ReadOnly Set of attribute name the access + /// has access to. + /// + /// Since 1.4 + public ICollection ListAttributeNames() + { + //ICollection names = CollectionUtil.NewCaseInsensitiveSet(); + //CollectionUtil.AddAll(names, _attrMap.Keys); + return CollectionUtil.AsReadOnlySet(_attrMap.Keys); + } + + /// + /// Determines if the set as the attribute specified. + /// + /// attribute name + /// true if the named attribute exists, false otherwise + public bool HasAttribute(String name) + { + return Find(name) != null; + } + + /// + /// Get the string value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the string value. + /// null if the value is null otherwise the string value for the + /// attribute. + /// if the object in the attribute is not a string. + /// if the attribute is a multi-valued (rather than + /// single-valued). + public String FindString(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetStringValue(a); + } + + /// + /// Get the char value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the char value. + /// null if the value is null otherwise the char value for the + /// attribute. + /// if the object in the attribute is not a char. + /// if the attribute is a multi-valued (rather than + /// single-valued). + /// Since 1.4 + public char? FindCharacter(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetCharacterValue(a); + } + + /// + /// Get the integer value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the long value. + /// null if the value is null otherwise the long value for the + /// attribute. + /// if the object in the attribute is not an long. + /// if the attribute is a multi-valued (rather than + /// single-valued). + public int? FindInteger(String name) + { + ConnectorAttribute a = Find(name); + return (a == null) ? null : ConnectorAttributeUtil.GetIntegerValue(a); + } + + /// + /// Get the long value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the long value. + /// null if the value is null otherwise the long value for the + /// attribute. + /// if the object in the attribute is not an long. + /// if the attribute is a multi-valued (rather than + /// single-valued). + public long? FindLong(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetLongValue(a); + } + + /// + /// Get the date value from the specified (single-valued) attribute that + /// contains a long. + /// + /// Attribute from which to retrieve the date value. + /// null if the value is null otherwise the date value for the + /// attribute. + /// if the object in the attribute is not an long. + /// if the attribute is a multi-valued (rather than + /// single-valued). + public DateTime? FindDateTime(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDateTimeValue(a); + } + + /// + /// Get the integer value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the integer value. + /// null if the value is null otherwise the integer value for the + /// attribute. + /// if the object in the attribute is not an integer. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + public double? FindDouble(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDoubleValue(a); + } + + /// + /// Get the float value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the float value. + /// null if the value is null otherwise the float value for the + /// attribute. + /// if the object in the attribute is not a float. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public float? FindFloat(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetFloatValue(a); + } + + /// + /// Get the big decimal value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the big decimal value. + /// null if the value is null otherwise the big decimal value for the + /// attribute. + /// if the object in the attribute is not a big decimal. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public BigDecimal FindBigDecimal(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetBigDecimalValue(a); + } + + /// + /// Get the boolean value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the boolean value. + /// null if the value is null otherwise the boolean value for the + /// attribute. + /// if the object in the attribute is not an . + /// if the attribute is a multi-valued (rather than + /// single-valued). + public bool? FindBoolean(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetBooleanValue(a); + } + + /// + /// Get the byte value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the byte value. + /// null if the value is null otherwise the byte value for the + /// attribute. + /// if the object in the attribute is not a byte. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public byte? FindByte(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetByteValue(a); + } + + /// + /// Get the byte array value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the byte array value. + /// null if the value is null otherwise the byte array value for the + /// attribute. + /// if the object in the attribute is not a byte array. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public byte[] FindByteArray(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetByteArrayValue(a); + } + + /// + /// Get the big integer value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the big integer value. + /// null if the value is null otherwise the big integer value for the + /// attribute. + /// if the object in the attribute is not a big integer. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public BigInteger FindBigInteger(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetBigIntegerValue(a); + } + + /// + /// Get the guarded byte array value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the guarded byte array value. + /// null if the value is null otherwise the guarded byte array value for the + /// attribute. + /// if the object in the attribute is not a guarded byte array. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public GuardedByteArray FindGuardedByteArray(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetGuardedByteArrayValue(a); + } + + /// + /// Get the guarded string value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the guarded string value. + /// null if the value is null otherwise the guarded string value for the + /// attribute. + /// if the object in the attribute is not a guarded string. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public GuardedString FindGuardedString(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); + } + + /// + /// Get the dictionary value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the dictionary value. + /// null if the value is null otherwise the byte dictionary for the + /// attribute. + /// if the object in the attribute is not a dictionary. + /// if the attribute is a multi-valued (rather than + /// single-valued).. + /// Since 1.4 + public IDictionary FindDictionary(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDictionaryValue(a); + } + } + #endregion + + #region CultureInfoCache + internal static class CultureInfoCache + { + private static readonly object LOCK = new Object(); + private static CultureInfo _instance; + + public static CultureInfo Instance + { + get + { + lock (LOCK) + { + if (_instance == null) + { + _instance = CultureInfo.CurrentCulture; + if (_instance == null) + { + _instance = CultureInfo.InstalledUICulture; + } + } + return _instance; + } + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/Framework/CommonObjectsFilter.cs b/dotnet/framework/Framework/CommonObjectsFilter.cs new file mode 100755 index 00000000..d5c4ae3c --- /dev/null +++ b/dotnet/framework/Framework/CommonObjectsFilter.cs @@ -0,0 +1,2535 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Linq; +using System.Text; +using System.Collections.Generic; +using System.Threading; +using Org.IdentityConnectors.Common; + +namespace Org.IdentityConnectors.Framework.Common.Objects.Filters +{ + #region AbstractFilterTranslator + /// + /// Base class to make it easier to implement Search. + /// + /// + /// A search filter may contain operators (such as 'contains' or 'in') + /// or may contain logical operators (such as 'AND', 'OR' or 'NOT') + /// that a connector cannot implement using the native API + /// of the target system or application. + /// A connector developer should subclass AbstractFilterTranslator + /// in order to declare which filter operations the connector does support. + /// This allows the FilterTranslator instance to analyze + /// a specified search filter and reduce the filter to its most efficient form. + /// The default (and worst-case) behavior is to return a null expression, + /// which means that the connector should return "everything" + /// (that is, should return all values for every requested attribute) + /// and rely on the common code in the framework to perform filtering. + /// This "fallback" behavior is good (in that it ensures consistency + /// of search behavior across connector implementations) but it is + /// obviously better for performance and scalability if each connector + /// performs as much filtering as the native API of the target can support. + /// + /// A subclass should override each of the following methods where possible: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Translation can then be performed using . + /// + /// + /// + /// + /// The result type of the translator. Commonly this will + /// be a string, but there are cases where you might need to return + /// a more complex data structure. For example if you are building a SQL + /// query, you will need not *just* the base WHERE clause but a list + /// of tables that need to be joined together. + abstract public class AbstractFilterTranslator : FilterTranslator + where T : class + { + /// + /// Main method to be called to translate a filter + /// + /// The filter to translate. + /// The list of queries to be performed. The list + /// size() may be one of the following: + /// + /// + /// 0 - This + /// signifies fetch everything. This may occur if your filter + /// was null or one of your create* methods returned null. + /// + /// + /// + /// 1 - List contains a single query that will return the results from the filter. + /// Note that the results may be a superset of those specified by + /// the filter in the case that one of your create* methods returned null. + /// That is OK from a behavior standpoint since ConnectorFacade performs + /// a second level of filtering. However it is undesirable from a performance standpoint. + /// + /// + /// + /// >1 - List contains multiple queries that must be performed in order to + /// meet the filter that was passed in. Note that this only occurs if your + /// method can return null. If this happens, it + /// is the responsibility of the connector implementor to perform each query + /// and combine the results. In order to eliminate duplicates, the connector + /// implementation must keep an in-memory HashSet of those UID + /// that have been visited thus far. This will not scale well if your + /// result sets are large. Therefore it is recommended that if + /// at all possible you implement + /// + /// + /// + public IList Translate(Filter filter) + { + if (filter == null) + { + return new List(); + } + //this must come first + filter = EliminateExternallyChainedFilters(filter); + filter = NormalizeNot(filter); + filter = SimplifyAndDistribute(filter); + //might have simplified it to the everything filter + if (filter == null) + { + return new List(); + } + IList result = TranslateInternal(filter); + //now "optimize" - we can eliminate exact matches at least + HashSet set = new HashSet(); + IList optimized = new List(result.Count); + foreach (T obj in result) + { + if (set.Add(obj)) + { + optimized.Add(obj); + } + } + return optimized; + } + + private Filter EliminateExternallyChainedFilters(Filter filter) + { + while (filter is ExternallyChainedFilter) + { + filter = ((ExternallyChainedFilter)filter).Filter; + } + return filter; + } + + /// + /// Pushes Not's so that they are just before the leaves of the tree + /// + private Filter NormalizeNot(Filter filter) + { + if (filter is AndFilter) + { + AndFilter af = (AndFilter)filter; + return new AndFilter(NormalizeNot(af.Left), + NormalizeNot(af.Right)); + } + else if (filter is OrFilter) + { + OrFilter of = (OrFilter)filter; + return new OrFilter(NormalizeNot(of.Left), + NormalizeNot(of.Right)); + } + else if (filter is NotFilter) + { + NotFilter nf = (NotFilter)filter; + return Negate(NormalizeNot(nf.Filter)); + } + else + { + return filter; + } + } + + /// + /// Given a filter, create a filter representing its negative. + /// + /// + /// This is used by normalizeNot. + /// + private Filter Negate(Filter filter) + { + if (filter is AndFilter) + { + AndFilter af = (AndFilter)filter; + return new OrFilter(Negate(af.Left), + Negate(af.Right)); + } + else if (filter is OrFilter) + { + OrFilter of = (OrFilter)filter; + return new AndFilter(Negate(of.Left), + Negate(of.Right)); + } + else if (filter is NotFilter) + { + NotFilter nf = (NotFilter)filter; + return nf.Filter; + } + else + { + return new NotFilter(filter); + } + } + + /// + /// Simultaneously prunes those portions of the + /// filter than cannot be implemented and distributes + /// Ands over Ors where needed if the resource does not + /// implement Or. + /// + /// Nots must already be normalized + /// a simplified filter or null to represent the + /// "everything" filter. + private Filter SimplifyAndDistribute(Filter filter) + { + if (filter is AndFilter) + { + AndFilter af = (AndFilter)filter; + Filter simplifiedLeft = + SimplifyAndDistribute(af.Left); + Filter simplifiedRight = + SimplifyAndDistribute(af.Right); + if (simplifiedLeft == null) + { + //left is "everything" - just return the right + return simplifiedRight; + } + else if (simplifiedRight == null) + { + //right is "everything" - just return the left + return simplifiedLeft; + } + else + { + //simulate translation of the left and right + //to see where we end up + IList leftExprs = + TranslateInternal(simplifiedLeft); + IList rightExprs = + TranslateInternal(simplifiedRight); + if (leftExprs.Count == 0) + { + //This can happen only when one of the create* methods + //is inconsistent from one invocation to the next + //(simplifiedLeft should have been null + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: " + leftExprs); + } + if (rightExprs.Count == 0) + { + //This can happen only when one of the create* methods + //is inconsistent from one invocation to the next + //(simplifiedRight should have been null + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: " + rightExprs); + } + + //Simulate ANDing each pair(left,right). + //If all of them return null (i.e., "everything"), + //then the request cannot be filtered. + bool anyAndsPossible = false; + foreach (T leftExpr in leftExprs) + { + foreach (T rightExpr in rightExprs) + { + T test = CreateAndExpression( + leftExpr, + rightExpr); + if (test != null) + { + anyAndsPossible = true; + break; + } + } + if (anyAndsPossible) + { + break; + } + } + + //If no AND filtering is possible, + //return whichever of left or right + //contains the fewest expressions. + if (!anyAndsPossible) + { + if (leftExprs.Count <= rightExprs.Count) + { + return simplifiedLeft; + } + else + { + return simplifiedRight; + } + } + + //Since AND filtering is possible for at least + //one expression, let's distribute. + if (leftExprs.Count > 1) + { + //The left can contain more than one expression + //only if the left-hand side is an unimplemented OR. + //Distribute our AND to the left. + OrFilter left = (OrFilter)simplifiedLeft; + OrFilter newFilter = + new OrFilter(new AndFilter(left.Left, + simplifiedRight), + new AndFilter(left.Right, + simplifiedRight)); + return SimplifyAndDistribute(newFilter); + } + else if (rightExprs.Count > 1) + { + //The right can contain more than one expression + //only if the right-hand side is an unimplemented OR. + //Distribute our AND to the right. + OrFilter right = (OrFilter)simplifiedRight; + OrFilter newFilter = + new OrFilter(new AndFilter(simplifiedLeft, + right.Left), + new AndFilter(simplifiedLeft, + right.Right)); + return SimplifyAndDistribute(newFilter); + } + else + { + //Each side contains exactly one expression + //and the translator does implement AND + //(anyAndsPossible must be true + //for them to have hit this branch). + if (!anyAndsPossible) + { + throw new Exception("expected anyAndsPossible"); + } + return new AndFilter(simplifiedLeft, simplifiedRight); + } + } + } + else if (filter is OrFilter) + { + OrFilter of = (OrFilter)filter; + Filter simplifiedLeft = + SimplifyAndDistribute(of.Left); + Filter simplifiedRight = + SimplifyAndDistribute(of.Right); + //If either left or right reduces to "everything", + //then simplify the OR to "everything". + if (simplifiedLeft == null || simplifiedRight == null) + { + return null; + } + //otherwise + return new OrFilter(simplifiedLeft, + simplifiedRight); + } + else + { + //Otherwise, it's a NOT(LEAF) or a LEAF. + //Simulate creating it. + T expr = CreateLeafExpression(filter); + if (expr == null) + { + //If the expression cannot be implemented, + //return the "everything" filter. + return null; + } + else + { + //Otherwise, return the filter. + return filter; + } + } + } + + /// + /// Translates the filter into a list of expressions. + /// + /// + /// The filter must have already been transformed + /// using normalizeNot followed by a simplifyAndDistribute. + /// + /// A filter (normalized, simplified, and distibuted) + /// A list of expressions or empty list for everything. + private IList TranslateInternal(Filter filter) + { + if (filter is AndFilter) + { + T result = TranslateAnd((AndFilter)filter); + IList rv = new List(); + if (result != null) + { + rv.Add(result); + } + return rv; + } + else if (filter is OrFilter) + { + return TranslateOr((OrFilter)filter); + } + else + { + //otherwise it's either a leaf or a NOT (leaf) + T expr = CreateLeafExpression(filter); + IList exprs = new List(); + if (expr != null) + { + exprs.Add(expr); + } + return exprs; + } + } + + private T TranslateAnd(AndFilter filter) + { + IList leftExprs = TranslateInternal(filter.Left); + IList rightExprs = TranslateInternal(filter.Right); + if (leftExprs.Count != 1) + { + //this can happen only if one of the create* methods + //is inconsistent from one invocation to the next + //(at this point we've already been simplified and + //distributed). + throw new InvalidOperationException("Translation method is inconsistent: " + leftExprs); + } + if (rightExprs.Count != 1) + { + //this can happen only if one of the create* methods + //is inconsistent from one invocation to the next + //(at this point we've already been simplified and + //distributed). + throw new InvalidOperationException("Translation method is inconsistent: " + rightExprs); + } + T rv = CreateAndExpression(leftExprs[0], rightExprs[0]); + if (rv == null) + { + //This could happen only if we're inconsistent + //(since the simplify logic already should have removed + //any expression that cannot be filtered). + throw new InvalidOperationException("createAndExpression is inconsistent"); + } + return rv; + } + + private IList TranslateOr(OrFilter filter) + { + IList leftExprs = TranslateInternal(filter.Left); + IList rightExprs = TranslateInternal(filter.Right); + if (leftExprs.Count == 0) + { + //This can happen only if one of the create* methods + //is inconsistent from one invocation to the next. + throw new InvalidOperationException("Translation method is inconsistent"); + } + if (rightExprs.Count == 0) + { + //This can happen only if one of the create* methods + //methods is inconsistent from on invocation to the next. + throw new InvalidOperationException("Translation method is inconsistent"); + } + if (leftExprs.Count == 1 && rightExprs.Count == 1) + { + //If each side contains exactly one expression, + //try to create a combined expression. + T val = CreateOrExpression(leftExprs[0], rightExprs[0]); + if (val != null) + { + IList rv = new List(); + rv.Add(val); + return rv; + } + //Otherwise, fall through + } + + //Return a list of queries from the left and from the right + IList rv2 = new List(leftExprs.Count + rightExprs.Count); + CollectionUtil.AddAll(rv2, leftExprs); + CollectionUtil.AddAll(rv2, rightExprs); + return rv2; + } + + /// + /// Creates an expression for a LEAF or a NOT(leaf) + /// + /// Must be either a leaf or a NOT(leaf) + /// The expression + private T CreateLeafExpression(Filter filter) + { + Filter leafFilter; + bool not; + if (filter is NotFilter) + { + NotFilter nf = (NotFilter)filter; + leafFilter = nf.Filter; + not = true; + } + else + { + leafFilter = filter; + not = false; + } + T expr = CreateLeafExpression(leafFilter, not); + return expr; + } + + /// + /// Creates a Leaf expression + /// + /// Must be a leaf expression + /// Is ! to be applied to the leaf expression + /// The expression or null (for everything) + private T CreateLeafExpression(Filter filter, bool not) + { + if (filter is ContainsFilter) + { + return CreateContainsExpression((ContainsFilter)filter, not); + } + else if (filter is EndsWithFilter) + { + return CreateEndsWithExpression((EndsWithFilter)filter, not); + } + else if (filter is EqualsFilter) + { + return CreateEqualsExpression((EqualsFilter)filter, not); + } + else if (filter is GreaterThanFilter) + { + return CreateGreaterThanExpression((GreaterThanFilter)filter, not); + } + else if (filter is GreaterThanOrEqualFilter) + { + return CreateGreaterThanOrEqualExpression((GreaterThanOrEqualFilter)filter, not); + } + else if (filter is LessThanFilter) + { + return CreateLessThanExpression((LessThanFilter)filter, not); + } + else if (filter is LessThanOrEqualFilter) + { + return CreateLessThanOrEqualExpression((LessThanOrEqualFilter)filter, not); + } + else if (filter is StartsWithFilter) + { + return CreateStartsWithExpression((StartsWithFilter)filter, not); + } + else if (filter is ContainsAllValuesFilter) + { + return CreateContainsAllValuesExpression((ContainsAllValuesFilter)filter, not); + } + else + { + //unrecognized expression - nothing we can do + return null; + } + } + + /// + /// Should be overridden by subclasses to create an AND expression + /// if the native resource supports AND. + /// + /// The left expression. Will never be null. + /// The right expression. Will never be null. + /// The AND expression. A return value of null means + /// a native AND query cannot be created for the given expressions. + /// In this case, the resulting query will consist of the + /// leftExpression only. + protected virtual T CreateAndExpression(T leftExpression, T rightExpression) + { + return null; + } + + /// + /// Should be overridden by subclasses to create an OR expression + /// if the native resource supports OR. + /// + /// The left expression. Will never be null. + /// The right expression. Will never be null. + /// The OR expression. A return value of null means + /// a native OR query cannot be created for the given expressions. + /// In this case, may return multiple queries, each + /// of which must be run and results combined. + protected virtual T CreateOrExpression(T leftExpression, T rightExpression) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a CONTAINS expression + /// if the native resource supports CONTAINS. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT CONTAINS + /// The CONTAINS expression. A return value of null means + /// a native CONTAINS query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateContainsExpression(ContainsFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a ENDS-WITH expression + /// if the native resource supports ENDS-WITH. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT ENDS-WITH + /// The ENDS-WITH expression. A return value of null means + /// a native ENDS-WITH query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a EQUALS expression + /// if the native resource supports EQUALS. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT EQUALS + /// The EQUALS expression. A return value of null means + /// a native EQUALS query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateEqualsExpression(EqualsFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a GREATER-THAN expression + /// if the native resource supports GREATER-THAN. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT GREATER-THAN + /// The GREATER-THAN expression. A return value of null means + /// a native GREATER-THAN query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateGreaterThanExpression(GreaterThanFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a GREATER-THAN-EQUAL expression + /// if the native resource supports GREATER-THAN-EQUAL. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT GREATER-THAN-EQUAL + /// The GREATER-THAN-EQUAL expression. A return value of null means + /// a native GREATER-THAN-EQUAL query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a LESS-THAN expression + /// if the native resource supports LESS-THAN. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT LESS-THAN + /// The LESS-THAN expression. A return value of null means + /// a native LESS-THAN query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateLessThanExpression(LessThanFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a LESS-THAN-EQUAL expression + /// if the native resource supports LESS-THAN-EQUAL. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT LESS-THAN-EQUAL + /// The LESS-THAN-EQUAL expression. A return value of null means + /// a native LESS-THAN-EQUAL query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) + { + return null; + } + + /// + /// Should be overridden by subclasses to create a STARTS-WITH expression + /// if the native resource supports STARTS-WITH. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT STARTS-WITH + /// The STARTS-WITH expression. A return value of null means + /// a native STARTS-WITH query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateStartsWithExpression(StartsWithFilter filter, bool not) + { + return null; + } + + protected virtual T CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) + { + return null; + } + } + #endregion + + #region AndFilter + public sealed class AndFilter : CompositeFilter + { + + /// + /// Left side of a composite based filter. + /// + private LinkedList _subFilters; + + /// + /// And the the left and right filters. + /// + public AndFilter(Filter left, Filter right) + : this(CollectionUtil.NewList(new[] { left, right })) + { + } + + public AndFilter(IEnumerable filters) + : base(null, null) + { + _subFilters = new LinkedList(filters); + } + + /// + /// Ands the left and right filters. + /// + /// + public override bool Accept(ConnectorObject obj) + { + bool result = true; + foreach (Filter subFilter in _subFilters) + { + result = subFilter.Accept(obj); + if (!result) + { + break; + } + } + return result; + } + + public override TR Accept(FilterVisitor v, TP p) + { + return v.VisitAndFilter(p, this); + } + + public override Filter Left + { + get + { + return _subFilters.First.Value; + } + } + + public override Filter Right + { + get + { + if (_subFilters.Count > 2) + { + var right = new LinkedList(_subFilters); + right.RemoveFirst(); + return new AndFilter(right); + } + else if (_subFilters.Count == 2) + { + return _subFilters.Last.Value; + } + else + { + return null; + } + } + } + + public override ICollection Filters + { + get + { + return CollectionUtil.AsReadOnlyList(new List(_subFilters)); + } + } + + public override string ToString() + { + StringBuilder builder = (new StringBuilder()).Append('('); + bool isFirst = true; + foreach (Filter subFilter in _subFilters) + { + if (isFirst) + { + isFirst = false; + } + else + { + builder.Append(" and "); + } + builder.Append(subFilter); + } + return builder.Append(')').ToString(); + } + } + #endregion + + #region AttributeFilter + public abstract class AttributeFilter : Filter + { + private readonly ConnectorAttribute _attribute; + + /// + /// Root filter for Attribute testing.. + /// + internal AttributeFilter(ConnectorAttribute attribute) + { + _attribute = attribute; + if (attribute == null) + { + throw new ArgumentException("Attribute not be null!"); + } + } + + /// + /// Get the internal attribute. + /// + public ConnectorAttribute GetAttribute() + { + return _attribute; + } + + /// + /// Name of the attribute to find in the . + /// + public string Name + { + get + { + return GetAttribute().Name; + } + } + + /// + /// Determines if the attribute provided is present in the + /// . + /// + public bool IsPresent(ConnectorObject obj) + { + return obj.GetAttributeByName(_attribute.Name) != null; + } + public abstract bool Accept(ConnectorObject obj); + public abstract TR Accept(FilterVisitor v, TP p); + } + #endregion + + #region ComparableAttributeFilter + /// + /// Filter for an attribute value that is comparable. + /// + public abstract class ComparableAttributeFilter : + SingleValueAttributeFilter + { + /// + /// Attempt compare attribute values. + /// + internal ComparableAttributeFilter(ConnectorAttribute attr) + : base(attr) + { + // determine if this attribute value is comparable.. + if (!(GetValue() is IComparable)) + { + String ERR = "Must be a comparable value!"; + throw new ArgumentException(ERR); + } + } + + /// + /// Call compareTo on the attribute values. + /// + /// + /// If the attribute is not present + /// in the return -1. + /// + public int Compare(ConnectorObject obj) + { + int ret = -1; + ConnectorAttribute attr = obj.GetAttributeByName(GetName()); + if (attr != null && attr.Value.Count == 1) + { + // it must be a comparable because that's were testing against + if (!(attr.Value[0] is IComparable)) + { + String ERR = "Attribute value must be comparable!"; + throw new ArgumentException(ERR); + } + // grab this value and the on from the attribute an compare.. + IComparable o1 = (IComparable)attr.Value[0]; + IComparable o2 = (IComparable)GetValue(); + ret = o1.CompareTo(o2); + } + return ret; + } + } + #endregion + + #region CompositeFilter + public abstract class CompositeFilter : Filter + { + /// + /// Left side of a composite based filter. + /// + private Filter _left; + + /// + /// Right side of a composite based filter. + /// + private Filter _right; + + public virtual Filter Left + { + get + { + return _left; + } + } + + public virtual Filter Right + { + get + { + return _right; + } + } + + public virtual ICollection Filters + { + get + { + return CollectionUtil.NewReadOnlyList(Left, Right); + } + } + + internal CompositeFilter(Filter left, Filter right) + { + _left = left; + _right = right; + } + public abstract bool Accept(ConnectorObject obj); + public abstract R Accept(FilterVisitor v, P p); + } + #endregion + + #region ContainsFilter + public sealed class ContainsFilter : StringFilter + { + public ContainsFilter(ConnectorAttribute attr) + : base(attr) + { + } + + public override bool Accept(String value) + { + return value.Contains(GetValue()); + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitContainsFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("CONTAINS: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region EndsWithFilter + public sealed class EndsWithFilter : StringFilter + { + public EndsWithFilter(ConnectorAttribute attr) + : base(attr) + { + } + + public override bool Accept(String value) + { + return value.EndsWith(GetValue()); + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitEndsWithFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("ENDSWITH: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region EqualsFilter + public sealed class EqualsFilter : AttributeFilter + { + /// + /// Determines if the attribute inside the is equal + /// to the provided. + /// + public EqualsFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determines if the attribute exists in the and if + /// its equal to the one provided. + /// + /// + public override bool Accept(ConnectorObject obj) + { + bool ret = false; + ConnectorAttribute thisAttr = GetAttribute(); + ConnectorAttribute attr = obj.GetAttributeByName(thisAttr.Name); + if (attr != null) + { + ret = thisAttr.Equals(attr); + } + return ret; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitEqualsFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("EQUALS: ").Append(GetAttribute()); + return bld.ToString(); + } + + } + #endregion + + #region ExtendedMatchFilter + public sealed class ExtendedMatchFilter : AttributeFilter + { + + /// + /// Creates a new {@code extended match} filter using the provided operator + /// and attribute assertion. + /// + /// + /// The operator. + /// + /// The assertion value. + public ExtendedMatchFilter(string @operator, ConnectorAttribute attribute) + : base(attribute) + { + if (StringUtil.IsBlank(@operator)) + { + throw new ArgumentException("Operator not be null!"); + } + Operator = @operator; + } + + public string Operator + { + get; + internal set; + } + + /// + /// Framework can not understand this filter, always return true. + /// . + /// + public override bool Accept(ConnectorObject obj) + { + return true; + } + + public override TR Accept(FilterVisitor v, TP p) + { + return v.VisitExtendedFilter(p, this); + } + } + #endregion + + #region Filter + public interface Filter + { + /// + /// Determines whether the specified matches this + /// filter. + /// + /// + /// - The specified ConnectorObject. + /// true if the object matches (that is, satisfies all + /// selection criteria of) this filter; otherwise false. + bool Accept(ConnectorObject obj); + + /// + /// Applies a FilterVisitor to this Filter. + /// + /// + /// The return type of the visitor's methods. + /// + /// The type of the additional parameters to the visitor's + /// methods. + /// + /// The filter visitor. + /// + /// Optional additional visitor parameter. + /// A result as specified by the visitor. + /// @since 1.4 + R Accept(FilterVisitor v, P p); + } + #endregion + + #region FilterVisitor + /// + /// A visitor of Filters, in the style of the visitor design + /// pattern. + /// + /// Classes implementing this interface can query filters in a type-safe manner. + /// When a visitor is passed to a filter's accept method, the corresponding visit + /// method most applicable to that filter is invoked. + /// + /// + /// + /// + /// The return type of this visitor's methods. Use + /// for visitors that do not need to return + /// results. + /// + /// The type of the additional parameter to this visitor's methods. + /// Use for visitors that do not need an + /// additional parameter. + /// + /// since 1.4 + public interface FilterVisitor + { + + /// + /// Visits an and filter. + /// + /// Implementation note: for the purposes of matching, an empty + /// sub-filters should always evaluate to true. + /// + /// + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitAndFilter(P p, AndFilter filter); + + /// + /// Visits a contains filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitContainsFilter(P p, ContainsFilter filter); + + /// + /// Visits a containsAll filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitContainsAllValuesFilter(P p, ContainsAllValuesFilter filter); + + /// + /// Visits a equality filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitEqualsFilter(P p, EqualsFilter filter); + + /// + /// Visits a comparison filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitExtendedFilter(P p, Filter filter); + + /// + /// Visits a greater than filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitGreaterThanFilter(P p, GreaterThanFilter filter); + + /// + /// Visits a greater than or equal to filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitGreaterThanOrEqualFilter(P p, GreaterThanOrEqualFilter filter); + + /// + /// Visits a less than filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitLessThanFilter(P p, LessThanFilter filter); + + /// + /// Visits a less than or equal to filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitLessThanOrEqualFilter(P p, LessThanOrEqualFilter filter); + + /// + /// Visits a no filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitNotFilter(P p, NotFilter filter); + + /// + /// Visits an or filter. + /// + /// Implementation note: for the purposes of matching, an empty + /// sub-filters should always evaluate to false. + /// + /// + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitOrFilter(P p, OrFilter filter); + + /// + /// Visits a starts with filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitStartsWithFilter(P p, StartsWithFilter filter); + + /// + /// Visits a ends wit filter. + /// + /// + /// A visitor specified parameter. + /// + /// The visited filter. + /// Returns a visitor specified result. + R VisitEndsWithFilter(P p, EndsWithFilter filter); + } + #endregion + + #region FilterBuilder + /// + /// FilterBuilder creates a object, that can determine if a + /// ConnectorObject will be filtered or not. + /// + /// Will Droste + /// $Revision: 1.7 $ + /// 1.0 + public static class FilterBuilder + { + /// + /// Determine if the value ends + /// with the value provided. + /// + /// + /// value to test against the + /// attribute value. + /// true if the attribute value contains the + /// attribute value provided. + public static Filter EndsWith(ConnectorAttribute attr) + { + return new EndsWithFilter(attr); + } + + /// + /// Determine if the value starts + /// with the value provided. + /// + /// + /// value to test against the + /// attribute value. + /// true if the attribute value contains the + /// attribute value provided. + public static Filter StartsWith(ConnectorAttribute attr) + { + return new StartsWithFilter(attr); + } + + public static Filter ContainsAllValues(ConnectorAttribute attr) + { + return new ContainsAllValuesFilter(attr); + } + + /// + /// Determine if the value contains + /// the value provided. + /// + /// + /// value to test against the + /// attribute value. + /// true if the attribute value contains the + /// attribute value provided. + public static Filter Contains(ConnectorAttribute attr) + { + return new ContainsFilter(attr); + } + + /// + /// The value provided is less than or equal to the + /// attribute value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is greater than or equal to the one + /// provided by the . + public static Filter GreaterThanOrEqualTo(ConnectorAttribute attr) + { + return new GreaterThanOrEqualFilter(attr); + } + + /// + /// The value provided is less than or equal to the + /// attribute value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is less than or equal to the one + /// provided by the . + public static Filter LessThanOrEqualTo(ConnectorAttribute attr) + { + return new LessThanOrEqualFilter(attr); + } + + /// + /// The value provided is less than the + /// attribute value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is less than the one provided by the + /// . + public static Filter LessThan(ConnectorAttribute attr) + { + return new LessThanFilter(attr); + } + + /// + /// ConnectorAttribute value is greater than the attribute + /// value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is greater than the one provided by + /// the . + public static Filter GreaterThan(ConnectorAttribute attr) + { + return new GreaterThanFilter(attr); + } + + /// + /// Determines if the provided exists in the + /// and is equal. + /// + public static Filter EqualTo(ConnectorAttribute attr) + { + return new EqualsFilter(attr); + } + + /// + /// Creates a new {@code extended match} filter using the provided operator + /// and attribute assertion. + /// + /// + /// The operator. + /// + /// The assertion value. + /// Since 1.5 + public static Filter ExtendedMatch(string @operator, ConnectorAttribute attribute) + { + return new ExtendedMatchFilter(@operator, attribute); + } + + /// + /// Ands the two . + /// + /// left side operand. + /// right side operand. + /// the result of leftOperand && rightOperand + public static Filter And(Filter leftOperand, Filter rightOperand) + { + return new AndFilter(leftOperand, rightOperand); + } + + /// + /// Creates a new "AND" filter using the provided list of sub-filters. + /// + /// Creating a new "AND" filter with a {@code null} or empty list of + /// sub-filters is equivalent to calling "alwaysTrue". + /// + /// + /// + /// + /// The list of sub-filters, may be empty or {@code null}. + /// The newly created "AND" filter. + public static Filter And(ICollection subFilters) + { + switch (subFilters.Count) + { + case 0: + return null; + case 1: + return subFilters.First(); + default: + return new AndFilter(new List(subFilters)); + } + } + + /// + /// ORs the two . + /// + /// left side operand. + /// right side operand. + /// the result of leftOperand || rightOperand + public static Filter Or(Filter leftOperand, Filter rightOperand) + { + return new OrFilter(leftOperand, rightOperand); + } + + /// + /// Creates a new "OR" filter using the provided list of sub-filters. + /// + /// Creating a new "OR" filter with a {@code null} or empty list of + /// sub-filters is equivalent to "alwaysTrue". + /// + /// + /// + /// + /// The list of sub-filters, may be empty or {@code null}. + /// The newly created {@code or} filter. + public static Filter Or(ICollection subFilters) + { + switch (subFilters.Count) + { + case 0: + return null; + case 1: + return subFilters.First(); + default: + return new OrFilter(new List(subFilters)); + } + } + + /// + /// NOT the . + /// + /// negate the result of . + /// the result of not . + public static Filter Not(Filter filter) + { + return new NotFilter(filter); + } + + /// + /// Creates a new presence filter using the provided attribute name. + /// + /// + /// The name of field within the which must be + /// present. + /// The newly created {@code presence} filter. + /// Since 1.5 + public static Filter Present(string attributeName) + { + return new PresenceFilter(attributeName); + } + } + #endregion + + #region FilteredResultsHandlerVisitor + + public enum FilterResult + { + False, + True, + Undefined + } + + static class EnumExtensionMethods + { + public static bool ToBoolean(this FilterResult instance) + { + return instance == FilterResult.True; // UNDEFINED collapses to FALSE. + } + + } + + /// + /// A FilteredResultsHandlerVisitor can accept the + /// . It + /// can be used for case-sensitive and case-ignore mode to accept the + /// and values. + /// + /// + /// since 1.5 + public class FilteredResultsHandlerVisitor : FilterVisitor + { + + public static readonly FilteredResultsHandlerVisitor DefaultCaseSensitiveVisitor = new FilteredResultsHandlerVisitor(false); + public static readonly FilteredResultsHandlerVisitor DefaultCaseIgnoreVisitor = new FilteredResultsHandlerVisitor(true); + + private FilterResult valueOf(bool b) + { + return b ? FilterResult.True : FilterResult.False; + } + + public static Filter WrapFilter(Filter nestedFilter, bool caseIgnore) + { + return null == nestedFilter ? null : new InnerVisitorFilter(nestedFilter, caseIgnore); + } + + private sealed class InnerVisitorFilter : Filter + { + private readonly Filter _nestedFilter; + private readonly bool _caseIgnore; + + public InnerVisitorFilter(Filter nestedFilter, bool caseIgnore) + { + _nestedFilter = nestedFilter; + _caseIgnore = caseIgnore; + } + + public bool Accept(ConnectorObject obj) + { + return _nestedFilter.Accept(_caseIgnore ? DefaultCaseIgnoreVisitor : DefaultCaseSensitiveVisitor, obj).ToBoolean(); + } + + public TR Accept(FilterVisitor v, TP p) + { + return v.VisitExtendedFilter(p, this); + } + } + + private readonly bool _caseIgnore; + + public FilteredResultsHandlerVisitor(bool caseIgnore) + { + _caseIgnore = caseIgnore; + } + + public virtual FilterResult VisitAndFilter(ConnectorObject connectorObject, AndFilter filter) + { + FilterResult result = FilterResult.True; + foreach (Filter subFilter in filter.Filters) + { + FilterResult r = subFilter.Accept(this, connectorObject); + if (r.CompareTo(result) < 0) + { + result = r; + } + if (result == FilterResult.False) + { + break; + } + } + return result; + } + + public virtual FilterResult VisitContainsFilter(ConnectorObject connectorObject, ContainsFilter filter) + { + FilterResult result = FilterResult.Undefined; + String valueAssertion = ExpectSingleValue(connectorObject, filter.Name, typeof(String)); + if (null != valueAssertion) + { + if (_caseIgnore) + { + result = valueOf(valueAssertion.ToLower(Thread.CurrentThread.CurrentUICulture).Contains(filter.GetValue().ToLower(Thread.CurrentThread.CurrentUICulture))); + } + else + { + result = valueOf(valueAssertion.Contains(filter.GetValue())); + } + } + return result; + } + + public virtual FilterResult VisitContainsAllValuesFilter(ConnectorObject connectorObject, ContainsAllValuesFilter filter) + { + FilterResult result = FilterResult.Undefined; + ConnectorAttribute attribute = connectorObject.GetAttributeByName(filter.Name); + IList attributeValues = null; + if (null != attribute && null != (attributeValues = attribute.Value)) + { + IList filterValues = filter.GetAttribute().Value; + if (filterValues.Count == 0) + { + result = FilterResult.True; + } + else if (attributeValues.Count == 0) + { + result = FilterResult.False; + } + else if (_caseIgnore) + { + bool stillContains = true; + foreach (object o in filter.GetAttribute().Value) + { + bool found = false; + if (o is string) + { + foreach (object c in attributeValues) + { + if (c is string && ((string)c).Equals((string)o, StringComparison.CurrentCultureIgnoreCase)) + { + found = true; + break; + } + } + } + else if (o is char) + { + foreach (object c in attributeValues) + { + if (c is char && char.ToUpper((char)c) != char.ToUpper((char)o)) + { + found = true; + break; + } + } + } + else + { + result = valueOf(filter.GetAttribute().Value.Except(attributeValues).Any()); + break; + } + if (!(stillContains = stillContains && found)) + { + break; + } + } + result = valueOf(stillContains); + } + else + { + result = valueOf(filter.GetAttribute().Value.Except(attributeValues).Any()); + } + } + return result; + } + public virtual FilterResult VisitEqualsFilter(ConnectorObject connectorObject, EqualsFilter filter) + { + FilterResult result = FilterResult.Undefined; + ConnectorAttribute attribute = connectorObject.GetAttributeByName(filter.Name); + if (null != attribute) + { + IList attributeValues = attribute.Value; + IList filterValues = filter.GetAttribute().Value; + result = valueOf(CollectionUtil.Equals(attributeValues, filterValues, _caseIgnore)); + } + return result; + } + public virtual FilterResult VisitExtendedFilter(ConnectorObject connectorObject, Filter filter) + { + FilterResult result = FilterResult.Undefined; + if (filter is PresenceFilter) + { + result = valueOf(null != connectorObject.GetAttributeByName(((PresenceFilter)filter).Name)); + } + return result; + } + public virtual FilterResult VisitGreaterThanFilter(ConnectorObject connectorObject, GreaterThanFilter filter) + { + FilterResult result = FilterResult.Undefined; + Object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is String) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) > 0); + } + } + else if (_caseIgnore && filterValue is char) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) > 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) > 0); + } + } + return result; + } + + + public virtual FilterResult VisitGreaterThanOrEqualFilter(ConnectorObject connectorObject, GreaterThanOrEqualFilter filter) + { + FilterResult result = FilterResult.Undefined; + object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is string) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) >= 0); + } + } + else if (_caseIgnore && filterValue is char) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) >= 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) >= 0); + } + } + return result; + } + public virtual FilterResult VisitLessThanFilter(ConnectorObject connectorObject, LessThanFilter filter) + { + FilterResult result = FilterResult.Undefined; + object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is string) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) < 0); + } + } + else if (_caseIgnore && filterValue is char?) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) < 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) < 0); + } + } + return result; + } + public virtual FilterResult VisitLessThanOrEqualFilter(ConnectorObject connectorObject, LessThanOrEqualFilter filter) + { + FilterResult result = FilterResult.Undefined; + object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is string) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) <= 0); + } + } + else if (_caseIgnore && filterValue is char?) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) <= 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) <= 0); + } + } + return result; + } + public virtual FilterResult VisitNotFilter(ConnectorObject connectorObject, NotFilter filter) + { + switch (filter.Filter.Accept(this, connectorObject)) + { + case FilterResult.False: + return FilterResult.True; + case FilterResult.Undefined: + return FilterResult.Undefined; + default: // TRUE + return FilterResult.False; + } + } + + public virtual FilterResult VisitOrFilter(ConnectorObject connectorObject, OrFilter filter) + { + FilterResult result = FilterResult.False; + foreach (Filter subFilter in filter.Filters) + { + FilterResult r = subFilter.Accept(this, connectorObject); + if (r.CompareTo(result) > 0) + { + result = r; + } + if (result == FilterResult.True) + { + break; + } + } + return result; + } + + public virtual FilterResult VisitStartsWithFilter(ConnectorObject connectorObject, StartsWithFilter filter) + { + FilterResult result = FilterResult.Undefined; + String valueAssertion = ExpectSingleValue(connectorObject, filter.Name, typeof(String)); + if (null != valueAssertion) + { + if (_caseIgnore) + { + result = valueOf(valueAssertion.ToLower(Thread.CurrentThread.CurrentUICulture).StartsWith(filter.GetValue().ToLower(Thread.CurrentThread.CurrentUICulture))); + } + else + { + result = valueOf(valueAssertion.StartsWith(filter.GetValue())); + } + } + return result; + } + + public virtual FilterResult VisitEndsWithFilter(ConnectorObject connectorObject, EndsWithFilter filter) + { + FilterResult result = FilterResult.Undefined; + String valueAssertion = ExpectSingleValue(connectorObject, filter.Name, typeof(String)); + if (null != valueAssertion) + { + if (_caseIgnore) + { + result = valueOf(valueAssertion.ToLower(Thread.CurrentThread.CurrentUICulture).EndsWith(filter.GetValue().ToLower(Thread.CurrentThread.CurrentUICulture))); + } + else + { + result = valueOf(valueAssertion.EndsWith(filter.GetValue())); + } + } + return result; + } + + protected internal virtual T ExpectSingleValue(ConnectorObject connectorObject, string attributeName, Type expect) + { + object o = ExpectSingleValue(connectorObject, attributeName); + if (null != o && expect.IsInstanceOfType(o)) + { + return (T)o; + } + return default(T); + } + + protected internal virtual object ExpectSingleValue(ConnectorObject connectorObject, string attributeName) + { + ConnectorAttribute attr = connectorObject.GetAttributeByName(attributeName); + if (null != attr && null != attr.Value && attr.Value.Count() == 1) + { + return attr.Value[0]; + } + return null; + } + + } + #endregion + + #region FilterTranslator + public interface FilterTranslator + { + IList Translate(Filter filter); + } + #endregion + + #region GreaterThanFilter + public sealed class GreaterThanFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public GreaterThanFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { + return IsPresent(obj) && Compare(obj) > 0; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitGreaterThanFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("GREATERTHAN: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region GreaterThanOrEqualFilter + public sealed class GreaterThanOrEqualFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public GreaterThanOrEqualFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { + return IsPresent(obj) && Compare(obj) >= 0; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitGreaterThanOrEqualFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("GREATERTHANOREQUAL: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region LessThanFilter + public sealed class LessThanFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public LessThanFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { + return IsPresent(obj) && this.Compare(obj) < 0; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitLessThanFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("LESSTHAN: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region LessThanOrEqualFilter + public sealed class LessThanOrEqualFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public LessThanOrEqualFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { + return IsPresent(obj) && this.Compare(obj) <= 0; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitLessThanOrEqualFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("LESSTHANOREQUAL: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region NotFilter + /// + /// Proxy the filter to return the negative of the value. + /// + public sealed class NotFilter : Filter + { + private readonly Filter _filter; + + /// + /// Take the value returned from the internal filter and NOT it. + /// + public NotFilter(Filter filter) + { + _filter = filter; + } + + /// + /// Get the internal filter that is being negated. + /// + public Filter Filter + { + get + { + return _filter; + } + } + + /// + /// Return the opposite the internal filters return value. + /// + /// + public bool Accept(ConnectorObject obj) + { + return !_filter.Accept(obj); + } + + public R Accept(FilterVisitor v, P p) + { + return v.VisitNotFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("NOT: ").Append(Filter); + return bld.ToString(); + } + } + #endregion + + #region OrFilter + public sealed class OrFilter : CompositeFilter + { + + /// + /// Left side of a composite based filter. + /// + private LinkedList _subFilters; + + /// + /// Or the the left and right filters. + /// + public OrFilter(Filter left, Filter right) + : this(CollectionUtil.NewList(new[] { left, right })) + { + } + + public OrFilter(IEnumerable filters) + : base(null, null) + { + _subFilters = new LinkedList(filters); + } + + /// + /// ORs the left and right filters. + /// + /// + public override bool Accept(ConnectorObject obj) + { + bool result = false; + foreach (Filter subFilter in _subFilters) + { + result = subFilter.Accept(obj); + if (result) + { + break; + } + } + return result; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitOrFilter(p, this); + } + + public override Filter Left + { + get + { + return _subFilters.First.Value; + } + } + + public override Filter Right + { + get + { + if (_subFilters.Count > 2) + { + var right = new LinkedList(_subFilters); + right.RemoveFirst(); + return new AndFilter(right); + } + if (_subFilters.Count == 2) + { + return _subFilters.Last.Value; + } + return null; + } + } + + public override ICollection Filters + { + get + { + return CollectionUtil.AsReadOnlyList(new List(_subFilters)); + } + } + + public override string ToString() + { + StringBuilder builder = (new StringBuilder()).Append('('); + bool isFirst = true; + foreach (Filter subFilter in _subFilters) + { + if (isFirst) + { + isFirst = false; + } + else + { + builder.Append(" or "); + } + builder.Append(subFilter); + } + return builder.Append(')').ToString(); + } + } + #endregion + + #region PresenceFilter + /// + /// A PresenceFilter determines if the attribute provided is present in the + /// + /// + /// since 1.5 + public class PresenceFilter : Filter + { + + private readonly string _name; + + public PresenceFilter(string attributeName) + { + this._name = attributeName; + if (StringUtil.IsBlank(_name)) + { + throw new ArgumentException("Attribute name not be null!"); + } + } + + /// + /// Name of the attribute to find in the . + /// + public virtual string Name + { + get + { + return _name; + } + } + + /// + /// Determines if the attribute provided is present in the + /// . + /// + public virtual bool Accept(ConnectorObject obj) + { + return obj.GetAttributeByName(_name) != null; + } + + public virtual R Accept(FilterVisitor v, P p) + { + return v.VisitExtendedFilter(p, this); + } + } + #endregion + + #region SingleValueAttributeFilter + /// + /// Get a single value out of the attribute to test w/. + /// + public abstract class SingleValueAttributeFilter : AttributeFilter + { + /// + /// Attempt to single out the value for comparison. + /// + internal SingleValueAttributeFilter(ConnectorAttribute attr) : + base(attr) + { + // make sure this is not a Uid.. + if (Uid.NAME.Equals(attr.Name)) + { + String MSG = "Uid can only be used for equals comparison."; + throw new ArgumentException(MSG); + } + // actual runtime.. + if (attr.Value.Count != 1) + { + String ERR = "Must only be one value!"; + throw new ArgumentException(ERR); + } + } + + /// + /// Value to test against. + /// + public Object GetValue() + { + return GetAttribute().Value[0]; + } + /// + /// Name of the attribute to find in the . + /// + public String GetName() + { + return GetAttribute().Name; + } + } + #endregion + + #region StartsWithFilter + public sealed class StartsWithFilter : StringFilter + { + public StartsWithFilter(ConnectorAttribute attr) + : base(attr) + { + } + + public override bool Accept(String value) + { + return value.StartsWith(GetValue()); + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitStartsWithFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("STARTSWITH: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region StringFilter + /// + /// Filter based on strings. + /// + public abstract class StringFilter : SingleValueAttributeFilter + { + /// + /// Attempts to get a string from the attribute. + /// + internal StringFilter(ConnectorAttribute attr) + : base(attr) + { + Object val = base.GetValue(); + if (!(val is string)) + { + String MSG = "Value must be a string!"; + throw new ArgumentException(MSG); + } + } + + /// + /// Get the string value from the afore mentioned attribute. + /// + /// + public new String GetValue() + { + return (String)base.GetValue(); + } + + /// + /// + /// iff the value from the 's attribute + /// of the same name as provided is not a string. + /// + public override bool Accept(ConnectorObject obj) + { + bool ret = false; + ConnectorAttribute attr = obj.GetAttributeByName(GetName()); + if (attr != null) + { + ret = Accept((string)attr.Value[0]); + } + return ret; + } + + public abstract bool Accept(String value); + } + #endregion + + #region ContainsAllValues Filter + public class ContainsAllValuesFilter : AttributeFilter + { + private readonly string _name; + private readonly IList _values; + + public ContainsAllValuesFilter(ConnectorAttribute attr) + : base(attr) + { + _name = attr.Name; + _values = attr.Value; + } + /// + /// Determine if the contains an + /// which contains all the values provided in the passed + /// into the filter. + /// + public override bool Accept(ConnectorObject obj) + { + bool rv = false; + ConnectorAttribute found = obj.GetAttributeByName(_name); + if (found != null) + { + // TODO: possible optimization using 'Set' + foreach (object o in _values) + { + if (!(rv = found.Value.Contains(o))) + { + break; + } + } + } + return rv; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitContainsAllValuesFilter(p, this); + } + } + #endregion + + #region ExternallyChainedFilter + /// + /// Externally chained filters e.g. the filter implementing case insensitive searches. + /// They are removed from translation. + /// + /// IMPORTANT: Currently, these filters have to be chained at the beginning of the filter tree. + /// + /// + public abstract class ExternallyChainedFilter : Filter + { + private readonly Filter _filter; + + /// + /// Get the internal filter that is being chained upon. + /// + public Filter Filter + { + get + { + return _filter; + } + } + + protected ExternallyChainedFilter(Filter filter) + { + _filter = filter; + } + + public abstract bool Accept(ConnectorObject obj); + public abstract R Accept(FilterVisitor v, P p); + } + + #endregion + + +} \ No newline at end of file diff --git a/dotnet/framework/Framework/CommonSerializer.cs b/dotnet/framework/Framework/CommonSerializer.cs new file mode 100644 index 00000000..4b8b5dc4 --- /dev/null +++ b/dotnet/framework/Framework/CommonSerializer.cs @@ -0,0 +1,330 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012 ForgeRock AS. + */ +using System; +using System.IO; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace Org.IdentityConnectors.Framework.Common.Serializer +{ + /// + /// Interface for reading objects from a stream. + /// + public interface BinaryObjectDeserializer + { + /// + /// Reads the next object from the stream. + /// + /// + /// Throws + /// a wrapped if end of stream is reached. + /// + /// The next object from the stream. + object ReadObject(); + + /// + /// Closes the underlying stream + /// + void Close(); + } + /// + /// Interface for writing objects to a stream. + /// + public interface BinaryObjectSerializer + { + /// + /// Writes the next object to the stream. + /// + /// The object to write. + /// + void WriteObject(object obj); + + /// + /// Flushes the underlying stream. + /// + void Flush(); + + /// + /// Closes the underylying stream after first flushing it. + /// + void Close(); + } + + /// + /// Serializer factory for serializing connector objects. + /// + /// + /// The list of + /// supported types are as follows: + /// TODO: list supported types + /// + /// + /// + /// + public abstract class ObjectSerializerFactory + { + // At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Serializer.ObjectSerializerFactoryImpl"; + + private static ObjectSerializerFactory _instance; + + private static readonly Object LOCK = new Object(); + + + /// + /// Get the singleton instance of the . + /// + public static ObjectSerializerFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { + SafeType t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + + _instance = t.CreateInstance(); + } + return _instance; + } + } + + /// + /// Creates a BinaryObjectSerializer for writing objects to + /// the given stream. + /// + /// + /// NOTE: consider using + /// for convenience serializing a single object. + /// + /// NOTE2: do not mix and match + /// with {. This is unsafe since there + /// is header information and state associated with the stream. Objects written + /// using one method must be read using the proper corresponding method. + /// + /// The stream + /// The serializer + public abstract BinaryObjectSerializer NewBinarySerializer(Stream os); + + /// + /// Creates a BinaryObjectDeserializer for reading objects from + /// the given stream. + /// + /// + /// NOTE: consider using + /// for convenience deserializing a single object. + /// NOTE2: do not mix and match + /// with {. This is unsafe since there + /// is header information and state associated with the stream. Objects written + /// using one method must be read using the proper corresponding method. + /// + /// The stream + /// The deserializer + public abstract BinaryObjectDeserializer NewBinaryDeserializer(Stream i); + + /// + /// Creates a BinaryObjectSerializer for writing objects to + /// the given stream. + /// + /// + /// NOTE: consider using + /// for convenience serializing a single object. + /// + /// NOTE2: do not mix and match + /// with {. + /// + /// The writer + /// True to include the xml header + /// Is this to produce a multi-object document. If false, only + /// a single object may be written. + /// The serializer + public abstract XmlObjectSerializer NewXmlSerializer(TextWriter w, + bool includeHeader, + bool multiObject); + + /// + /// Deserializes XML objects from a stream + /// NOTE: Consider using + /// for convenience deserializing a single object. + /// + /// + /// NOTE2: Do not mix and match + /// with {. + /// + /// The input source + /// The callback to receive objects from the stream + /// True iff we are to validate + public abstract void DeserializeXmlStream(TextReader reader, + XmlObjectResultsHandler handler, + bool validate); + + } + + /// + /// Bag of utilities for serialization + /// + public static class SerializerUtil + { + /// + /// Serializes the given object to bytes + /// + /// The object to serialize + /// The bytes + /// + public static byte[] SerializeBinaryObject(object obj) + { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + MemoryStream mem = new MemoryStream(); + BinaryObjectSerializer ser = fact.NewBinarySerializer(mem); + ser.WriteObject(obj); + ser.Close(); + return mem.ToArray(); + } + + /// + /// Serializes the given object to Base64 string + /// + /// The object to serialize + /// The Base64 string + /// + /// Since 1.4 + public static string SerializeBase64Object(object obj) + { + return Convert.ToBase64String(SerializeBinaryObject(obj)); + } + + /// + /// Deserializes the given object from bytes + /// + /// The bytes to deserialize + /// The object + /// + public static object DeserializeBinaryObject(byte[] bytes) + { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + MemoryStream mem = new MemoryStream(bytes); + BinaryObjectDeserializer des = fact.NewBinaryDeserializer(mem); + return des.ReadObject(); + } + + /// + /// Deserializes the given object from Base64 string + /// + /// The string to deserialize + /// The object + /// Since 1.4 + public static object DeserializeBase64Object(string encdata) + { + return DeserializeBinaryObject(Convert.FromBase64String(encdata)); + } + + + /// + /// Serializes the given object to xml + /// + /// The object to serialize + /// True if we are to include the xml header. + /// The xml + /// + public static String SerializeXmlObject(Object obj, bool includeHeader) + { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + StringWriter w = new StringWriter(); + XmlObjectSerializer ser = fact.NewXmlSerializer(w, includeHeader, false); + ser.WriteObject(obj); + ser.Close(true); + return w.ToString(); + } + + /// + /// Deserializes the given object from xml + /// + /// The xml to deserialize + /// True if we are to validate the xml + /// The object + /// + public static Object DeserializeXmlObject(String str, bool validate) + { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + StringReader source = new StringReader(str); + IList rv = new List(); + fact.DeserializeXmlStream(source, + obj => + { + rv.Add(obj); + return true; + }, validate); + if (rv.Count > 0) + { + return rv[0]; + } + else + { + return null; + } + } + + /// + /// Clones the given object by serializing it to bytes and then + /// deserializing it. + /// + /// The object. + /// A clone of the object + public static object CloneObject(Object obj) + { + byte[] bytes = SerializeBinaryObject(obj); + return DeserializeBinaryObject(bytes); + } + + } + + /// + /// Callback interface to receive xml objects from a stream of objects. + /// + public delegate bool XmlObjectResultsHandler(Object obj); + + /// + /// Interface for writing objects to a stream. + /// + public interface XmlObjectSerializer + { + /// + /// Writes the next object to the stream. + /// + /// The object to write. + /// + /// if there is more than one object + /// and this is not configured for multi-object document. + void WriteObject(Object obj); + + /// + /// Flushes the underlying stream. + /// + void Flush(); + + /// + /// Adds document end tag and optinally closes the underlying stream + /// + void Close(bool closeUnderlyingStream); + } +} \ No newline at end of file diff --git a/dotnet/framework/Framework/Framework.csproj b/dotnet/framework/Framework/Framework.csproj new file mode 100644 index 00000000..0fcd0114 --- /dev/null +++ b/dotnet/framework/Framework/Framework.csproj @@ -0,0 +1,106 @@ + + + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Debug + AnyCPU + Library + Org.IdentityConnectors.Framework + Framework + Open Connectors Framework + v4.5.2 + False + False + 4 + false + true + + + + prompt + 4 + AnyCPU + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + true + prompt + 4 + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + false + + + false + + + + + + 4.0 + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/Framework/Spi.cs b/dotnet/framework/Framework/Spi.cs new file mode 100644 index 00000000..dfaab7ea --- /dev/null +++ b/dotnet/framework/Framework/Spi.cs @@ -0,0 +1,547 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace Org.IdentityConnectors.Framework.Spi +{ + #region AttributeNormalizer + /// + /// Interface to be implemented by connectors that need + /// to normalize certain attributes. + /// + /// + /// This might, for + /// example, be used to normalize whitespace within + /// DN's to ensure consistent filtering whether that + /// filtering is natively on the resource or by the + /// connector framework. For connectors implementing + /// this interface, the method + /// will be applied to each of the following: + /// + /// + /// The filter passed to . + /// + /// + /// + /// The results returned from . + /// + /// + /// + /// The results returned from . + /// + /// + /// + /// The attributes passed to . + /// + /// + /// + /// The Uid returned from . + /// + /// + /// + /// The attributes passed to . + /// + /// + /// + /// The Uid returned from . + /// + /// + /// + /// The attributes passed to . + /// + /// + /// + /// The Uid returned from . + /// + /// + /// + /// The Uid passed to . + /// + /// + /// + /// + public interface AttributeNormalizer + { + ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute); + } + #endregion + + #region Configuration + /// + /// Encapsulates the configuration of a connector. + /// + /// Implementations of the Configuration interface must have a default + /// constructor. All properties are considered configuration for the connector. + /// The initial value of the property is + /// considered the default value of the property. The types of the properties + /// can be only those returned by and + /// multi-dimensional arrays thereof. The properties are not required by default, + /// but a property can be marked as required through use of the . + /// + /// + /// Each property corresponds to two entries in a resource named Messages: + /// display_[Property] and help_[Property]. For example, + /// help_hostname and display_helphostname would be the keys + /// corresponding to a hostname property. The display message is the display + /// name of the property and can be used to display the property in a view. The help + /// message holds the description of the property. The names of the two keys can be overridden + /// through the ConfigurationProperty annotation. + /// + /// + public interface Configuration + { + /// + /// Gets or sets the {@link ConnectorMessages message catalog} instance that allows the Connector + /// to localize messages. The setter is called before any bean property setter, + /// the method or this property getter. + /// + ConnectorMessages ConnectorMessages { get; set; } + + /// + /// Determines if the configuration is valid. + /// + /// A valid configuration is one that is ready to be used by the connector: + /// it is complete (all the required properties have been given values) + /// and the property values are well-formed (are in the expected range, + /// have the expected format, etc.) + /// + /// + /// Implementations of this method should not connect to the resource + /// in an attempt to validate the configuration. For example, implementations + /// should not attempt to check that a host of the specified name exists + /// by making a connection to it. Such checks can be performed in the implementation + /// of the method. + /// + /// + /// iff the configuration is not valid. Implementations + /// are encouraged to throw the most specific exception available. + /// When no specific exception is available, implementations can throw + /// . + void Validate(); + } + #endregion + + #region AbstractConfiguration + public abstract class AbstractConfiguration : Configuration + { + public interface IConfigurationChangeCallback + { + void NotifyUpdate(); + } + + private IConfigurationChangeCallback _callback; + + public ConnectorMessages ConnectorMessages { get; set; } + + public abstract void Validate(); + + public void AddChangeCallback(IConfigurationChangeCallback handler) + { + if (null != _callback) + { + throw new InvalidOperationException("Configuration change update handler has been set"); + } + _callback = handler; + } + + protected internal virtual void NotifyConfigurationUpdate() + { + if (null != _callback) + { + try + { + _callback.NotifyUpdate(); + } + catch (Exception) + { + // Ignored + } + } + } + } + #endregion + + #region ConnectorClassAttribute + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ConnectorClassAttribute : System.Attribute + { + + private readonly String _connectorDisplayNameKey; + private readonly String _connectorCategoryKey; + private readonly SafeType _connectorConfigurationClass; + + public ConnectorClassAttribute(String connectorDisplayNameKey, + Type connectorConfigurationClass) + { + _connectorDisplayNameKey = connectorDisplayNameKey; + _connectorCategoryKey = string.Empty; + _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); + } + + public ConnectorClassAttribute(String connectorDisplayNameKey, String connectorCategoryKey, + Type connectorConfigurationClass) + { + _connectorDisplayNameKey = connectorDisplayNameKey; + _connectorCategoryKey = connectorCategoryKey; + _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); + } + + /// + /// The display name key. This must be a key in the message catalog. + /// + public string ConnectorDisplayNameKey + { + get + { + return _connectorDisplayNameKey; + } + } + + /// + /// Category the connector belongs to such as 'LDAP' or 'DB'. + /// + public string ConnectorCategoryKey + { + get + { + return _connectorCategoryKey; + } + } + + public SafeType ConnectorConfigurationType + { + get + { + return _connectorConfigurationClass; + } + } + + /// + /// The resource path(s) to the message catalog. + /// Message catalogs are searched in the order given such + /// that the first one wins. By default, if no paths are + /// specified, we use connector-package.Messages.resx + /// + public string[] MessageCatalogPaths { get; set; } + + } + #endregion + + #region ConfigurationrClassAttribute + /// + /// The interface is traversed through reflection. This + /// annotation provides a way to override the default "add all property" behaviour. + /// + /// @since 1.4 + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ConfigurationClassAttribute : System.Attribute + { + + private readonly Boolean _skipUnsupported; + private readonly String[] _ignore; + + public ConfigurationClassAttribute(Boolean skipUnsupported, String[] ignore) + { + _skipUnsupported = skipUnsupported; + _ignore = ignore ?? new string[]{} ; + } + + /// + /// Silently skips properties with unsupported types. + /// + public Boolean SkipUnsupported + { + get + { + return _skipUnsupported; + } + } + + /// + /// List of properties which should be excluded from configuration properties.. + /// + public string[] Ignore + { + get + { + return _ignore; + } + } + } + #endregion + + #region ConfigurationPropertyAttribute + /// + /// The interface is traversed through reflection. This + /// annotation provides a way to override the default configuration operation for + /// each property. + /// + /// + /// + /// public class MyClass : Configuration { + /// [ConfigurationPropertionOptions(Confidential=true)] + /// public string MyProperty {get ; set;} + /// } + /// + /// + [AttributeUsage(AttributeTargets.Property)] + public class ConfigurationPropertyAttribute : System.Attribute + { + + /// + /// Order in which this property is displayed. + /// + public int Order { get; set; } + /// + /// Is this a confidential property whose value should be + /// encrypted by the application when persisted? + /// + public bool Confidential { get; set; } + /// + /// Is this a required property? + /// + public bool Required { get; set; } + /// + /// Change the default help message key. + /// + public string HelpMessageKey { get; set; } + /// + /// Change the default display message key. + /// + public string DisplayMessageKey { get; set; } + /// + /// Change the default group message key. + /// + public string GroupMessageKey { get; set; } + + /// + /// List of operations for which this property must be specified. + /// + /// + /// This is used for the case where a connector may or may not + /// implement certain operations depending in the configuration. + /// The default value of "empty array" is special in that + /// it means that this property is applicable to all operations. + /// MUST be SPI operations + /// + public Type[] OperationTypes { get; set; } + + /// + /// List of operations for which this property must be specified. + /// + /// + /// This is used for the case where a connector may or may not + /// implement certain operations depending in the configuration. + /// The default value of "empty array" is special in that + /// it means that this property is applicable to all operations. + /// + public SafeType[] Operations + { + get + { + Type[] types = OperationTypes; + SafeType[] rv = new SafeType[types.Length]; + for (int i = 0; i < types.Length; i++) + { + rv[i] = + SafeType.ForRawType(types[i]); + } + return rv; + } + } + + /// + /// Default constructor + /// + public ConfigurationPropertyAttribute() + { + Order = 1; + Confidential = false; + Required = false; + HelpMessageKey = null; + DisplayMessageKey = null; + OperationTypes = new Type[0]; + GroupMessageKey = null; + } + } + #endregion + + #region Connector + /// + /// This is the main interface to declare a connector. Developers must implement + /// this interface. The life-cycle for a is as follows + /// is called then any of the operations implemented + /// in the Connector and finally dispose. The and + /// allow for block operations. For instance bulk creates or + /// deletes and the use of before and after actions. Once is + /// called the object is discarded. + /// + public interface Connector : IDisposable + { + + /// + /// Initialize the connector with its configuration. For instance in a JDBC + /// Connector this would include the database URL, password, and user. + /// + /// instance of the object implemented by + /// the developer and populated with information + /// in order to initialize the . + void Init(Configuration configuration); + } + #endregion + + #region PoolableConnector + /// + /// To be implemented by Connectors that wish to be pooled. + /// + public interface PoolableConnector : Connector + { + /// + /// Checks if the connector is still alive. + /// + /// A connector can spend a large amount of time in the pool before + /// being used. This method is intended to check if the connector is + /// alive and operations can be invoked on it (for instance, an implementation + /// would check that the connector's physical connection to the resource + /// has not timed out). + /// + /// + /// The major difference between this method and is that + /// this method must do only the minimum that is necessary to check that the + /// connector is still alive. TestOp.Test() does a more thorough + /// check of the environment specified in the Configuration, and can therefore + /// be much slower. + /// + /// + /// This method can be called often. Implementations should do their + /// best to keep this method fast. + /// + /// + /// if the connector is no longer alive. + void CheckAlive(); + } + #endregion + + #region SearchResultsHandler + /// + /// A SearchResultsHandler is a completion handler for consuming the results of a + /// search request. + /// + /// A search result completion handler may be specified when performing search + /// requests using a + /// object. The method is invoked each time a matching + /// + /// resource is returned, followed by indicating that no + /// more ConnectorObject resources will be returned. + /// + /// + /// Implementations of these methods should complete in a timely manner so as to + /// avoid keeping the invoking thread from dispatching to other completion + /// handlers. + /// + /// + /// + /// Since 1.4 + public class SearchResultsHandler : ResultsHandler + { + + /// + /// Invoked when the request has completed successfully. + /// + /// + /// The query result indicating that no more resources are to be + /// returned and, if applicable, including information which + /// should be used for subsequent paged results query requests. + public Action HandleResult; + + } + #endregion + + #region StatefulConfiguration + /// + /// A Stateful Configuration interface extends the default + /// and makes the framework keep the same instance. + ///

+ /// The default Configuration object instance is constructed every single time + /// before the is called. If the + /// configuration class implements this interface then the Framework keeps one + /// instance of Configuration and the is + /// called with the same instance. This requires extra caution because the + /// framework only guaranties to create one instance and set the properties + /// before it calls the on different + /// connector instances in multiple different threads at the same time. The + /// Connector developer must quarantine that the necessary resource + /// initialisation are thread-safe. + /// + ///

+ /// If the connector implements the then this + /// configuration is kept in the + /// + /// and when the + /// + /// calls the method. If the connector implements only the + /// then this configuration is kept in the + /// and the + /// application must take care of releasing. + /// + ///

+ public interface StatefulConfiguration : Configuration + { + + /// + /// Release any allocated resources. + /// + void Release(); + + } + #endregion + + #region SyncTokenResultsHandler + /// + /// A SyncTokenResultsHandler is a Callback interface that an application + /// implements in order to handle results from + /// in a + /// stream-processing fashion. + /// + /// + /// Since 1.4 + public class SyncTokenResultsHandler : SyncResultsHandler + { + + /// + /// Invoked when the request has completed successfully. + /// + /// + /// The sync result indicating that no more resources are to be + /// returned and, if applicable, including information which + /// should be used for next sync requests. + //void HandleResult(SyncToken result); + public Action HandleResult; + + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/Framework/SpiOperations.cs b/dotnet/framework/Framework/SpiOperations.cs new file mode 100644 index 00000000..36516aa2 --- /dev/null +++ b/dotnet/framework/Framework/SpiOperations.cs @@ -0,0 +1,520 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Collections.Generic; + +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Spi.Operations +{ + /// + /// Authenticate an object based on their unique identifier and password. + /// + public interface AuthenticateOp : SPIOperation + { + /// + /// Simple authentication with two parameters presumed to be user name and + /// password. + /// + /// + /// The developer is expected to attempt to + /// authenticate these credentials natively. If the authentication fails the + /// developer should throw a type of either + /// or if a native exception is available + /// and if its of type simple throw it. If the + /// native exception is not a wrap it in one and + /// throw it. This will provide the most detail for logging problem and + /// failed attempts. + /// + /// The developer is of course encourage to try and throw the most + /// informative exception as possible. In that regards there are several + /// exceptions provided in the exceptions package. For instance one of the + /// most common is . + /// + /// + /// + /// the name based credential for authentication. + /// the password based credential for authentication. + /// iff native authentication fails. If a native exception if + /// available attempt to throw it. + Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); + } + + #region IConnectorEventSubscriptionOp + /// + /// A ConnectorEventSubscriptionOp. + /// + /// since 1.5 + public interface IConnectorEventSubscriptionOp : SPIOperation + { + ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver observer, OperationOptions operationOptions); + } + #endregion + + public interface ResolveUsernameOp : SPIOperation + { + /// + /// This is a companion to the simple with two parameters + /// presumed to be user name and password. + /// + /// + /// The difference is that this + /// method does not try to authenticate the credentials; instead, it + /// return the of the username that would be authenticated. + /// + /// If the authentication fails the + /// developer should throw a type of either + /// or if a native exception is available + /// and if its of type simple throw it. If the + /// native exception is not a wrap it in one and + /// throw it. This will provide the most detail for logging problem and + /// failed attempts. + /// + /// + /// The developer is of course encourage to try and throw the most + /// informative exception as possible. In that regards there are several + /// exceptions provided in the exceptions package. For instance one of the + /// most common is . + /// + /// + /// The object class to use for authenticate. + /// Will typically be an account. Will not be null. + /// the username that would be authenticated. Will not be null. + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. + /// Uid The uid of the account that would be authenticated. + /// iff native authentication fails. If a native exception is + /// available attempt to throw it. + Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options); + } + + /// + /// The developer is responsible for taking the attributes + /// given (which always includes the ) and create an object + /// and its . + /// + /// + /// The developer must return the + /// so that the caller can refer to the created object. + /// + /// The developer should make a best effort to create the + /// object otherwise throw an informative telling the + /// caller why the operation could not be completed. It reasonable to use + /// defaults for required s as long as they are documented. + /// + /// + /// Will Droste + /// $Revision $ + /// 1.0 + public interface CreateOp : SPIOperation + { + /// + /// The developer is responsible for taking the attributes + /// given (which always includes the ) and create an + /// object and its . + /// + /// + /// The developer must return + /// the so that the caller can refer to the created object. + /// + /// specifies the name of the object to create. + /// includes all the attributes necessary to create the resource + /// object including the attribute. + /// the unique id for the object that is created. For instance in + /// LDAP this would be the 'dn', for a database this would be the + /// primary key, and for 'ActiveDirectory' this would be the GUID. + Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); + } + + /// + /// Deletes an object with the specified Uid and ObjectClass on the + /// resource. + /// + public interface DeleteOp : SPIOperation + { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// The type of object to delete. + /// The unique identitfier for the object to delete. + /// Throws UnknowUid if the object does not exist. + void Delete(ObjectClass objClass, Uid uid, OperationOptions options); + } + + public interface SchemaOp : SPIOperation + { + /// + /// Determines what types of objects this supports. + /// + /// + /// This + /// method is considered an operation since determining supported objects may + /// require configuration information and allows this determination to be + /// dynamic. + /// + /// basic schema supported by this . + Schema Schema(); + } + /// + /// Operation that runs a script in the environment of the connector. + /// + /// + /// (Compare to , which runs a script + /// on the target resource that the connector manages.) + /// A connector that intends to provide to scripts + /// more than is required by the basic contract + /// specified in the javadoc for + /// should implement this interface. + /// + /// Each connector that implements this interface must support + /// at least the behavior specified by . + /// A connector also may expose additional variables for use by scripts + /// and may respond to specific . + /// Each connector that implements this interface + /// must describe in its javadoc as available "for use by connector scripts" + /// any such additional variables or supported options. + /// + /// + public interface ScriptOnConnectorOp : SPIOperation + { + + /// + /// Runs the script request. + /// + /// The script and arguments to run. + /// Additional options that control how the script is + /// run. + /// The result of the script. The return type must be + /// a type that the framework supports for serialization. + /// See for a list of supported types. + Object RunScriptOnConnector(ScriptContext request, + OperationOptions options); + } + /// + /// Operation that runs a script directly on a target resource. + /// + /// + /// (Compare to , which runs a script + /// in the context of a particular connector.) + /// + /// A connector that intends to support + /// + /// should implement this interface. Each connector that implements + /// this interface must document which script languages the connector supports, + /// as well as any supported . + /// + /// + public interface ScriptOnResourceOp : SPIOperation + { + /// + /// Run the specified script on the target resource + /// that this connector manages. + /// + /// The script and arguments to run. + /// Additional options that control + /// how the script is run. + /// The result of the script. The return type must be + /// a type that the framework supports for serialization. + /// See for a list of supported types. + Object RunScriptOnResource(ScriptContext request, + OperationOptions options); + } + #region SearchOp + /// + /// Implement this interface to allow the Connector to search for resource + /// objects. + /// + public interface SearchOp : SPIOperation where T : class + { + /// + /// Creates a filter translator that will translate + /// a specified filter to the native filter. + /// + /// + /// The + /// translated filters will be subsequently passed to + /// + /// + /// The object class for the search. Will never be null. + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. + /// A filter translator. + FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options); + /// + /// This will be called by ConnectorFacade, once for each native query produced + /// by the FilterTranslator. + /// + /// + /// If there is more than one query the results will + /// automatically be merged together and duplicates eliminated. NOTE + /// that this implies an in-memory data structure that holds a set of + /// Uids, so memory usage in the event of multiple queries will be O(N) + /// where N is the number of results. That is why it is important that + /// the FilterTranslator implement OR if possible. + /// + /// The object class for the search. Will never be null. + /// The native query to run. A value of null means 'return everything for the given object class'. + /// Results should be returned to this handler + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. + void ExecuteQuery(ObjectClass oclass, T query, ResultsHandler handler, OperationOptions options); + } + #endregion + + #region ISyncEventSubscriptionOp + /// + /// ASyncEventSubscriptionOp. + /// + /// since 1.5 + public interface ISyncEventSubscriptionOp : SPIOperation + { + ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver asyncHandler, OperationOptions operationOptions); + } + #endregion + + #region SyncOp + /// + /// Receive synchronization events from the resource. + /// + /// + public interface SyncOp : SPIOperation + { + /// + /// Perform a synchronization. + /// + /// The object class to synchronize. Must not be null. + /// The token representing the last token from the previous sync. + /// Should be null if this is the first sync for the given + /// resource. + /// The result handler Must not be null. + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. + void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options); + /// + /// Returns the token corresponding to the latest sync delta. + /// + /// + /// This is to support applications that may wish to sync starting + /// "now". + /// + /// The latest token or null if there is no sync data. + SyncToken GetLatestSyncToken(ObjectClass objectClass); + } + #endregion + + /// + /// The developer of a Connector should implement either this interface or the + /// interface if the Connector will allow an authorized + /// caller to update (i.e., modify or replace) objects on the target resource. + /// + /// + /// + /// This update method is simpler to implement than {link UpdateAttributeValuesOp}, + /// which must handle any of several different types of update that the caller + /// may specify. However a true implementation of {link UpdateAttributeValuesOp} + /// offers better performance and atomicity semantics. + /// + /// + /// Will Droste + /// $Revision $ + /// 1.0 + public interface UpdateOp : SPIOperation + { + /// + /// Update the object specified by the and , + /// replacing the current values of each attribute with the values + /// provided. + /// + /// + /// + /// For each input attribute, replace + /// all of the current values of that attribute in the target object with + /// the values of that attribute. + /// + /// + /// If the target object does not currently contain an attribute that the + /// input set contains, then add this + /// attribute (along with the provided values) to the target object. + /// + /// + /// If the value of an attribute in the input set is + /// null, then do one of the following, depending on + /// which is most appropriate for the target: + /// + /// + /// If possible, remove that attribute from the target + /// object entirely. + /// + /// + /// + /// Otherwise, replace all of the current values of that + /// attribute in the target object with a single value of + /// null. + /// + /// + /// + /// + /// + /// the type of object to modify. Will never be null. + /// the uid of the object to modify. Will never be null. + /// set of new . the values in this set + /// represent the new, merged values to be applied to the object. + /// This set may also include . + /// Will never be null. + /// additional options that impact the way this operation is run. + /// Will never be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. + Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options); + } + + /// + /// More advanced implementation of to be implemented by + /// connectors that wish to offer better performance and atomicity semantics + /// for the methods + /// and . + /// + public interface UpdateAttributeValuesOp : UpdateOp + { + + /// + /// Update the object specified by the and , + /// adding to the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, add to + /// the current values of that attribute in the target object all of the + /// values of that attribute in the input set. + /// + /// + /// NOTE that this does not specify how to handle duplicate values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute may contain duplicates. + /// Therefore, in general simply append the provided values + /// to the current value for each attribute. + /// + /// + /// + /// + /// the type of object to modify. Will never be null. + /// the uid of the object to modify. Will never be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to add to attributes in the object. + /// merged. This set will never include . + /// Will never be null. + /// additional options that impact the way this operation is run. + /// Will never be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. + Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options); + + /// + /// Update the object specified by the and , + /// removing from the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, + /// remove from the current values of that attribute in the target object + /// any value that matches one of the values of the attribute from the input set. + /// + /// + /// NOTE that this does not specify how to handle unmatched values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute are merely representational state. + /// Therefore, the implementer should simply ignore any provided value + /// that does not match a current value of that attribute in the target + /// object. Deleting an unmatched value should always succeed. + /// + /// + /// the type of object to modify. Will never be null. + /// the uid of the object to modify. Will never be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to remove from attributes in the object. + /// merged. This set will never include . + /// Will never be null. + /// additional options that impact the way this operation is run. + /// Will never be null.. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. + Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options); + } + + /// + /// Tests the connector . + /// + /// Unlike validation performed by , testing a configuration + /// checks that any pieces of environment referred by the configuration are available. + /// For example, the connector could make a physical connection to a host specified + /// in the configuration to check that it exists and that the credentials + /// specified in the configuration are usable. + /// + /// + /// This operation may be invoked before the configuration has been validated. + /// An implementation is free to validate the configuration before testing it. + /// + public interface TestOp : SPIOperation + { + /// + /// Tests the with the connector. + /// + /// iff the configuration is not valid or the test failed. Implementations + /// are encouraged to throw the most specific exception available. + /// When no specific exception is available, implementations can throw + /// . + void Test(); + } + + /// + /// Tagging interface for the SPI. + /// + public interface SPIOperation + { + } +} \ No newline at end of file diff --git a/dotnet/framework/Framework/version.template b/dotnet/framework/Framework/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/Framework/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/Api.cs b/dotnet/framework/FrameworkInternal/Api.cs new file mode 100755 index 00000000..64bc8693 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/Api.cs @@ -0,0 +1,1550 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Security; +using System.Linq; +using System.Collections.Generic; +using System.Configuration; +using System.Globalization; +using System.Resources; +using System.Reflection; +using System.Diagnostics; +using System.Text; +using System.Threading; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using ConfigurationProperty = Org.IdentityConnectors.Framework.Api.ConfigurationProperty; + +namespace Org.IdentityConnectors.Framework.Impl.Api +{ + #region ConfigurationPropertyImpl + /// + /// Internal class, public only for unit tests + /// + public class ConfigurationPropertyImpl : ConfigurationProperty + { + private ICollection> _operations; + + public ConfigurationPropertyImpl() + { + } + + public ConfigurationPropertyImpl(ConfigurationPropertyImpl parent) + { + _operations = parent.Operations; + Parent = null; + Order = parent.Order; + Name = parent.Name; + HelpMessageKey = parent.HelpMessageKey; + DisplayMessageKey = parent.DisplayMessageKey; + Value = parent.Value; + GroupMessageKey = parent.GroupMessageKey; + ValueType = parent.ValueType; + IsConfidential = parent.IsConfidential; + IsRequired = parent.IsRequired; + } + + public ICollection> Operations + { + get + { + return _operations; + } + set + { + _operations = CollectionUtil.NewReadOnlySet(value); + } + } + + public ConfigurationPropertiesImpl Parent { get; set; } + + public int Order { get; set; } + + public string Name { get; set; } + + public string HelpMessageKey { get; set; } + + public string DisplayMessageKey { get; set; } + + public string GetHelpMessage(string def) + { + return FormatMessage(HelpMessageKey, def); + } + + public string GetDisplayName(string def) + { + return FormatMessage(DisplayMessageKey, def); + } + + public object Value { get; set; } + + public string GroupMessageKey { get; set; } + + public Type ValueType { get; set; } + + public bool IsConfidential { get; set; } + + public bool IsRequired { get; set; } + + public string GetGroup(string def) + { + return FormatMessage(GroupMessageKey, def); + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(Object o) + { + ConfigurationPropertyImpl other = o as ConfigurationPropertyImpl; + if (other != null) + { + if (!Name.Equals(other.Name)) + { + return false; + } + if (!CollectionUtil.Equals(Value, other.Value)) + { + return false; + } + if (Order != other.Order) + { + return false; + } + if (!CollectionUtil.Equals(HelpMessageKey, other.HelpMessageKey)) + { + return false; + } + if (!CollectionUtil.Equals(DisplayMessageKey, other.DisplayMessageKey)) + { + return false; + } + if (!CollectionUtil.Equals(GroupMessageKey, other.GroupMessageKey)) + { + return false; + } + if (IsConfidential != other.IsConfidential) + { + return false; + } + if (IsRequired != other.IsRequired) + { + return false; + } + if (!CollectionUtil.Equals(ValueType, other.ValueType)) + { + return false; + } + if (!CollectionUtil.Equals(_operations, other._operations)) + { + return false; + } + + return true; + } + return false; + } + private String FormatMessage(String key, String dflt, params object[] args) + { + APIConfigurationImpl apiConfig = Parent.Parent; + ConnectorMessages messages = + apiConfig.ConnectorInfo.Messages; + return messages.Format(key, dflt, args); + } + } + #endregion + + #region ConfigurationPropertiesImpl + /// + /// Internal class, public only for unit tests + /// + public class ConfigurationPropertiesImpl : ConfigurationProperties + { + //properties, sorted by "order" + private IList _properties; + //property names, sorted by "order + private IList _propertyNames; + private IDictionary _propertiesByName; + + public IList Properties + { + get + { + return _properties; + } + set + { + ConfigurationPropertyImpl[] arr = + value == null ? new ConfigurationPropertyImpl[0] : value.ToArray(); + + Array.Sort(arr, + (p1, p2) => { return p1.Order.CompareTo(p2.Order); }); + _properties = + CollectionUtil.NewReadOnlyList(arr); + IList propertyNames = new List(); + IDictionary propertiesByName = + new Dictionary(); + foreach (ConfigurationPropertyImpl property in _properties) + { + propertyNames.Add(property.Name); + propertiesByName[property.Name] = property; + property.Parent = this; + } + _propertyNames = CollectionUtil.AsReadOnlyList(propertyNames); + _propertiesByName = CollectionUtil.AsReadOnlyDictionary(propertiesByName); + } + } + public ConfigurationProperty GetProperty(String name) + { + return CollectionUtil.GetValue(_propertiesByName, name, null); + } + public IList PropertyNames + { + get + { + return _propertyNames; + } + } + public APIConfigurationImpl Parent { get; set; } + public void SetPropertyValue(String name, object val) + { + ConfigurationProperty property = GetProperty(name); + if (property == null) + { + String MSG = "Property '" + name + "' does not exist."; + throw new ArgumentException(MSG); + } + property.Value = val; + } + + public override int GetHashCode() + { + return CollectionUtil.GetHashCode(_properties); + } + + public override bool Equals(object o) + { + ConfigurationPropertiesImpl other = o as ConfigurationPropertiesImpl; + if (other != null) + { + return CollectionUtil.SetsEqual(_properties, + other._properties); + } + return false; + } + } + #endregion + + #region APIConfigurationImpl + /// + /// Internal class, public only for unit tests + /// + public class APIConfigurationImpl : APIConfiguration + { + // ======================================================================= + // Fields + // ======================================================================= + /// + /// All configuration related to connector pooling. + /// + private ObjectPoolConfiguration _connectorPoolConfiguration; + + private ResultsHandlerConfiguration _resultsHandlerConfiguration; + private ConfigurationPropertiesImpl _configurationProperties; + private ICollection> _supportedOperations = + CollectionUtil.NewReadOnlySet>(new SafeType[0]); + + private IDictionary, int> _timeoutMap = + new Dictionary, int>(); + + public ConfigurationProperties ConfigurationProperties + { + get + { + return _configurationProperties; + } + set + { + if (_configurationProperties != null) + { + _configurationProperties.Parent = null; + } + _configurationProperties = (ConfigurationPropertiesImpl)value; + if (_configurationProperties != null) + { + _configurationProperties.Parent = this; + } + } + } + public IDictionary, int> TimeoutMap + { + get + { + return _timeoutMap; + } + set + { + _timeoutMap = value; + } + } + public bool IsConnectorPoolingSupported { get; set; } + public ObjectPoolConfiguration ConnectorPoolConfiguration + { + get + { + if (_connectorPoolConfiguration == null) + { + _connectorPoolConfiguration = new ObjectPoolConfiguration(); + } + return _connectorPoolConfiguration; + } + set + { + _connectorPoolConfiguration = value; + } + } + public ResultsHandlerConfiguration ResultsHandlerConfiguration + { + get + { + if (_resultsHandlerConfiguration == null) + { + _resultsHandlerConfiguration = new ResultsHandlerConfiguration(); + } + return _resultsHandlerConfiguration; + } + set + { + _resultsHandlerConfiguration = value; + } + } + public ICollection> SupportedOperations + { + get + { + return _supportedOperations; + } + set + { + _supportedOperations = CollectionUtil.NewReadOnlySet>(value); + } + } + + public bool IsSupportedOperation(SafeType api) + { + return _supportedOperations.Contains(api); + } + + public int GetTimeout(SafeType operation) + { + return Math.Max(CollectionUtil.GetValue(_timeoutMap, operation, + APIConstants.NO_TIMEOUT), APIConstants.NO_TIMEOUT); + } + public void SetTimeout(SafeType operation, int timeout) + { + _timeoutMap[operation] = timeout; + } + + public AbstractConnectorInfo ConnectorInfo { get; set; } + + public IConfigurationPropertyChangeListener ChangeListener { get; set; } + + public int ProducerBufferSize { get; set; } + + // ======================================================================= + // Constructors + // ======================================================================= + public APIConfigurationImpl() + { + ProducerBufferSize = 100; + } + + public APIConfigurationImpl(APIConfigurationImpl other) + { + if (null != other._connectorPoolConfiguration) + { + ConnectorPoolConfiguration = new ObjectPoolConfiguration(other._connectorPoolConfiguration); + } + if (null != other._resultsHandlerConfiguration) + { + ResultsHandlerConfiguration = new ResultsHandlerConfiguration(other._resultsHandlerConfiguration); + } + IsConnectorPoolingSupported = other.IsConnectorPoolingSupported; + ConfigurationPropertiesImpl prop = new ConfigurationPropertiesImpl(); + prop.Properties = ((ConfigurationPropertiesImpl)other.ConfigurationProperties).Properties; + ConfigurationProperties = prop; + + ProducerBufferSize = other.ProducerBufferSize; + TimeoutMap = new Dictionary, int>(other.TimeoutMap); + SupportedOperations = new HashSet>(other.SupportedOperations); + + ConnectorInfo = other.ConnectorInfo; + ChangeListener = other.ChangeListener; + } + } + #endregion + + #region AbstractConnectorInfo + /// + /// internal class, public only for unit tests + /// + public class AbstractConnectorInfo : ConnectorInfo + { + private APIConfigurationImpl _defaultAPIConfiguration; + + + public string GetConnectorDisplayName() + { + return Messages.Format(ConnectorDisplayNameKey, + ConnectorKey.ConnectorName); + } + + public ConnectorKey ConnectorKey { get; set; } + + public string ConnectorDisplayNameKey { get; set; } + + public string ConnectorCategoryKey { get; set; } + + public ConnectorMessages Messages { get; set; } + + public APIConfigurationImpl DefaultAPIConfiguration + { + get + { + return _defaultAPIConfiguration; + } + set + { + if (value != null) + { + value.ConnectorInfo = this; + } + _defaultAPIConfiguration = value; + } + } + + public APIConfiguration CreateDefaultAPIConfiguration() + { + APIConfigurationImpl rv = + (APIConfigurationImpl) + SerializerUtil.CloneObject(_defaultAPIConfiguration); + rv.ConnectorInfo = this; + return rv; + } + + public string GetConnectorCategory() + { + return Messages.Format(ConnectorCategoryKey, null); + } + } + #endregion + + #region ConnectorMessagesImpl + /// + /// internal class, public only for unit tests + /// + public class ConnectorMessagesImpl : ConnectorMessages + { + private IDictionary> + _catalogs = new Dictionary>(); + + public String Format(String key, String dflt, params object[] args) + { + if (key == null) + { + return dflt; + } + CultureInfo locale = CultureInfo.CurrentUICulture; + if (locale == null) + { + locale = CultureInfo.CurrentCulture; + } + if (dflt == null) + { + dflt = key; + } + CultureInfo foundCulture = locale; + String message = GetCatalogMessage(foundCulture, key); + //check neutral culture + if (message == null) + { + foundCulture = foundCulture.Parent; + message = GetCatalogMessage(foundCulture, key); + } + //check invariant culture + if (message == null) + { + foundCulture = foundCulture.Parent; + message = GetCatalogMessage(foundCulture, key); + } + //and default to framework + if (message == null) + { + message = GetFrameworkMessage(locale, key); + } + if (message == null) + { + return dflt; + } + else + { + //TODO: think more about this since the formatting + //is slightly different than Java + return String.Format(foundCulture, message, args); + } + } + + private String GetCatalogMessage(CultureInfo culture, String key) + { + IDictionary catalog = CollectionUtil.GetValue(_catalogs, culture, null); + return catalog != null ? CollectionUtil.GetValue(catalog, key, null) : null; + } + + private String GetFrameworkMessage(CultureInfo culture, String key) + { + ResourceManager manager = + new ResourceManager("Org.IdentityConnectors.Resources", + typeof(ConnectorMessagesImpl).Assembly); + String contents = (String)manager.GetObject(key, culture); + return contents; + } + + public IDictionary> Catalogs + { + get + { + return _catalogs; + } + set + { + if (value == null) + { + _catalogs = + new Dictionary>(); + } + else + { + _catalogs = value; + } + } + } + } + #endregion + + #region ConnectorInfoManagerFactoryImpl + public sealed class ConnectorInfoManagerFactoryImpl : + ConnectorInfoManagerFactory + { + private class RemoteManagerKey + { + private readonly String _host; + private readonly int _port; + + public RemoteManagerKey(RemoteFrameworkConnectionInfo info) + { + _host = info.Host; + _port = info.Port; + } + + public override bool Equals(Object o) + { + if (o is RemoteManagerKey) + { + RemoteManagerKey other = (RemoteManagerKey)o; + if (!_host.Equals(other._host)) + { + return false; + } + if (_port != other._port) + { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() + { + return _host.GetHashCode() ^ _port; + } + + } + + private Object LOCAL_LOCK = new Object(); + private Object REMOTE_LOCK = new Object(); + + private ConnectorInfoManager + _localManagerCache; + + private IDictionary + _remoteManagerCache = new Dictionary(); + + public ConnectorInfoManagerFactoryImpl() + { + + } + + public override void ClearRemoteCache() + { + lock (REMOTE_LOCK) + { + _remoteManagerCache.Clear(); + } + } + + public override ConnectorInfoManager GetLocalManager() + { + lock (LOCAL_LOCK) + { + ConnectorInfoManager rv = _localManagerCache; + if (rv == null) + { + rv = new LocalConnectorInfoManagerImpl(); + } + _localManagerCache = rv; + return rv; + } + } + + public override ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info) + { + RemoteManagerKey key = new RemoteManagerKey(info); + lock (REMOTE_LOCK) + { + RemoteConnectorInfoManagerImpl rv = CollectionUtil.GetValue(_remoteManagerCache, key, null); + if (rv == null) + { + rv = new RemoteConnectorInfoManagerImpl(info); + } + _remoteManagerCache[key] = rv; + return rv.Derive(info); + } + } + + } + #endregion + + #region AbstractConnectorFacade + public abstract class AbstractConnectorFacade : ConnectorFacade + { + private readonly APIConfigurationImpl _configuration; + private readonly String _connectorFacadeKey; + + /// + /// Builds up the maps of supported operations and calls. + /// + public AbstractConnectorFacade(APIConfigurationImpl configuration) + { + Assertions.NullCheck(configuration, "configuration"); + //clone in case application tries to modify + //after the fact. this is necessary to + //ensure thread-safety of a ConnectorFacade + //also, configuration is used as a key in the + //pool, so it is important that it not be modified. + byte[] bytes = SerializerUtil.SerializeBinaryObject(configuration); + _connectorFacadeKey = Convert.ToBase64String(bytes); + _configuration = (APIConfigurationImpl)SerializerUtil.DeserializeBinaryObject(bytes); + //parent ref not included in the clone + _configuration.ConnectorInfo = configuration.ConnectorInfo; + _configuration.ChangeListener = configuration.ChangeListener; ; + } + + public AbstractConnectorFacade(string configuration, AbstractConnectorInfo connectorInfo) + : this(configuration, connectorInfo, null) + { + } + + /// + /// Builds up the maps of supported operations and calls. + /// + public AbstractConnectorFacade(string configuration, AbstractConnectorInfo connectorInfo, IConfigurationPropertyChangeListener changeListener) + { + Assertions.NullCheck(configuration, "configuration"); + Assertions.NullCheck(connectorInfo, "connectorInfo"); + _connectorFacadeKey = configuration; + _configuration = (APIConfigurationImpl)SerializerUtil.DeserializeBase64Object(configuration); + // parent ref not included in the clone + _configuration.ConnectorInfo = connectorInfo; + _configuration.ChangeListener = changeListener; + } + + /// + /// Return an instance of an API operation. + /// + /// + /// null if the operation is not support otherwise + /// return an instance of the operation. + /// + public APIOperation GetOperation(SafeType api) + { + if (!_configuration.IsSupportedOperation(api)) + { + return null; + } + return GetOperationImplementation(api); + } + + /// + /// Gets the unique generated identifier of this ConnectorFacade. + /// + /// It's not guarantied that the equivalent configuration will generate the + /// same configuration key. Always use the generated value and maintain it in + /// the external application. + /// + /// identifier of this ConnectorFacade instance. + public string ConnectorFacadeKey + { + get + { + return _connectorFacadeKey; + } + } + + public ICollection> SupportedOperations + { + get + { + return _configuration.SupportedOperations; + } + } + + // ======================================================================= + // Operation API Methods + // ======================================================================= + public Schema Schema() + { + return ((SchemaApiOp)GetOperationCheckSupported(SafeType.Get())) + .Schema(); + } + + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) + { + return ((CreateApiOp)GetOperationCheckSupported(SafeType.Get())).Create(oclass, attrs, options); + } + + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) + { + ((DeleteApiOp)GetOperationCheckSupported(SafeType.Get())) + .Delete(objClass, uid, options); + } + + public SearchResult Search(ObjectClass objectClass, Filter filter, ResultsHandler handler, OperationOptions options) + { + return ((SearchApiOp)GetOperationCheckSupported(SafeType.Get())).Search( + objectClass, filter, handler, options); + } + + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) + { + return ((UpdateApiOp)GetOperationCheckSupported(SafeType.Get())) + .Update(objclass, uid, attrs, options); + } + + public Uid AddAttributeValues( + ObjectClass objclass, + Uid uid, + ICollection attrs, + OperationOptions options) + { + return ((UpdateApiOp)GetOperationCheckSupported(SafeType.Get())) + .AddAttributeValues(objclass, uid, attrs, options); + } + + public Uid RemoveAttributeValues( + ObjectClass objclass, + Uid uid, + ICollection attrs, + OperationOptions options) + { + return ((UpdateApiOp)GetOperationCheckSupported(SafeType.Get())) + .RemoveAttributeValues(objclass, uid, attrs, options); + } + + public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) + { + return ((AuthenticationApiOp)GetOperationCheckSupported(SafeType.Get())).Authenticate( + objectClass, username, password, options); + } + + public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) + { + return ((ResolveUsernameApiOp)GetOperationCheckSupported(SafeType.Get())).ResolveUsername( + objectClass, username, options); + } + + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) + { + return ((GetApiOp)GetOperationCheckSupported(SafeType.Get())) + .GetObject(objClass, uid, options); + } + + public Object RunScriptOnConnector(ScriptContext request, + OperationOptions options) + { + return ((ScriptOnConnectorApiOp)GetOperationCheckSupported(SafeType.Get())) + .RunScriptOnConnector(request, options); + } + + public Object RunScriptOnResource(ScriptContext request, + OperationOptions options) + { + return ((ScriptOnResourceApiOp)GetOperationCheckSupported(SafeType.Get())) + .RunScriptOnResource(request, options); + } + + public void Test() + { + ((TestApiOp)GetOperationCheckSupported(SafeType.Get())).Test(); + } + + public void Validate() + { + ((ValidateApiOp)GetOperationCheckSupported(SafeType.Get())).Validate(); + } + + public SyncToken Sync(ObjectClass objectClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) + { + return ((SyncApiOp)GetOperationCheckSupported(SafeType.Get())) + .Sync(objectClass, token, handler, options); + } + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + return ((SyncApiOp)GetOperationCheckSupported(SafeType.Get())) + .GetLatestSyncToken(objectClass); + } + + public ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, + OperationOptions operationOptions) + { + return ((IConnectorEventSubscriptionApiOp)GetOperationCheckSupported(SafeType.Get())) + .Subscribe(objectClass, eventFilter, handler, operationOptions); + } + + public ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions) + { + return ((ISyncEventSubscriptionApiOp)GetOperationCheckSupported(SafeType.Get())) + .Subscribe(objectClass, token, handler, operationOptions); + } + + protected const String Msg = "Operation ''{0}'' not supported."; + + private APIOperation GetOperationCheckSupported(SafeType api) + { + // check if this operation is supported. + if (!SupportedOperations.Contains(api)) + { + + String str = String.Format(Msg, api); + throw new InvalidOperationException(str); + } + return GetOperationImplementation(api); + } + + /// + /// Gets the implementation of the given operation + /// + /// The operation to implement. + /// The implementation + protected abstract APIOperation GetOperationImplementation(SafeType api); + + protected APIConfigurationImpl GetAPIConfiguration() + { + return _configuration; + } + + /// + /// Creates a new proxy given a handler. + /// + protected APIOperation NewAPIOperationProxy(SafeType api, InvocationHandler handler) + { + return (APIOperation)Proxy.NewProxyInstance(api.RawType, handler); + } + + protected readonly static bool LOGGINGPROXY_ENABLED; + static AbstractConnectorFacade() + { + string enabled = ConfigurationManager.AppSettings.Get("logging.proxy"); + LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); + } + + /// + /// Creates the timeout proxy for the given operation. + /// + /// + /// The operation + /// + /// The underlying object + /// The proxy + protected internal APIOperation CreateTimeoutProxy(SafeType api, APIOperation target) + { + + int timeout = GetAPIConfiguration().GetTimeout(api); + int bufferSize = GetAPIConfiguration().ProducerBufferSize; + + DelegatingTimeoutProxy handler = new DelegatingTimeoutProxy(target, timeout, bufferSize); + + return NewAPIOperationProxy(api, handler); + } + + protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) + { + APIOperation ret = target; + if (LOGGINGPROXY_ENABLED) + { + LoggingProxy logging = new LoggingProxy(api, target); + ret = NewAPIOperationProxy(api, logging); + } + return ret; + } + } + #endregion + + #region ConnectorFacadeFactoryImpl + public class ConnectorFacadeFactoryImpl : ConnectorFacadeFactory + { + public override ConnectorFacade NewInstance(APIConfiguration config) + { + ConnectorFacade ret = null; + APIConfigurationImpl impl = (APIConfigurationImpl)config; + AbstractConnectorInfo connectorInfo = impl.ConnectorInfo; + if (connectorInfo is LocalConnectorInfoImpl) + { + LocalConnectorInfoImpl localInfo = + (LocalConnectorInfoImpl)connectorInfo; + // create a new Provisioner.. + ret = new LocalConnectorFacadeImpl(localInfo, impl); + } + else + { + ret = new RemoteConnectorFacadeImpl(impl); + } + return ret; + } + + public override ConnectorFacade NewInstance(ConnectorInfo connectorInfo, String config) + { + ConnectorFacade ret = null; + if (connectorInfo is LocalConnectorInfoImpl) + { + try + { + // create a new Provisioner. + ret = new LocalConnectorFacadeImpl((LocalConnectorInfoImpl)connectorInfo, config); + } + catch (Exception ex) + { + String connector = connectorInfo.ConnectorKey.ToString(); + Trace.TraceError("Failed to create new connector facade: {1}, {2}: {0}", connector, config, ex); + throw new ConnectorException(ex); + } + } + else if (connectorInfo is RemoteConnectorInfoImpl) + { + ret = new RemoteConnectorFacadeImpl((RemoteConnectorInfoImpl)connectorInfo, config); + } + return ret; + } + + /// + /// Dispose of all object pools and other resources associated with this + /// class. + /// + public override void Dispose() + { + ConnectorPoolManager.Dispose(); + } + + } + #endregion + + #region ManagedConnectorFacadeFactoryImpl + public class ManagedConnectorFacadeFactoryImpl : ConnectorFacadeFactoryImpl + { + + /// + /// Cache of the various ConnectorFacades. + /// + private static readonly ConcurrentDictionary> CACHE = + new ConcurrentDictionary>(); + + /// + /// {@inheritDoc} + /// + public override ConnectorFacade NewInstance(APIConfiguration config) + { + ConnectorFacade facade = base.NewInstance(config); + lock (CACHE) + { + Pair cachedFacade = CollectionUtil.GetValue(CACHE, facade.ConnectorFacadeKey, null); + if (null == cachedFacade) + { + CACHE[facade.ConnectorFacadeKey] = Pair.Of(DateTime.Now, facade); + } + else + { + Trace.TraceInformation("ConnectorFacade found in cache"); + cachedFacade.First = DateTime.Now; + facade = cachedFacade.Second; + } + } + return facade; + } + + public override ConnectorFacade NewInstance(ConnectorInfo connectorInfo, string config) + { + Pair facade = CollectionUtil.GetValue(CACHE, config, null); + if (null == facade) + { + lock (CACHE) + { + facade = CollectionUtil.GetValue(CACHE, config, null); + if (null == facade) + { + facade = Pair.Of(DateTime.Now, base.NewInstance(connectorInfo, config)); + CACHE[facade.Second.ConnectorFacadeKey] = facade; + } + else + { + facade.First = DateTime.Now; + Trace.TraceInformation("ConnectorFacade found in cache"); + } + } + } + else + { + facade.First = DateTime.Now; + } + return facade.Second; + } + + /// + /// Dispose of all object pools and other resources associated with this + /// class. + /// + public override void Dispose() + { + base.Dispose(); + foreach (Pair facade in CACHE.Values) + { + LocalConnectorFacadeImpl tmp = facade.Second as LocalConnectorFacadeImpl; + if (tmp != null) + { + try + { + tmp.Dispose(); + } + catch (Exception e) + { + Trace.TraceWarning("Failed to dispose facade: {0} {1}", e, facade); + } + } + } + CACHE.Clear(); + } + + public virtual void EvictIdle(TimeSpan unit) + { + if (unit == null) + { + throw new NullReferenceException(); + } + DateTime lastTime = DateTime.Now.Subtract(unit); + foreach (KeyValuePair> entry in CACHE) + { + if (lastTime.CompareTo(entry.Value.First) > 0) + { + Pair value; + if (CACHE.TryRemove(entry.Key, out value)) + { + if (value.Second is LocalConnectorFacadeImpl) + { + try + { + LocalConnectorFacadeImpl tmp = value.Second as LocalConnectorFacadeImpl; + if (tmp != null) + { + tmp.Dispose(); + Trace.TraceInformation("Disposed managed facade: {0}", entry.Value); + } + } + catch (Exception e) + { + Trace.TraceWarning("Failed to dispose facade: {0}, Exception: {1}", entry.Value, e); + } + } + } + } + } + } + + /// + /// Finds the {@code ConnectorFacade} in the cache. + /// + /// This is used for testing only. + /// + /// + /// the key to find the {@code ConnectorFacade}. + /// The {@code ConnectorFacade} or {@code null} if not found. + public virtual ConnectorFacade Find(string facadeKey) + { + Pair pair; + CACHE.TryGetValue(facadeKey, out pair); + if (pair != null) + { + return pair.Second; + } + return null; + } + } + #endregion + + #region ObjectStreamHandler + internal interface ObjectStreamHandler + { + bool Handle(Object obj); + } + #endregion + + #region StreamHandlerUtil + internal static class StreamHandlerUtil + { + /// + /// Adapts from a ObjectStreamHandler to a ResultsHandler + /// + private class ResultsHandlerAdapter + { + private readonly ObjectStreamHandler _target; + public ResultsHandlerAdapter(ObjectStreamHandler target) + { + _target = target; + } + + public ResultsHandler ResultsHandler + { + get + { + return new ResultsHandler() + { + Handle = obj => + { + return _target.Handle(obj); + } + + }; + } + } + } + + /// + /// Adapts from a ObjectStreamHandler to a SyncResultsHandler + /// + private class SyncResultsHandlerAdapter + { + private readonly ObjectStreamHandler _target; + public SyncResultsHandlerAdapter(ObjectStreamHandler target) + { + _target = target; + } + public SyncResultsHandler SyncResultsHandler + { + get + { + return new SyncResultsHandler() + { + + Handle = delta => + { + return _target.Handle(delta); + } + }; + } + } + } + + /// + /// Adapts from a ObjectStreamHandler to a SyncResultsHandler + /// + private class ObjectStreamHandlerAdapter : ObjectStreamHandler + { + private readonly Type _targetInterface; + private readonly Object _target; + public ObjectStreamHandlerAdapter(Type targetInterface, Object target) + { + Assertions.NullCheck(targetInterface, "targetInterface"); + Assertions.NullCheck(target, "target"); + if (!targetInterface.IsAssignableFrom(target.GetType())) + { + throw new ArgumentException("Target" + targetInterface + " " + target); + } + if (!IsAdaptableToObjectStreamHandler(targetInterface)) + { + throw new ArgumentException("Target interface not supported: " + targetInterface); + } + _targetInterface = targetInterface; + _target = target; + } + public bool Handle(Object obj) + { + if (_targetInterface.Equals(typeof(ResultsHandler))) + { + return ((ResultsHandler)_target).Handle((ConnectorObject)obj); + } + else if (_targetInterface.Equals(typeof(SyncResultsHandler))) + { + return ((SyncResultsHandler)_target).Handle((SyncDelta)obj); + } + else + { + throw new InvalidOperationException("Unhandled case: " + _targetInterface); + } + } + } + + public static bool IsAdaptableToObjectStreamHandler(Type clazz) + { + return (typeof(ResultsHandler).IsAssignableFrom(clazz) || + typeof(SyncResultsHandler).IsAssignableFrom(clazz)); + } + + public static ObjectStreamHandler AdaptToObjectStreamHandler(Type interfaceType, + Object target) + { + return new ObjectStreamHandlerAdapter(interfaceType, target); + } + public static Object AdaptFromObjectStreamHandler(Type interfaceType, + ObjectStreamHandler target) + { + if (interfaceType.Equals(typeof(ResultsHandler))) + { + return new ResultsHandlerAdapter(target).ResultsHandler; + } + else if (interfaceType.Equals(typeof(SyncResultsHandler))) + { + return new SyncResultsHandlerAdapter(target).SyncResultsHandler; + } + else + { + throw new InvalidOperationException("Unhandled case: " + interfaceType); + } + } + } + #endregion + + #region LoggingProxy + public class LoggingProxy : InvocationHandler + { + private readonly SafeType _op; + private readonly object _target; + private readonly Guid _guid; + + public LoggingProxy(SafeType api, object target) + { + _op = api; + _target = target; + _guid = Guid.NewGuid(); + } + /// + /// Log all operations. + /// + public Object Invoke(Object proxy, MethodInfo method, Object[] args) + { + //do not proxy equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + StringBuilder bld = new StringBuilder(); + bld.Append("Enter: "); + AddMethodName(bld, method); + bld.Append('('); + for (int i = 0; args != null && i < args.Length; i++) + { + if (i != 0) + { + bld.Append(", "); + } + bld.Append('{').Append(i).Append('}'); + } + bld.Append(')'); + // write out trace header + Trace.TraceInformation(bld.ToString(), args); + // invoke the method + try + { + object ret = method.Invoke(_target, args); + // clear out buffer. + bld.Length = 0; + bld.Append("Return: "); + AddMethodName(bld, method); + bld.Append("({0})"); + Trace.TraceInformation(bld.ToString(), ret); + return ret; + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + LogException(method, root); + throw root; + } + catch (Exception e) + { + LogException(method, e); + throw e; + } + } + + private void AddMethodName(StringBuilder bld, MethodInfo method) + { + bld.Append(_op.RawType.Name); + bld.Append('.'); + bld.Append(method.Name); + bld.Append(" - id[").Append(_guid).Append(']'); + } + + private void LogException(MethodInfo method, Exception e) + { + StringBuilder bld = new StringBuilder(); + bld.Append("Exception: "); + AddMethodName(bld, method); + TraceUtil.ExceptionToString(bld, e, string.Empty); + + // write out trace header + Trace.TraceInformation(bld.ToString()); + } + } + #endregion + + #region DelegatingTimeoutProxy + /// + /// Delegating timeout proxy that selects the appropriate timeout handler + /// depending on the method. + /// + public class DelegatingTimeoutProxy : InvocationHandler + { + /// + /// Default timeout for all operations. + /// + public static int NO_TIMEOUT = -1; + + /// + /// The underlying operation that we are providing a timeout for + /// + private readonly object target; + + /// + /// The timeout + /// + private readonly int timeoutMillis; + + /// + /// The buffer size + /// + private readonly int bufferSize; + + /// + /// Create a new MethodTimeoutProxy. + /// + /// + /// The object we are wrapping + /// + public DelegatingTimeoutProxy(object target, int timeoutMillis, int bufferSize) + { + this.target = target; + this.timeoutMillis = timeoutMillis; + this.bufferSize = bufferSize; + } + + public Object Invoke(object proxy, MethodInfo method, object[] args) + { + + // do not timeout equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + + // figure out the actual handler that we want to delegate to + InvocationHandler handler = null; + + // if this is as stream handler method, we need the + // buffered results proxy (if configured) + if (IsStreamHandlerMethod(method)) + { + if (timeoutMillis != NO_TIMEOUT || bufferSize != 0) + { + // handler = new BufferedResultsProxy(target, bufferSize, timeoutMillis); + } + } + // otherwise it's a basic timeout proxy + else + { + if (timeoutMillis != NO_TIMEOUT) + { + // everything else is a general purpose timeout proxy + handler = new MethodTimeoutProxy(target, timeoutMillis); + } + } + + // delegate to the timeout handler if specified + if (handler != null) + { + return handler.Invoke(proxy, method, args); + } + // otherwise, pass the call directly to the object + else + { + try + { + return method.Invoke(target, args); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + } + } + + private bool IsStreamHandlerMethod(MethodInfo method) + { + foreach (ParameterInfo paramType in method.GetParameters()) + { + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType.GetType())) + { + return true; + } + } + return false; + } + } + #endregion + + #region MethodTimeoutProxy + /// + /// General-purpose timeout proxy for providing timeouts on all methods on the + /// underlying object. Currently just used for APIOperations, but could wrap any + /// object. NOTE: this is not used for search because search needs timeout on an + /// element by element basis. Moreover, it would be unsafe for search since the + /// thread could continue to return elements after it has timed out and we need + /// to guarantee that not happen. + /// + public class MethodTimeoutProxy : InvocationHandler + { + /// + /// The underlying operation that we are providing a timeout for + /// + private readonly object target; + + /// + /// The timeout + /// + private readonly int timeoutMillis; + + /// + /// Create a new MethodTimeoutProxy. + /// + /// + /// The object we are wrapping + /// + public MethodTimeoutProxy(object target, int timeoutMillis) + { + this.target = target; + this.timeoutMillis = timeoutMillis; + } + + public Object Invoke(object proxy, MethodInfo method, Object[] args) + { + + // do not timeout equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + + //Locale locale = CurrentLocale.get(); + + try + { + var tokenSource = new CancellationTokenSource(); + CancellationToken token = tokenSource.Token; + //int timeOut = 1000; // 1000 ms + object result = null; + var task = Task.Factory.StartNew(() => + { + try + { + // propagate current locale + // since this is a thread pool + //CurrentLocale.set(locale); + result = method.Invoke(target, args); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + finally + { + //CurrentLocale.clear(); + } + }, token); + if (!task.Wait(timeoutMillis, token)) + { + throw new OperationTimeoutException("The Task timed out!"); + } + return result; + } + catch (TimeoutException ex) + { + throw new OperationTimeoutException(ex); + } + catch (AggregateException ex) + { + throw ex.InnerException; + } + } + + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/ApiLocal.cs b/dotnet/framework/FrameworkInternal/ApiLocal.cs new file mode 100644 index 00000000..34b1038c --- /dev/null +++ b/dotnet/framework/FrameworkInternal/ApiLocal.cs @@ -0,0 +1,1616 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Threading; +using System.IO; +using System.Linq; +using Org.IdentityConnectors.Common.Proxy; + +namespace Org.IdentityConnectors.Framework.Impl.Api.Local +{ + #region ConnectorPoolManager + public class ConnectorPoolManager + { + public class ConnectorPoolKey + { + private readonly ConnectorKey _connectorKey; + private readonly ConfigurationPropertiesImpl _configProperties; + private readonly ObjectPoolConfiguration _poolingConfig; + public ConnectorPoolKey(ConnectorKey connectorKey, + ConfigurationPropertiesImpl configProperties, + ObjectPoolConfiguration poolingConfig) + { + _connectorKey = connectorKey; + _configProperties = configProperties; + _poolingConfig = poolingConfig; + } + public override int GetHashCode() + { + return _connectorKey.GetHashCode(); + } + public override bool Equals(Object o) + { + if (o is ConnectorPoolKey) + { + ConnectorPoolKey other = (ConnectorPoolKey)o; + if (!_connectorKey.Equals(other._connectorKey)) + { + return false; + } + if (!_configProperties.Equals(other._configProperties)) + { + return false; + } + if (!_poolingConfig.Equals(other._poolingConfig)) + { + return false; + } + return true; + } + return false; + } + } + + private sealed class InternalConfigurationChangeHandler : AbstractConfiguration.IConfigurationChangeCallback + { + private readonly APIConfigurationImpl _apiConfiguration; + private readonly Configuration _configuration; + + public InternalConfigurationChangeHandler(APIConfigurationImpl apiConfiguration, Configuration configuration) + { + _apiConfiguration = apiConfiguration; + _configuration = configuration; + } + + public void NotifyUpdate() + { + try + { + IConfigurationPropertyChangeListener listener = _apiConfiguration.ChangeListener; + if (null != listener) + { + IList diff = + CSharpClassProperties.CalculateDiff(_apiConfiguration.ConfigurationProperties, _configuration); + if (diff.Count > 0) + { + listener.ConfigurationPropertyChange(diff); + } + } + } + catch (Exception e) + { + TraceUtil.TraceException("Configuration change notification is failed for" + _configuration.GetType(), e); + } + } + } + + private class ConnectorPoolHandler : ObjectPoolHandler + { + private readonly APIConfigurationImpl _apiConfiguration; + private readonly LocalConnectorInfoImpl _localInfo; + private readonly OperationalContext _context; + + public ConnectorPoolHandler(APIConfigurationImpl apiConfiguration, + LocalConnectorInfoImpl localInfo) + { + _apiConfiguration = apiConfiguration; + _localInfo = localInfo; + if (_localInfo.ConfigurationStateless) + { + _context = null; + } + else + { + _context = new OperationalContext(localInfo, apiConfiguration); + } + } + + public ObjectPoolConfiguration Validate(ObjectPoolConfiguration original) + { + ObjectPoolConfiguration configuration = (ObjectPoolConfiguration)SerializerUtil.CloneObject(original); + configuration.Validate(); + return configuration; + } + + public PoolableConnector MakeObject() + { + SafeType clazz = _localInfo.ConnectorClass; + PoolableConnector connector = null; + if (ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), clazz.RawType)) + { + + Configuration config = null; + if (null == _context) + { + config = + CSharpClassProperties.CreateBean( + (ConfigurationPropertiesImpl) _apiConfiguration.ConfigurationProperties, + _localInfo.ConnectorConfigurationClass); + if (null != _apiConfiguration.ChangeListener + && config is AbstractConfiguration) + { + ((AbstractConfiguration) config) + .AddChangeCallback(new InternalConfigurationChangeHandler( + _apiConfiguration, config)); + } + } + else + { + config = _context.GetConfiguration(); + } + + connector = (PoolableConnector)clazz.CreateInstance(); + connector.Init(config); + } + else + { + throw new ConnectorException("The Connector is not PoolableConnector: " + _localInfo.ConnectorKey); + } + return connector; + } + public void TestObject(PoolableConnector obj) + { + obj.CheckAlive(); + } + public void DisposeObject(PoolableConnector obj) + { + obj.Dispose(); + } + public void Shutdown() + { + if (null != _context) + { + _context.Dispose(); + } + } + } + + /// + /// Cache of the various _pools.. + /// + private static readonly IDictionary> + _pools = new Dictionary>(); + + + /// + /// Get a object pool for this connector if it supports connector pooling. + /// + public static Pair> GetPool(APIConfigurationImpl impl, LocalConnectorInfoImpl localInfo) + { + return GetPool2(impl, localInfo); + } + + public static ObjectPool GetPool(ConnectorPoolKey connectorPoolKey) + { + return CollectionUtil.GetValue(_pools, connectorPoolKey, null); + } + + /// + /// Get a object pool for this connector if it supports connector pooling. + /// + private static Pair> GetPool2(APIConfigurationImpl impl, LocalConnectorInfoImpl localInfo) + { + // determine if this connector wants generic connector pooling.. + if (impl.IsConnectorPoolingSupported) + { + ConnectorPoolKey key = new ConnectorPoolKey(impl.ConnectorInfo.ConnectorKey, + (ConfigurationPropertiesImpl)impl.ConfigurationProperties, impl.ConnectorPoolConfiguration); + lock (_pools) + { + // get the pool associated.. + ObjectPool pool = CollectionUtil.GetValue(_pools, key, null); + // create a new pool if it doesn't exist.. + if (pool == null) + { + Trace.TraceInformation("Creating new pool: {0}", impl.ConnectorInfo.ConnectorKey); + // this instance is strictly used for the pool.. + pool = new ObjectPool(new ConnectorPoolHandler(impl, localInfo), impl.ConnectorPoolConfiguration); + // add back to the map of POOLS.. + _pools[key] = pool; + } + + return Pair>.Of(key, pool); + } + } + else if (!localInfo.ConfigurationStateless) + { + return Pair>.Of(new ConnectorPoolKey(impl.ConnectorInfo.ConnectorKey, + (ConfigurationPropertiesImpl)impl.ConfigurationProperties, impl.ConnectorPoolConfiguration), (ObjectPool)null); + } + return Pair>.Of((ConnectorPoolKey)null, (ObjectPool)null); + } + + public static void Dispose(ConnectorPoolKey connectorPoolKey) + { + lock (_pools) + { + ObjectPool pool = null; + if (_pools.TryGetValue(connectorPoolKey, out pool)) + { + _pools.Remove(connectorPoolKey); + try + { + pool.Shutdown(); + } + catch (Exception e) + { + Trace.TraceWarning("Failed to close pool: {0} Exception: {1}", pool, e); + } + } + } + } + + public static void Dispose() + { + lock (_pools) + { + // close each pool.. + foreach (ObjectPool pool in _pools.Values) + { + try + { + pool.Shutdown(); + } + catch (Exception e) + { + Trace.TraceWarning("Failed to close pool: {0} {1}", e.Message, pool); + TraceUtil.TraceException("Failed to close pool", e); + } + } + // clear the map of all _pools.. + _pools.Clear(); + } + } + } + #endregion + + #region CSharpClassProperties + internal static class CSharpClassProperties + { + + /// + /// Calculate the difference between the given config and the properties. + /// + /// + /// + /// @return + /// + public static IList CalculateDiff(ConfigurationProperties properties, + Configuration config) + { + SafeType configType = SafeType.ForRawType(config.GetType()); + IDictionary descriptors = GetFilteredProperties(configType); + IList result = new List(); + var propertiesImpl = properties as ConfigurationPropertiesImpl; + if (null != propertiesImpl) + { + foreach (ConfigurationPropertyImpl property in propertiesImpl.Properties) + { + string name = property.Name; + PropertyInfo desc = descriptors[name]; + if (desc == null) + { + continue; + } + object oldValue = property.Value; + try + { + object newValue = desc.GetValue(config); + if (!CollectionUtil.Equals(oldValue, newValue)) + { + ConfigurationPropertyImpl change = new ConfigurationPropertyImpl(property) + { + Value = SerializerUtil.CloneObject(newValue) + }; + result.Add(change); + } + } + catch (Exception) + { + //Ignore + } + } + } + return result; + } + + public static ConfigurationPropertiesImpl + CreateConfigurationProperties(Configuration defaultObject) + { + SafeType config = SafeType.Get(defaultObject); + ConfigurationPropertiesImpl properties = + new ConfigurationPropertiesImpl(); + IList temp = + new List(); + IDictionary descs = GetFilteredProperties(config); + + foreach (PropertyInfo desc in descs.Values) + { + + String name = desc.Name; + + // get the configuration options.. + ConfigurationPropertyAttribute options = + GetPropertyOptions(desc); + // use the options to set internal properties.. + int order = 0; + String helpKey = "help_" + name; + String displKey = "display_" + name; + string grpKey = "group_" + name; + bool confidential = false; + bool required = false; + if (options != null) + { + // determine the display and help keys.. + if (!StringUtil.IsBlank(options.HelpMessageKey)) + { + helpKey = options.HelpMessageKey; + } + if (!StringUtil.IsBlank(options.DisplayMessageKey)) + { + displKey = options.DisplayMessageKey; + } + if (!StringUtil.IsBlank(options.GroupMessageKey)) + { + grpKey = options.GroupMessageKey; + } + // determine the order.. + order = options.Order; + required = options.Required; + confidential = options.Confidential; + } + Type type = desc.PropertyType; + if (!FrameworkUtil.IsSupportedConfigurationType(type)) + { + const String MSG = "Property type ''{0}'' is not supported."; + throw new ArgumentException(String.Format(MSG, type)); + } + + Object value = desc.GetValue(defaultObject, null); + + ConfigurationPropertyImpl prop = new ConfigurationPropertyImpl(); + prop.IsConfidential = confidential; + prop.IsRequired = required; + prop.DisplayMessageKey = displKey; + prop.HelpMessageKey = helpKey; + prop.GroupMessageKey = grpKey; + prop.Name = name; + prop.Order = order; + prop.Value = value; + prop.ValueType = type; + prop.Operations = options == null ? null : TranslateOperations(options.Operations); + + temp.Add(prop); + + } + properties.Properties = (temp); + return properties; + } + + private static ICollection> TranslateOperations(SafeType[] ops) + { + ICollection> set = + new HashSet>(); + foreach (SafeType spi in ops) + { + CollectionUtil.AddAll(set, FrameworkUtil.Spi2Apis(spi)); + } + return set; + } + + public static Configuration + CreateBean(ConfigurationPropertiesImpl properties, + SafeType configType) + { + Configuration rv = configType.CreateInstance(); + rv.ConnectorMessages = properties.Parent.ConnectorInfo.Messages; + MergeIntoBean(properties, rv); + return rv; + } + + public static void + MergeIntoBean(ConfigurationPropertiesImpl properties, + Configuration config) + { + SafeType configType = + SafeType.Get(config); + IDictionary descriptors = + GetFilteredProperties(configType); + foreach (ConfigurationPropertyImpl property in properties.Properties) + { + String name = property.Name; + PropertyInfo desc = + CollectionUtil.GetValue(descriptors, name, null); + if (desc == null) + { + String FMT = + "Class ''{0}'' does not have a property ''{1}''."; + String MSG = String.Format(FMT, + configType.RawType.Name, + name); + throw new ArgumentException(MSG); + } + object val = property.Value; + //some value types such as arrays + //are mutable. make sure the config object + //has its own copy + val = SerializerUtil.CloneObject(val); + desc.SetValue(config, val, null); + } + } + + private static IDictionary + GetFilteredProperties(SafeType config) + { + IDictionary rv = + new Dictionary(); + PropertyInfo[] descriptors = config.RawType.GetProperties(); + SortedSet excludes = new SortedSet(); + // exclude connectorMessages since its part of the interface. + excludes.Add("ConnectorMessages"); + + bool filterUnsupported = false; + ConfigurationClassAttribute options = GetConfigurationOptions(config); + if (null != options) + { + foreach (string s in options.Ignore) + { + excludes.Add(s); + } + filterUnsupported = options.SkipUnsupported; + } + foreach (PropertyInfo descriptor in descriptors) + { + String propName = descriptor.Name; + if (!descriptor.CanWrite) + { + //if there's no setter, ignore it + continue; + } + if (excludes.Contains(propName)) + { + continue; + } + if (filterUnsupported && !FrameworkUtil.IsSupportedConfigurationType(descriptor.PropertyType)) + { + //Silently ignore if the property type is not supported + continue; + } + if (!descriptor.CanRead) + { + const String FMT = + "Found setter ''{0}'' but not the corresponding getter."; + String MSG = String.Format(FMT, propName); + throw new ArgumentException(MSG); + } + rv[propName] = descriptor; + } + return rv; + } + + /// + /// Get the option from the property. + /// + private static ConfigurationPropertyAttribute GetPropertyOptions( + PropertyInfo propertyInfo) + { + Object[] objs = + propertyInfo.GetCustomAttributes( + typeof(ConfigurationPropertyAttribute), true); + if (objs.Length == 0) + { + return null; + } + else + { + return (ConfigurationPropertyAttribute)objs[0]; + } + } + + /// + /// Get the option from the property. + /// + private static ConfigurationClassAttribute GetConfigurationOptions( + SafeType configClass) + { + Object[] objs = + configClass.RawType.GetCustomAttributes( + typeof(ConfigurationClassAttribute), true); + if (objs.Length == 0) + { + return null; + } + else + { + return (ConfigurationClassAttribute)objs[0]; + } + } + + } + #endregion + + #region LocalConnectorInfoManagerImpl + internal class LocalConnectorInfoManagerImpl : ConnectorInfoManager + { + private IList _connectorInfo; + + public LocalConnectorInfoManagerImpl() + { + _connectorInfo = new List(); + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + thisAssemblyFile.Directory; + FileInfo[] files = + directory.GetFiles("*.Connector.dll"); + foreach (FileInfo file in files) + { + Assembly lib = + Assembly.LoadFrom(file.ToString()); + CollectionUtil.AddAll(_connectorInfo, ConnectorAssemblyUtility.ProcessAssembly(lib)); + } + // also handle connector DLL file names with a version + FileInfo[] versionedFiles = directory.GetFiles("*.Connector-*.dll"); + foreach (FileInfo versionedFile in versionedFiles) + { + Assembly lib = + Assembly.LoadFrom(versionedFile.ToString()); + CollectionUtil.AddAll(_connectorInfo, ConnectorAssemblyUtility.ProcessAssembly(lib)); + } + } + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + foreach (ConnectorInfo info in _connectorInfo) + { + if (info.ConnectorKey.Equals(key)) + { + return info; + } + } + return null; + } + + public IList ConnectorInfos + { + get + { + return CollectionUtil.AsReadOnlyList(_connectorInfo); + } + } + } + + public static class ConnectorAssemblyUtility + { + public static IList ProcessAssembly(Assembly assembly) + { + IList rv = new List(); + + Type[] types = null; + try + { + types = assembly.GetTypes(); + } + catch (Exception e) + { + TraceUtil.TraceException("Unable to load assembly: " + assembly.FullName + ". Assembly will be ignored.", e); + if (e is System.Reflection.ReflectionTypeLoadException) + { + var typeLoadException = e as ReflectionTypeLoadException; + var loaderExceptions = typeLoadException.LoaderExceptions; + foreach (var item in loaderExceptions) + { + TraceUtil.TraceException(" - loader exception: " + item, item); + } + } + return rv; + } + + foreach (Type type in types) + { + Object[] attributes = type.GetCustomAttributes( + typeof(ConnectorClassAttribute), + false); + if (attributes.Length > 0) + { + ConnectorClassAttribute attribute = + (ConnectorClassAttribute)attributes[0]; + LocalConnectorInfoImpl info = CreateConnectorInfo(assembly, type, attribute); + UriBuilder uri = new UriBuilder(assembly.CodeBase); + Trace.TraceInformation("Add ConnectorInfo {0} to Local Connector Info Manager from {1}", + info.ConnectorKey, Uri.UnescapeDataString(uri.Path)); + + rv.Add(info); + } + } + return rv; + } + + public static LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, + Type rawConnectorClass, + ConnectorClassAttribute attribute) + { + String fileName = assembly.Location; + if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) + { + String MSG = ("File " + fileName + + " declares a connector " + rawConnectorClass + + " that does not implement Connector."); + throw new ConfigurationException(MSG); + } + SafeType connectorClass = + SafeType.ForRawType(rawConnectorClass); + SafeType connectorConfigurationClass = attribute.ConnectorConfigurationType; + if (connectorConfigurationClass == null) + { + String MSG = ("File " + fileName + + " contains a ConnectorInfo attribute " + + "with no connector configuration class."); + throw new ConfigurationException(MSG); + } + String connectorDisplayNameKey = + attribute.ConnectorDisplayNameKey; + if (connectorDisplayNameKey == null) + { + String MSG = ("File " + fileName + + " contains a ConnectorInfo attribute " + + "with no connector display name."); + throw new ConfigurationException(MSG); + } + ConnectorKey key = + new ConnectorKey(assembly.GetName().Name, + assembly.GetName().Version.ToString(), + connectorClass.RawType.Namespace + "." + connectorClass.RawType.Name); + LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); + rv.ConnectorClass = connectorClass; + rv.ConnectorConfigurationClass = connectorConfigurationClass; + rv.ConnectorDisplayNameKey = connectorDisplayNameKey; + rv.ConnectorCategoryKey = attribute.ConnectorCategoryKey; + rv.ConnectorKey = key; + rv.DefaultAPIConfiguration = CreateDefaultApiConfiguration(rv); + rv.Messages = LoadMessages(assembly, rv, attribute.MessageCatalogPaths); + return rv; + } + + public static APIConfigurationImpl + CreateDefaultApiConfiguration(LocalConnectorInfoImpl localInfo) + { + SafeType connectorClass = + localInfo.ConnectorClass; + APIConfigurationImpl rv = new APIConfigurationImpl(); + Configuration config = + localInfo.ConnectorConfigurationClass.CreateInstance(); + bool pooling = IsPoolingSupported(connectorClass); + rv.IsConnectorPoolingSupported = pooling; + rv.ConfigurationProperties = (CSharpClassProperties.CreateConfigurationProperties(config)); + rv.ConnectorInfo = (localInfo); + rv.SupportedOperations = (FrameworkUtil.GetDefaultSupportedOperations(connectorClass)); + return rv; + } + + private static bool IsPoolingSupported(SafeType clazz) + { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), clazz.RawType); + } + + /// + /// Given an assembly, returns the list of cultures that + /// it is localized for + /// + /// + /// + private static CultureInfo[] GetLocalizedCultures(Assembly assembly) + { + FileInfo assemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + assemblyFile.Directory; + IList temp = new List(); + DirectoryInfo[] subdirs = directory.GetDirectories(); + foreach (DirectoryInfo subdir in subdirs) + { + String name = subdir.Name; + CultureInfo cultureInfo; + //get the culture if the directory is the name of the + //culture + try + { + cultureInfo = new CultureInfo(name); + } + catch (ArgumentException) + { + //invalid culture + continue; + } + //see if there's a satellite assembly for this + try + { + assembly.GetSatelliteAssembly(cultureInfo); + } + catch (Exception) + { + //invalid assembly + continue; + } + temp.Add(cultureInfo); + } + temp.Add(CultureInfo.InvariantCulture); + return temp.ToArray(); + } + + public static ConnectorMessagesImpl LoadMessages(Assembly assembly, + LocalConnectorInfoImpl info, + String[] nameBases) + { + if (nameBases == null || nameBases.Length == 0) + { + String pkage = + info.ConnectorClass.RawType.Namespace; + nameBases = new String[] { pkage + ".Messages" }; + } + ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); + CultureInfo[] cultures = GetLocalizedCultures(assembly); + for (int i = nameBases.Length - 1; i >= 0; i--) + { + String nameBase = nameBases[i]; + ResourceManager manager = new ResourceManager(nameBase, assembly); + foreach (CultureInfo culture in cultures) + { + ResourceSet resourceSet = manager.GetResourceSet(culture, true, false); + if (resourceSet != null) + { + IDictionary temp = + CollectionUtil.GetValue(rv.Catalogs, culture, null); + if (temp == null) + { + temp = new Dictionary(); + rv.Catalogs[culture] = temp; + } + foreach (System.Collections.DictionaryEntry entry in resourceSet) + { + String key = "" + entry.Key; + String val = "" + entry.Value; + temp[key] = val; + } + } + } + } + + return rv; + } + } + #endregion + + #region LocalConnectorInfoImpl + /// + /// Internal class, public only for unit tests + /// + public class LocalConnectorInfoImpl : AbstractConnectorInfo + { + public RemoteConnectorInfoImpl ToRemote() + { + RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); + rv.ConnectorDisplayNameKey = ConnectorDisplayNameKey; + rv.ConnectorKey = ConnectorKey; + rv.DefaultAPIConfiguration = DefaultAPIConfiguration; + rv.Messages = Messages; + return rv; + } + public SafeType ConnectorClass { get; set; } + public SafeType ConnectorConfigurationClass { get; set; } + public bool ConfigurationStateless + { + get + { + return !ReflectionUtil.IsParentTypeOf(typeof(StatefulConfiguration), ConnectorConfigurationClass.RawType); + } + } + public bool ConnectorPoolingSupported + { + get + { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), ConnectorClass.RawType); + } + } + } + #endregion + + #region LocalConnectorFacadeImpl + public class LocalConnectorFacadeImpl : AbstractConnectorFacade + { + // ======================================================================= + // Constants + // ======================================================================= + /// + /// Map the API interfaces to their implementation counterparts. + /// + private static readonly IDictionary, ConstructorInfo> API_TO_IMPL = + new Dictionary, ConstructorInfo>(); + + private static void AddImplementation(SafeType inter, + SafeType impl) + { + ConstructorInfo info = + impl.RawType.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), + typeof(Connector)}); + if (info == null) + { + throw new ArgumentException(impl + " does not define the proper constructor"); + } + API_TO_IMPL[inter] = info; + } + + static LocalConnectorFacadeImpl() + { + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + } + + // ======================================================================= + // Fields + // ======================================================================= + /// + /// Pool used to acquire connection from to use during operations. + /// + + /// + /// The connector info + /// + private readonly LocalConnectorInfoImpl connectorInfo; + + + /// + /// Shared OperationalContext for stateful facades + /// + private readonly ConnectorOperationalContext operationalContext; + + /// + /// Shared thread counter. + /// + private readonly ReferenceCounter referenceCounter = new ReferenceCounter(); + + /// + /// Builds up the maps of supported operations and calls. + /// + public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration) + : base(apiConfiguration) + { + this.connectorInfo = connectorInfo; + if (connectorInfo.ConfigurationStateless && !connectorInfo.ConnectorPoolingSupported) + { + operationalContext = null; + } + else + { + operationalContext = new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration()); + } + } + + public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, string configuration) + : base(configuration, connectorInfo) + { + this.connectorInfo = connectorInfo; + if (connectorInfo.ConfigurationStateless && !connectorInfo.ConnectorPoolingSupported) + { + operationalContext = null; + } + else + { + operationalContext = new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration()); + } + } + + public LocalConnectorFacadeImpl( + LocalConnectorInfoImpl connectorInfo, String config, IConfigurationPropertyChangeListener changeListener) + : this(connectorInfo, config) + { + GetAPIConfiguration().ChangeListener = changeListener; + } + + public void Dispose() + { + if (null != operationalContext) + { + operationalContext.Dispose(); + } + } + + protected internal ConnectorOperationalContext OperationalContext + { + get + { + if (null == operationalContext) + { + return new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration()); + } + return operationalContext; + } + } + + // ======================================================================= + // ConnectorFacade Interface + // ======================================================================= + + protected override APIOperation GetOperationImplementation(SafeType api) + { + APIOperation proxy; + Boolean enableTimeoutProxy = true; + //first create the inner proxy - this is the proxy that obtaining + //a connection from the pool, etc + //NOTE: we want to skip this part of the proxy for + //validate op, but we will want the timeout proxy + if (api.RawType.Equals(typeof(ValidateApiOp))) + { + OperationalContext context = + new OperationalContext(connectorInfo, GetAPIConfiguration()); + proxy = new ValidateImpl(context); + } + else if (api.RawType.Equals(typeof(GetApiOp))) + { + ConstructorInfo constructor = + API_TO_IMPL[SafeType.Get()]; + + ConnectorAPIOperationRunnerProxy handler = + new ConnectorAPIOperationRunnerProxy(OperationalContext, constructor); + proxy = + new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(), handler)); + } + else if (api.RawType == typeof(IConnectorEventSubscriptionApiOp)) + { + ConnectorAPIOperationRunnerProxy handler = new ConnectorAPIOperationRunnerProxy(OperationalContext, (opContext, connector) => new ConnectorEventSubscriptionApiOpImp( + opContext, connector, referenceCounter)); + proxy = NewAPIOperationProxy(api, handler); + enableTimeoutProxy = false; + } + else if (api.RawType == typeof(ISyncEventSubscriptionApiOp)) + { + ConnectorAPIOperationRunnerProxy handler = new ConnectorAPIOperationRunnerProxy(OperationalContext, (opContext, connector) => new SyncEventSubscriptionApiOpImpl( + opContext, connector, referenceCounter)); + proxy = NewAPIOperationProxy(api, handler); + enableTimeoutProxy = false; + } + else + { + ConstructorInfo constructor = + API_TO_IMPL[api]; + + ConnectorAPIOperationRunnerProxy handler = + new ConnectorAPIOperationRunnerProxy(OperationalContext, constructor); + proxy = + NewAPIOperationProxy(api, handler); + } + + if (enableTimeoutProxy) + { + // now wrap the proxy in the appropriate timeout proxy + proxy = CreateTimeoutProxy(api, proxy); + } + // add logging proxy.. + proxy = CreateLoggingProxy(api, proxy); + proxy = NewAPIOperationProxy(api, new ReferenceCountingProxy(proxy, referenceCounter)); + + return proxy; + } + public virtual bool IsUnusedFor(TimeSpan duration) + { + return referenceCounter.IsUnusedFor(duration); + } + + public class ReferenceCounter + { + private Int32 _threadCounts = 0; + private DateTime _lastUsed = DateTime.Now; + + public virtual bool IsUnusedFor(TimeSpan duration) + { + return _threadCounts == 0 && DateTime.Now - _lastUsed > duration; + } + + public virtual void Acquire() + { + Interlocked.Increment(ref _threadCounts); + } + + public virtual void Release() + { + if (Interlocked.Decrement(ref _threadCounts) <= 0) + { + _lastUsed = DateTime.Now; + + } + } + } + + private class ReferenceCountingProxy : InvocationHandler + { + private readonly object _target; + private readonly ReferenceCounter _referenceCounter; + + public ReferenceCountingProxy(object target, ReferenceCounter referenceCounter) + { + _target = target; + _referenceCounter = referenceCounter; + } + + public object Invoke(object proxy, MethodInfo method, object[] arguments) + { + // do not log equals, hashCode, toString + if (method.DeclaringType == typeof(object)) + { + return method.Invoke(this, arguments); + } + try + { + _referenceCounter.Acquire(); + return method.Invoke(_target, arguments); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + finally + { + _referenceCounter.Release(); + } + } + } + } + #endregion + + #region ObjectPool + public class ObjectPool where T : class + { + /// + /// Statistics bean + /// + public sealed class Statistics + { + private readonly int _numIdle; + private readonly int _numActive; + + internal Statistics(int numIdle, int numActive) + { + _numIdle = numIdle; + _numActive = numActive; + } + + /// + /// Returns the number of idle objects + /// + public int NumIdle + { + get + { + return _numIdle; + } + } + + /// + /// Returns the number of active objects + /// + public int NumActive + { + get + { + return _numActive; + } + } + } + + /// + /// An object plus additional book-keeping + /// information about the object + /// + private class PooledObject where T2 : class + { + /// + /// The underlying object + /// + private readonly T2 _object; + + /// + /// True if this is currently active, false if + /// it is idle + /// + private bool _isActive; + + /// + /// Last state change (change from active to + /// idle or vice-versa) + /// + private long _lastStateChangeTimestamp; + + /// + /// Is this a freshly created object (never been pooled)? + /// + private bool _isNew; + + public PooledObject(T2 obj) + { + _object = obj; + _isNew = true; + Touch(); + } + + public T2 Object + { + get + { + return _object; + } + } + + public bool IsActive + { + get + { + return _isActive; + } + set + { + if (_isActive != value) + { + Touch(); + _isActive = value; + } + } + } + + public bool IsNew + { + get + { + return _isNew; + } + set + { + _isNew = value; + } + } + + + private void Touch() + { + _lastStateChangeTimestamp = DateTimeUtil.GetCurrentUtcTimeMillis(); + } + + public long LastStateChangeTimestamp + { + get + { + return _lastStateChangeTimestamp; + } + } + } + + /// + /// The lock object we use for everything + /// + private readonly Object LOCK = new Object(); + + /// + /// Map from the object to the + /// PooledObject (use IdentityHashMap so it's + /// always object equality) + /// + private readonly IDictionary> + _activeObjects = CollectionUtil.NewIdentityDictionary>(); + + /// + /// Queue of idle objects. + /// + /// + /// The one that has + /// been idle for the longest comes first in the queue + /// + private readonly LinkedList> + _idleObjects = new LinkedList>(); + + /// + /// ObjectPoolHandler we use for managing object lifecycle + /// + private readonly ObjectPoolHandler _handler; + + /// + /// Configuration for this pool. + /// + private readonly ObjectPoolConfiguration _config; + + /// + /// Is the pool shutdown + /// + private bool _isShutdown; + + /// + /// Create a new ObjectPool + /// + /// Handler for objects + /// Configuration for the pool + public ObjectPool(ObjectPoolHandler handler, + ObjectPoolConfiguration config) + { + + Assertions.NullCheck(handler, "handler"); + Assertions.NullCheck(config, "config"); + + _handler = handler; + //clone it + _config = + (ObjectPoolConfiguration)SerializerUtil.CloneObject(config); + //validate it + _config.Validate(); + + } + + /// + /// Return an object to the pool + /// + /// + public void ReturnObject(T obj) + { + Assertions.NullCheck(obj, "object"); + lock (LOCK) + { + //remove it from the active list + PooledObject pooled = + CollectionUtil.GetValue(_activeObjects, obj, null); + + //they are attempting to return something + //we haven't allocated (or that they've + //already returned) + if (pooled == null) + { + throw new InvalidOperationException("Attempt to return an object not in the pool: " + obj); + } + _activeObjects.Remove(obj); + + //set it to idle and add to idle list + //(this might get evicted right away + //by evictIdleObjects if we're over the + //limit or if we're shutdown) + pooled.IsActive = (false); + pooled.IsNew = (false); + _idleObjects.AddLast(pooled); + + //finally evict idle objects + EvictIdleObjects(); + + //wake anyone up who was waiting on a object + Monitor.PulseAll(LOCK); + } + } + + /// + /// Borrow an object from the pool. + /// + /// An object + public T BorrowObject() + { + while (true) + { + PooledObject rv = BorrowObjectNoTest(); + try + { + //make sure we are testing it outside + //of synchronization. otherwise this + //can create an IO bottleneck + _handler.TestObject(rv.Object); + return rv.Object; + } + catch (Exception) + { + //it's bad - remove from active objects + lock (LOCK) + { + _activeObjects.Remove(rv.Object); + } + DisposeNoException(rv.Object); + //if it's a new object, break out of the loop + //immediately + if (rv.IsNew) + { + throw; + } + } + } + } + + /// + /// Borrow an object from the pool, but don't test + /// it (it gets tested by the caller *outside* of + /// synchronization) + /// + /// the object + private PooledObject BorrowObjectNoTest() + { + //time when the call began + long startTime = DateTimeUtil.GetCurrentUtcTimeMillis(); + + lock (LOCK) + { + EvictIdleObjects(); + while (true) + { + if (_isShutdown) + { + throw new InvalidOperationException("Object pool already shutdown"); + } + + PooledObject pooledConn = null; + + //first try to recycle an idle object + if (_idleObjects.Count > 0) + { + pooledConn = _idleObjects.First(); + _idleObjects.RemoveFirst(); + } + //otherwise, allocate a new object if + //below the limit + else if (_activeObjects.Count < _config.MaxObjects) + { + pooledConn = + new PooledObject(_handler.MakeObject()); + } + + //if there's an object available, return it + //and break out of the loop + if (pooledConn != null) + { + pooledConn.IsActive = (true); + _activeObjects[pooledConn.Object] = + pooledConn; + return pooledConn; + } + + //see if we haven't timed-out yet + long elapsed = + DateTimeUtil.GetCurrentUtcTimeMillis() - startTime; + long remaining = _config.MaxWait - elapsed; + + //wait if we haven't timed out + if (remaining > 0) + { + Monitor.Wait(LOCK, (int)remaining); + } + else + { + //otherwise throw + throw new ConnectorException("Max objects exceeded"); + } + } + } + } + + /// + /// Closes any idle objects in the pool. + /// + /// + /// Existing active objects will remain alive and + /// be allowed to shutdown gracefully, but no more + /// objects will be allocated. + /// + public void Shutdown() + { + lock (LOCK) + { + _isShutdown = true; + //just evict idle objects + //if there are any active objects still + //going, leave them alone so they can return + //gracefully + try + { + EvictIdleObjects(); + } + finally + { + _handler.Shutdown(); + } + //wake anyone up who was waiting on an object + Monitor.PulseAll(LOCK); + } + } + + /// + /// Gets a snapshot of the pool's stats at a point in time. + /// + /// The statistics + public Statistics GetStatistics() + { + lock (LOCK) + { + return new Statistics(_idleObjects.Count, + _activeObjects.Count); + } + } + + /// + /// Evicts idle objects as needed (evicts + /// all idle objects if we're shutdown) + /// + private void EvictIdleObjects() + { + while (TooManyIdleObjects()) + { + PooledObject conn = _idleObjects.First(); + _idleObjects.RemoveFirst(); + DisposeNoException(conn.Object); + } + } + + /// + /// Returns true if any of the following are true: + /// + /// + /// We're shutdown and there are idle objects + /// + /// + /// + /// Max idle objects exceeded + /// + /// + /// + /// Min idle objects exceeded and there are old objects + /// + /// + /// + /// + private bool TooManyIdleObjects() + { + + if (_isShutdown && _idleObjects.Count > 0) + { + return true; + } + + if (_config.MaxIdle < _idleObjects.Count) + { + return true; + } + if (_config.MinIdle >= _idleObjects.Count) + { + return false; + } + + PooledObject oldest = + _idleObjects.First(); + + long age = + (DateTimeUtil.GetCurrentUtcTimeMillis() - oldest.LastStateChangeTimestamp); + + + return age > _config.MinEvictableIdleTimeMillis; + } + + /// + /// Dispose of an object, but don't throw any exceptions + /// + /// + private void DisposeNoException(T obj) + { + try + { + _handler.DisposeObject(obj); + } + catch (Exception e) + { + TraceUtil.TraceException("disposeObject() is not supposed to throw", e); + } + } + } + #endregion + + #region ObjectPoolHandler + public interface ObjectPoolHandler where T : class + { + /// + /// Validates, copies and updates the original + /// {@code ObjectPoolConfiguration}. + ///

+ /// This class can validate and if necessary it changes the {@code original} + /// configuration. + ///

+ /// + /// custom configured instance. + /// new instance of the {@code original} config. + ObjectPoolConfiguration Validate(ObjectPoolConfiguration original); + + /// + /// Makes a new instance of the pooled object. + ///

+ /// This method is called whenever a new instance is needed. + ///

+ /// new instance of T. + T MakeObject(); // T NewObject(); + + /// + /// Tests the borrowed object. + ///

+ /// This method is invoked on head instances to make sure they can be + /// borrowed from the pool. + ///

+ /// + /// the pooled object. + void TestObject(T obj); + + /// + /// Disposes the object. + ///

+ /// This method is invoked on every instance when it is being "dropped" from + /// the pool (whether due to the response from , + /// or for reasons specific to the pool implementation.) + ///

+ /// + /// The "dropped" object. + void DisposeObject(T obj); + + /// + /// Releases any allocated resources. + ///

+ /// Existing active objects will remain alive and be allowed to shutdown + /// gracefully, but no more objects will be allocated. + ///

+ void Shutdown(); + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/ApiLocalOperations.cs b/dotnet/framework/FrameworkInternal/ApiLocalOperations.cs new file mode 100644 index 00000000..cd8c217b --- /dev/null +++ b/dotnet/framework/FrameworkInternal/ApiLocalOperations.cs @@ -0,0 +1,2193 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ +using System; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using Org.IdentityConnectors.Framework.Api; +using System.Text; +using System.Diagnostics; +using System.Threading; + +namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations +{ + #region APIOperationRunner + /// + /// NOTE: internal class, public only for unit tests + /// Base class for API operation runners. + /// + public abstract class APIOperationRunner + { + /// + /// Context that has all the information required to execute an operation. + /// + private readonly OperationalContext _context; + + /// + /// Creates the API operation so it can called multiple times. + /// + public APIOperationRunner(OperationalContext context) + { + _context = context; + //TODO: verify this + // get the APIOperation that this class implements.. + //List> apiOps = getInterfaces(this + //.getClass(), APIOperation.class); + // there should be only one.. + //if (apiOps.size() > 1) { + // final String MSG = "Must only implement one operation."; + // throw new IllegalStateException(MSG); + //} + } + + /// + /// Get the current operational context. + /// + public OperationalContext GetOperationalContext() + { + return _context; + } + + } + #endregion + + #region ConnectorAPIOperationRunner + /// + /// NOTE: internal class, public only for unit tests + /// Subclass of APIOperationRunner for operations that require a connector. + /// + public abstract class ConnectorAPIOperationRunner : APIOperationRunner + { + /// + /// The connector instance + /// + private readonly Connector _connector; + + /// + /// Creates the API operation so it can called multiple times. + /// + public ConnectorAPIOperationRunner(ConnectorOperationalContext context, + Connector connector) + : base(context) + { + _connector = connector; + } + + public Connector GetConnector() + { + return _connector; + } + + public ObjectNormalizerFacade GetNormalizer(ObjectClass oclass) + { + AttributeNormalizer norm = null; + Connector connector = GetConnector(); + if (connector is AttributeNormalizer) + { + norm = (AttributeNormalizer)connector; + } + return new ObjectNormalizerFacade(oclass, norm); + } + } + #endregion + + #region ConnectorAPIOperationRunnerProxy + /// + /// Proxy for APIOperationRunner that takes care of setting up underlying + /// connector and creating the implementation of APIOperationRunner. + /// + /// + /// The implementation of APIOperationRunner gets created whenever the + /// actual method is invoked. + /// + internal class ConnectorAPIOperationRunnerProxy : InvocationHandler + { + /// + /// The operational context + /// + private readonly ConnectorOperationalContext _context; + + /// + /// The implementation constructor. + /// + /// + /// The instance is lazily created upon + /// invocation + /// + private readonly ConstructorInfo _runnerImplConstructor; + + private readonly Func _runnerImplFunc; + + /// + /// Create an APIOperationRunnerProxy + /// + /// The operational context + /// The implementation constructor. Implementation + /// must define a two-argument constructor(OperationalContext,Connector) + public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, + ConstructorInfo runnerImplConstructor) + { + _context = context; + _runnerImplConstructor = runnerImplConstructor; + } + + public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, + Func runnerImplConstructor) + { + _context = context; + _runnerImplFunc = runnerImplConstructor; + } + + public Object Invoke(Object proxy, MethodInfo method, object[] args) + { + //do not proxy equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + object ret = null; + Connector connector = null; + ObjectPool pool = _context.Pool; + // get the connector class.. + SafeType connectorClazz = _context.GetConnectorClass(); + try + { + // pooling is implemented get one.. + if (pool != null) + { + connector = pool.BorrowObject(); + } + else + { + // get a new instance of the connector.. + connector = connectorClazz.CreateInstance(); + // initialize the connector.. + connector.Init(_context.GetConfiguration()); + } + APIOperationRunner runner = null != _runnerImplFunc ? _runnerImplFunc(_context, connector) : + (APIOperationRunner)_runnerImplConstructor.Invoke(new object[]{ + _context, + connector}); + ret = method.Invoke(runner, args); + // call out to the operation.. + if (ret is ISubscription) + { + //Dispose later + ret = new DeferredSubscriptionDisposer((ISubscription)ret, connector, pool); + connector = null; + } + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + finally + { + // make sure dispose of the connector properly + if (connector != null) + { + DisposeConnector(connector, pool); + } + } + return ret; + } + + internal static void DisposeConnector(Connector connector, ObjectPool pool) + { + // determine if there was a pool.. + if (pool != null) + { + try + { + //try to return it to the pool even though an + //exception may have happened that leaves it in + //a bad state. The contract of checkAlive + //is that it will tell you if the connector is + //still valid and so we leave it up to the pool + //and connector to work it out. + pool.ReturnObject((PoolableConnector)connector); + } + catch (Exception e) + { + //don't let pool exceptions propagate or mask + //other exceptions. do log it though. + TraceUtil.TraceException(null, e); + } + } + //not pooled - just dispose + else + { + //dispose it not supposed to throw, but just in case, + //catch the exception and log it so we know about it + //but don't let the exception prevent additional + //cleanup that needs to happen + try + { + connector.Dispose(); + } + catch (Exception e) + { + //log this though + TraceUtil.TraceException(null, e); + } + } + } + private sealed class DeferredSubscriptionDisposer : ISubscription + { + private readonly Connector _connector; + private readonly ObjectPool _poolEntry; + private readonly ISubscription _subscription; + private Int32 _active = 1; + public DeferredSubscriptionDisposer(ISubscription subscription, Connector connector, ObjectPool poolEntry) + { + _subscription = subscription; + _connector = connector; + _poolEntry = poolEntry; + } + + public void Dispose() + { + try + { + _subscription.Dispose(); + } + finally + { + if (Interlocked.CompareExchange(ref _active, 0, 1) == 1) + { + DisposeConnector(_connector, _poolEntry); + } + } + } + + public bool Unsubscribed + { + get + { + return _subscription.Unsubscribed; + } + } + } + } + + #endregion + + #region ConnectorOperationalContext + /// + /// NOTE: internal class, public only for unit tests + /// Simple structure to pass more variables through the constructor of + /// . + /// + public class ConnectorOperationalContext : OperationalContext + { + /// + /// Pool Key for Connectors + /// + private ConnectorPoolManager.ConnectorPoolKey connectorPoolKey; + + public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration) + : base(connectorInfo, apiConfiguration) + { + } + + public ObjectPool Pool + { + get + { + if (apiConfiguration.IsConnectorPoolingSupported) + { + if (null == connectorPoolKey) + { + Pair> pool = ConnectorPoolManager.GetPool(apiConfiguration, connectorInfo); + + connectorPoolKey = pool.First; + return pool.Second; + } + else + { + ObjectPool pool = ConnectorPoolManager.GetPool(connectorPoolKey); + if (null == pool) + { + // + Pair> poolPair = ConnectorPoolManager.GetPool(apiConfiguration, connectorInfo); + + connectorPoolKey = poolPair.First; + pool = poolPair.Second; + } + return pool; + } + } + else + { + return null; + } + } + } + + public SafeType GetConnectorClass() + { + return GetConnectorInfo().ConnectorClass; + } + + public override void Dispose() + { + base.Dispose(); + if (null != connectorPoolKey) + { + ConnectorPoolManager.Dispose(connectorPoolKey); + connectorPoolKey = null; + } + } + + } + #endregion + + #region AuthenticationImpl + internal class AuthenticationImpl : ConnectorAPIOperationRunner, + AuthenticationApiOp + { + /// + /// Pass the configuration etc to the abstract class. + /// + public AuthenticationImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + /// + /// Authenticate using the basic credentials. + /// + /// + public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(username, "username"); + Assertions.NullCheck(password, "password"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + return ((AuthenticateOp)GetConnector()).Authenticate(objectClass, username, password, options); + } + } + #endregion + + #region ResolveUsernameImpl + internal class ResolveUsernameImpl : ConnectorAPIOperationRunner, + ResolveUsernameApiOp + { + /// + /// Pass the configuration etc to the abstract class. + /// + public ResolveUsernameImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + /// + /// Resolve the username to an Uid. + /// + public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(username, "username"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + return ((ResolveUsernameOp)GetConnector()).ResolveUsername(objectClass, username, options); + } + } + #endregion + + #region CreateImpl + internal class CreateImpl : ConnectorAPIOperationRunner, + CreateApiOp + { + /// + /// Initializes the operation works. + /// + public CreateImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + /// + /// Calls the create method on the Connector side. + /// + /// + public Uid Create(ObjectClass objectClass, ICollection createAttributes, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(createAttributes, "createAttributes"); + // check to make sure there's not a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(createAttributes) != null) + { + throw new InvalidAttributeValueException("Parameter 'createAttributes' contains a uid."); + } + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + // validate input.. + HashSet dups = new HashSet(); + foreach (ConnectorAttribute attr in createAttributes) + { + if (dups.Contains(attr.Name)) + { + throw new InvalidAttributeValueException("Duplicate attribute name exists: " + attr.Name); + } + dups.Add(attr.Name); + } + Connector connector = GetConnector(); + ObjectNormalizerFacade normalizer = GetNormalizer(objectClass); + ICollection normalizedAttributes = + normalizer.NormalizeAttributes(createAttributes); + // create the object.. + Uid ret = ((CreateOp)connector).Create(objectClass, normalizedAttributes, options); + return (Uid)normalizer.NormalizeAttribute(ret); + } + } + #endregion + + #region DeleteImpl + internal class DeleteImpl : ConnectorAPIOperationRunner, + DeleteApiOp + { + /// + /// Initializes the operation works. + /// + public DeleteImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + /// + /// Calls the delete method on the Connector side. + /// + /// + public void Delete(ObjectClass objectClass, Uid uid, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(uid, "uid"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + Connector connector = GetConnector(); + ObjectNormalizerFacade normalizer = GetNormalizer(objectClass); + // delete the object.. + ((DeleteOp)connector).Delete(objectClass, + (Uid)normalizer.NormalizeAttribute(uid), + options); + } + } + #endregion + + #region AttributesToGetResultsHandler + public abstract class AttributesToGetResultsHandler + { + // ======================================================================= + // Fields + // ======================================================================= + private readonly string[] _attrsToGet; + + // ======================================================================= + // Constructors + // ======================================================================= + /// + /// Keep the attribute to get.. + /// + public AttributesToGetResultsHandler(string[] attrsToGet) + { + Assertions.NullCheck(attrsToGet, "attrsToGet"); + _attrsToGet = attrsToGet; + } + + /// + /// Simple method that clones the object and remove the attribute thats are + /// not in the set. + /// + /// case insensitive set of attribute names. + public ICollection ReduceToAttrsToGet( + ICollection attrs) + { + ICollection ret = new HashSet(); + IDictionary map = ConnectorAttributeUtil.ToMap(attrs); + foreach (string attrName in _attrsToGet) + { + ConnectorAttribute attr = CollectionUtil.GetValue(map, attrName, null); + // TODO: Should we throw if the attribute is not yet it was + // requested?? Or do we ignore because the API maybe asking + // for what the resource doesn't have?? + if (attr != null) + { + ret.Add(attr); + } + } + return ret; + } + public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) + { + // clone the object and reduce the attributes only the set of + // attributes. + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid(obj.Uid); + bld.SetName(obj.Name); + bld.ObjectClass = obj.ObjectClass; + ICollection objAttrs = obj.GetAttributes(); + ICollection attrs = ReduceToAttrsToGet(objAttrs); + bld.AddAttributes(attrs); + return bld.Build(); + } + } + #endregion + + #region SearchAttributesToGetResultsHandler + public sealed class SearchAttributesToGetResultsHandler : + AttributesToGetResultsHandler + { + // ======================================================================= + // Fields + // ======================================================================= + private readonly ResultsHandler _handler; + + // ======================================================================= + // Constructors + // ======================================================================= + public SearchAttributesToGetResultsHandler( + ResultsHandler handler, string[] attrsToGet) + : base(attrsToGet) + { + Assertions.NullCheck(handler, "handler"); + this._handler = handler; + } + + public ResultsHandler ResultsHandler + { + get + { + return new ResultsHandler() + { + Handle = obj => + { + // clone the object and reduce the attributes only the set of + // attributes. + return _handler.Handle(ReduceToAttrsToGet(obj)); + } + }; + } + } + } + #endregion + + #region SubscriptionImpl + + public class ConnectorEventSubscriptionApiOpImp : SubscriptionImpl, IConnectorEventSubscriptionApiOp + { + + /// + /// Creates the API operation so it can called multiple times. + /// + /// + /// + /// + public ConnectorEventSubscriptionApiOpImp(ConnectorOperationalContext context, Connector connector, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + : base(context, connector, referenceCounter) + { + } + + public ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, OperationOptions operationOptions) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if (operationOptions == null) + { + operationOptions = new OperationOptionsBuilder().Build(); + } + IConnectorEventSubscriptionOp operation = ((IConnectorEventSubscriptionOp)GetConnector()); + InternalObserver observer = new InternalObserver(handler, ReferenceCounter); + try + { + ReferenceCounter.Acquire(); + return observer.Subscription(operation.Subscribe(objectClass, eventFilter, observer, operationOptions)); + } + catch (Exception e) + { + observer.OnError(e); + throw; + } + } + } + + public class SyncEventSubscriptionApiOpImpl : SubscriptionImpl, ISyncEventSubscriptionApiOp + { + + /// + /// Creates the API operation so it can called multiple times. + /// + /// + /// + /// + public SyncEventSubscriptionApiOpImpl(ConnectorOperationalContext context, Connector connector, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + : base(context, connector, referenceCounter) + { + } + + public ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if (operationOptions == null) + { + operationOptions = new OperationOptionsBuilder().Build(); + } + + ISyncEventSubscriptionOp operation = ((ISyncEventSubscriptionOp)GetConnector()); + InternalObserver observer = new InternalObserver(handler, ReferenceCounter); + try + { + ReferenceCounter.Acquire(); + return observer.Subscription(operation.Subscribe(objectClass, token, observer, operationOptions)); + } + catch (Exception e) + { + observer.OnError(e); + throw; + } + } + } + public class SubscriptionImpl : ConnectorAPIOperationRunner + { + + protected readonly LocalConnectorFacadeImpl.ReferenceCounter ReferenceCounter; + + /// + /// Creates the API operation so it can called multiple times. + /// + /// + /// + protected internal SubscriptionImpl(ConnectorOperationalContext context, Connector connector, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + : base(context, connector) + { + ReferenceCounter = referenceCounter; + } + + + protected sealed class InternalObserver : IObserver + { + private readonly IObserver _observer; + private readonly CancellationSubscription _subscribed = new CancellationSubscription(); + + public InternalObserver(IObserver observer, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + { + _observer = observer; + _subscribed.Token.Register(referenceCounter.Release); + } + + public CancellationSubscription Subscription(ISubscription subscription) + { + Assertions.NullCheck(subscription, "subscription"); + _subscribed.Token.Register(subscription.Dispose); + return _subscribed; + } + + public void OnCompleted() + { + if (!_subscribed.Unsubscribed) + { + try + { + _subscribed.Dispose(); + } + finally + { + _observer.OnCompleted(); + } + } + } + + public void OnError(Exception e) + { + if (!_subscribed.Unsubscribed) + { + try + { + _subscribed.Dispose(); + } + finally + { + _observer.OnError(e); + } + } + } + + public void OnNext(T connectorObject) + { + try + { + if (!_subscribed.Unsubscribed) + { + _observer.OnNext(connectorObject); + } + } + catch (Exception t) + { + OnError(t); + } + } + } + } + #endregion + + #region SyncAttributesToGetResultsHandler + public sealed class SyncAttributesToGetResultsHandler : + AttributesToGetResultsHandler + { + // ======================================================================= + // Fields + // ======================================================================= + private readonly SyncResultsHandler _handler; + + // ======================================================================= + // Constructors + // ======================================================================= + public SyncAttributesToGetResultsHandler( + SyncResultsHandler handler, string[] attrsToGet) + : base(attrsToGet) + { + Assertions.NullCheck(handler, "handler"); + this._handler = handler; + } + + public SyncResultsHandler SyncResultsHandler + { + get + { + return new SyncResultsHandler() + { + Handle = delta => + { + SyncDeltaBuilder bld = new SyncDeltaBuilder(); + bld.Uid = delta.Uid; + bld.Token = delta.Token; + bld.DeltaType = delta.DeltaType; + if (delta.Object != null) + { + bld.Object = ReduceToAttrsToGet(delta.Object); + } + return _handler.Handle(bld.Build()); + } + }; + } + } + } + #endregion + + #region DuplicateFilteringResultsHandler + public sealed class DuplicateFilteringResultsHandler + { + // ======================================================================= + // Fields + // ======================================================================= + private readonly SearchResultsHandler _handler; + private readonly HashSet _visitedUIDs = new HashSet(); + + private bool _stillHandling = true; + + // ======================================================================= + // Constructors + // ======================================================================= + /// + /// Filter chain for producers. + /// + /// Producer to filter. + public DuplicateFilteringResultsHandler(SearchResultsHandler handler) + { + // there must be a producer.. + if (handler == null) + { + throw new ArgumentException("Handler must not be null!"); + } + this._handler = handler; + } + + public SearchResultsHandler ResultsHandler + { + get + { + return new SearchResultsHandler() + { + Handle = obj => + { + String uid = + obj.Uid.GetUidValue(); + if (!_visitedUIDs.Add(uid)) + { + //we've already seen this - don't pass it + //throw + return true; + } + _stillHandling = _handler.Handle(obj); + return _stillHandling; + }, + HandleResult = result => + { + _handler.HandleResult(result); + } + + }; + } + + } + + public bool IsStillHandling + { + get + { + return _stillHandling; + } + } + } + #endregion + + #region FilteredResultsHandler + public sealed class FilteredResultsHandler + { + // ======================================================================= + // Fields + // ======================================================================= + readonly ResultsHandler handler; + readonly Filter filter; + + // ======================================================================= + // Constructors + // ======================================================================= + /// + /// Filter chain for producers. + /// + /// Producer to filter. + /// Filter to use to accept objects. + public FilteredResultsHandler(ResultsHandler handler, Filter filter) + { + // there must be a producer.. + if (handler == null) + { + throw new ArgumentException("Producer must not be null!"); + } + this.handler = handler; + // use a default pass through filter.. + this.filter = filter == null ? new PassThroughFilter() : filter; + } + + public ResultsHandler ResultsHandler + { + get + { + return new ResultsHandler + { + Handle = obj => + { + if (filter.Accept(obj)) + { + return handler.Handle(obj); + } + else + { + return true; + } + } + }; + } + } + + /// + /// Use a pass through filter to use if a null filter is provided. + /// + class PassThroughFilter : Filter + { + public bool Accept(ConnectorObject obj) + { + return true; + } + + public R Accept(FilterVisitor v, P p) + { + return v.VisitExtendedFilter(p, this); + } + } + } + #endregion + + #region GetImpl + /// + /// Uses to find the object that is referenced by the + /// provided. + /// + public class GetImpl : GetApiOp + { + readonly SearchApiOp op; + + private class ResultAdapter + { + private IList _list = new List(); + public ResultsHandler ResultsHandler + { + get + { + return new ResultsHandler() + { + Handle = obj => + { + _list.Add(obj); + return false; + } + }; + } + } + public ConnectorObject GetResult() + { + return _list.Count == 0 ? null : _list[0]; + } + } + + public GetImpl(SearchApiOp search) + { + this.op = search; + } + + public ConnectorObject GetObject(ObjectClass objectClass, Uid uid, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(uid, "uid"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + Filter filter = FilterBuilder.EqualTo(uid); + ResultAdapter adapter = new ResultAdapter(); + op.Search(objectClass, filter, adapter.ResultsHandler, options); + return adapter.GetResult(); + } + } + #endregion + + #region OperationalContext + /// + /// NOTE: internal class, public only for unit tests + /// OperationalContext - base class for operations that do not require a + /// connector instance. + /// + public class OperationalContext : AbstractConfiguration.IConfigurationChangeCallback + { + + /// + /// ConnectorInfo + /// + protected readonly LocalConnectorInfoImpl connectorInfo; + + /// + /// Contains the . + /// + protected readonly APIConfigurationImpl apiConfiguration; + + private volatile Configuration configuration; + + /// + /// Creates a new OperationalContext but it does not initiates the + /// Configuration because the method must do it + /// when it's called from a block where the classloader of the Thread is set + /// to Connector. + /// + /// + /// + public OperationalContext(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration) + { + this.connectorInfo = connectorInfo; + this.apiConfiguration = apiConfiguration; + } + + /* + * This method must be called when the Bundle ClassLoader is the Thread + * Context ClassLoader. + */ + public Configuration GetConfiguration() + { + + if (null == configuration) + { + lock (this) + { + if (null == configuration) + { + configuration = + CSharpClassProperties.CreateBean( + (ConfigurationPropertiesImpl) apiConfiguration.ConfigurationProperties, + connectorInfo.ConnectorConfigurationClass); + if (null != apiConfiguration.ChangeListener + && configuration is AbstractConfiguration) + { + ((AbstractConfiguration) configuration).AddChangeCallback(this); + } + } + } + } + return configuration; + } + + protected LocalConnectorInfoImpl GetConnectorInfo() + { + + return connectorInfo; + + } + + public ResultsHandlerConfiguration getResultsHandlerConfiguration() + { + return new ResultsHandlerConfiguration(apiConfiguration.ResultsHandlerConfiguration); + } + + public virtual void Dispose() + { + if (configuration is StatefulConfiguration) + { + // dispose it not supposed to throw, but just in case, + // catch the exception and log it so we know about it + // but don't let the exception prevent additional + // cleanup that needs to happen + try + { + StatefulConfiguration config = (StatefulConfiguration)configuration; + configuration = null; + config.Release(); + } + catch (Exception e) + { + // log this though + Trace.TraceWarning(e.Message); + } + } + } + + public void NotifyUpdate() + { + try + { + IConfigurationPropertyChangeListener listener = apiConfiguration.ChangeListener; + if (null != listener) + { + IList diff = + CSharpClassProperties.CalculateDiff(apiConfiguration.ConfigurationProperties, configuration); + if (diff.Count > 0) + { + listener.ConfigurationPropertyChange(diff); + } + } + } + catch (Exception e) + { + TraceUtil.TraceException("Configuration change notification is failed for" + configuration.GetType(), e); + } + } + } + #endregion + + #region NormalizingResultsHandler + public class NormalizingResultsHandler + { + private readonly ResultsHandler _target; + private readonly ObjectNormalizerFacade _normalizer; + + public NormalizingResultsHandler(ResultsHandler target, + ObjectNormalizerFacade normalizer) + { + Assertions.NullCheck(target, "target"); + Assertions.NullCheck(normalizer, "normalizer"); + _target = target; + _normalizer = normalizer; + } + + public ResultsHandler ResultsHandler + { + get + { + return new ResultsHandler() + { + + Handle = obj => + { + ConnectorObject normalized = _normalizer.NormalizeObject(obj); + return _target.Handle(normalized); + } + }; + } + } + } + #endregion + + #region NormalizingSyncResultsHandler + public class NormalizingSyncResultsHandler + { + private readonly SyncResultsHandler _target; + private readonly ObjectNormalizerFacade _normalizer; + + public NormalizingSyncResultsHandler(SyncResultsHandler target, + ObjectNormalizerFacade normalizer) + { + Assertions.NullCheck(target, "target"); + Assertions.NullCheck(normalizer, "normalizer"); + _target = target; + _normalizer = normalizer; + } + + public SyncResultsHandler SyncResultsHandler + { + get + { + return new SyncResultsHandler() + { + + Handle = delta => + { + SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); + return _target.Handle(normalized); + } + }; + } + } + } + #endregion + + #region CaseNormalizer + public sealed class CaseNormalizer : AttributeNormalizer + { + public static ObjectNormalizerFacade CreateCaseNormalizerFacade(ObjectClass oclass) + { + return new ObjectNormalizerFacade(oclass, new CaseNormalizer()); + } + + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + { + // Trace.TraceInformation("Starting CaseNormalizer.NormalizeAttribute({0}, {1})", oclass, attribute.GetDetails()); + ConnectorAttribute rv = attribute; + bool converted = false; + + IList values = rv.Value; + if (values != null) + { + IList newValues = new List(); + + foreach (object value in values) + { + if (value is string) + { + newValues.Add(((string)value).ToUpper()); + converted = true; + } + else + { + newValues.Add(value); + } + } + + if (converted) // only when something changed; to save a few cpu cycles... + { + rv = ConnectorAttributeBuilder.Build(attribute.Name, newValues); + } + } + + // Trace.TraceInformation("Finishing CaseNormalizer.NormalizeAttribute, converted = {0}, return value = {1}", converted, rv.GetDetails()); + return rv; + } + } + #endregion + + #region NormalizingFilter + /// + /// Proxy the filter to filter based on object normalized version. + /// Similar to ObjectNormalizerFacade.NormalizeFilter, + /// but this one DOES NOT expect that it gets object to be accepted/rejected + /// in normalized form - it normalizes the object just before deciding. + /// Currently used for case insensitive filtering. + /// + public sealed class NormalizingFilter : ExternallyChainedFilter + { + private readonly ObjectNormalizerFacade _normalizationFacade; + + public NormalizingFilter(Filter filter, ObjectNormalizerFacade facade) + : base(facade.NormalizeFilter(filter)) + { + _normalizationFacade = facade; + } + + /// + /// Return the decision based on normalized version of the object. + /// + /// + public override bool Accept(ConnectorObject obj) + { + bool result = Filter.Accept(_normalizationFacade.NormalizeObject(obj)); + // Trace.TraceInformation("NormalizingFilter.Accept returns {0} for {1}", result, obj.GetAttributeByName("__NAME__")); + return result; + } + + public override R Accept(FilterVisitor v, P p) + { + return v.VisitExtendedFilter(p, this); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("NORMALIZE USING ").Append(_normalizationFacade).Append(": ").Append(Filter); + return bld.ToString(); + } + } + #endregion + + #region ObjectNormalizerFacade + public sealed class ObjectNormalizerFacade + { + /// + /// The (non-null) object class + /// + private readonly ObjectClass _objectClass; + /// + /// The (possibly null) attribute normalizer + /// + private readonly AttributeNormalizer _normalizer; + + /// + /// Create a new ObjectNormalizer + /// + /// The object class + /// The normalizer. May be null. + public ObjectNormalizerFacade(ObjectClass objectClass, + AttributeNormalizer normalizer) + { + Assertions.NullCheck(objectClass, "objectClass"); + _objectClass = objectClass; + _normalizer = normalizer; + } + + /// + /// Returns the normalized value of the attribute. + /// + /// + /// If no normalizer is specified, returns the original + /// attribute. + /// + /// The attribute to normalize. + /// The normalized attribute + public ConnectorAttribute NormalizeAttribute(ConnectorAttribute attribute) + { + if (attribute == null) + { + return null; + } + else if (_normalizer != null) + { + return _normalizer.NormalizeAttribute(_objectClass, attribute); + } + else + { + return attribute; + } + } + + /// + /// Returns the normalized set of attributes or null + /// if the original set is null. + /// + /// The original attributes. + /// The normalized attributes or null if + /// the original set is null. + public ICollection NormalizeAttributes(ICollection attributes) + { + if (attributes == null) + { + return null; + } + ICollection temp = new HashSet(); + foreach (ConnectorAttribute attribute in attributes) + { + temp.Add(NormalizeAttribute(attribute)); + } + return CollectionUtil.AsReadOnlySet(temp); + } + + /// + /// Returns the normalized object. + /// + /// The original object + /// The normalized object. + public ConnectorObject NormalizeObject(ConnectorObject orig) + { + return new ConnectorObject(orig.ObjectClass, + NormalizeAttributes(orig.GetAttributes())); + } + + /// + /// Returns the normalized sync delta. + /// + /// The original delta. + /// The normalized delta. + public SyncDelta NormalizeSyncDelta(SyncDelta delta) + { + SyncDeltaBuilder builder = new + SyncDeltaBuilder(delta); + if (delta.Object != null) + { + builder.Object = NormalizeObject(delta.Object); + } + return builder.Build(); + } + + /// + /// Returns a filter consisting of the original with + /// all attributes normalized. + /// + /// The original. + /// The normalized filter. + public Filter NormalizeFilter(Filter filter) + { + if (filter is ContainsFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new ContainsFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is EndsWithFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new EndsWithFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is EqualsFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new EqualsFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is GreaterThanFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new GreaterThanFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is GreaterThanOrEqualFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new GreaterThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is LessThanFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new LessThanFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is LessThanOrEqualFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new LessThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is StartsWithFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new StartsWithFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is ContainsAllValuesFilter) + { + AttributeFilter afilter = + (AttributeFilter)filter; + return new ContainsAllValuesFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is NotFilter) + { + NotFilter notFilter = + (NotFilter)filter; + return new NotFilter(NormalizeFilter(notFilter.Filter)); + } + else if (filter is AndFilter) + { + AndFilter andFilter = + (AndFilter)filter; + return new AndFilter(NormalizeFilter(andFilter.Left), + NormalizeFilter(andFilter.Right)); + } + else if (filter is OrFilter) + { + OrFilter orFilter = + (OrFilter)filter; + return new OrFilter(NormalizeFilter(orFilter.Left), + NormalizeFilter(orFilter.Right)); + } + else + { + return filter; + } + } + } + #endregion + + #region SchemaImpl + internal class SchemaImpl : ConnectorAPIOperationRunner, SchemaApiOp + { + /// + /// Initializes the operation works. + /// + public SchemaImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + /// + /// Retrieve the schema from the . + /// + /// + public Schema Schema() + { + return ((SchemaOp)GetConnector()).Schema(); + } + } + #endregion + + #region ScriptOnConnectorImpl + public class ScriptOnConnectorImpl : ConnectorAPIOperationRunner, + ScriptOnConnectorApiOp + { + public ScriptOnConnectorImpl(ConnectorOperationalContext context, + Connector connector) : + base(context, connector) + { + } + + public Object RunScriptOnConnector(ScriptContext request, + OperationOptions options) + { + Assertions.NullCheck(request, "request"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + Object rv; + if (GetConnector() is ScriptOnConnectorOp) + { + rv = ((ScriptOnConnectorOp)GetConnector()).RunScriptOnConnector(request, options); + } + else + { + String language = request.ScriptLanguage; + Assembly assembly = GetConnector().GetType().Assembly; + + ScriptExecutor executor = + ScriptExecutorFactory.NewInstance(language).NewScriptExecutor( + BuildReferenceList(assembly), + request.ScriptText, + false); + IDictionary scriptArgs = + new Dictionary(request.ScriptArguments); + scriptArgs["connector"] = GetConnector(); //add the connector instance itself + rv = executor.Execute(scriptArgs); + } + return SerializerUtil.CloneObject(rv); + } + + private Assembly[] BuildReferenceList(Assembly assembly) + { + List list = new List(); + foreach (var assemblyName in assembly.GetReferencedAssemblies()) + { + list.Add(Assembly.Load(assemblyName)); + } + // Just add the connector itself. + list.Add(assembly); + return list.ToArray(); + } + } + #endregion + + #region ScriptOnResourceImpl + public class ScriptOnResourceImpl : ConnectorAPIOperationRunner, + ScriptOnResourceApiOp + { + public ScriptOnResourceImpl(ConnectorOperationalContext context, + Connector connector) : + base(context, connector) + { + } + + public Object RunScriptOnResource(ScriptContext request, + OperationOptions options) + { + Assertions.NullCheck(request, "request"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + Object rv + = ((ScriptOnResourceOp)GetConnector()).RunScriptOnResource(request, options); + return SerializerUtil.CloneObject(rv); + } + + } + #endregion + + #region SearchImpl + internal class SearchImpl : ConnectorAPIOperationRunner, SearchApiOp + { + /// + /// Initializes the operation works. + /// + public SearchImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + /// + /// Call the SPI search routines to return the results to the + /// . + /// + /// + public SearchResult Search(ObjectClass objectClass, Filter originalFilter, ResultsHandler handler, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + ResultsHandlerConfiguration hdlCfg = null != GetOperationalContext() ? + GetOperationalContext().getResultsHandlerConfiguration() : new ResultsHandlerConfiguration(); + ResultsHandler handlerChain = handler; + Filter actualFilter = originalFilter; // actualFilter is used for chaining filters - it points to the filter where new filters should be chained + + if (hdlCfg.EnableFilteredResultsHandler && hdlCfg.EnableCaseInsensitiveFilter && actualFilter != null) + { + Trace.TraceInformation("Creating case insensitive filter"); + ObjectNormalizerFacade normalizer = CaseNormalizer.CreateCaseNormalizerFacade(objectClass); + actualFilter = new NormalizingFilter(actualFilter, normalizer); + } + + if (hdlCfg.EnableNormalizingResultsHandler) + { + ObjectNormalizerFacade normalizer = GetNormalizer(objectClass); + //chain a normalizing handler (must come before + //filter handler) + ResultsHandler normalizingHandler = new NormalizingResultsHandler(handler, normalizer).ResultsHandler; + // chain a filter handler.. + if (hdlCfg.EnableFilteredResultsHandler) + { + // chain a filter handler.. + Filter normalizedFilter = normalizer.NormalizeFilter(actualFilter); + handlerChain = new FilteredResultsHandler(normalizingHandler, normalizedFilter).ResultsHandler; + actualFilter = normalizedFilter; + } + else + { + handlerChain = normalizingHandler; + } + } + else if (hdlCfg.EnableFilteredResultsHandler) + { + // chain a filter handler.. + handlerChain = new FilteredResultsHandler(handlerChain, actualFilter).ResultsHandler; + } + + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(SearchOp<>), GetConnector().GetType()); + Type[] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) + { + throw new Exception("Unexpected type: " + interfaceType); + } + Type queryType = val[0]; + Type searcherRawType = typeof(RawSearcherImpl<>); + Type searcherType = + searcherRawType.MakeGenericType(queryType); + RawSearcher searcher = (RawSearcher)Activator.CreateInstance(searcherType); + + // add attributes to get handler + string[] attrsToGet = options.AttributesToGet; + if (attrsToGet != null && attrsToGet.Length > 0 && hdlCfg.EnableAttributesToGetSearchResultsHandler) + { + handlerChain = new SearchAttributesToGetResultsHandler( + handlerChain, attrsToGet).ResultsHandler; + } + SearchResult result = null; + SearchResultsHandler innreHandler = new SearchResultsHandler() + { + Handle = obj => + { + return handlerChain.Handle(obj); + }, + + HandleResult = obj => + { + result = obj; + } + }; + searcher.RawSearch(GetConnector(), objectClass, actualFilter, innreHandler, options); + return result; + } + } + #endregion + + #region RawSearcher + internal interface RawSearcher + { + /// + /// Public because it is used by TestHelpers. + /// + /// + /// Raw, + /// SPI-level search. + /// + /// The underlying implementation of search + /// (generally the connector itself) + /// The object class + /// The filter + /// The handler + /// The options + void RawSearch(Object search, + ObjectClass oclass, + Filter filter, + SearchResultsHandler handler, + OperationOptions options); + } + #endregion + + #region RawSearcherImpl + internal class RawSearcherImpl : RawSearcher where T : class + { + public void RawSearch(Object search, + ObjectClass oclass, + Filter filter, + SearchResultsHandler handler, + OperationOptions options) + { + RawSearch((SearchOp)search, oclass, filter, handler, options); + } + + /// + /// Public because it is used by TestHelpers. + /// + /// + /// Raw, + /// SPI-level search. + /// + /// The underlying implementation of search + /// (generally the connector itself) + /// The object class + /// The filter + /// The handler + /// The options + public static void RawSearch(SearchOp search, + ObjectClass oclass, + Filter filter, + SearchResultsHandler handler, + OperationOptions options) + { + FilterTranslator translator = + search.CreateFilterTranslator(oclass, options); + IList queries = + (IList)translator.Translate(filter); + if (queries.Count == 0) + { + search.ExecuteQuery(oclass, + null, handler, options); + } + else + { + //eliminate dups if more than one + bool eliminateDups = queries.Count > 1; + DuplicateFilteringResultsHandler dups = null; + if (eliminateDups) + { + dups = new DuplicateFilteringResultsHandler(handler); + handler = dups.ResultsHandler; + } + foreach (T query in queries) + { + search.ExecuteQuery(oclass, + query, handler, options); + //don't run any more queries if the consumer + //has stopped + if (dups != null) + { + if (!dups.IsStillHandling) + { + break; + } + } + } + } + } + + } + #endregion + + #region SyncImpl + public class SyncImpl : ConnectorAPIOperationRunner, + SyncApiOp + { + public SyncImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + public SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) + { + //token is allowed to be null, objClass and handler must not be null + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + // add a handler in the chain to remove attributes + string[] attrsToGet = options.AttributesToGet; + if (attrsToGet != null && attrsToGet.Length > 0) + { + handler = new SyncAttributesToGetResultsHandler(handler, attrsToGet).SyncResultsHandler; + } + //chain a normalizing results handler + if (GetConnector() is AttributeNormalizer) + { + handler = new NormalizingSyncResultsHandler(handler, GetNormalizer(objectClass)).SyncResultsHandler; + } + + SyncToken result = null; + Boolean doAll = ObjectClass.ALL.Equals(objectClass); + ((SyncOp)GetConnector()).Sync(objectClass, token, new SyncTokenResultsHandler() + { + Handle = delta => + { + if (doAll && SyncDeltaType.DELETE.Equals(delta.DeltaType) + && null == delta.ObjectClass) + { + throw new ConnectorException( + "Sync '__ALL__' operation requires the connector to set 'objectClass' parameter for sync event."); + } + return handler.Handle(delta); + }, + HandleResult = obj => + { + result = obj; + } + }, options); + return result; + } + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + Assertions.NullCheck(objectClass, "objectClass"); + return ((SyncOp)GetConnector()).GetLatestSyncToken(objectClass); + } + } + #endregion + + #region TestImpl + /// + /// Provides a method for the API to call the SPI's test method on the + /// connector. + /// + /// + /// The test method is intended to determine if the + /// is ready to perform the various operations it supports. + /// + /// Will Droste + internal class TestImpl : ConnectorAPIOperationRunner, TestApiOp + { + public TestImpl(ConnectorOperationalContext context, Connector connector) + : base(context, connector) + { + } + + public void Test() + { + ((TestOp)GetConnector()).Test(); + } + + } + #endregion + + #region UpdateImpl + /// + /// NOTE: internal class, public only for unit tests + /// Handles both version of update this include simple replace and the advance + /// update. + /// + public class UpdateImpl : ConnectorAPIOperationRunner, UpdateApiOp + { + /// + /// All the operational attributes that can not be added or deleted. + /// + static readonly HashSet OPERATIONAL_ATTRIBUTE_NAMES = new HashSet(); + + const String OPERATIONAL_ATTRIBUTE_ERR = + "Operational attribute '{0}' can not be added or deleted only replaced."; + + static UpdateImpl() + { + OPERATIONAL_ATTRIBUTE_NAMES.Add(Name.NAME); + CollectionUtil.AddAll(OPERATIONAL_ATTRIBUTE_NAMES, + OperationalAttributes.OPERATIONAL_ATTRIBUTE_NAMES); + } + + /// + /// Determines which type of update a connector supports and then uses that + /// handler. + /// + public UpdateImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + public Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options) + { + // validate all the parameters.. + ValidateInput(objclass, uid, replaceAttributes, false); + //cast null as empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + replaceAttributes = + normalizer.NormalizeAttributes(replaceAttributes); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret = op.Update(objclass, uid, replaceAttributes, options); + return (Uid)normalizer.NormalizeAttribute(ret); + } + + public Uid AddAttributeValues(ObjectClass objectClass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options) + { + // validate all the parameters.. + ValidateInput(objectClass, uid, valuesToAdd, true); + //cast null as empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objectClass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + valuesToAdd = + normalizer.NormalizeAttributes(valuesToAdd); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret; + if (op is UpdateAttributeValuesOp) + { + UpdateAttributeValuesOp valueOp = + (UpdateAttributeValuesOp)op; + ret = valueOp.AddAttributeValues(objectClass, uid, valuesToAdd, options); + } + else + { + ICollection replaceAttributes = + FetchAndMerge(objectClass, uid, valuesToAdd, true, options); + ret = op.Update(objectClass, uid, replaceAttributes, options); + } + return (Uid)normalizer.NormalizeAttribute(ret); + } + + public Uid RemoveAttributeValues(ObjectClass objectClass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options) + { + // validate all the parameters.. + ValidateInput(objectClass, uid, valuesToRemove, true); + //cast null as empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objectClass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + valuesToRemove = + normalizer.NormalizeAttributes(valuesToRemove); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret; + if (op is UpdateAttributeValuesOp) + { + UpdateAttributeValuesOp valueOp = + (UpdateAttributeValuesOp)op; + ret = valueOp.RemoveAttributeValues(objectClass, uid, valuesToRemove, options); + } + else + { + ICollection replaceAttributes = + FetchAndMerge(objectClass, uid, valuesToRemove, false, options); + ret = op.Update(objectClass, uid, replaceAttributes, options); + } + return (Uid)normalizer.NormalizeAttribute(ret); + } + + private ICollection FetchAndMerge(ObjectClass objclass, Uid uid, + ICollection valuesToChange, + bool add, + OperationOptions options) + { + // check that this connector supports Search.. + if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>), GetConnector().GetType()) == null) + { + String MSG = "Connector must support search"; + throw new InvalidOperationException(MSG); + } + + //add attrs to get to operation options, so that the + //object we fetch has exactly the set of attributes we require + //(there may be ones that are not in the default set) + OperationOptionsBuilder builder = new OperationOptionsBuilder(options); + ICollection attrNames = new HashSet(); + foreach (ConnectorAttribute attribute in valuesToChange) + { + attrNames.Add(attribute.Name); + } + builder.AttributesToGet = (attrNames.ToArray()); + options = builder.Build(); + + // get the connector object from the resource... + ConnectorObject o = GetConnectorObject(objclass, uid, options); + if (o == null) + { + throw new UnknownUidException(uid, objclass); + } + // merge the update data.. + ICollection mergeAttrs = Merge(valuesToChange, o.GetAttributes(), add); + return mergeAttrs; + } + + /// + /// Merges two connector objects into a single updated object. + /// + public ICollection Merge(ICollection updateAttrs, + ICollection baseAttrs, bool add) + { + // return the merged attributes + ICollection ret = new HashSet(); + // create map that can be modified to get the subset of changes + IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); + // run through attributes of the current object.. + foreach (ConnectorAttribute updateAttr in updateAttrs) + { + // get the name of the update attributes + String name = updateAttr.Name; + // remove each attribute that is an update attribute.. + ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap, name, null); + IList values; + ConnectorAttribute modifiedAttr; + if (add) + { + if (baseAttr == null) + { + modifiedAttr = updateAttr; + } + else + { + // create a new list with the base attribute to add to.. + values = CollectionUtil.NewList(baseAttr.Value); + CollectionUtil.AddAll(values, updateAttr.Value); + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + else + { + if (baseAttr == null) + { + // nothing to actually do the attribute do not exist + continue; + } + else + { + // create a list with the base attribute to remove from.. + values = CollectionUtil.NewList(baseAttr.Value); + foreach (Object val in updateAttr.Value) + { + values.Remove(val); + } + // if the values are empty send a null to the connector.. + if (values.Count == 0) + { + modifiedAttr = ConnectorAttributeBuilder.Build(name); + } + else + { + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + } + ret.Add(modifiedAttr); + } + return ret; + } + + /// + /// Get the to modify. + /// + private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) + { + // attempt to get the connector object.. + GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(), + GetConnector())); + return get.GetObject(oclass, uid, options); + } + + /// + /// Makes things easier if you can trust the input. + /// + public static void ValidateInput(ObjectClass objectClass, + Uid uid, + ICollection replaceAttributes, bool isDelta) + { + Assertions.NullCheck(uid, "uid"); + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(replaceAttributes, "replaceAttributes"); + // check to make sure there's not a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(replaceAttributes) != null) + { + throw new ArgumentException( + "Parameter 'attrs' contains a uid."); + } + // check for things only valid during ADD/DELETE + if (isDelta) + { + foreach (ConnectorAttribute attr in replaceAttributes) + { + Assertions.NullCheck(attr, "attr"); + // make sure that none of the values are null.. + if (attr.Value == null) + { + throw new ArgumentException( + "Can not add or remove a 'null' value."); + } + // make sure that if this an delete/add that it doesn't include + // certain attributes because it doesn't make any sense.. + String name = attr.Name; + if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) + { + String msg = String.Format(OPERATIONAL_ATTRIBUTE_ERR, name); + throw new ArgumentException(msg); + } + } + } + } + } + #endregion + + #region ValidateImpl + internal class ValidateImpl : APIOperationRunner, ValidateApiOp + { + + public ValidateImpl(OperationalContext context) + : base(context) + { + } + + public void Validate() + { + GetOperationalContext().GetConfiguration().Validate(); + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/ApiRemote.cs b/dotnet/framework/FrameworkInternal/ApiRemote.cs new file mode 100644 index 00000000..b822a23b --- /dev/null +++ b/dotnet/framework/FrameworkInternal/ApiRemote.cs @@ -0,0 +1,698 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Security.Authentication; +using System.Globalization; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; + +namespace Org.IdentityConnectors.Framework.Impl.Api.Remote +{ + #region RemoteFrameworkConnection + public class RemoteFrameworkConnection : IDisposable + { + private TcpClient _socket; + private Stream _stream; + + private BinaryObjectSerializer _encoder; + private BinaryObjectDeserializer _decoder; + + public RemoteFrameworkConnection(RemoteFrameworkConnectionInfo info) + { + Init(info); + } + + public RemoteFrameworkConnection(TcpClient socket, Stream stream) + { + Init(socket, stream); + } + + private void Init(RemoteFrameworkConnectionInfo connectionInfo) + { + IPAddress[] addresses = + Dns.GetHostAddresses(connectionInfo.Host); + TcpClient client = new TcpClient(addresses[0].AddressFamily); + client.SendTimeout = connectionInfo.Timeout; + client.ReceiveTimeout = connectionInfo.Timeout; + client.Connect(addresses[0], connectionInfo.Port); + Stream stream; + try + { + stream = client.GetStream(); + } + catch (Exception) + { + try { client.Close(); } + catch (Exception) { } + throw; + } + try + { + if (connectionInfo.UseSSL) + { + if (connectionInfo.CertificateValidationCallback != null) + { + RemoteCertificateValidationCallback callback = + connectionInfo.CertificateValidationCallback; + + stream = new SslStream( + stream, false, callback); + } + else + { + stream = new SslStream(stream, + false); + } + ((SslStream)stream).AuthenticateAsClient(connectionInfo.Host, + new X509CertificateCollection(new X509Certificate[0]), + SslProtocols.Tls, + false); + } + } + catch (Exception) + { + try { stream.Close(); } + catch (Exception) { } + try { client.Close(); } + catch (Exception) { } + throw; + } + Init(client, stream); + } + + private void Init(TcpClient socket, Stream stream) + { + _socket = socket; + _stream = stream; + ObjectSerializerFactory fact = + ObjectSerializerFactory.GetInstance(); + _encoder = fact.NewBinarySerializer(_stream); + _decoder = fact.NewBinaryDeserializer(_stream); + } + + public void Dispose() + { + Flush(); + _stream.Close(); + _socket.Close(); + } + + public void Flush() + { + _encoder.Flush(); + } + + public void WriteObject(object obj) + { + _encoder.WriteObject(obj); + } + + public object ReadObject() + { + //flush first in case there is any data in the + //output buffer + Flush(); + return _decoder.ReadObject(); + } + } + #endregion + + #region RemoteConnectorInfoImpl + /// + /// internal class, public only for unit tests + /// + public sealed class RemoteConnectorInfoImpl : AbstractConnectorInfo + { + + + public RemoteConnectorInfoImpl() + { + + } + + //transient field, not serialized + public RemoteFrameworkConnectionInfo RemoteConnectionInfo { get; set; } + + } + #endregion + + #region RemoteConnectorInfoManagerImpl + public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager + { + private IList _connectorInfo; + + private RemoteConnectorInfoManagerImpl() + { + + } + + public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) + { + using (RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(info)) + { + connection.WriteObject(CultureInfo.CurrentUICulture); + connection.WriteObject(info.Key); + connection.WriteObject(new HelloRequest(HelloRequest.CONNECTOR_INFO)); + HelloResponse response = (HelloResponse)connection.ReadObject(); + if (response.Exception != null) + { + throw response.Exception; + } + IList remoteInfos = + response.ConnectorInfos; + //populate transient fields not serialized + foreach (RemoteConnectorInfoImpl remoteInfo in remoteInfos) + { + remoteInfo.RemoteConnectionInfo = info; + } + _connectorInfo = + CollectionUtil.NewReadOnlyList(remoteInfos); + } + + } + + /// + /// Derives another RemoteConnectorInfoManagerImpl with + /// a different RemoteFrameworkConnectionInfo but with the + /// same metadata + /// + /// + public RemoteConnectorInfoManagerImpl Derive(RemoteFrameworkConnectionInfo info) + { + RemoteConnectorInfoManagerImpl rv = new RemoteConnectorInfoManagerImpl(); + IList remoteInfosObj = + (IList)SerializerUtil.CloneObject(_connectorInfo); + IList remoteInfos = + CollectionUtil.NewList(remoteInfosObj); + foreach (ConnectorInfo remoteInfo in remoteInfos) + { + ((RemoteConnectorInfoImpl)remoteInfo).RemoteConnectionInfo = (info); + } + rv._connectorInfo = + CollectionUtil.AsReadOnlyList(remoteInfos); + return rv; + } + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + foreach (ConnectorInfo info in _connectorInfo) + { + if (info.ConnectorKey.Equals(key)) + { + return info; + } + } + return null; + } + + public IList ConnectorInfos + { + get + { + return _connectorInfo; + } + } + } + #endregion + + #region RemoteConnectorFacadeImpl + /// + /// Implements all the methods of the facade + /// + public class RemoteConnectorFacadeImpl : AbstractConnectorFacade + { + + internal readonly string remoteConnectorFacadeKey; + + /// + /// Builds up the maps of supported operations and calls. + /// + public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) + : base(GenerateRemoteConnectorFacadeKey(configuration), configuration.ConnectorInfo) + { + // Restore the original configuration settings + GetAPIConfiguration().ProducerBufferSize = configuration.ProducerBufferSize; + GetAPIConfiguration().TimeoutMap = configuration.TimeoutMap; + remoteConnectorFacadeKey = ConnectorFacadeKey; + } + + public RemoteConnectorFacadeImpl(RemoteConnectorInfoImpl connectorInfo, string configuration) + : base(configuration, connectorInfo) + { + remoteConnectorFacadeKey = GenerateRemoteConnectorFacadeKey(GetAPIConfiguration()); + } + + public RemoteConnectorFacadeImpl(RemoteConnectorInfoImpl connectorInfo, String config, + IConfigurationPropertyChangeListener changeListener) + : this(connectorInfo, config) + { + GetAPIConfiguration().ChangeListener = changeListener; + } + + private static string GenerateRemoteConnectorFacadeKey(APIConfigurationImpl configuration) + { + APIConfigurationImpl copy = new APIConfigurationImpl(configuration); + copy.ProducerBufferSize = 0; + copy.TimeoutMap = new Dictionary, int>(); + return SerializerUtil.SerializeBase64Object(copy); + } + + protected override APIOperation GetOperationImplementation(SafeType api) + { + if (api.RawType == typeof(IConnectorEventSubscriptionApiOp) || api.RawType == typeof(ISyncEventSubscriptionApiOp)) + { + //Not supported remotely with legacy communication protocol + return null; + } + // add remote proxy + InvocationHandler handler = new RemoteOperationInvocationHandler( + (RemoteConnectorInfoImpl)GetAPIConfiguration().ConnectorInfo, remoteConnectorFacadeKey, api); + APIOperation proxy = NewAPIOperationProxy(api, handler); + // now wrap the proxy in the appropriate timeout proxy + proxy = CreateTimeoutProxy(api, proxy); + // add logging proxy + proxy = CreateLoggingProxy(api, proxy); + + return proxy; + } + } + #endregion + + #region RemoteOperationInvocationHandler + /// + /// Invocation handler for all of our operations + /// + public class RemoteOperationInvocationHandler : InvocationHandler + { + private readonly RemoteConnectorInfoImpl _connectorInfo; + private readonly String _connectorFacadeKey; + private readonly SafeType _operation; + + public RemoteOperationInvocationHandler(RemoteConnectorInfoImpl connectorInfo, + String connectorFacadeKey, + SafeType operation) + { + _connectorInfo = connectorInfo; + _connectorFacadeKey = connectorFacadeKey; + _operation = operation; + } + + + public Object Invoke(Object proxy, MethodInfo method, Object[] args) + { + //don't proxy toString, hashCode, or equals + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + + //partition arguments into arguments that can + //be simply marshalled as part of the request and + //those that are response handlers + IList simpleMarshallArgs = + CollectionUtil.NewList(args); + ObjectStreamHandler streamHandlerArg = + ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method), simpleMarshallArgs); + + //build the request object + RemoteFrameworkConnectionInfo connectionInfo = + _connectorInfo.RemoteConnectionInfo; + OperationRequest request = new OperationRequest( + _connectorInfo.ConnectorKey, _connectorFacadeKey, + _operation, + method.Name, + simpleMarshallArgs); + + //create the connection + RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(connectionInfo); + try + { + connection.WriteObject(CultureInfo.CurrentUICulture); + connection.WriteObject(connectionInfo.Key); + //send the request + connection.WriteObject(request); + + //now process each response stream (if any) + if (streamHandlerArg != null) + { + HandleStreamResponse(connection, streamHandlerArg); + } + + //finally return the actual return value + OperationResponsePart response = + (OperationResponsePart)connection.ReadObject(); + if (response.Exception != null) + { + throw response.Exception; + } + return response.Result; + } + finally + { + connection.Dispose(); + } + } + /// + /// Handles a stream response until the end of the stream + /// + private static void HandleStreamResponse(RemoteFrameworkConnection connection, ObjectStreamHandler streamHandler) + { + Object response; + bool handleMore = true; + while (true) + { + response = connection.ReadObject(); + if (response is OperationResponsePart) + { + OperationResponsePart part = (OperationResponsePart)response; + if (part.Exception != null) + { + throw part.Exception; + } + object obj = + part.Result; + if (handleMore) + { + handleMore = streamHandler.Handle(obj); + } + } + else if (response is OperationResponsePause) + { + if (handleMore) + { + connection.WriteObject(new OperationRequestMoreData()); + } + else + { + connection.WriteObject(new OperationRequestStopData()); + } + } + else if (response is OperationResponseEnd) + { + break; + } + else + { + throw new ConnectorException("Unexpected response: " + response); + } + } + } + + /// + /// Partitions arguments into regular arguments and + /// stream arguments. + /// + /// The param types of the method + /// The passed-in arguments. As a + /// side-effect will be set to just the regular arguments. + /// The stream handler arguments. + private static ObjectStreamHandler ExtractStreamHandler(Type[] paramTypes, + IList arguments) + { + ObjectStreamHandler rv = null; + IList filteredArguments = new List(); + for (int i = 0; i < paramTypes.Length; i++) + { + Type paramType = paramTypes[i]; + object arg = arguments[i]; + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) + { + ObjectStreamHandler handler = StreamHandlerUtil.AdaptToObjectStreamHandler(paramType, arg); + if (rv != null) + { + throw new InvalidOperationException("Multiple stream handlers not supported"); + } + rv = handler; + } + else + { + filteredArguments.Add(arg); + } + } + arguments.Clear(); + CollectionUtil.AddAll(arguments, filteredArguments); + return rv; + } + } + #endregion + + #region RemoteWrappedException + /// + /// RemoteWrappedException wraps every exception which are received from Remote + /// Connector Server. + ///

+ /// This Exception is not allowed to use in Connectors!!! + ///

+ /// + /// + /// + /// This type of exception is not allowed to be serialise because this exception + /// represents any after deserialization. + /// + /// This code example show how to get the remote stack trace and how to use the + /// same catches to handle the exceptions regardless its origin. + /// + ///

+    /// 
+    ///  String stackTrace = null;
+    ///  try {
+    ///      try {
+    ///          facade.GetObject(ObjectClass.ACCOUNT, uid, null);
+    ///      } catch (RemoteWrappedException e) {
+    ///          stackTrace = e.StackTrace;
+    ///      }
+    ///  } catch (Throwable t) {
+    ///      
+    ///  }
+    /// 
+    /// 
+ ///
+ /// Since 1.4 + public sealed class RemoteWrappedException : ConnectorException + { + public const string FIELD_CLASS = "class"; + public const string FIELD_MESSAGE = "message"; + public const string FIELD_CAUSE = "cause"; + public const string FIELD_STACK_TRACE = "stackTrace"; + + private readonly string stackTrace; + + /// + ///
+        ///     
+        ///         {
+        ///              "class": "org.identityconnectors.framework.common.exceptions.ConnectorIOException",
+        ///              "message": "Sample Error Message",
+        ///              "cause": {
+        ///                  "class": "java.net.SocketTimeoutException",
+        ///                  "message": "Sample Error Message",
+        ///                  "cause": {
+        ///                      "class": "edu.example.CustomException",
+        ///                      "message": "Sample Error Message"
+        ///                  }
+        ///              },
+        ///              "stackTrace": "full stack trace for logging"
+        ///          }
+        ///     
+        /// 
+ ///
+ private IDictionary exception = null; + + /// + internal RemoteWrappedException(IDictionary exception) + : base((string)exception[FIELD_MESSAGE]) + { + this.exception = exception; + this.stackTrace = (string)exception[FIELD_STACK_TRACE]; + } + + public RemoteWrappedException(string throwableClass, string message, RemoteWrappedException cause, string stackTrace) + : base(message) + { + exception = new Dictionary(4); + exception[FIELD_CLASS] = Assertions.BlankChecked(throwableClass, "throwableClass"); + exception[FIELD_MESSAGE] = message; + if (null != cause) + { + exception[FIELD_CAUSE] = cause.exception; + } + if (null != stackTrace) + { + exception[FIELD_STACK_TRACE] = stackTrace; + } + this.stackTrace = stackTrace; + } + + /// + /// Gets the class name of the original exception. + /// + /// This value is constructed by {@code Exception.Type.FullName}. + /// + /// name of the original exception. + public string ExceptionClass + { + get + { + return (string)exception[FIELD_CLASS]; + } + } + + /// + /// Checks if the exception is the expected class. + /// + /// + /// the expected throwable class. + /// {@code true} if the class name are equals. + public bool Is(Type expected) + { + if (null == expected) + { + return false; + } + string className = ((string)exception[FIELD_CLASS]); + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + string classExpected = expected.FullName; + return classExpected.Equals(className, StringComparison.CurrentCultureIgnoreCase); + } + + /// + /// Returns the cause of original throwable or {@code null} if the cause is + /// nonexistent or unknown. (The cause is the throwable that caused the + /// original throwable to get thrown.) + /// + /// the cause of this throwable or {@code null} if the cause is + /// nonexistent or unknown. + public RemoteWrappedException Cause + { + get + { + object o = exception[FIELD_CAUSE]; + if (o is IDictionary) + { + return new RemoteWrappedException((IDictionary)o); + } + else + { + return null; + } + } + } + + public override string StackTrace + { + get + { + if (null == stackTrace) + { + return base.StackTrace; + } + else + { + return stackTrace; + } + } + } + + public string ReadStackTrace() + { + return stackTrace; + } + + /// + /// Wraps the Throwable into a RemoteWrappedException instance. + /// + /// + /// Exception to wrap or cast and return. + /// a RemoteWrappedException that either is the + /// specified exception or contains the specified exception. + public static RemoteWrappedException Wrap(Exception ex) + { + if (null == ex) + { + return null; + } + // don't bother to wrap a exception that is already a + // RemoteWrappedException. + if (ex is RemoteWrappedException) + { + return (RemoteWrappedException)ex; + } + return new RemoteWrappedException(convert(ex)); + } + + /// + /// Converts the throwable object to a new Map object that representing + /// itself. + /// + /// + /// the {@code Throwable} to be converted + /// the Map representing the throwable. + public static Dictionary convert(Exception throwable) + { + Dictionary exception = null; + if (null != throwable) + { + exception = new Dictionary(4); + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + exception[FIELD_CLASS] = throwable.GetType().FullName; + exception[FIELD_MESSAGE] = throwable.Message; + if (null != throwable.InnerException) + { + exception[FIELD_CAUSE] = buildCause(throwable.InnerException); + } + exception[FIELD_STACK_TRACE] = throwable.StackTrace; + } + return exception; + } + + private static IDictionary buildCause(Exception throwable) + { + IDictionary cause = new Dictionary(null != throwable.InnerException ? 3 : 2); + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + cause[FIELD_CLASS] = throwable.GetType().FullName; + cause[FIELD_MESSAGE] = throwable.Message; + if (null != throwable.InnerException) + { + cause[FIELD_CAUSE] = buildCause(throwable.InnerException); + } + return cause; + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/ApiRemoteMessages.cs b/dotnet/framework/FrameworkInternal/ApiRemoteMessages.cs new file mode 100644 index 00000000..445afac6 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/ApiRemoteMessages.cs @@ -0,0 +1,369 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages +{ + #region HelloRequest + /// + /// internal class, public only for unit tests + /// + public class HelloRequest : Message + { + public const int SERVER_INFO = 4; + public const int CONNECTOR_KEY_LIST = 16; + // private const int DEFAULT_CONFIG = 32; + public const int CONNECTOR_INFO = CONNECTOR_KEY_LIST | SERVER_INFO; + + private readonly int _level; + + public HelloRequest(int infoLevel) + { + _level = infoLevel; + } + + public int GetInfoLevel() + { + return _level; + } + + private bool checkInfoLevel(int info) + { + return ((_level & info) == info); + } + + public bool isServerInfo() + { + return checkInfoLevel(SERVER_INFO); + } + + public bool isConnectorKeys() + { + return checkInfoLevel(CONNECTOR_KEY_LIST); + } + + public bool isConnectorInfo() + { + return checkInfoLevel(CONNECTOR_INFO); + } + } + #endregion + + #region HelloResponse + /// + /// internal class, public only for unit tests + /// + public class HelloResponse : Message + { + public const string SERVER_START_TIME = "SERVER_START_TIME"; + + /// + /// The exception + /// + private Exception _exception; + + private IDictionary _serverInfo; + + /// + /// List of connector infos, containing infos for all the connectors + /// on the server. + /// + private IList _connectorInfos; + + /// + /// List of connector keys, containing the keys of all the connectors + /// on the server. + /// + private IList _connectorKeys; + + public HelloResponse(Exception exception, + IDictionary serverInfo, + IList connectorKeys, + IList connectorInfos) + { + _exception = exception; + _serverInfo = CollectionUtil.AsReadOnlyDictionary(serverInfo); + _connectorKeys = CollectionUtil.NewReadOnlyList(connectorKeys); + _connectorInfos = CollectionUtil.NewReadOnlyList(connectorInfos); + } + + public Exception Exception + { + get + { + return _exception; + } + } + + public IList ConnectorInfos + { + get + { + return _connectorInfos; + } + } + + public IList ConnectorKeys + { + get + { + return _connectorKeys; + } + } + + public IDictionary ServerInfo + { + get + { + return _serverInfo; + } + } + + public DateTime? getStartTime() + { + object time = ServerInfo[SERVER_START_TIME]; + if (time is long) + { + return new DateTime((long)time); + } + return null; + } + } + #endregion + + #region Message + /// + /// internal class, public only for unit tests + /// + public interface Message + { + } + #endregion + + #region OperationRequest + /// + /// internal class, public only for unit tests + /// + public class OperationRequest : Message + { + /// + /// The key of the connector to operate on. + /// + private readonly ConnectorKey _connectorKey; + + /// + /// The configuration information to use. + /// + private readonly String _connectorFacadeKey; + + /// + /// The operation to perform. + /// + private readonly SafeType _operation; + + /// + /// The name of the method since operations can have more + /// than one method. + /// + /// + /// NOTE: this is case-insensitive + /// + private readonly String _operationMethodName; + + /// + /// The arguments to the operation. + /// + /// + /// In general, these correspond + /// to the actual arguments of the method. The one exception is + /// search - in this case, the callback is not passed. + /// + private readonly IList _arguments; + + public OperationRequest(ConnectorKey key, + String connectorFacadeKey, + SafeType operation, + string operationMethodName, + IList arguments) + { + _connectorKey = key; + _connectorFacadeKey = connectorFacadeKey; + _operation = operation; + _operationMethodName = operationMethodName; + _arguments = CollectionUtil.NewReadOnlyList(arguments); + } + + public ConnectorKey ConnectorKey + { + get + { + return _connectorKey; + } + } + + public String ConnectorFacadeKey + { + get + { + return _connectorFacadeKey; + } + } + + public SafeType Operation + { + get + { + return _operation; + } + } + + public string OperationMethodName + { + get + { + return _operationMethodName; + } + } + + public IList Arguments + { + get + { + return _arguments; + } + } + } + #endregion + + #region OperationRequestMoreData + /// + /// internal class, public only for unit tests + /// + public class OperationRequestMoreData : Message + { + public OperationRequestMoreData() + { + } + } + #endregion + + #region OperationRequestStopData + /// + /// internal class, public only for unit tests + /// + public class OperationRequestStopData : Message + { + public OperationRequestStopData() + { + } + } + #endregion + + #region OperationResponseEnd + /// + /// internal class, public only for unit tests + /// + public class OperationResponseEnd : Message + { + public OperationResponseEnd() + { + } + } + #endregion + + #region OperationResponsePart + /// + /// internal class, public only for unit tests + /// + public class OperationResponsePart : Message + { + private RemoteWrappedException _exception; + private Object _result; + + public OperationResponsePart(Exception ex, Object result) + { + _exception = RemoteWrappedException.Wrap(ex); + _result = result; + } + + public RemoteWrappedException Exception + { + get + { + return _exception; + } + } + + public Object Result + { + get + { + return _result; + } + } + } + #endregion + + #region OperationResponsePause + /// + /// internal class, public only for unit tests + /// + public class OperationResponsePause : Message + { + public OperationResponsePause() + { + } + } + #endregion + + #region EchoMessage + public class EchoMessage : Message + { + private object _object; + private string _objectXml; + public EchoMessage(object obj, string xml) + { + _object = obj; + _objectXml = xml; + } + public object Object + { + get + { + return _object; + } + } + public string ObjectXml + { + get + { + return _objectXml; + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/ExceptionUtil.cs b/dotnet/framework/FrameworkInternal/ExceptionUtil.cs new file mode 100644 index 00000000..353e36e9 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/ExceptionUtil.cs @@ -0,0 +1,83 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Reflection; +using Org.IdentityConnectors.Common; + +namespace Org.IdentityConnectors.Framework.Impl +{ + /// + /// Contains utilities to handle exceptions. + /// + public static class ExceptionUtil + { + private const string PreserveStackTraceMethodName = "InternalPreserveStackTrace"; + + /// + /// Preserves the stack trace of . + /// + /// The exception, the stack trace of which to be preserved. + /// In the .Net Framework the stack trace of an exception starts to get populated when it is thrown, + /// hence if an exception is re-thrown by a method upper in the call chain the stack trace will reflect that the + /// exception occurred at that position where the exception was actually re-thrown and the original stack trace will + /// be lost. + /// + /// + /// try + /// { + /// function_that_throws_an_exception_with_a_nested_one(); + /// } + /// catch( Exception ex ) + /// { + /// throw ex.InnerException; //clears the stack trace of the nested exception + /// } + /// + /// + /// There is no built-in support in .Net to preserve the stack trace, however, an internal method + /// of the class called + /// can be used to achieve this. Since it is an internal method it might be subject to change, therefore if it is + /// not possible to invoke the method by any reason the error cause will be traced, but it will not break the execution. + /// + public static void PreserveStackTrace(Exception exception) + { + Assertions.NullCheck(exception, "exception"); + + try + { + MethodInfo preserveStackTrace = typeof(Exception).GetMethod( + PreserveStackTraceMethodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod); + + preserveStackTrace.Invoke(exception, null); + } + catch (Exception ex) + { + //it should not ever happen, but we have to make sure that if a next release of .Net Framework does not + //include the invoked method it will not break the execution + TraceUtil.TraceException(string.Format( + @"Could not set an exception to preserve its stack trace. Exception: ""{0}""", exception), ex); + return; + } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/FrameworkInternal.csproj b/dotnet/framework/FrameworkInternal/FrameworkInternal.csproj new file mode 100644 index 00000000..f4b090f6 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/FrameworkInternal.csproj @@ -0,0 +1,123 @@ + + + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + Debug + AnyCPU + Library + Org.IdentityConnectors + FrameworkInternal + Open Connectors Framework Internal + v4.5.2 + True + False + 4 + false + true + + + + prompt + 4 + true + bin\Debug\ + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + true + prompt + 4 + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + false + + + false + + + + + + + 4.0 + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {E6A207D2-E083-41BF-B522-D9D3EC09323E} + TestCommon + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/Resources.resx b/dotnet/framework/FrameworkInternal/Resources.resx new file mode 100755 index 00000000..92b2f09e --- /dev/null +++ b/dotnet/framework/FrameworkInternal/Resources.resx @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version='1.0' encoding='UTF-8'?> +<!--=======================================================--> +<!--= =--> +<!--= DTD for Connector Objects =--> +<!--= =--> +<!--=======================================================--> + +<!--=======================================================--> +<!--= =--> +<!--= All XML Objects =--> +<!--= =--> +<!--=======================================================--> + +<!ENTITY % exceptionTypes + "AlreadyExistsException | ConfigurationException | ConnectionBrokenException + | ConnectionFailedException | ConnectorIOException | InvalidPasswordException + | UnknownUidException | InvalidCredentialException | PermissionDeniedException + | ConnectorSecurityException | OperationTimeoutException | InvalidAttributeValueException + | PreconditionRequiredException | PreconditionFailedException | RetryableException + | RemoteWrappedException | ConnectorException + | RuntimeException | Exception | Throwable | PasswordExpiredException | IllegalArgumentException + "> + +<!ENTITY % messageTypes + "HelloRequest | HelloResponse | OperationRequest | OperationResponseEnd | + OperationResponsePart | OperationRequestMoreData | OperationRequestStopData | + OperationResponsePause | EchoMessage + "> + +<!ENTITY % filterTypes + "AndFilter | ContainsFilter | EndsWithFilter | EqualsFilter | ExtendedMatchFilter | + GreaterThanFilter | GreaterThanOrEqualFilter | LessThanFilter | + LessThanOrEqualFilter | NotFilter | OrFilter | PresenceFilter | StartsWithFilter | + ContainsAllValuesFilter + "> + +<!ENTITY % attributeTypes + "Attribute | Uid | Name"> + +<!ENTITY % primitiveTypes + "null | Array | Boolean | boolean | Character | char | Integer | + int | Long | long | Float | float | Double | double | String | + URI | File | BigDecimal | BigInteger | ByteArray | Class | + Map | List | Set | Locale | GuardedByteArray | GuardedString | + Byte | byte + "> + +<!ENTITY % xmlObject + "%primitiveTypes; | %exceptionTypes; | %messageTypes; | %filterTypes; | %attributeTypes; | +ObjectPoolConfiguration | ResultsHandlerConfiguration | ConfigurationProperty | ConfigurationProperties | +APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | +UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | +ObjectClassInfo | Schema | Script | ScriptContext | OperationOptions | +OperationOptionInfo | SearchResult | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid +"> + + + +<!--=======================================================--> +<!--= =--> +<!--= Top Level Element for object streams =--> +<!--= =--> +<!--=======================================================--> + + +<!ELEMENT MultiObject (( + %xmlObject; +)*)> + + +<!--=======================================================--> +<!--= =--> +<!--= Primitives =--> +<!--= =--> +<!--=======================================================--> + + + +<!ELEMENT null EMPTY> +<!ELEMENT Array ((%xmlObject;)*)> +<!ATTLIST Array + componentType CDATA #REQUIRED +> +<!ELEMENT Boolean (#PCDATA)> +<!ELEMENT boolean (#PCDATA)> +<!ELEMENT Character (#PCDATA)> +<!ELEMENT char (#PCDATA)> +<!ELEMENT Integer (#PCDATA)> +<!ELEMENT int (#PCDATA)> +<!ELEMENT Byte (#PCDATA)> +<!ELEMENT byte (#PCDATA)> +<!ELEMENT Long (#PCDATA)> +<!ELEMENT long (#PCDATA)> +<!ELEMENT Float (#PCDATA)> +<!ELEMENT float (#PCDATA)> +<!ELEMENT Double (#PCDATA)> +<!ELEMENT double (#PCDATA)> +<!ELEMENT String (#PCDATA)> +<!ELEMENT URI (#PCDATA)> +<!ELEMENT File (#PCDATA)> +<!ELEMENT BigDecimal EMPTY> +<!ATTLIST BigDecimal + unscaled CDATA #REQUIRED + scale CDATA #REQUIRED +> +<!ELEMENT BigInteger (#PCDATA)> +<!ELEMENT ByteArray (#PCDATA)> +<!ELEMENT Class (#PCDATA)> +<!ELEMENT Map ((MapEntry)*)> +<!ATTLIST Map + caseInsensitive CDATA #IMPLIED +> +<!ELEMENT MapEntry ((%xmlObject;),(%xmlObject;))> +<!ELEMENT Keys ((%xmlObject;)*)> +<!ELEMENT List ((%xmlObject;)*)> +<!ELEMENT Set ((%xmlObject;)*)> +<!ATTLIST Set + caseInsensitive CDATA #IMPLIED +> +<!ELEMENT Locale EMPTY> +<!ATTLIST Locale + language CDATA #IMPLIED + country CDATA #IMPLIED + variant CDATA #IMPLIED +> +<!ELEMENT GuardedByteArray (#PCDATA)> +<!ELEMENT GuardedString (#PCDATA)> + +<!--=======================================================--> +<!--= =--> +<!--= APIConfiguration =--> +<!--= =--> +<!--=======================================================--> + +<!ELEMENT ObjectPoolConfiguration EMPTY> +<!ATTLIST ObjectPoolConfiguration + maxObjects CDATA #IMPLIED + maxIdle CDATA #IMPLIED + maxWait CDATA #IMPLIED + minEvictableIdleTimeMillis CDATA #IMPLIED + minIdle CDATA #IMPLIED +> + +<!ELEMENT ResultsHandlerConfiguration EMPTY> +<!ATTLIST ResultsHandlerConfiguration + enableNormalizingResultsHandler CDATA #IMPLIED + enableFilteredResultsHandler CDATA #IMPLIED + enableCaseInsensitiveFilter CDATA #IMPLIED + enableAttributesToGetSearchResultsHandler CDATA #IMPLIED +> + +<!ELEMENT ConfigurationProperty (value,operations)> +<!ATTLIST ConfigurationProperty + order CDATA #IMPLIED + confidential CDATA #IMPLIED + required CDATA #IMPLIED + name CDATA #REQUIRED + helpMessageKey CDATA #REQUIRED + displayMessageKey CDATA #REQUIRED + groupMessageKey CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT value (%xmlObject;)> +<!ELEMENT operations (Class)*> +<!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> + +<!ELEMENT APIConfiguration (connectorPoolConfiguration,resultsHandlerConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations)> +<!ATTLIST APIConfiguration + connectorPoolingSupported CDATA #REQUIRED + producerBufferSize CDATA #REQUIRED +> +<!ELEMENT connectorPoolConfiguration ((ObjectPoolConfiguration))> +<!ELEMENT resultsHandlerConfiguration ((ResultsHandlerConfiguration))> +<!ELEMENT timeoutMap (Map)> +<!ELEMENT SupportedOperations ((Class)*)> +<!ELEMENT ConnectorMessages (catalogs)> +<!ELEMENT catalogs (Map)> +<!ELEMENT ConnectorKey EMPTY> +<!ATTLIST ConnectorKey + bundleName CDATA #REQUIRED + bundleVersion CDATA #REQUIRED + connectorName CDATA #REQUIRED +> +<!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> +<!ATTLIST ConnectorInfo + connectorDisplayNameKey CDATA #REQUIRED + connectorCategoryKey CDATA #REQUIRED +> + +<!--=======================================================--> +<!--= =--> +<!--= Common Objects =--> +<!--= =--> +<!--=======================================================--> +<!ELEMENT Attribute (Values)?> +<!ELEMENT Values ((%xmlObject;)*)> +<!ATTLIST Attribute + name CDATA #REQUIRED +> + +<!ELEMENT Uid (#PCDATA)> +<!ATTLIST Uid + uid CDATA #IMPLIED + revision CDATA #IMPLIED +> +<!ELEMENT Name (#PCDATA)> + + + +<!ELEMENT UpdateApiOpType (#PCDATA)> + + +<!ELEMENT AlreadyExistsException EMPTY> +<!ATTLIST AlreadyExistsException + message CDATA #IMPLIED +> +<!ELEMENT ConfigurationException EMPTY> +<!ATTLIST ConfigurationException + message CDATA #IMPLIED +> +<!ELEMENT ConnectionBrokenException EMPTY> +<!ATTLIST ConnectionBrokenException + message CDATA #IMPLIED +> +<!ELEMENT ConnectionFailedException EMPTY> +<!ATTLIST ConnectionFailedException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorIOException EMPTY> +<!ATTLIST ConnectorIOException + message CDATA #IMPLIED +> +<!ELEMENT InvalidPasswordException EMPTY> +<!ATTLIST InvalidPasswordException + message CDATA #IMPLIED +> +<!ELEMENT PasswordExpiredException (Uid?)> +<!ATTLIST PasswordExpiredException + message CDATA #IMPLIED +> +<!ELEMENT UnknownUidException EMPTY> +<!ATTLIST UnknownUidException + message CDATA #IMPLIED +> +<!ELEMENT InvalidCredentialException EMPTY> +<!ATTLIST InvalidCredentialException + message CDATA #IMPLIED +> +<!ELEMENT PermissionDeniedException EMPTY> +<!ATTLIST PermissionDeniedException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorSecurityException EMPTY> +<!ATTLIST ConnectorSecurityException + message CDATA #IMPLIED +> +<!ELEMENT OperationTimeoutException EMPTY> +<!ATTLIST OperationTimeoutException + message CDATA #IMPLIED +> +<!ELEMENT InvalidAttributeValueException EMPTY> +<!ATTLIST InvalidAttributeValueException + message CDATA #IMPLIED +> +<!ELEMENT PreconditionFailedException EMPTY> +<!ATTLIST PreconditionFailedException + message CDATA #IMPLIED +> +<!ELEMENT PreconditionRequiredException EMPTY> +<!ATTLIST PreconditionRequiredException + message CDATA #IMPLIED +> +<!ELEMENT RetryableException EMPTY> +<!ATTLIST RetryableException + message CDATA #IMPLIED +> +<!ELEMENT RemoteWrappedException (RemoteWrappedException?)> +<!ATTLIST RemoteWrappedException + class CDATA #IMPLIED + message CDATA #IMPLIED + stackTrace CDATA #IMPLIED +> +<!ELEMENT ConnectorException EMPTY> +<!ATTLIST ConnectorException + message CDATA #IMPLIED +> +<!ELEMENT IllegalArgumentException EMPTY> +<!ATTLIST IllegalArgumentException + message CDATA #IMPLIED +> +<!ELEMENT RuntimeException EMPTY> +<!ATTLIST RuntimeException + message CDATA #IMPLIED +> +<!ELEMENT Exception EMPTY> +<!ATTLIST Exception + message CDATA #IMPLIED +> +<!ELEMENT Throwable EMPTY> +<!ATTLIST Throwable + message CDATA #IMPLIED +> +<!ELEMENT AttributeInfo (AttributeInfoFlag*)> +<!ATTLIST AttributeInfo + name CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT AttributeInfoFlag EMPTY> +<!ATTLIST AttributeInfoFlag + value ( REQUIRED | MULTIVALUED | NOT_CREATABLE | NOT_UPDATEABLE | NOT_READABLE | NOT_RETURNED_BY_DEFAULT ) #REQUIRED +> +<!ELEMENT ConnectorObject (ObjectClass,Attributes)> +<!ELEMENT Attributes ((%attributeTypes;)*)> + +<!ELEMENT ObjectClass EMPTY> +<!ATTLIST ObjectClass + type CDATA #REQUIRED +> + +<!ELEMENT ObjectClassInfo (AttributeInfos)> +<!ATTLIST ObjectClassInfo + type CDATA #REQUIRED + container CDATA #IMPLIED +> +<!ELEMENT AttributeInfos ((AttributeInfo)*)> + +<!ELEMENT Schema (ObjectClassInfos,OperationOptionInfos,objectClassesByOperation,optionsByOperation)> +<!ELEMENT ObjectClassInfos ((ObjectClassInfo)*)> +<!ELEMENT OperationOptionInfos ((OperationOptionInfo)*)> +<!ELEMENT objectClassesByOperation (Map)> +<!ELEMENT optionsByOperation (Map)> + +<!ELEMENT scriptText (#PCDATA)> +<!ELEMENT scriptArguments (Map)> + +<!ELEMENT Script (scriptText)> +<!ATTLIST Script + scriptLanguage CDATA #REQUIRED +> + +<!ELEMENT ScriptContext (scriptArguments,scriptText)> +<!ATTLIST ScriptContext + scriptLanguage CDATA #REQUIRED +> + +<!ELEMENT OperationOptions (options)> +<!ELEMENT options (Map)> +<!ELEMENT OperationOptionInfo EMPTY> +<!ATTLIST OperationOptionInfo + name CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT SyncDeltaType EMPTY> +<!ATTLIST SyncDeltaType + value ( CREATE | UPDATE | CREATE_OR_UPDATE | DELETE ) #REQUIRED +> +<!ELEMENT SyncToken (value)> +<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,PreviousUid?,ObjectClass?,Uid,ConnectorObject?)> + +<!ELEMENT PreviousUid (#PCDATA)> +<!ATTLIST PreviousUid + uid CDATA #IMPLIED + revision CDATA #IMPLIED +> + +<!ELEMENT QualifiedUid (ObjectClass,Uid)> + +<!ELEMENT CountPolicy EMPTY> +<!ATTLIST CountPolicy + value ( NONE | ESTIMATE | EXACT ) #REQUIRED +> +<!ELEMENT SearchResult (CountPolicy?)> +<!ATTLIST SearchResult + pagedResultsCookie CDATA #REQUIRED + remainingPagedResults CDATA #REQUIRED + totalPagedResults CDATA #REQUIRED +> + +<!--=======================================================--> +<!--= =--> +<!--= Filters =--> +<!--= =--> +<!--=======================================================--> + + +<!ELEMENT attribute (%attributeTypes;)> +<!ELEMENT AndFilter ((%filterTypes;),(%filterTypes;))> +<!ELEMENT ContainsFilter (attribute)> +<!ELEMENT EndsWithFilter (attribute)> +<!ELEMENT EqualsFilter (attribute)> +<!ELEMENT ExtendedMatchFilter (attribute)> +<!ATTLIST ExtendedMatchFilter + operator CDATA #IMPLIED +> +<!ELEMENT GreaterThanFilter (attribute)> +<!ELEMENT GreaterThanOrEqualFilter (attribute)> +<!ELEMENT LessThanFilter (attribute)> +<!ELEMENT LessThanOrEqualFilter (attribute)> +<!ELEMENT NotFilter (%filterTypes;)> +<!ELEMENT OrFilter ((%filterTypes;),(%filterTypes;))> +<!ELEMENT PresenceFilter (#PCDATA)> +<!ATTLIST PresenceFilter + name CDATA #IMPLIED +> +<!ELEMENT StartsWithFilter (attribute)> +<!ELEMENT ContainsAllValuesFilter (attribute)> + +<!--=======================================================--> +<!--= =--> +<!--= Messages =--> +<!--= =--> +<!--=======================================================--> +<!ELEMENT HelloRequest EMPTY> +<!ATTLIST HelloRequest + infoLevel CDATA #REQUIRED +> +<!ELEMENT serverInfoMap (Map)> +<!ELEMENT ConnectorKeys ((ConnectorKey)*)> +<!ELEMENT ConnectorInfos ((ConnectorInfo)*)> +<!ELEMENT exception (%exceptionTypes;)> +<!ELEMENT HelloResponse (exception,serverInfoMap,ConnectorInfos,ConnectorKeys)> +<!ELEMENT OperationRequest (ConnectorKey,Arguments)> +<!ATTLIST OperationRequest + operation CDATA #REQUIRED + operationMethodName CDATA #REQUIRED + connectorFacadeKey CDATA #REQUIRED +> +<!ELEMENT Arguments ((%xmlObject;)*)> +<!ELEMENT OperationResponseEnd EMPTY> +<!ELEMENT OperationResponsePart (exception,result)> +<!ELEMENT result ((%xmlObject;)*)> +<!ELEMENT OperationRequestMoreData EMPTY> +<!ELEMENT OperationRequestStopData EMPTY> +<!ELEMENT OperationResponsePause EMPTY> +<!ELEMENT EchoMessage (value,objectXml?)> +<!ELEMENT objectXml (#PCDATA)> + + + Test Framework Value + + + Account + + + Person + + + Group + + + Organization + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/Security.cs b/dotnet/framework/FrameworkInternal/Security.cs new file mode 100644 index 00000000..e16a75bc --- /dev/null +++ b/dotnet/framework/FrameworkInternal/Security.cs @@ -0,0 +1,136 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System.Security.Cryptography; + +namespace Org.IdentityConnectors.Common.Security.Impl +{ + public class EncryptorFactoryImpl : EncryptorFactory + { + private readonly Encryptor _defaultEncryptor; + + public EncryptorFactoryImpl() + { + _defaultEncryptor = new EncryptorImpl(); + } + + public override Encryptor GetDefaultEncryptor() + { + return _defaultEncryptor; + } + } + + public class EncryptorImpl : Encryptor + { + private readonly static byte[] _defaultKeyBytes = + { + (byte) 0x23,(byte) 0x65,(byte) 0x87,(byte) 0x22, + (byte) 0x59,(byte) 0x78,(byte) 0x54,(byte) 0x43, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBD, + (byte) 0x34,(byte) 0xA2,(byte) 0x34,(byte) 0x57, + }; + private readonly static byte[] _defaultIvBytes = + { + (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, + (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, + }; + + public EncryptorImpl() + { + } + + public UnmanagedArray Decrypt(byte[] bytes) + { + using (SymmetricAlgorithm algo = Aes.Create()) + { + algo.Padding = PaddingMode.PKCS7; + algo.Mode = CipherMode.CBC; + algo.Key = _defaultKeyBytes; + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateDecryptor()) + { + return Decrypt2(bytes, transform); + } + } + } + + private unsafe UnmanagedArray Decrypt2(byte[] bytes, ICryptoTransform transform) + { + byte[] managedBytes = transform.TransformFinalBlock(bytes, 0, bytes.Length); + //pin it ASAP. this is a small race condition that is unavoidable due + //to the way the crypto API's return byte[]. + fixed (byte* dummy = managedBytes) + { + try + { + UnmanagedByteArray rv = new UnmanagedByteArray(managedBytes.Length); + for (int i = 0; i < rv.Length; i++) + { + rv[i] = managedBytes[i]; + } + return rv; + } + finally + { + SecurityUtil.Clear(managedBytes); + } + } + } + + public byte[] Encrypt(UnmanagedArray bytes) + { + using (SymmetricAlgorithm algo = Aes.Create()) + { + algo.Padding = PaddingMode.PKCS7; + algo.Mode = CipherMode.CBC; + algo.Key = _defaultKeyBytes; + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateEncryptor()) + { + return Encrypt2(bytes, transform); + } + } + } + + private unsafe byte[] Encrypt2(UnmanagedArray bytes, ICryptoTransform transform) + { + byte[] managedBytes = new byte[bytes.Length]; + fixed (byte* dummy = managedBytes) + { + try + { + SecurityUtil.UnmanagedBytesToManagedBytes(bytes, managedBytes); + byte[] rv = transform.TransformFinalBlock(managedBytes, 0, managedBytes.Length); + return rv; + } + finally + { + SecurityUtil.Clear(managedBytes); + } + } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/Serializer.cs b/dotnet/framework/FrameworkInternal/Serializer.cs new file mode 100755 index 00000000..e09a9500 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/Serializer.cs @@ -0,0 +1,3169 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.IO; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; +using System.Globalization; +namespace Org.IdentityConnectors.Framework.Impl.Serializer +{ + #region Serialization Framework + internal abstract class AbstractObjectSerializationHandler + : ObjectTypeMapperImpl, ObjectSerializationHandler + { + + protected AbstractObjectSerializationHandler(Type handledClass, + String type) + : base(handledClass, type) + { + } + /// + /// Called to serialize the object. + /// + abstract public void Serialize(Object obj, ObjectEncoder encoder); + + /// + /// Called to deserialize the object. + /// + abstract public Object Deserialize(ObjectDecoder decoder); + } + + + internal class EnumSerializationHandler : + AbstractObjectSerializationHandler + { + public EnumSerializationHandler(Type clazz, String name) + : base(clazz, name) + { + } + + public override object Deserialize(ObjectDecoder decoder) + { + String val = decoder.ReadStringField("value", null); + Type enumClass = HandledObjectType; + Object rv = Enum.Parse(enumClass, val); + return rv; + } + + public override void Serialize(object obj, ObjectEncoder encoder) + { + Enum e = (Enum)obj; + encoder.WriteStringField("value", Enum.GetName(e.GetType(), e)); + } + } + + /// + /// Interface to abstract away the difference between deserializing + /// xml and binary + /// + internal interface ObjectDecoder + { + /// + /// Reads an object using the appropriate serializer for that object + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + Object ReadObjectField(String fieldName, + Type expectedType, + Object dflt); + + /// + /// Reads a bool. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + bool ReadBooleanField(String fieldName, bool dflt); + + /// + /// Reads an int. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + int ReadIntField(String fieldName, int dflt); + + /// + /// Reads a long. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + long ReadLongField(String fieldName, long dflt); + + /// + /// Reads a float. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + float ReadFloatField(String fieldName, float dflt); + + /// + /// Reads a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + /// The value to serialize + double ReadDoubleField(String fieldName, double dflt); + + /// + /// Reads a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + /// The value to serialize + string ReadStringField(String fieldName, string dflt); + + /// + /// Reads a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + /// The value to serialize + Type ReadClassField(String fieldName, Type dflt); + + /// + /// Reads the value in-line. + /// + String ReadStringContents(); + + /// + /// Reads the value in-line. + /// + bool ReadBooleanContents(); + + /// + /// Reads the value in-line. + /// + int ReadIntContents(); + + /// + /// reads the value in-line. + /// + long ReadLongContents(); + + /// + /// Reads the value in-line. + /// + float ReadFloatContents(); + + /// + /// reads the value in-line. + /// + double ReadDoubleContents(); + + /// + /// reads the value in-line. + /// + byte ReadByteContents(); + + /// + /// reads the value in-line. + /// + byte[] ReadByteArrayContents(); + + /// + /// reads the value in-line. + /// + Type ReadClassContents(); + + /// + /// Returns the number of anonymous sub-objects. + /// + int GetNumSubObjects(); + + /// + /// Reads a sub-object + /// + Object ReadObjectContents(int index); + } + + /// + /// Interface to abstract away the difference between serializing + /// xml and binary + /// + internal interface ObjectEncoder + { + /// + /// Writes an object using the appropriate serializer for that object + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The object to serialize + void WriteObjectField(String fieldName, Object obj, bool inline); + + /// + /// Writes a boolean. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteBooleanField(String fieldName, bool v); + + /// + /// Writes an int. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteIntField(String fieldName, int v); + + /// + /// Writes a long. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteLongField(String fieldName, long v); + + /// + /// Writes a float. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteFloatField(String fieldName, float v); + + /// + /// Writes a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteDoubleField(String fieldName, double v); + + /// + /// Writes a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteStringField(String fieldName, string v); + + /// + /// Writes a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteClassField(String fieldName, Type v); + + /// + /// Writes the value in-line. + /// + void WriteStringContents(String str); + + /// + /// Writes the value in-line. + /// + void WriteBooleanContents(bool v); + + /// + /// Writes the value in-line. + /// + void WriteIntContents(int v); + + /// + /// Writes the value in-line. + /// + void WriteLongContents(long v); + + /// + /// Writes the value in-line. + /// + void WriteFloatContents(float v); + + /// + /// Writes the value in-line. + /// + void WriteDoubleContents(double v); + + /// + /// Writes the value in-line. + /// + void WriteByteContents(byte v); + + /// + /// Special case for byte [] that uses base64 encoding for XML + /// + void WriteByteArrayContents(byte[] v); + + /// + /// Writes the value in-line. + /// + void WriteClassContents(Type v); + + /// + /// Writes a sub-object + /// + void WriteObjectContents(object o); + } + + /// + /// Interface to be implemented to handle the serialization/ + /// deserialization of an object. + /// + internal interface ObjectSerializationHandler : ObjectTypeMapper + { + /// + /// Called to serialize the object. + /// + void Serialize(Object obj, ObjectEncoder encoder); + + /// + /// Called to deserialize the object. + /// + Object Deserialize(ObjectDecoder decoder); + } + + internal static class ObjectSerializerRegistry + { + private static readonly IList HANDLERS = + new List(); + + private static readonly IDictionary + HANDLERS_BY_SERIAL_TYPE = new Dictionary(); + + static ObjectSerializerRegistry() + { + CollectionUtil.AddAll(HANDLERS, Primitives.HANDLERS); + CollectionUtil.AddAll(HANDLERS, OperationMappings.MAPPINGS); + CollectionUtil.AddAll(HANDLERS, APIConfigurationHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS, FilterHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS, CommonObjectHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS, MessageHandlers.HANDLERS); + //object is special - just map the type, but don't actually + //serialize + HANDLERS.Add(new ObjectTypeMapperImpl(typeof(object), "Object")); + + foreach (ObjectTypeMapper handler in HANDLERS) + { + if (HANDLERS_BY_SERIAL_TYPE.ContainsKey(handler.HandledSerialType)) + { + throw new Exception("More than one handler of the" + + " same type: " + handler.HandledSerialType); + } + HANDLERS_BY_SERIAL_TYPE[handler.HandledSerialType] = + handler; + } + } + + /// + /// Mapping by class. + /// + /// + /// Dynamically built since actual class may be + /// a subclass. + /// + private static readonly IDictionary + HANDLERS_BY_OBJECT_TYPE = + new Dictionary(); + + public static ObjectTypeMapper GetMapperBySerialType(String type) + { + return CollectionUtil.GetValue(HANDLERS_BY_SERIAL_TYPE, type, null); + } + + public static ObjectTypeMapper GetMapperByObjectType(Type clazz) + { + lock (HANDLERS_BY_OBJECT_TYPE) + { + ObjectTypeMapper rv = + CollectionUtil.GetValue(HANDLERS_BY_OBJECT_TYPE, clazz, null); + if (rv == null) + { + foreach (ObjectTypeMapper handler in HANDLERS) + { + IDelegatingObjectTypeMapper delegator = + handler as IDelegatingObjectTypeMapper; + ObjectTypeMapper effectiveHandler; + if (delegator != null) + { + effectiveHandler = delegator.FindMapperDelegate(clazz); + } + else + { + effectiveHandler = handler; + } + if (effectiveHandler != null) + { + Type handledClass = + effectiveHandler.HandledObjectType; + if (effectiveHandler.MatchSubclasses) + { + if (handledClass.IsAssignableFrom(clazz)) + { + rv = effectiveHandler; + break; + } + } + else if (handledClass.Equals(clazz)) + { + rv = effectiveHandler; + break; + } + } + } + HANDLERS_BY_OBJECT_TYPE[clazz] = rv; + } + return rv; + } + } + public static ObjectSerializationHandler GetHandlerBySerialType(String type) + { + ObjectTypeMapper rv = GetMapperBySerialType(type); + return rv as ObjectSerializationHandler; + } + + public static ObjectSerializationHandler GetHandlerByObjectType(Type clazz) + { + ObjectTypeMapper rv = GetMapperByObjectType(clazz); + return rv as ObjectSerializationHandler; + } + } + + /// + /// ObjectTypeMappers can implement this interface as well. If + /// they do, they can handle specific generic types as well. + /// + internal interface IDelegatingObjectTypeMapper + { + ObjectTypeMapper FindMapperDelegate(Type type); + } + + /// + /// Interface to be implemented to handle the serialization/ + /// deserialization of an object. + /// + internal interface ObjectTypeMapper + { + /// + /// Returns the type of object being serialized. + /// + /// + /// This is + /// an abstract type name that is intended to be language + /// neutral. + /// + String HandledSerialType { get; } + + /// + /// Returns the java class handled by this handler. + /// + Type HandledObjectType { get; } + + /// + /// Should we match subclasses of the given class or only + /// the exact class? + /// + bool MatchSubclasses { get; } + } + + internal class ObjectTypeMapperImpl : ObjectTypeMapper + { + private Type _handledClass; + private String _handledType; + + public ObjectTypeMapperImpl(Type handledClass, String handledType) + { + _handledClass = handledClass; + _handledType = handledType; + } + + public Type HandledObjectType + { + get + { + return _handledClass; + } + } + + public String HandledSerialType + { + get + { + return _handledType; + } + } + + public virtual bool MatchSubclasses + { + get + { + return false; + } + } + } + + internal abstract class AbstractExceptionHandler : AbstractObjectSerializationHandler + where T : Exception + { + protected AbstractExceptionHandler(String typeName) + : base(typeof(T), typeName) + { + } + + public override Object Deserialize(ObjectDecoder decoder) + { + String message = decoder.ReadStringField("message", null); + return CreateException(message); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Exception val = (Exception)obj; + encoder.WriteStringField("message", val.Message); + } + public override bool MatchSubclasses + { + get { return true; } + } + protected abstract T CreateException(String message); + } + + + #endregion + + #region Primitives + + internal static class Primitives + { + public static readonly IList HANDLERS = + new List(); + static Primitives() + { + HANDLERS.Add(new BooleanHandler(typeof(bool?), "Boolean")); + HANDLERS.Add(new BooleanHandler(typeof(bool), "boolean")); + HANDLERS.Add(new ByteHandler(typeof(byte?), "Byte")); + HANDLERS.Add(new ByteHandler(typeof(byte), "byte")); + HANDLERS.Add(new CharacterHandler(typeof(char?), "Character")); + HANDLERS.Add(new CharacterHandler(typeof(char), "char")); + HANDLERS.Add(new IntegerHandler(typeof(int?), "Integer")); + HANDLERS.Add(new IntegerHandler(typeof(int), "int")); + HANDLERS.Add(new LongHandler(typeof(long?), "Long")); + HANDLERS.Add(new LongHandler(typeof(long), "long")); + HANDLERS.Add(new FloatHandler(typeof(float?), "Float")); + HANDLERS.Add(new FloatHandler(typeof(float), "float")); + HANDLERS.Add(new DoubleHandler(typeof(double?), "Double")); + HANDLERS.Add(new DoubleHandler(typeof(double), "double")); + HANDLERS.Add(new StringHandler()); + HANDLERS.Add(new URIHandler()); + HANDLERS.Add(new FileHandler()); + HANDLERS.Add(new BigIntegerHandler()); + HANDLERS.Add(new BigDecimalHandler()); + HANDLERS.Add(new ByteArrayHandler()); + HANDLERS.Add(new ClassHandler()); + HANDLERS.Add(new MapEntryHandler()); + HANDLERS.Add(new MapHandler()); + HANDLERS.Add(new ListHandler()); + HANDLERS.Add(new SetHandler()); + HANDLERS.Add(new LocaleHandler()); + HANDLERS.Add(new GuardedByteArrayHandler()); + HANDLERS.Add(new GuardedStringHandler()); + } + private class BooleanHandler : AbstractObjectSerializationHandler + { + public BooleanHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + bool val = decoder.ReadBooleanContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + bool val = (bool)obj; + encoder.WriteBooleanContents(val); + } + } + private class CharacterHandler : AbstractObjectSerializationHandler + { + public CharacterHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String val = decoder.ReadStringContents(); + return val.ToCharArray()[0]; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + char val = (char)obj; + encoder.WriteStringContents(val.ToString()); + } + } + private class IntegerHandler : AbstractObjectSerializationHandler + { + public IntegerHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + int val = decoder.ReadIntContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + int val = (int)obj; + encoder.WriteIntContents(val); + } + } + private class LongHandler : AbstractObjectSerializationHandler + { + public LongHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + long val = decoder.ReadLongContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + long val = (long)obj; + encoder.WriteLongContents(val); + } + } + private class FloatHandler : AbstractObjectSerializationHandler + { + public FloatHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + float val = decoder.ReadFloatContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + float val = (float)obj; + encoder.WriteFloatContents(val); + } + } + private class DoubleHandler : AbstractObjectSerializationHandler + { + public DoubleHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + double val = decoder.ReadDoubleContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + double val = (double)obj; + encoder.WriteDoubleContents(val); + } + } + private class StringHandler : AbstractObjectSerializationHandler + { + public StringHandler() + : base(typeof(string), "String") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string val = decoder.ReadStringContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + string val = (string)obj; + encoder.WriteStringContents(val); + } + } + private class URIHandler : AbstractObjectSerializationHandler + { + public URIHandler() + : base(typeof(Uri), "URI") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string val = decoder.ReadStringContents(); + return new Uri(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Uri val = (Uri)obj; + encoder.WriteStringContents(val.ToString()); + } + } + private class FileHandler : AbstractObjectSerializationHandler + { + public FileHandler() + : base(typeof(FileName), "File") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string val = decoder.ReadStringContents(); + return new FileName(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + FileName val = (FileName)obj; + encoder.WriteStringContents(val.Path); + } + } + private class BigDecimalHandler : AbstractObjectSerializationHandler + { + public BigDecimalHandler() + : base(typeof(BigDecimal), "BigDecimal") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + BigInteger unscaled = + new BigInteger(decoder.ReadStringField("unscaled", null)); + int scale = decoder.ReadIntField("scale", 0); + return new BigDecimal(unscaled, scale); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + BigDecimal val = (BigDecimal)obj; + encoder.WriteStringField("unscaled", val.UnscaledValue.Value); + encoder.WriteIntField("scale", val.Scale); + } + } + private class BigIntegerHandler : AbstractObjectSerializationHandler + { + public BigIntegerHandler() + : base(typeof(BigInteger), "BigInteger") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string val = decoder.ReadStringContents(); + return new BigInteger(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + BigInteger val = (BigInteger)obj; + encoder.WriteStringContents(val.Value); + } + } + private class ByteHandler : AbstractObjectSerializationHandler + { + public ByteHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return decoder.ReadByteContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + byte val = (byte)obj; + encoder.WriteByteContents(val); + } + } + private class ByteArrayHandler : AbstractObjectSerializationHandler + { + public ByteArrayHandler() + : base(typeof(byte[]), "ByteArray") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return decoder.ReadByteArrayContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + byte[] val = (byte[])obj; + encoder.WriteByteArrayContents(val); + } + } + private class ClassHandler : AbstractObjectSerializationHandler + { + public ClassHandler() + : base(typeof(Type), "Class") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return decoder.ReadClassContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Type val = (Type)obj; + encoder.WriteClassContents(val); + } + + /// + /// In C#, the actual Type of Type is RuntimeType + /// + public override bool MatchSubclasses + { + get { return true; } + } + } + + private class MapEntry + { + internal object key; + internal object val; + public MapEntry(object key, object val) + { + this.key = key; + this.val = val; + } + } + private class MapEntryHandler : + AbstractObjectSerializationHandler + { + + public MapEntryHandler() + : base(typeof(MapEntry), "MapEntry") + { + } + public override Object Deserialize(ObjectDecoder decoder) + { + Object key = decoder.ReadObjectContents(0); + Object val = decoder.ReadObjectContents(1); + return new MapEntry(key, val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + MapEntry entry = (MapEntry)obj; + encoder.WriteObjectContents(entry.key); + encoder.WriteObjectContents(entry.val); + } + } + private class MapHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper + { + public MapHandler() + : base(typeof(IDictionary), "Map") + { + } + public ObjectTypeMapper FindMapperDelegate(Type type) + { + //get the IDictionary interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(IDictionary<,>), type); + if (interfaceType != null) + { + Type[] keyAndValue = interfaceType.GetGenericArguments(); + if (keyAndValue.Length != 2) + { + throw new Exception("Cannot serialize type: " + type); + } + Type mapHandlerRawType = typeof(MapHandler<,>); + Type mapHandlerType = + mapHandlerRawType.MakeGenericType(keyAndValue); + return (ObjectTypeMapper)Activator.CreateInstance(mapHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) + { + bool caseInsensitive = + decoder.ReadBooleanField("caseInsensitive", false); + if (caseInsensitive) + { + IDictionary rv = + CollectionUtil.NewCaseInsensitiveDictionary(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); + rv["" + entry.key] = entry.val; + } + return rv; + } + else + { + IDictionary rv = + new Dictionary(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); + rv[entry.key] = entry.val; + } + return rv; + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + IDictionary map = (IDictionary)obj; + if (CollectionUtil.IsCaseInsensitiveDictionary(map)) + { + encoder.WriteBooleanField("caseInsensitive", true); + } + else if (map is SortedDictionary) + { + throw new Exception("Serialization of SortedDictionary not supported"); + } + foreach (KeyValuePair entry in map) + { + MapEntry myEntry = new MapEntry(entry.Key, entry.Value); + encoder.WriteObjectContents(myEntry); + } + } + + public override bool MatchSubclasses + { + get { return true; } + } + } + private class ListHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper + { + + public ListHandler() + : base(typeof(IList), "List") + { + } + public ObjectTypeMapper FindMapperDelegate(Type type) + { + //in C#, arrays implement IList + if (type.IsArray) + { + return null; + } + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(IList<>), type); + if (interfaceType != null) + { + Type[] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) + { + throw new Exception("Cannot serialize type: " + type); + } + Type listHandlerRawType = typeof(ListHandler<>); + Type listHandlerType = + listHandlerRawType.MakeGenericType(val); + return (ObjectTypeMapper)Activator.CreateInstance(listHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) + { + IList rv = + new List(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + rv.Add(decoder.ReadObjectContents(i)); + } + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + IList list = (IList)obj; + foreach (T o in list) + { + encoder.WriteObjectContents(o); + } + } + + public override bool MatchSubclasses + { + get { return true; } + } + } + private class SetHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper + { + + public SetHandler() + : base(typeof(ICollection), "Set") + { + } + public ObjectTypeMapper FindMapperDelegate(Type type) + { + //in C#, arrays implement IList + if (type.IsArray) + { + return null; + } + + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(ICollection<>), type); + if (interfaceType != null) + { + Type[] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) + { + throw new Exception("Cannot serialize type: " + type); + } + Type setHandlerRawType = typeof(SetHandler<>); + Type setHandlerType = + setHandlerRawType.MakeGenericType(val); + return (ObjectTypeMapper)Activator.CreateInstance(setHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) + { + bool caseInsensitive = + decoder.ReadBooleanField("caseInsensitive", false); + if (caseInsensitive) + { + ICollection rv = + CollectionUtil.NewCaseInsensitiveSet(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + rv.Add("" + decoder.ReadObjectContents(i)); + } + return rv; + } + else + { + ICollection rv = + new HashSet(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + rv.Add(decoder.ReadObjectContents(i)); + } + return rv; + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ICollection list = (ICollection)obj; + if (CollectionUtil.IsCaseInsensitiveSet(list)) + { + encoder.WriteBooleanField("caseInsensitive", true); + } + foreach (T o in list) + { + encoder.WriteObjectContents(o); + } + } + + public override bool MatchSubclasses + { + get { return true; } + } + } + private class LocaleHandler : AbstractObjectSerializationHandler + { + public LocaleHandler() + : base(typeof(CultureInfo), "Locale") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string language = decoder.ReadStringField("language", ""); + string country = decoder.ReadStringField("country", ""); + string variant = decoder.ReadStringField("variant", ""); + Locale locale = new Locale(language, country, variant); + return locale.ToCultureInfo(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + CultureInfo cultureInfo = (CultureInfo)obj; + Locale locale = Locale.FindLocale(cultureInfo); + encoder.WriteStringField("language", locale.Language); + encoder.WriteStringField("country", locale.Country); + encoder.WriteStringField("variant", locale.Variant); + + } + } + + private class GuardedByteArrayHandler : AbstractObjectSerializationHandler + { + public GuardedByteArrayHandler() + : base(typeof(GuardedByteArray), "GuardedByteArray") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + byte[] encryptedBytes = null; + UnmanagedArray clearBytes = null; + try + { + encryptedBytes = decoder.ReadByteArrayContents(); + clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); + GuardedByteArray rv = new GuardedByteArray(); + for (int i = 0; i < clearBytes.Length; i++) + { + rv.AppendByte(clearBytes[i]); + } + return rv; + } + finally + { + if (clearBytes != null) + { + clearBytes.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + GuardedByteArray str = (GuardedByteArray)obj; + str.Access(new GuardedByteArray.LambdaAccessor( + clearBytes => + { + byte[] encryptedBytes = null; + try + { + encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); + encoder.WriteByteArrayContents(encryptedBytes); + } + finally + { + SecurityUtil.Clear(encryptedBytes); + } + })); + } + } + + private class GuardedStringHandler : AbstractObjectSerializationHandler + { + public GuardedStringHandler() + : base(typeof(GuardedString), "GuardedString") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + byte[] encryptedBytes = null; + UnmanagedArray clearBytes = null; + UnmanagedArray clearChars = null; + try + { + encryptedBytes = decoder.ReadByteArrayContents(); + clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); + clearChars = SecurityUtil.BytesToChars(clearBytes); + GuardedString rv = new GuardedString(); + for (int i = 0; i < clearChars.Length; i++) + { + rv.AppendChar(clearChars[i]); + } + return rv; + } + finally + { + if (clearBytes != null) + { + clearBytes.Dispose(); + } + if (clearChars != null) + { + clearChars.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + GuardedString str = (GuardedString)obj; + str.Access(new GuardedString.LambdaAccessor( + clearChars => + { + UnmanagedArray clearBytes = null; + byte[] encryptedBytes = null; + try + { + clearBytes = SecurityUtil.CharsToBytes(clearChars); + encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); + encoder.WriteByteArrayContents(encryptedBytes); + } + finally + { + if (clearBytes != null) + { + clearBytes.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + })); + } + } + } + #endregion + + #region APIConfigurationHandlers + internal static class APIConfigurationHandlers + { + public static readonly IList HANDLERS = + new List(); + static APIConfigurationHandlers() + { + HANDLERS.Add(new ConnectionPoolingConfigurationHandler()); + HANDLERS.Add(new ResultsHandlerConfigurationHandler()); + HANDLERS.Add(new ConfigurationPropertyHandler()); + HANDLERS.Add(new ConfigurationPropertiesHandler()); + HANDLERS.Add(new APIConfigurationHandler()); + HANDLERS.Add(new ConnectorMessagesHandler()); + HANDLERS.Add(new ConnectorKeyHandler()); + HANDLERS.Add(new ConnectorInfoHandler()); + } + private class ConnectionPoolingConfigurationHandler : AbstractObjectSerializationHandler + { + public ConnectionPoolingConfigurationHandler() + : base(typeof(ObjectPoolConfiguration), "ObjectPoolConfiguration") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ObjectPoolConfiguration rv = + new ObjectPoolConfiguration(); + rv.MaxObjects = (decoder.ReadIntField("maxObjects", rv.MaxObjects)); + rv.MaxIdle = (decoder.ReadIntField("maxIdle", rv.MaxIdle)); + rv.MaxWait = (decoder.ReadLongField("maxWait", rv.MaxWait)); + rv.MinEvictableIdleTimeMillis = ( + decoder.ReadLongField("minEvictableIdleTimeMillis", rv.MinEvictableIdleTimeMillis)); + rv.MinIdle = ( + decoder.ReadIntField("minIdle", rv.MinIdle)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ObjectPoolConfiguration val = + (ObjectPoolConfiguration)obj; + encoder.WriteIntField("maxObjects", + val.MaxObjects); + encoder.WriteIntField("maxIdle", + val.MaxIdle); + encoder.WriteLongField("maxWait", + val.MaxWait); + encoder.WriteLongField("minEvictableIdleTimeMillis", + val.MinEvictableIdleTimeMillis); + encoder.WriteIntField("minIdle", + val.MinIdle); + } + } + private class ResultsHandlerConfigurationHandler : AbstractObjectSerializationHandler + { + public ResultsHandlerConfigurationHandler() + : base(typeof(ResultsHandlerConfiguration), "ResultsHandlerConfiguration") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ResultsHandlerConfiguration rv = + new ResultsHandlerConfiguration(); + rv.EnableNormalizingResultsHandler = (decoder.ReadBooleanField("enableNormalizingResultsHandler", rv.EnableNormalizingResultsHandler)); + rv.EnableFilteredResultsHandler = (decoder.ReadBooleanField("enableFilteredResultsHandler", rv.EnableFilteredResultsHandler)); + rv.EnableCaseInsensitiveFilter = (decoder.ReadBooleanField("enableCaseInsensitiveFilter", rv.EnableCaseInsensitiveFilter)); + rv.EnableAttributesToGetSearchResultsHandler = ( + decoder.ReadBooleanField("enableAttributesToGetSearchResultsHandler", rv.EnableAttributesToGetSearchResultsHandler)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ResultsHandlerConfiguration val = + (ResultsHandlerConfiguration)obj; + encoder.WriteBooleanField("enableNormalizingResultsHandler", + val.EnableNormalizingResultsHandler); + encoder.WriteBooleanField("enableFilteredResultsHandler", + val.EnableFilteredResultsHandler); + encoder.WriteBooleanField("enableCaseInsensitiveFilter", + val.EnableCaseInsensitiveFilter); + encoder.WriteBooleanField("enableAttributesToGetSearchResultsHandler", + val.EnableAttributesToGetSearchResultsHandler); + } + } + private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler + { + public ConfigurationPropertyHandler() + : base(typeof(ConfigurationPropertyImpl), "ConfigurationProperty") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ConfigurationPropertyImpl rv = new ConfigurationPropertyImpl(); + rv.Order = (decoder.ReadIntField("order", 0)); + rv.IsConfidential = (decoder.ReadBooleanField("confidential", false)); + rv.IsRequired = decoder.ReadBooleanField("required", false); + rv.Name = (decoder.ReadStringField("name", null)); + rv.HelpMessageKey = ( + decoder.ReadStringField("helpMessageKey", null)); + rv.DisplayMessageKey = ( + decoder.ReadStringField("displayMessageKey", null)); + rv.GroupMessageKey = ( + decoder.ReadStringField("groupMessageKey", null)); + rv.ValueType = ( + decoder.ReadClassField("type", null)); + rv.Value = ( + decoder.ReadObjectField("value", null, null)); + ICollection operationsObj = + (ICollection)decoder.ReadObjectField("operations", typeof(ICollection), null); + ICollection> operations = + new HashSet>(); + foreach (object o in operationsObj) + { + Type type = (Type)o; + operations.Add(SafeType.ForRawType(type)); + } + rv.Operations = operations; + + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ConfigurationPropertyImpl val = + (ConfigurationPropertyImpl)obj; + encoder.WriteIntField("order", + val.Order); + encoder.WriteBooleanField("confidential", + val.IsConfidential); + encoder.WriteBooleanField("required", + val.IsRequired); + encoder.WriteStringField("name", + val.Name); + encoder.WriteStringField("helpMessageKey", + val.HelpMessageKey); + encoder.WriteStringField("displayMessageKey", + val.DisplayMessageKey); + encoder.WriteStringField("groupMessageKey", + val.GroupMessageKey); + encoder.WriteClassField("type", + val.ValueType); + encoder.WriteObjectField("value", + val.Value, + false); + ICollection operationsObj = + new HashSet(); + foreach (SafeType op in val.Operations) + { + operationsObj.Add(op.RawType); + } + encoder.WriteObjectField("operations", operationsObj, true); + } + } + private class ConfigurationPropertiesHandler : AbstractObjectSerializationHandler + { + public ConfigurationPropertiesHandler() + : base(typeof(ConfigurationPropertiesImpl), "ConfigurationProperties") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ConfigurationPropertiesImpl rv = + new ConfigurationPropertiesImpl(); + IList props = + new List + (); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + ConfigurationPropertyImpl prop = + (ConfigurationPropertyImpl)decoder.ReadObjectContents(i); + props.Add(prop); + } + rv.Properties = props; + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ConfigurationPropertiesImpl val = + (ConfigurationPropertiesImpl)obj; + IList props = + val.Properties; + foreach (ConfigurationPropertyImpl prop in props) + { + encoder.WriteObjectContents(prop); + } + } + } + private class APIConfigurationHandler : AbstractObjectSerializationHandler + { + public APIConfigurationHandler() + : base(typeof(APIConfigurationImpl), "APIConfiguration") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + APIConfigurationImpl rv = new APIConfigurationImpl(); + rv.IsConnectorPoolingSupported = ( + decoder.ReadBooleanField("connectorPoolingSupported", false)); + rv.ConnectorPoolConfiguration = ( + (ObjectPoolConfiguration) + decoder.ReadObjectField("connectorPoolConfiguration", null, null)); + rv.ResultsHandlerConfiguration = ( + (ResultsHandlerConfiguration) + decoder.ReadObjectField("resultsHandlerConfiguration", null, null)); + rv.ConfigurationProperties = ((ConfigurationPropertiesImpl) + decoder.ReadObjectField("ConfigurationProperties", typeof(ConfigurationPropertiesImpl), null)); + IDictionary timeoutMapObj = + (IDictionary)decoder.ReadObjectField("timeoutMap", null, null); + IDictionary, int> timeoutMap = + new Dictionary, int>(); + foreach (KeyValuePair entry in timeoutMapObj) + { + Type type = (Type)entry.Key; + int val = (int)entry.Value; + timeoutMap[SafeType.ForRawType(type)] = val; + } + rv.TimeoutMap = timeoutMap; + ICollection supportedOperationsObj = + (ICollection)decoder.ReadObjectField("SupportedOperations", typeof(ICollection), null); + ICollection> supportedOperations = + new HashSet>(); + foreach (object obj in supportedOperationsObj) + { + Type type = (Type)obj; + supportedOperations.Add(SafeType.ForRawType(type)); + } + rv.SupportedOperations = supportedOperations; + rv.ProducerBufferSize = (decoder.ReadIntField("producerBufferSize", 0)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + APIConfigurationImpl val = + (APIConfigurationImpl)obj; + + ICollection supportedOperations = + new HashSet(); + if (val.SupportedOperations != null) + { + foreach (SafeType op in val.SupportedOperations) + { + supportedOperations.Add(op.RawType); + } + } + IDictionary timeoutMap = + new Dictionary(); + if (val.TimeoutMap != null) + { + foreach (KeyValuePair, int> entry in val.TimeoutMap) + { + timeoutMap[entry.Key.RawType] = entry.Value; + } + } + + encoder.WriteIntField("producerBufferSize", + val.ProducerBufferSize); + encoder.WriteBooleanField("connectorPoolingSupported", + val.IsConnectorPoolingSupported); + encoder.WriteObjectField("connectorPoolConfiguration", + val.ConnectorPoolConfiguration, false); + encoder.WriteObjectField("resultsHandlerConfiguration", + val.ResultsHandlerConfiguration, false); + encoder.WriteObjectField("ConfigurationProperties", + val.ConfigurationProperties, true); + encoder.WriteObjectField("timeoutMap", + timeoutMap, false); + encoder.WriteObjectField("SupportedOperations", + supportedOperations, true); + } + } + private class ConnectorMessagesHandler : AbstractObjectSerializationHandler + { + public ConnectorMessagesHandler() + : base(typeof(ConnectorMessagesImpl), "ConnectorMessages") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); + IDictionary catalogsObj = + (IDictionary)decoder.ReadObjectField("catalogs", null, null); + IDictionary> + catalogs = new Dictionary>(); + foreach (KeyValuePair entry in catalogsObj) + { + CultureInfo key = (CultureInfo)entry.Key; + IDictionary valObj = (IDictionary)entry.Value; + IDictionary val = + CollectionUtil.NewDictionary(valObj); + catalogs[key] = val; + } + rv.Catalogs = (catalogs); + return rv; + + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorMessagesImpl val = + (ConnectorMessagesImpl)obj; + encoder.WriteObjectField("catalogs", + val.Catalogs, false); + } + } + + private class ConnectorKeyHandler : AbstractObjectSerializationHandler + { + public ConnectorKeyHandler() + : base(typeof(ConnectorKey), "ConnectorKey") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String bundleName = + decoder.ReadStringField("bundleName", null); + String bundleVersion = + decoder.ReadStringField("bundleVersion", null); + String connectorName = + decoder.ReadStringField("connectorName", null); + return new ConnectorKey(bundleName, bundleVersion, connectorName); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorKey val = (ConnectorKey)obj; + encoder.WriteStringField("bundleName", + val.BundleName); + encoder.WriteStringField("bundleVersion", + val.BundleVersion); + encoder.WriteStringField("connectorName", + val.ConnectorName); + } + } + + private class ConnectorInfoHandler : AbstractObjectSerializationHandler + { + public ConnectorInfoHandler() + : base(typeof(RemoteConnectorInfoImpl), "ConnectorInfo") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); + rv.ConnectorDisplayNameKey = ( + decoder.ReadStringField("connectorDisplayNameKey", null)); + rv.ConnectorCategoryKey = ( + decoder.ReadStringField("connectorCategoryKey", null)); + rv.ConnectorKey = ((ConnectorKey) + decoder.ReadObjectField("ConnectorKey", typeof(ConnectorKey), null)); + rv.Messages = ((ConnectorMessagesImpl) + decoder.ReadObjectField("ConnectorMessages", typeof(ConnectorMessagesImpl), null)); + rv.DefaultAPIConfiguration = ((APIConfigurationImpl) + decoder.ReadObjectField("APIConfiguration", typeof(APIConfigurationImpl), null)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + RemoteConnectorInfoImpl val = + (RemoteConnectorInfoImpl)obj; + encoder.WriteStringField("connectorDisplayNameKey", + val.ConnectorDisplayNameKey); + encoder.WriteStringField("connectorCategoryKey", + val.ConnectorCategoryKey); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey, true); + encoder.WriteObjectField("ConnectorMessages", + val.Messages, true); + encoder.WriteObjectField("APIConfiguration", + val.DefaultAPIConfiguration, true); + } + } + + } + #endregion + + #region ObjectSerializerFactoryImpl + public class ObjectSerializerFactoryImpl : ObjectSerializerFactory + { + public override BinaryObjectDeserializer NewBinaryDeserializer(Stream i) + { + return new BinaryObjectDecoder(i); + } + + public override BinaryObjectSerializer NewBinarySerializer(Stream os) + { + return new BinaryObjectEncoder(os); + } + public override XmlObjectSerializer NewXmlSerializer(TextWriter w, + bool includeHeader, + bool multiObject) + { + return new XmlObjectSerializerImpl(w, includeHeader, multiObject); + } + + public override void DeserializeXmlStream(TextReader reader, + XmlObjectResultsHandler handler, + bool validate) + { + XmlObjectParser.parse(reader, handler, validate); + } + + } + #endregion + + #region OperationMappings + internal static class OperationMappings + { + public static readonly IList MAPPINGS = + new List(); + + static OperationMappings() + { + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(AuthenticationApiOp), + "AuthenticationApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ResolveUsernameApiOp), + "ResolveUsernameApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SearchApiOp), + "SearchApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ValidateApiOp), + "ValidateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(CreateApiOp), + "CreateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SchemaApiOp), + "SchemaApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(UpdateApiOp), + "UpdateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(DeleteApiOp), + "DeleteApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(GetApiOp), + "GetApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(TestApiOp), + "TestApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnConnectorApiOp), + "ScriptOnConnectorApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnResourceApiOp), + "ScriptOnResourceApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SyncApiOp), + "SyncApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(IConnectorEventSubscriptionApiOp), + "ConnectorEventSubscriptionApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ISyncEventSubscriptionApiOp), + "SyncEventSubscriptionApiOp")); + } + } + #endregion + + #region CommonObjectHandlers + internal static class CommonObjectHandlers + { + public static readonly IList HANDLERS = + new List(); + static CommonObjectHandlers() + { + HANDLERS.Add(new AlreadyExistsExceptionHandler()); + HANDLERS.Add(new ConfigurationExceptionHandler()); + HANDLERS.Add(new ConnectionBrokenExceptionHandler()); + HANDLERS.Add(new ConnectionFailedExceptionHandler()); + HANDLERS.Add(new ConnectorIOExceptionHandler()); + HANDLERS.Add(new InvalidAttributeValueExceptionHandler()); + HANDLERS.Add(new PasswordExpiredExceptionHandler()); + HANDLERS.Add(new InvalidPasswordExceptionHandler()); + HANDLERS.Add(new UnknownUidExceptionHandler()); + HANDLERS.Add(new InvalidCredentialExceptionHandler()); + HANDLERS.Add(new PermissionDeniedExceptionHandler()); + HANDLERS.Add(new PreconditionFailedExceptionHandler()); + HANDLERS.Add(new PreconditionRequiredExceptionHandler()); + HANDLERS.Add(new RemoteWrappedExceptionHandler()); + HANDLERS.Add(new RetryableExceptionHandler()); + HANDLERS.Add(new ConnectorSecurityExceptionHandler()); + HANDLERS.Add(new OperationTimeoutExceptionHandler()); + HANDLERS.Add(new ConnectorExceptionHandler()); + HANDLERS.Add(new ArgumentExceptionHandler()); + HANDLERS.Add(new RuntimeExceptionHandler()); + HANDLERS.Add(new ExceptionHandler()); + HANDLERS.Add(new ThrowableHandler()); + HANDLERS.Add(new AttributeHandler()); + HANDLERS.Add(new AttributeInfoHandler()); + HANDLERS.Add(new ConnectorObjectHandler()); + HANDLERS.Add(new NameHandler()); + HANDLERS.Add(new ObjectClassHandler()); + HANDLERS.Add(new EnumSerializationHandler(typeof(SearchResult.CountPolicy), + "CountPolicy")); + HANDLERS.Add(new SearchResultHandler()); + HANDLERS.Add(new SortKeyHandler()); + HANDLERS.Add(new ObjectClassInfoHandler()); + HANDLERS.Add(new SchemaHandler()); + HANDLERS.Add(new UidHandler()); + HANDLERS.Add(new ScriptHandler()); + HANDLERS.Add(new ScriptContextHandler()); + HANDLERS.Add(new OperationOptionsHandler()); + HANDLERS.Add(new OperationOptionInfoHandler()); + HANDLERS.Add(new EnumSerializationHandler(typeof(ConnectorAttributeInfo.Flags), + "AttributeInfoFlag")); + HANDLERS.Add(new EnumSerializationHandler(typeof(SyncDeltaType), + "SyncDeltaType")); + HANDLERS.Add(new SyncTokenHandler()); + HANDLERS.Add(new SyncDeltaHandler()); + HANDLERS.Add(new QualifiedUidHandler()); + } + + private class AlreadyExistsExceptionHandler : AbstractExceptionHandler + { + public AlreadyExistsExceptionHandler() + : base("AlreadyExistsException") + { + + } + protected override AlreadyExistsException CreateException(String msg) + { + return new AlreadyExistsException(msg); + } + } + private class ConfigurationExceptionHandler : AbstractExceptionHandler + { + public ConfigurationExceptionHandler() + : base("ConfigurationException") + { + + } + protected override ConfigurationException CreateException(String msg) + { + return new ConfigurationException(msg); + } + } + private class ConnectionBrokenExceptionHandler : AbstractExceptionHandler + { + public ConnectionBrokenExceptionHandler() + : base("ConnectionBrokenException") + { + + } + protected override ConnectionBrokenException CreateException(String msg) + { + return new ConnectionBrokenException(msg); + } + } + private class ConnectionFailedExceptionHandler : AbstractExceptionHandler + { + public ConnectionFailedExceptionHandler() + : base("ConnectionFailedException") + { + + } + protected override ConnectionFailedException CreateException(String msg) + { + return new ConnectionFailedException(msg); + } + } + private class ConnectorIOExceptionHandler : AbstractExceptionHandler + { + public ConnectorIOExceptionHandler() + : base("ConnectorIOException") + { + + } + protected override ConnectorIOException CreateException(String msg) + { + return new ConnectorIOException(msg); + } + } + private class InvalidAttributeValueExceptionHandler : AbstractExceptionHandler + { + public InvalidAttributeValueExceptionHandler() + : base("InvalidAttributeValueException") + { + + } + protected override InvalidAttributeValueException CreateException(String msg) + { + return new InvalidAttributeValueException(msg); + } + } + private class PasswordExpiredExceptionHandler : AbstractExceptionHandler + { + public PasswordExpiredExceptionHandler() + : base("PasswordExpiredException") + { + + } + + public override Object Deserialize(ObjectDecoder decoder) + { + Uid uid = (Uid)decoder.ReadObjectField("Uid", typeof(Uid), null); + PasswordExpiredException ex = + (PasswordExpiredException)base.Deserialize(decoder); + ex.Uid = uid; + return ex; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + base.Serialize(obj, encoder); + PasswordExpiredException val = (PasswordExpiredException)obj; + encoder.WriteObjectField("Uid", val.Uid, true); + } + protected override PasswordExpiredException CreateException(String msg) + { + return new PasswordExpiredException(msg); + } + } + private class InvalidPasswordExceptionHandler : AbstractExceptionHandler + { + public InvalidPasswordExceptionHandler() + : base("InvalidPasswordException") + { + + } + protected override InvalidPasswordException CreateException(String msg) + { + return new InvalidPasswordException(msg); + } + } + private class UnknownUidExceptionHandler : AbstractExceptionHandler + { + public UnknownUidExceptionHandler() + : base("UnknownUidException") + { + + } + protected override UnknownUidException CreateException(String msg) + { + return new UnknownUidException(msg); + } + } + private class InvalidCredentialExceptionHandler : AbstractExceptionHandler + { + public InvalidCredentialExceptionHandler() + : base("InvalidCredentialException") + { + + } + protected override InvalidCredentialException CreateException(String msg) + { + return new InvalidCredentialException(msg); + } + } + private class PermissionDeniedExceptionHandler : AbstractExceptionHandler + { + public PermissionDeniedExceptionHandler() + : base("PermissionDeniedException") + { + + } + protected override PermissionDeniedException CreateException(String msg) + { + return new PermissionDeniedException(msg); + } + } + private class ConnectorSecurityExceptionHandler : AbstractExceptionHandler + { + public ConnectorSecurityExceptionHandler() + : base("ConnectorSecurityException") + { + + } + protected override ConnectorSecurityException CreateException(String msg) + { + return new ConnectorSecurityException(msg); + } + } + private class OperationTimeoutExceptionHandler : AbstractExceptionHandler + { + public OperationTimeoutExceptionHandler() + : base("OperationTimeoutException") + { + + } + protected override OperationTimeoutException CreateException(String msg) + { + return new OperationTimeoutException(msg); + } + } + private class ConnectorExceptionHandler : AbstractExceptionHandler + { + public ConnectorExceptionHandler() + : base("ConnectorException") + { + + } + protected override ConnectorException CreateException(String msg) + { + return new ConnectorException(msg); + } + } + private class PreconditionFailedExceptionHandler : AbstractExceptionHandler + { + public PreconditionFailedExceptionHandler() + : base("PreconditionFailedException") + { + + } + protected override PreconditionFailedException CreateException(String msg) + { + return new PreconditionFailedException(msg); + } + } + private class PreconditionRequiredExceptionHandler : AbstractExceptionHandler + { + public PreconditionRequiredExceptionHandler() + : base("PreconditionRequiredException") + { + + } + protected override PreconditionRequiredException CreateException(String msg) + { + return new PreconditionRequiredException(msg); + } + } + private class RemoteWrappedExceptionHandler : AbstractObjectSerializationHandler + { + public RemoteWrappedExceptionHandler() + : base(typeof(RemoteWrappedException), "RemoteWrappedException") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + string throwableClass = decoder.ReadStringField(RemoteWrappedException.FIELD_CLASS, typeof(ConnectorException).FullName); + string message = decoder.ReadStringField(RemoteWrappedException.FIELD_MESSAGE, null); + RemoteWrappedException cause = (RemoteWrappedException)decoder.ReadObjectField("RemoteWrappedException", typeof(RemoteWrappedException), null); + string stackTrace = decoder.ReadStringField(RemoteWrappedException.FIELD_STACK_TRACE, null); + + return new RemoteWrappedException(throwableClass, message, cause, stackTrace); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + RemoteWrappedException val = (RemoteWrappedException)obj; + encoder.WriteStringField(RemoteWrappedException.FIELD_CLASS, val.ExceptionClass); + encoder.WriteStringField(RemoteWrappedException.FIELD_MESSAGE, val.Message); + encoder.WriteObjectField("RemoteWrappedException", val.InnerException, true); + encoder.WriteStringField(RemoteWrappedException.FIELD_STACK_TRACE, val.ReadStackTrace()); + } + } + private class RetryableExceptionHandler : AbstractExceptionHandler + { + public RetryableExceptionHandler() + : base("RetryableException") + { + + } + protected override RetryableException CreateException(String msg) + { + return RetryableException.Wrap(msg, (Exception)null); + } + } + + //RuntimeException, Exception, and Throwable + //when going from Java to C#, these always become + //Exception. + //when going from C# to Java, these always become + //RuntimeException + private class RuntimeExceptionHandler : AbstractExceptionHandler + { + public RuntimeExceptionHandler() + : base("RuntimeException") + { + + } + protected override Exception CreateException(String msg) + { + return new Exception(msg); + } + } + + private class ArgumentExceptionHandler : AbstractExceptionHandler + { + public ArgumentExceptionHandler() + : base("IllegalArgumentException") + { + + } + protected override ArgumentException CreateException(string msg) + { + return new ArgumentException(msg); + } + } + + private class ExceptionHandler : AbstractExceptionHandler + { + public ExceptionHandler() + : base("Exception") + { + + } + protected override Exception CreateException(String msg) + { + return new Exception(msg); + } + } + private class ThrowableHandler : AbstractExceptionHandler + { + public ThrowableHandler() + : base("Throwable") + { + + } + protected override Exception CreateException(String msg) + { + return new Exception(msg); + } + } + + private abstract class AbstractAttributeHandler + : AbstractObjectSerializationHandler + where T : ConnectorAttribute + { + + protected AbstractAttributeHandler(String typeName) + : base(typeof(T), typeName) + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + String name = decoder.ReadStringField("name", null); + IList val = (IList)decoder.ReadObjectField("Values", typeof(IList), null); + return CreateAttribute(name, val); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorAttribute val = (ConnectorAttribute)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteObjectField("Values", val.Value, true); + } + + protected abstract T CreateAttribute(String name, IList val); + } + + private class AttributeHandler + : AbstractAttributeHandler + { + public AttributeHandler() + : base("Attribute") + { + + } + protected override ConnectorAttribute CreateAttribute(String name, IList value) + { + return ConnectorAttributeBuilder.Build(name, value); + } + } + + private class AttributeInfoHandler : AbstractObjectSerializationHandler + { + public AttributeInfoHandler() + : base(typeof(ConnectorAttributeInfo), "AttributeInfo") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name = ( + decoder.ReadStringField("name", null)); + builder.ValueType = ( + decoder.ReadClassField("type", null)); + ConnectorAttributeInfo.Flags flags = ConnectorAttributeInfo.Flags.NONE; + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) + { + object o = decoder.ReadObjectContents(i); + if (o is ConnectorAttributeInfo.Flags) + { + ConnectorAttributeInfo.Flags f = + (ConnectorAttributeInfo.Flags)o; + flags |= f; + } + } + builder.InfoFlags = flags; + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorAttributeInfo val = (ConnectorAttributeInfo)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteClassField("type", val.ValueType); + ConnectorAttributeInfo.Flags flags = val.InfoFlags; + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) + { + ConnectorAttributeInfo.Flags flag = + (ConnectorAttributeInfo.Flags)e; + if ((flags & flag) != 0) + { + encoder.WriteObjectContents(flag); + } + } + } + } + + private class ConnectorObjectHandler : AbstractObjectSerializationHandler + { + public ConnectorObjectHandler() + : base(typeof(ConnectorObject), "ConnectorObject") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ObjectClass oclass = + (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass), null); + ICollection attsObj = + (ICollection)decoder.ReadObjectField("Attributes", typeof(ICollection), null); + ICollection atts = + CollectionUtil.NewSet(attsObj); + return new ConnectorObject(oclass, atts); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorObject val = (ConnectorObject)obj; + encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); + encoder.WriteObjectField("Attributes", val.GetAttributes(), true); + } + } + private class NameHandler + : AbstractObjectSerializationHandler + { + public NameHandler() + : base(typeof(Name), "Name") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String str = decoder.ReadStringContents(); + return new Name(str); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Name val = (Name)obj; + encoder.WriteStringContents(val.GetNameValue()); + } + } + private class ObjectClassHandler : AbstractObjectSerializationHandler + { + public ObjectClassHandler() + : base(typeof(ObjectClass), "ObjectClass") + { + + } + public override object Deserialize(ObjectDecoder decoder) + { + string type = decoder.ReadStringField("type", null); + return new ObjectClass(type); + } + + public override void Serialize(object obj, ObjectEncoder encoder) + { + ObjectClass val = (ObjectClass)obj; + encoder.WriteStringField("type", val.GetObjectClassValue()); + } + } + + private class ObjectClassInfoHandler : AbstractObjectSerializationHandler + { + public ObjectClassInfoHandler() + : base(typeof(ObjectClassInfo), "ObjectClassInfo") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string type = + decoder.ReadStringField("type", null); + bool container = + decoder.ReadBooleanField("container", false); + + ICollection attrInfoObj = + (ICollection)decoder.ReadObjectField("AttributeInfos", typeof(ICollection), null); + + ICollection attrInfo = + CollectionUtil.NewSet(attrInfoObj); + + return new ObjectClassInfo(type, attrInfo, container); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ObjectClassInfo val = (ObjectClassInfo)obj; + encoder.WriteStringField("type", val.ObjectType); + encoder.WriteBooleanField("container", val.IsContainer); + encoder.WriteObjectField("AttributeInfos", val.ConnectorAttributeInfos, true); + } + } + private class SchemaHandler : AbstractObjectSerializationHandler + { + public SchemaHandler() + : base(typeof(Schema), "Schema") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + + ICollection objectClassesObj = + (ICollection)decoder.ReadObjectField("ObjectClassInfos", typeof(ICollection), null); + ICollection objectClasses = + CollectionUtil.NewSet(objectClassesObj); + IDictionary objectClassesByName = new Dictionary(); + foreach (ObjectClassInfo info in objectClasses) + { + objectClassesByName[info.ObjectType] = info; + } + ICollection operationOptionsObj = + (ICollection)decoder.ReadObjectField("OperationOptionInfos", typeof(ICollection), null); + ICollection operationOptions = + CollectionUtil.NewSet(operationOptionsObj); + IDictionary optionsByName = new Dictionary(); + foreach (OperationOptionInfo info in operationOptions) + { + optionsByName[info.Name] = info; + } + IDictionary objectClassNamesByOperationObj = + (IDictionary)decoder.ReadObjectField("objectClassesByOperation", null, null); + IDictionary, ICollection> + objectClassesByOperation = + new Dictionary, ICollection>(); + foreach (KeyValuePair entry in objectClassNamesByOperationObj) + { + SafeType op = SafeType.ForRawType((Type)entry.Key); + ICollection namesObj = + (ICollection)entry.Value; + ICollection infos = + new HashSet(); + foreach (object name in namesObj) + { + ObjectClassInfo objectClass = CollectionUtil.GetValue(objectClassesByName, (string)name, null); + if (objectClass != null) + { + infos.Add(objectClass); + } + } + objectClassesByOperation[op] = infos; + } + IDictionary optionsByOperationObj = + (IDictionary)decoder.ReadObjectField("optionsByOperation", null, null); + IDictionary, ICollection> + optionsByOperation = + new Dictionary, ICollection>(); + foreach (KeyValuePair entry in optionsByOperationObj) + { + SafeType op = SafeType.ForRawType((Type)entry.Key); + ICollection namesObj = + (ICollection)entry.Value; + ICollection infos = + new HashSet(); + foreach (object name in namesObj) + { + OperationOptionInfo info = CollectionUtil.GetValue(optionsByName, (string)name, null); + if (info != null) + { + infos.Add(info); + } + } + optionsByOperation[op] = infos; + } + return new Schema(objectClasses, + operationOptions, + objectClassesByOperation, + optionsByOperation); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Schema val = (Schema)obj; + encoder.WriteObjectField("ObjectClassInfos", val.ObjectClassInfo, true); + encoder.WriteObjectField("OperationOptionInfos", val.OperationOptionInfo, true); + IDictionary> + objectClassNamesByOperation = new Dictionary>(); + IDictionary> + optionNamesByOperation = new Dictionary>(); + + + foreach (KeyValuePair, ICollection> + entry in val.SupportedObjectClassesByOperation) + { + ICollection value = entry.Value; + ICollection names = new HashSet(); + foreach (ObjectClassInfo info in value) + { + names.Add(info.ObjectType); + } + objectClassNamesByOperation[entry.Key.RawType] = names; + } + foreach (KeyValuePair, ICollection> + entry in val.SupportedOptionsByOperation) + { + ICollection value = entry.Value; + ICollection names = new HashSet(); + foreach (OperationOptionInfo info in value) + { + names.Add(info.Name); + } + optionNamesByOperation[entry.Key.RawType] = names; + } + encoder.WriteObjectField("objectClassesByOperation", objectClassNamesByOperation, false); + encoder.WriteObjectField("optionsByOperation", optionNamesByOperation, false); + } + } + private class UidHandler + : AbstractObjectSerializationHandler + { + public UidHandler() + : base(typeof(Uid), "Uid") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String val = decoder.ReadStringField("uid", null); + String revision = decoder.ReadStringField("revision", null); + if (null == revision) + { + return new Uid(val); + } + else + { + return new Uid(val, revision); + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Uid val = (Uid)obj; + encoder.WriteStringField("uid", val.GetUidValue()); + encoder.WriteStringField("revision", val.Revision); + } + } + private class ScriptHandler : AbstractObjectSerializationHandler + { + public ScriptHandler() + : base(typeof(Script), "Script") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptLanguage = decoder.ReadStringField("scriptLanguage", null); + builder.ScriptText = (String)decoder.ReadObjectField("scriptText", typeof(string), null); + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Script val = (Script)obj; + encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); + encoder.WriteObjectField("scriptText", val.ScriptText, true); + } + } + private class ScriptContextHandler : AbstractObjectSerializationHandler + { + public ScriptContextHandler() + : base(typeof(ScriptContext), "ScriptContext") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String scriptLanguage = + decoder.ReadStringField("scriptLanguage", null); + IDictionary arguments = + (IDictionary)decoder.ReadObjectField("scriptArguments", null, null); + String scriptText = + (String)decoder.ReadObjectField("scriptText", typeof(string), null); + IDictionary arguments2 = + CollectionUtil.NewDictionary(arguments); + return new ScriptContext(scriptLanguage, scriptText, arguments2); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ScriptContext val = (ScriptContext)obj; + encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); + encoder.WriteObjectField("scriptArguments", val.ScriptArguments, false); + encoder.WriteObjectField("scriptText", val.ScriptText, true); + } + } + private class OperationOptionsHandler : AbstractObjectSerializationHandler + { + public OperationOptionsHandler() + : base(typeof(OperationOptions), "OperationOptions") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + IDictionary options = + (IDictionary)decoder.ReadObjectField("options", null, null); + IDictionary options2 = + CollectionUtil.NewDictionary(options); + return new OperationOptions(options2); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + OperationOptions val = (OperationOptions)obj; + encoder.WriteObjectField("options", val.Options, false); + } + } + private class SearchResultHandler : AbstractObjectSerializationHandler + { + public SearchResultHandler() + : base(typeof(SearchResult), "SearchResult") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String pagedResultsCookie = decoder.ReadStringField("pagedResultsCookie", null); + int totalPagedResults = decoder.ReadIntField("totalPagedResults", -1); + int remainingPagedResults = decoder.ReadIntField("remainingPagedResults", -1); + SearchResult.CountPolicy policy = + (SearchResult.CountPolicy) + decoder.ReadObjectField("CountPolicy", typeof (SearchResult.CountPolicy), null); + return new SearchResult(pagedResultsCookie, policy, totalPagedResults, remainingPagedResults); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + SearchResult val = (SearchResult)obj; + encoder.WriteStringField("pagedResultsCookie", val.PagedResultsCookie); + encoder.WriteIntField("totalPagedResults", val.TotalPagedResults); + encoder.WriteIntField("remainingPagedResults", val.RemainingPagedResults); + encoder.WriteObjectField("CountPolicy", val.TotalPagedResultsPolicy, true); + } + } + private class SortKeyHandler : AbstractObjectSerializationHandler + { + public SortKeyHandler() + : base(typeof(Org.IdentityConnectors.Framework.Common.Objects.SortKey), "SortKey") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return new Org.IdentityConnectors.Framework.Common.Objects.SortKey(decoder.ReadStringField("field", null), decoder.ReadBooleanField("isAscending", true)); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Org.IdentityConnectors.Framework.Common.Objects.SortKey val = (Org.IdentityConnectors.Framework.Common.Objects.SortKey)obj; + encoder.WriteStringField("field", val.Field); + encoder.WriteBooleanField("isAscending", val.IsAscendingOrder()); + } + } + private class OperationOptionInfoHandler : AbstractObjectSerializationHandler + { + public OperationOptionInfoHandler() + : base(typeof(OperationOptionInfo), "OperationOptionInfo") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + String name = decoder.ReadStringField("name", null); + Type type = decoder.ReadClassField("type", null); + return new OperationOptionInfo(name, type); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + OperationOptionInfo val = (OperationOptionInfo)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteClassField("type", val.OptionType); + } + } + private class SyncTokenHandler : AbstractObjectSerializationHandler + { + public SyncTokenHandler() + : base(typeof(SyncToken), "SyncToken") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + Object value = decoder.ReadObjectField("value", null, null); + return new SyncToken(value); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + SyncToken val = (SyncToken)obj; + encoder.WriteObjectField("value", val.Value, false); + } + } + private class SyncDeltaHandler : AbstractObjectSerializationHandler + { + public SyncDeltaHandler() + : base(typeof(SyncDelta), "SyncDelta") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.DeltaType = ((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType", typeof(SyncDeltaType), null)); + builder.Token = ((SyncToken)decoder.ReadObjectField("SyncToken", typeof(SyncToken), null)); + builder.PreviousUid = ((Uid)decoder.ReadObjectField("PreviousUid", typeof(Uid), null)); + builder.ObjectClass = ((ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass), null)); + builder.Uid = ((Uid)decoder.ReadObjectField("Uid", typeof(Uid), null)); + builder.Object = ((ConnectorObject)decoder.ReadObjectField("ConnectorObject", typeof(ConnectorObject), null)); + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + SyncDelta val = (SyncDelta)obj; + encoder.WriteObjectField("SyncDeltaType", val.DeltaType, true); + encoder.WriteObjectField("SyncToken", val.Token, true); + encoder.WriteObjectField("PreviousUid", val.PreviousUid, true); + encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); + encoder.WriteObjectField("Uid", val.Uid, true); + encoder.WriteObjectField("ConnectorObject", val.Object, true); + } + } + private class QualifiedUidHandler : AbstractObjectSerializationHandler + { + public QualifiedUidHandler() + : base(typeof(QualifiedUid), "QualifiedUid") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ObjectClass objectClass = (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass), null); + Uid uid = (Uid)decoder.ReadObjectField("Uid", typeof(Uid), null); + return new QualifiedUid(objectClass, uid); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + QualifiedUid val = (QualifiedUid)obj; + encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); + encoder.WriteObjectField("Uid", val.Uid, true); + } + } + } + #endregion + + #region FilterHandlers + internal static class FilterHandlers + { + public static readonly IList HANDLERS = + new List(); + static FilterHandlers() + { + HANDLERS.Add(new AndFilterHandler()); + HANDLERS.Add(new ContainsFilterHandler()); + HANDLERS.Add(new EndsWithFilterHandler()); + HANDLERS.Add(new EqualsFilterHandler()); + HANDLERS.Add(new ExtendedMatchFilterHandler()); + HANDLERS.Add(new GreaterThanFilterHandler()); + HANDLERS.Add(new GreaterThanOrEqualFilterHandler()); + HANDLERS.Add(new LessThanFilterHandler()); + HANDLERS.Add(new LessThanOrEqualFilterHandler()); + HANDLERS.Add(new NotFilterHandler()); + HANDLERS.Add(new OrFilterHandler()); + HANDLERS.Add(new PresenceFilterHandler()); + HANDLERS.Add(new StartsWithFilterHandler()); + HANDLERS.Add(new ContainsAllValuesFilterHandler()); + } + + private abstract class CompositeFilterHandler + : AbstractObjectSerializationHandler + where T : CompositeFilter + { + + protected CompositeFilterHandler(String typeName) + : base(typeof(T), typeName) + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + Filter left = (Filter)decoder.ReadObjectContents(0); + Filter right = (Filter)decoder.ReadObjectContents(1); + return CreateFilter(left, right); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + CompositeFilter val = (CompositeFilter)obj; + encoder.WriteObjectContents(val.Left); + encoder.WriteObjectContents(val.Right); + } + + protected abstract T CreateFilter(Filter left, Filter right); + } + + private abstract class AttributeFilterHandler + : AbstractObjectSerializationHandler + where T : AttributeFilter + { + + protected AttributeFilterHandler(String typeName) + : base(typeof(T), typeName) + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute", null, null); + return CreateFilter(attribute); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + AttributeFilter val = (AttributeFilter)obj; + encoder.WriteObjectField("attribute", val.GetAttribute(), false); + } + + protected abstract T CreateFilter(ConnectorAttribute attribute); + } + + + + + private class AndFilterHandler : CompositeFilterHandler + { + public AndFilterHandler() + : base("AndFilter") + { + } + protected override AndFilter CreateFilter(Filter left, Filter right) + { + return new AndFilter(left, right); + } + } + + private class ContainsFilterHandler : AttributeFilterHandler + { + public ContainsFilterHandler() + : base("ContainsFilter") + { + } + protected override ContainsFilter CreateFilter(ConnectorAttribute attribute) + { + return new ContainsFilter(attribute); + } + } + + private class EndsWithFilterHandler : AttributeFilterHandler + { + public EndsWithFilterHandler() + : base("EndsWithFilter") + { + } + protected override EndsWithFilter CreateFilter(ConnectorAttribute attribute) + { + return new EndsWithFilter(attribute); + } + } + + private class EqualsFilterHandler : AttributeFilterHandler + { + public EqualsFilterHandler() + : base("EqualsFilter") + { + } + protected override EqualsFilter CreateFilter(ConnectorAttribute attribute) + { + return new EqualsFilter(attribute); + } + } + + private class ExtendedMatchFilterHandler + : AbstractObjectSerializationHandler + { + + public ExtendedMatchFilterHandler() + : base(typeof(ExtendedMatchFilter), "ExtendedMatchFilter") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + String op = decoder.ReadStringField("operator", null); + ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute", null, null); + return new ExtendedMatchFilter(op, attribute); + + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + ExtendedMatchFilter val = (ExtendedMatchFilter)obj; + encoder.WriteStringField("operator", val.Operator); + encoder.WriteObjectField("attribute", val.GetAttribute(), false); + + + } + + } + + private class GreaterThanFilterHandler : AttributeFilterHandler + { + public GreaterThanFilterHandler() + : base("GreaterThanFilter") + { + } + protected override GreaterThanFilter CreateFilter(ConnectorAttribute attribute) + { + return new GreaterThanFilter(attribute); + } + } + + private class GreaterThanOrEqualFilterHandler : AttributeFilterHandler + { + public GreaterThanOrEqualFilterHandler() + : base("GreaterThanOrEqualFilter") + { + } + protected override GreaterThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) + { + return new GreaterThanOrEqualFilter(attribute); + } + } + private class LessThanFilterHandler : AttributeFilterHandler + { + public LessThanFilterHandler() + : base("LessThanFilter") + { + } + protected override LessThanFilter CreateFilter(ConnectorAttribute attribute) + { + return new LessThanFilter(attribute); + } + } + private class LessThanOrEqualFilterHandler : AttributeFilterHandler + { + public LessThanOrEqualFilterHandler() + : base("LessThanOrEqualFilter") + { + } + protected override LessThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) + { + return new LessThanOrEqualFilter(attribute); + } + } + private class NotFilterHandler + : AbstractObjectSerializationHandler + { + + public NotFilterHandler() + : base(typeof(NotFilter), "NotFilter") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + Filter filter = + (Filter)decoder.ReadObjectContents(0); + return new NotFilter(filter); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + NotFilter val = (NotFilter)obj; + encoder.WriteObjectContents(val.Filter); + } + + } + private class OrFilterHandler : CompositeFilterHandler + { + public OrFilterHandler() + : base("OrFilter") + { + } + protected override OrFilter CreateFilter(Filter left, Filter right) + { + return new OrFilter(left, right); + } + } + private class PresenceFilterHandler + : AbstractObjectSerializationHandler + { + + public PresenceFilterHandler() + : base(typeof(PresenceFilter), "PresenceFilter") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + String name = decoder.ReadStringField("name", null); + return new PresenceFilter(name); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + PresenceFilter val = (PresenceFilter)obj; + encoder.WriteStringField("name", val.Name); + } + + } + private class StartsWithFilterHandler : AttributeFilterHandler + { + public StartsWithFilterHandler() + : base("StartsWithFilter") + { + } + protected override StartsWithFilter CreateFilter(ConnectorAttribute attribute) + { + return new StartsWithFilter(attribute); + } + } + + private class ContainsAllValuesFilterHandler : AttributeFilterHandler + { + public ContainsAllValuesFilterHandler() + : base("ContainsAllValuesFilter") + { + } + protected override ContainsAllValuesFilter CreateFilter(ConnectorAttribute attribute) + { + return new ContainsAllValuesFilter(attribute); + } + } + } + #endregion + + #region MessageHandlers + internal static class MessageHandlers + { + public static readonly IList HANDLERS = + new List(); + static MessageHandlers() + { + HANDLERS.Add(new HelloRequestHandler()); + HANDLERS.Add(new HelloResponseHandler()); + HANDLERS.Add(new OperationRequestHandler()); + HANDLERS.Add(new OperationResponseEndHandler()); + HANDLERS.Add(new OperationResponsePartHandler()); + HANDLERS.Add(new OperationRequestMoreDataHandler()); + HANDLERS.Add(new OperationRequestStopDataHandler()); + HANDLERS.Add(new OperationResponsePauseHandler()); + HANDLERS.Add(new EchoMessageHandler()); + } + private class HelloRequestHandler + : AbstractObjectSerializationHandler + { + + public HelloRequestHandler() + : base(typeof(HelloRequest), "HelloRequest") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new HelloRequest(decoder.ReadIntField("infoLevel", HelloRequest.CONNECTOR_INFO)); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + HelloRequest val = (HelloRequest)obj; + encoder.WriteIntField("infoLevel", val.GetInfoLevel()); + } + + } + private class HelloResponseHandler + : AbstractObjectSerializationHandler + { + + public HelloResponseHandler() + : base(typeof(HelloResponse), "HelloResponse") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + Exception exception = + (Exception)decoder.ReadObjectField("exception", null, null); + IDictionary serverInfoRaw = + (IDictionary)decoder.ReadObjectField("serverInfoMap", null, null); + IDictionary serverInfo = null; + if (null != serverInfoRaw) + { + serverInfo = new Dictionary(serverInfoRaw.Count); + foreach (KeyValuePair entry in serverInfoRaw) + { + serverInfo.Add(entry.Key.ToString(), entry.Value); + } + } + + IList connectorInfosObj = + (IList)decoder.ReadObjectField("ConnectorInfos", typeof(IList), null); + IList connectorInfos = + CollectionUtil.NewList(connectorInfosObj); + IList connectorKeysObj = + (IList)decoder.ReadObjectField("ConnectorKeys", typeof(IList), null); + IList connectorKeys = CollectionUtil.NewList(connectorKeysObj); + return new HelloResponse(exception, serverInfo, connectorKeys, connectorInfos); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + HelloResponse val = (HelloResponse)obj; + encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("serverInfoMap", val.ServerInfo, false); + encoder.WriteObjectField("ConnectorInfos", val.ConnectorInfos, true); + encoder.WriteObjectField("ConnectorKeys", val.ConnectorKeys, true); + } + + } + private class OperationRequestHandler + : AbstractObjectSerializationHandler + { + + public OperationRequestHandler() + : base(typeof(OperationRequest), "OperationRequest") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + ConnectorKey connectorKey = + (ConnectorKey)decoder.ReadObjectField("ConnectorKey", typeof(ConnectorKey), null); + String connectorFacadeKey = + decoder.ReadStringField("connectorFacadeKey", null); + Type operation = + decoder.ReadClassField("operation", null); + string operationMethodName = + decoder.ReadStringField("operationMethodName", null); + IList arguments = (IList) + decoder.ReadObjectField("Arguments", typeof(IList), null); + return new OperationRequest(connectorKey, + connectorFacadeKey, + SafeType.ForRawType(operation), + operationMethodName, + arguments); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + OperationRequest val = + (OperationRequest)obj; + encoder.WriteClassField("operation", + val.Operation.RawType); + encoder.WriteStringField("operationMethodName", + val.OperationMethodName); + encoder.WriteStringField("connectorFacadeKey", + val.ConnectorFacadeKey); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey, true); + encoder.WriteObjectField("Arguments", + val.Arguments, true); + } + + } + private class OperationResponseEndHandler + : AbstractObjectSerializationHandler + { + + public OperationResponseEndHandler() + : base(typeof(OperationResponseEnd), "OperationResponseEnd") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new OperationResponseEnd(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationResponsePartHandler + : AbstractObjectSerializationHandler + { + + public OperationResponsePartHandler() + : base(typeof(OperationResponsePart), "OperationResponsePart") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + Exception exception = + (Exception)decoder.ReadObjectField("exception", null, null); + Object result = + decoder.ReadObjectField("result", null, null); + + return new OperationResponsePart(exception, result); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + OperationResponsePart val = (OperationResponsePart)obj; + encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("result", val.Result, false); + } + } + private class OperationRequestMoreDataHandler + : AbstractObjectSerializationHandler + { + + public OperationRequestMoreDataHandler() + : base(typeof(OperationRequestMoreData), "OperationRequestMoreData") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new OperationRequestMoreData(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationRequestStopDataHandler + : AbstractObjectSerializationHandler + { + + public OperationRequestStopDataHandler() + : base(typeof(OperationRequestStopData), "OperationRequestStopData") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new OperationRequestStopData(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationResponsePauseHandler + : AbstractObjectSerializationHandler + { + + public OperationResponsePauseHandler() + : base(typeof(OperationResponsePause), "OperationResponsePause") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new OperationResponsePause(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class EchoMessageHandler + : AbstractObjectSerializationHandler + { + + public EchoMessageHandler() + : base(typeof(EchoMessage), "EchoMessage") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new EchoMessage(decoder.ReadObjectField("value", null, null), + (String)decoder.ReadObjectField("objectXml", typeof(string), null)); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + EchoMessage val = (EchoMessage)obj; + encoder.WriteObjectField("value", val.Object, false); + encoder.WriteObjectField("objectXml", val.ObjectXml, true); + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/SerializerBinary.cs b/dotnet/framework/FrameworkInternal/SerializerBinary.cs new file mode 100644 index 00000000..ce554b96 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/SerializerBinary.cs @@ -0,0 +1,908 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using System.IO; +using System.Text; +namespace Org.IdentityConnectors.Framework.Impl.Serializer.Binary +{ + internal class InternalEncoder + { + /// + /// Mapping from type name to the ID we serialize so we only have to + /// + private IDictionary _constantPool = + new Dictionary(); + + private IList _constantBuffer = new List(); + + private IList _outputBufferStack = new List(); + internal Stream _rootOutput; + private bool _firstObject = true; + + public InternalEncoder(Stream output) + { + _rootOutput = output; + } + + public void WriteObject(ObjectEncoder encoder, Object obj) + { + + if (_firstObject) + { + WriteInt(BinaryObjectEncoder.OBJECT_MAGIC); + WriteInt(BinaryObjectEncoder.ENCODING_VERSION); + _firstObject = false; + } + + MemoryStream objectBuffer = new MemoryStream(); + _outputBufferStack.Add(objectBuffer); + + if (obj == null) + { + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_NULL); + } + else + { + Type clazz = obj.GetType(); + WriteClass(clazz); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if (handler == null) + { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if (obj is Array) + { + Array array = (Array)obj; + int length = array.Length; + for (int i = 0; i < length; i++) + { + Object val = array.GetValue(i); + StartAnonymousField(); + WriteObject(encoder, val); + EndField(); + } + } + else + { + throw new ConnectorException("No serializer for class: " + clazz); + } + } + else + { + handler.Serialize(obj, encoder); + } + } + + //write end-object into the current obj buffer + WriteByte(BinaryObjectEncoder.FIELD_TYPE_END_OBJECT); + + //pop the stack + _outputBufferStack.RemoveAt(_outputBufferStack.Count - 1); + + //it's a top-level object, flush the constant pool + if (_outputBufferStack.Count == 0) + { + WriteInt(_constantBuffer.Count); + foreach (String constant in _constantBuffer) + { + WriteString(constant, false); + WriteInt(_constantPool[constant]); + } + _constantBuffer.Clear(); + } + + //now write the actual object + objectBuffer.Close(); + byte[] bytes = objectBuffer.ToArray(); + WriteBytes(bytes); + } + + public void WriteClass(Type clazz) + { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperByObjectType(clazz); + if (handler == null && clazz.IsArray) + { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_ARRAY); + WriteClass(clazz.GetElementType()); + } + else if (mapper == null) + { + throw new ConnectorException("No serializer for class: " + clazz); + } + else + { + String typeName = mapper.HandledSerialType; + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_CLASS); + WriteString(typeName, true); + } + } + + public void StartAnonymousField() + { + WriteByte(BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD); + MemoryStream buf = new MemoryStream(); + _outputBufferStack.Add(buf); + } + + public void StartField(String name) + { + WriteByte(BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD); + WriteString(name, true); + MemoryStream buf = new MemoryStream(); + _outputBufferStack.Add(buf); + } + + public void EndField() + { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count - 1]; + _outputBufferStack.RemoveAt(_outputBufferStack.Count - 1); + buf.Close(); + byte[] bytes = buf.ToArray(); + WriteByteArray(bytes); + } + + public void WriteInt(int v) + { + Stream output = GetCurrentOutput(); + output.WriteByte((byte)(0xff & (v >> 24))); + output.WriteByte((byte)(0xff & (v >> 16))); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); + } + + public void WriteLong(long v) + { + Stream output = GetCurrentOutput(); + output.WriteByte((byte)(0xff & (v >> 56))); + output.WriteByte((byte)(0xff & (v >> 48))); + output.WriteByte((byte)(0xff & (v >> 40))); + output.WriteByte((byte)(0xff & (v >> 32))); + output.WriteByte((byte)(0xff & (v >> 24))); + output.WriteByte((byte)(0xff & (v >> 16))); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); + } + + public void WriteDouble(double l) + { + long val = BitConverter.DoubleToInt64Bits(l); + WriteLong(val); + } + + public void WriteByteArray(byte[] v) + { + WriteInt(v.Length); + WriteBytes(v); + } + + public void WriteByte(byte b) + { + GetCurrentOutput().WriteByte(b); + } + + public void WriteBoolean(bool b) + { + WriteByte(b ? (byte)1 : (byte)0); + } + + public void WriteString(String str, bool intern) + { + if (intern) + { + int code = InternIdentifier(str); + WriteInt(code); + return; + } + byte[] bytes = Encoding.UTF8.GetBytes(str); + WriteByteArray(bytes); + } + + private int InternIdentifier(String name) + { + int code = CollectionUtil.GetValue(_constantPool, name, -1); + if (code == -1) + { + code = _constantPool.Count; + _constantPool[name] = code; + _constantBuffer.Add(name); + } + return code; + } + + private void WriteBytes(byte[] v) + { + //only write if length > 0 - C# seems to have a problem with + //zero-length byte arrays + if (v.Length > 0) + { + GetCurrentOutput().Write(v, 0, v.Length); + } + } + + private Stream GetCurrentOutput() + { + if (_outputBufferStack.Count == 0) + { + return _rootOutput; + } + else + { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count - 1]; + return buf; + } + } + } + + internal class BinaryObjectEncoder : ObjectEncoder, BinaryObjectSerializer + { + public const int ENCODING_VERSION = 2; + + public const int OBJECT_MAGIC = 0xFAFB; + + public const byte OBJECT_TYPE_NULL = 60; + public const byte OBJECT_TYPE_CLASS = 61; + public const byte OBJECT_TYPE_ARRAY = 62; + + public const byte FIELD_TYPE_ANONYMOUS_FIELD = 70; + public const byte FIELD_TYPE_NAMED_FIELD = 71; + public const byte FIELD_TYPE_END_OBJECT = 72; + + + private InternalEncoder _internalEncoder; + + public BinaryObjectEncoder(Stream output) + { + _internalEncoder = new InternalEncoder(new BufferedStream(output, 4096)); + } + + public void Flush() + { + _internalEncoder._rootOutput.Flush(); + } + + public void Close() + { + _internalEncoder._rootOutput.Close(); + } + + public void WriteObject(Object o) + { + _internalEncoder.WriteObject(this, o); + } + + public void WriteBooleanContents(bool v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteBoolean(v); + _internalEncoder.EndField(); + } + + public void WriteBooleanField(String fieldName, bool v) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteBoolean(v); + _internalEncoder.EndField(); + } + + public void WriteByteContents(byte v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteByte(v); + _internalEncoder.EndField(); + } + + public void WriteByteArrayContents(byte[] v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteByteArray(v); + _internalEncoder.EndField(); + } + + public void WriteClassContents(Type v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteClass(v); + _internalEncoder.EndField(); + } + + public void WriteClassField(string fieldName, Type v) + { + if (v != null) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteClass(v); + _internalEncoder.EndField(); + } + } + + public void WriteDoubleContents(double v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteDouble(v); + _internalEncoder.EndField(); + } + + public void WriteDoubleField(String fieldName, double v) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteDouble(v); + _internalEncoder.EndField(); + } + + public void WriteFloatContents(float v) + { + _internalEncoder.StartAnonymousField(); + //write as double since C# only knows how to deal with that + _internalEncoder.WriteDouble((double)v); + _internalEncoder.EndField(); + } + + public void WriteFloatField(String fieldName, float v) + { + _internalEncoder.StartField(fieldName); + //write as double since C# only knows how to deal with that + _internalEncoder.WriteDouble((double)v); + _internalEncoder.EndField(); + } + + public void WriteIntContents(int v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteInt(v); + _internalEncoder.EndField(); + } + + public void WriteIntField(String fieldName, int v) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteInt(v); + _internalEncoder.EndField(); + } + + public void WriteLongContents(long v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteLong(v); + _internalEncoder.EndField(); + } + + public void WriteLongField(String fieldName, long v) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteLong(v); + _internalEncoder.EndField(); + } + + public void WriteObjectContents(Object obj) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteObject(this, obj); + _internalEncoder.EndField(); + } + + public void WriteObjectField(String fieldName, Object obj, bool inline) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteObject(this, obj); + _internalEncoder.EndField(); + } + + public void WriteStringContents(String str) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteString(str, false); + _internalEncoder.EndField(); + } + + public void WriteStringField(String fieldName, String val) + { + if (val != null) + { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteString(val, false); + _internalEncoder.EndField(); + } + } + } + + internal class ReadState + { + public IDictionary objectFields = new Dictionary(); + public IList anonymousFields = new List(); + public Stream currentInput; + public ReadState() + { + } + public bool StartField(String name) + { + currentInput = null; + byte[] content = CollectionUtil.GetValue(objectFields, name, null); + if (content == null) + { + return false; + } + else + { + currentInput = new MemoryStream(content); + return true; + } + } + public void StartAnonymousField(int index) + { + if (index >= anonymousFields.Count) + { + throw new ConnectorException("Anonymous content not found"); + } + currentInput = new MemoryStream(anonymousFields[index]); + } + } + + internal class InternalDecoder + { + private readonly byte[] _int_buf = new byte[4]; + private readonly byte[] _long_buf = new byte[8]; + private readonly byte[] _byte_buf = new byte[1]; + + private bool _firstObject = true; + + private readonly IDictionary _constantPool = + new Dictionary(); + + private readonly IList _readStateStack = new List(); + internal readonly Stream _rootInput; + + public InternalDecoder(Stream input) + { + _rootInput = input; + } + + public Object ReadObject(ObjectDecoder decoder) + { + + if (_firstObject) + { + int magic = ReadInt(); + if (magic != BinaryObjectEncoder.OBJECT_MAGIC) + { + throw new ConnectorException("Bad magic number: " + magic); + } + int version = ReadInt(); + if (version != BinaryObjectEncoder.ENCODING_VERSION) + { + throw new ConnectorException("Unexpected version: " + version); + } + _firstObject = false; + } + + //if it's a top-level object, it's proceeded by a constant pool + if (_readStateStack.Count == 0) + { + int size = ReadInt(); + for (int i = 0; i < size; i++) + { + String constant = ReadString(false); + int code = ReadInt(); + _constantPool[code] = constant; + } + } + + Type clazz = ReadClass(); + + ReadState state = new ReadState(); + while (true) + { + byte type = ReadByte(); + if (type == BinaryObjectEncoder.FIELD_TYPE_END_OBJECT) + { + break; + } + else if (type == BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD) + { + byte[] bytes = ReadByteArray(); + state.anonymousFields.Add(bytes); + } + else if (type == BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD) + { + String fieldName = ReadString(true); + byte[] bytes = ReadByteArray(); + state.objectFields[fieldName] = bytes; + } + else + { + throw new ConnectorException("Unknown type: " + type); + } + } + _readStateStack.Add(state); + + Object rv; + if (clazz == null) + { + rv = null; + } + else + { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if (handler == null) + { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if (clazz.IsArray) + { + int length = GetNumAnonymousFields(); + Array array = Array.CreateInstance(clazz.GetElementType(), + length); + for (int i = 0; i < length; i++) + { + StartAnonymousField(i); + Object element = ReadObject(decoder); + array.SetValue(element, i); + } + rv = array; + } + else + { + throw new ConnectorException("No deserializer for type: " + clazz); + } + } + else + { + rv = handler.Deserialize(decoder); + } + } + _readStateStack.RemoveAt(_readStateStack.Count - 1); + return rv; + } + + public Type ReadClass() + { + int type = ReadByte(); + if (type == BinaryObjectEncoder.OBJECT_TYPE_NULL) + { + return null; + } + else if (type == BinaryObjectEncoder.OBJECT_TYPE_ARRAY) + { + Type componentClass = ReadClass(); + return componentClass.MakeArrayType(); + } + else if (type == BinaryObjectEncoder.OBJECT_TYPE_CLASS) + { + String typeName = ReadString(true); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperBySerialType(typeName); + if (mapper == null) + { + throw new ConnectorException("No deserializer for type: " + typeName); + } + return mapper.HandledObjectType; + } + else + { + throw new ConnectorException("Bad type value: " + type); + } + } + + public int GetNumAnonymousFields() + { + ReadState readState = _readStateStack[_readStateStack.Count - 1]; + return readState.anonymousFields.Count; + } + + public void StartAnonymousField(int index) + { + ReadState readState = _readStateStack[_readStateStack.Count - 1]; + readState.StartAnonymousField(index); + } + + public bool StartField(String name) + { + ReadState readState = _readStateStack[_readStateStack.Count - 1]; + return readState.StartField(name); + } + + public int ReadInt() + { + ReadByteArrayFully(_int_buf); + return (((_int_buf[0] & 0xff) << 24) | ((_int_buf[1] & 0xff) << 16) | + ((_int_buf[2] & 0xff) << 8) | (_int_buf[3] & 0xff)); + } + + public long ReadLong() + { + ReadByteArrayFully(_long_buf); + return (((long)(_long_buf[0] & 0xff) << 56) | + ((long)(_long_buf[1] & 0xff) << 48) | + ((long)(_long_buf[2] & 0xff) << 40) | + ((long)(_long_buf[3] & 0xff) << 32) | + ((long)(_long_buf[4] & 0xff) << 24) | + ((long)(_long_buf[5] & 0xff) << 16) | + ((long)(_long_buf[6] & 0xff) << 8) | + ((_long_buf[7] & 0xff))); + } + + public double ReadDouble() + { + long v = ReadLong(); + return BitConverter.Int64BitsToDouble(v); + } + + public byte[] ReadByteArray() + { + int len = ReadInt(); + byte[] bytes = new byte[len]; + ReadByteArrayFully(bytes); + return bytes; + } + + public byte ReadByte() + { + ReadByteArrayFully(_byte_buf); + return _byte_buf[0]; + } + + public bool ReadBoolean() + { + byte b = ReadByte(); + return b != 0; + } + + public String ReadString(bool interned) + { + if (interned) + { + int code = ReadInt(); + String name = CollectionUtil.GetValue(_constantPool, code, null); + if (name == null) + { + throw new ConnectorException("Undeclared code: " + code); + } + return name; + } + byte[] bytes = ReadByteArray(); + return Encoding.UTF8.GetString(bytes); + } + + private Stream GetCurrentInput() + { + if (_readStateStack.Count > 0) + { + ReadState state = _readStateStack[_readStateStack.Count - 1]; + return state.currentInput; + } + else + { + return _rootInput; + } + } + + private void ReadByteArrayFully(byte[] bytes) + { + int pos = 0; + while (pos < bytes.Length) + { + int count = GetCurrentInput().Read(bytes, pos, bytes.Length - pos); + if (count <= 0) + { + throw new EndOfStreamException(); + } + pos += count; + } + } + } + + internal class BinaryObjectDecoder : ObjectDecoder, BinaryObjectDeserializer + { + + private InternalDecoder _internalDecoder; + + public BinaryObjectDecoder(Stream inp) + { + _internalDecoder = new InternalDecoder(new BufferedStream(inp, 4096)); + } + + public void Close() + { + _internalDecoder._rootInput.Close(); + } + + public Object ReadObject() + { + return _internalDecoder.ReadObject(this); + } + + public bool ReadBooleanContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadBoolean(); + } + + public bool ReadBooleanField(String fieldName, bool dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return _internalDecoder.ReadBoolean(); + } + else + { + return dflt; + } + } + + public byte[] ReadByteArrayContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadByteArray(); + } + + public byte ReadByteContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadByte(); + } + + public Type ReadClassContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadClass(); + } + + public Type ReadClassField(string fieldName, Type dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return _internalDecoder.ReadClass(); + } + else + { + return dflt; + } + } + + public double ReadDoubleContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadDouble(); + } + + public double ReadDoubleField(String fieldName, double dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return ReadDoubleContents(); + } + else + { + return dflt; + } + } + + public float ReadFloatContents() + { + _internalDecoder.StartAnonymousField(0); + //read as double since C# only knows how to deal with that + return (float)_internalDecoder.ReadDouble(); + } + + public float ReadFloatField(String fieldName, float dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + //read as double since C# only knows how to deal with that + return (float)_internalDecoder.ReadDouble(); + } + else + { + return dflt; + } + } + + public int ReadIntContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadInt(); + } + + public int ReadIntField(String fieldName, int dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return _internalDecoder.ReadInt(); + } + else + { + return dflt; + } + } + + public long ReadLongContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadLong(); + } + + public long ReadLongField(String fieldName, long dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return _internalDecoder.ReadLong(); + } + else + { + return dflt; + } + } + + public int GetNumSubObjects() + { + return _internalDecoder.GetNumAnonymousFields(); + } + + public Object ReadObjectContents(int index) + { + _internalDecoder.StartAnonymousField(index); + return _internalDecoder.ReadObject(this); + } + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return _internalDecoder.ReadObject(this); + } + else + { + return dflt; + } + } + + public String ReadStringContents() + { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadString(false); + } + + public String ReadStringField(String fieldName, String dflt) + { + if (_internalDecoder.StartField(fieldName)) + { + return _internalDecoder.ReadString(false); + } + else + { + return dflt; + } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/SerializerXml.cs b/dotnet/framework/FrameworkInternal/SerializerXml.cs new file mode 100644 index 00000000..e219a915 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/SerializerXml.cs @@ -0,0 +1,952 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Resources; +using System.Text; +using System.Net; +using System.Xml; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +namespace Org.IdentityConnectors.Framework.Impl.Serializer.Xml +{ + public class XmlObjectEncoder : ObjectEncoder + { + private StringBuilder _rootBuilder; + private XmlWriter _writer; + + public XmlObjectEncoder(StringBuilder builder) + { + Assertions.NullCheck(builder, "builder"); + _rootBuilder = builder; + } + + public String WriteObject(Object o) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.OmitXmlDeclaration = true; + _writer = XmlWriter.Create(_rootBuilder, settings); + String rv = WriteObjectInternal(o, false); + _writer.Close(); + return rv; + } + + public void WriteBooleanContents(bool v) + { + WriteStringContentsInternal(EncodeBoolean(v)); + } + + public void WriteBooleanField(String fieldName, bool v) + { + WriteAttributeInternal(fieldName, EncodeBoolean(v)); + } + + public void WriteByteContents(byte v) + { + WriteStringContentsInternal(EncodeByte(v)); + } + + public void WriteByteArrayContents(byte[] v) + { + WriteStringContentsInternal(EncodeByteArray(v)); + } + + public void WriteClassContents(Type v) + { + WriteStringContentsInternal(EncodeClass(v)); + } + + public void WriteClassField(String name, Type v) + { + if (v != null) + { + WriteAttributeInternal(name, EncodeClass(v)); + } + } + + public void WriteDoubleContents(double v) + { + WriteStringContentsInternal(EncodeDouble(v)); + } + + public void WriteDoubleField(String fieldName, double v) + { + WriteAttributeInternal(fieldName, EncodeDouble(v)); + } + + public void WriteFloatContents(float v) + { + WriteStringContentsInternal(EncodeFloat(v)); + } + + public void WriteFloatField(String fieldName, float v) + { + WriteAttributeInternal(fieldName, EncodeFloat(v)); + } + + public void WriteIntContents(int v) + { + WriteStringContentsInternal(EncodeInt(v)); + } + + public void WriteIntField(String fieldName, int v) + { + WriteAttributeInternal(fieldName, EncodeInt(v)); + } + + public void WriteLongContents(long v) + { + WriteStringContentsInternal(EncodeLong(v)); + } + + public void WriteLongField(String fieldName, long v) + { + WriteAttributeInternal(fieldName, EncodeLong(v)); + } + + public void WriteObjectContents(Object o) + { + WriteObjectInternal(o, false); + } + + public void WriteObjectField(String fieldName, Object obj, bool inline) + { + if (inline && obj == null) + { + return; //don't write anything + } + BeginElement(fieldName); + WriteObjectInternal(obj, inline); + EndElement(); + } + + public void WriteStringContents(String str) + { + WriteStringContentsInternal(str); + } + + public void WriteStringField(String fieldName, String str) + { + if (str != null) + { + WriteAttributeInternal(fieldName, str); + } + } + + internal static String EncodeBoolean(bool b) + { + return b.ToString(); + } + + private static String EncodeByte(byte singleByte) + { + return Convert.ToString(singleByte); + } + + private static String EncodeByteArray(byte[] bytes) + { + return Convert.ToBase64String(bytes); + } + + private static String EncodeClass(Type clazz) + { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperByObjectType(clazz); + if (handler == null && clazz.IsArray) + { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + return EncodeClass(clazz.GetElementType()) + "[]"; + } + else if (mapper == null) + { + throw new ConnectorException("No serializer for class: " + clazz); + } + else + { + String typeName = mapper.HandledSerialType; + return typeName; + } + } + + internal static String EncodeDouble(double d) + { + return d.ToString("R"); + } + + internal static String EncodeFloat(float d) + { + return d.ToString("R"); + } + + internal static String EncodeInt(int d) + { + return d.ToString(); + } + + internal static String EncodeLong(long d) + { + return d.ToString(); + } + + /// + /// Writes the object + /// + /// + /// + /// The type name (regardless of whether it was inlined) + String WriteObjectInternal(Object obj, bool inline) + { + if (obj == null) + { + if (inline) + { + throw new ArgumentException("null cannot be inlined"); + } + BeginElement("null"); + EndElement(); + return "null"; + } + else + { + Type clazz = obj.GetType(); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if (handler == null) + { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if (clazz.IsArray) + { + if (!inline) + { + String componentTypeName = EncodeClass(clazz.GetElementType()); + BeginElement("Array"); + WriteAttributeInternal("componentType", componentTypeName); + } + Array array = (Array)obj; + int length = array.Length; + for (int i = 0; i < length; i++) + { + Object val = array.GetValue(i); + WriteObjectInternal(val, false); + } + if (!inline) + { + EndElement(); + } + return "Array"; + } + else + { + throw new ConnectorException("No serializer for class: " + clazz); + } + } + else + { + String typeName = EncodeClass(clazz); + if (!inline) + { + BeginElement(typeName); + } + handler.Serialize(obj, this); + if (!inline) + { + EndElement(); + } + return typeName; + } + } + + } + + ////////////////////////////////////////////////////////////////// + // + // xml encoding + // + ///////////////////////////////////////////////////////////////// + + private void BeginElement(String name) + { + _writer.WriteStartElement(name); + } + + private void EndElement() + { + _writer.WriteEndElement(); + } + private void WriteAttributeInternal(String fieldName, String str) + { + _writer.WriteAttributeString(fieldName, str); + } + private void WriteStringContentsInternal(String str) + { + _writer.WriteString(str); + } + } + + public class XmlObjectDecoder : ObjectDecoder + { + + private readonly XmlElement _node; + private readonly Type _expectedClass; + + public XmlObjectDecoder(XmlElement node, Type expectedClass) + { + _node = node; + _expectedClass = expectedClass; + } + + public Object ReadObject() + { + return ReadObjectInternal(); + } + + public bool ReadBooleanContents() + { + return DecodeBoolean(ReadStringContentsInternal()); + } + + public bool ReadBooleanField(String fieldName, bool dflt) + { + return DecodeBoolean(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeBoolean(dflt))); + } + + public byte[] ReadByteArrayContents() + { + return DecodeByteArray(ReadStringContentsInternal()); + } + + public byte ReadByteContents() + { + return DecodeByte(ReadStringContentsInternal()); + } + + public Type ReadClassContents() + { + return DecodeClass(ReadStringContentsInternal()); + } + + public Type ReadClassField(String name, Type dflt) + { + String val = ReadStringAttributeInternal(name, null); + if (val == null) + { + return dflt; + } + else + { + return DecodeClass(val); + } + } + + public double ReadDoubleContents() + { + return DecodeDouble(ReadStringContentsInternal()); + } + + public double ReadDoubleField(String fieldName, double dflt) + { + return DecodeDouble(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeDouble(dflt))); + } + + public float ReadFloatContents() + { + return DecodeFloat(ReadStringContentsInternal()); + } + + public float ReadFloatField(String fieldName, float dflt) + { + return DecodeFloat(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeFloat(dflt))); + } + + public int ReadIntContents() + { + return DecodeInt(ReadStringContentsInternal()); + } + + public int ReadIntField(String fieldName, int dflt) + { + return DecodeInt(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeInt(dflt))); + } + + public long ReadLongContents() + { + return DecodeLong(ReadStringContentsInternal()); + } + + public long ReadLongField(String fieldName, long dflt) + { + return DecodeLong(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeLong(dflt))); + } + + public int GetNumSubObjects() + { + int count = 0; + for (XmlElement subElement = XmlUtil.GetFirstChildElement(_node); + subElement != null; + subElement = XmlUtil.GetNextElement(subElement)) + { + count++; + } + return count; + } + + public Object ReadObjectContents(int index) + { + XmlElement subElement = XmlUtil.GetFirstChildElement(_node); + for (int i = 0; i < index; i++) + { + subElement = XmlUtil.GetNextElement(subElement); + } + + if (subElement == null) + { + throw new ConnectorException("Missing subelement number: " + index); + } + + return new XmlObjectDecoder(subElement, null).ReadObject(); + } + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) + { + XmlElement child = XmlUtil.FindImmediateChildElement(_node, fieldName); + if (child == null) + { + return dflt; + } + if (expected != null) + { + return new XmlObjectDecoder(child, expected).ReadObject(); + } + XmlElement subElement = XmlUtil.GetFirstChildElement(child); + if (subElement == null) + { + return dflt; + } + //if they specify null, don't apply defaults + return new XmlObjectDecoder(subElement, null).ReadObject(); + } + + public String ReadStringContents() + { + String rv = ReadStringContentsInternal(); + return rv == null ? "" : rv; + } + + public String ReadStringField(String fieldName, String dflt) + { + return ReadStringAttributeInternal(fieldName, dflt); + } + + private String ReadStringContentsInternal() + { + String xml = XmlUtil.GetContent(_node); + return xml; + } + + private String ReadStringAttributeInternal(String name, String dflt) + { + XmlAttribute attr = _node.GetAttributeNode(name); + if (attr == null) + { + return dflt; + } + return attr.Value; + } + + private bool DecodeBoolean(String v) + { + return Boolean.Parse(v); + } + + private byte[] DecodeByteArray(String base64) + { + return Convert.FromBase64String(base64); + } + + private byte DecodeByte(String v) + { + return Convert.ToByte(v); + } + + private Type DecodeClass(String type) + { + if (type.EndsWith("[]")) + { + String componentName = type.Substring(0, type.Length - "[]".Length); + Type componentClass = + DecodeClass(componentName); + Type arrayClass = + componentClass.MakeArrayType(); + return arrayClass; + } + else + { + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperBySerialType(type); + if (mapper == null) + { + throw new ConnectorException("No deserializer for type: " + type); + } + Type clazz = mapper.HandledObjectType; + return clazz; + } + } + + private double DecodeDouble(String val) + { + return Double.Parse(val); + } + + private float DecodeFloat(String val) + { + return Single.Parse(val); + } + + private int DecodeInt(String val) + { + return Int32.Parse(val); + } + + private long DecodeLong(String val) + { + return Int64.Parse(val); + } + + private Object ReadObjectInternal() + { + if (_expectedClass != null) + { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(_expectedClass); + if (handler == null) + { + if (_expectedClass.IsArray) + { + IList temp = new List(); + for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; + child = XmlUtil.GetNextElement(child)) + { + XmlObjectDecoder sub = new XmlObjectDecoder(child, null); + Object obj = sub.ReadObject(); + temp.Add(obj); + } + int length = temp.Count; + Array array = Array.CreateInstance(_expectedClass.GetElementType(), length); + for (int i = 0; i < length; i++) + { + Object element = temp[i]; + array.SetValue(element, i); + } + return array; + } + else + { + throw new ConnectorException("No deserializer for type: " + _expectedClass); + } + } + else + { + return handler.Deserialize(this); + } + } + else if (_node.LocalName.Equals("null")) + { + return null; + } + else if (_node.LocalName.Equals("Array")) + { + String componentType = XmlUtil.GetAttribute(_node, "componentType"); + if (componentType == null) + { + componentType = "Object"; + } + Type componentClass = DecodeClass(componentType); + IList temp = new List(); + for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; + child = XmlUtil.GetNextElement(child)) + { + XmlObjectDecoder sub = new XmlObjectDecoder(child, null); + Object obj = sub.ReadObject(); + temp.Add(obj); + } + int length = temp.Count; + Array array = Array.CreateInstance(componentClass, + length); + for (int i = 0; i < length; i++) + { + Object element = temp[i]; + array.SetValue(element, i); + } + return array; + } + else + { + Type clazz = + DecodeClass(_node.LocalName); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if (handler == null) + { + throw new ConnectorException("No deserializer for type: " + clazz); + } + else + { + return handler.Deserialize(this); + } + } + } + } + + public class XmlObjectParser + { + public static void parse(TextReader inputSource, + XmlObjectResultsHandler handler, + bool validate) + { + XmlReaderSettings mySettings = + new XmlReaderSettings(); + if (validate) + { + mySettings.ValidationType = ValidationType.DTD; + } + mySettings.DtdProcessing = DtdProcessing.Parse; + //mySettings.ProhibitDtd = false; + mySettings.XmlResolver = new MyEntityResolver(validate); + XmlReader reader = XmlReader.Create(inputSource, mySettings); + MyParser parser = new MyParser(handler); + parser.Parse(reader); + } + + private class MyEntityResolver : XmlResolver + { + private readonly bool _validate; + + public MyEntityResolver(bool validate) + { + _validate = validate; + } + + public override Object GetEntity(Uri absoluteUri, string role, Type ofObject) + { + String text = null; + if (absoluteUri.AbsolutePath.EndsWith(XmlObjectSerializerImpl.CONNECTORS_DTD)) + { + if (!_validate) + { + text = ""; + } + else + { + text = GetDTD(); + } + } + if (text != null) + { + byte[] bytes = Encoding.UTF8.GetBytes(text); + return new MemoryStream(bytes); + } + return null; + } + + public override ICredentials Credentials + { + set + { + + } + } + private static String GetDTD() + { + ResourceManager manager = + new ResourceManager("Org.IdentityConnectors.Resources", + typeof(XmlObjectParser).Assembly); + String contents = (String)manager.GetObject(XmlObjectSerializerImpl.CONNECTORS_DTD); + return contents; + } + } + + private class MyParser + { + /// + /// The document for the current top-level element. + /// + /// + /// with each top-level element, + /// we discard the previous to avoid accumulating memory + /// + private XmlDocument _currentTopLevelElementDocument; + + + /// + /// Stack of elements we are creating + /// + private IList _elementStack = new List(10); + + /// + /// Results handler that we write our objects to + /// + private readonly XmlObjectResultsHandler _handler; + + /// + /// Is the handler still handing + /// + private bool _stillHandling = true; + + + public MyParser(XmlObjectResultsHandler handler) + { + _handler = handler; + } + + public void Parse(XmlReader reader) + { + while (_stillHandling && reader.Read()) + { + XmlNodeType nodeType = reader.NodeType; + switch (nodeType) + { + case XmlNodeType.Element: + StartElement(reader.LocalName); + bool empty = reader.IsEmptyElement; + if (reader.MoveToFirstAttribute()) + { + AddAttribute(reader.LocalName, reader.Value); + while (reader.MoveToNextAttribute()) + { + AddAttribute(reader.LocalName, reader.Value); + } + } + if (empty) + { + EndElement(); + } + break; + case XmlNodeType.Text: + case XmlNodeType.CDATA: + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + AddText(reader.Value); + break; + case XmlNodeType.EndElement: + EndElement(); + break; + } + } + } + + private XmlElement GetCurrentElement() + { + if (_elementStack.Count > 0) + { + return _elementStack[_elementStack.Count - 1]; + } + else + { + return null; + } + } + + private void AddText(String text) + { + XmlElement currentElement = GetCurrentElement(); + if (currentElement != null) + { + currentElement.AppendChild(_currentTopLevelElementDocument.CreateTextNode(text)); + } + } + + private void EndElement() + { + if (_elementStack.Count > 0) //we don't push the top-level MULTI_OBJECT_ELEMENT on the stack + { + XmlElement element = _elementStack[_elementStack.Count - 1]; + _elementStack.RemoveAt(_elementStack.Count - 1); + if (_elementStack.Count == 0) + { + _currentTopLevelElementDocument = null; + if (_stillHandling) + { + XmlObjectDecoder decoder = new XmlObjectDecoder(element, null); + Object obj = decoder.ReadObject(); + _stillHandling = _handler(obj); + } + } + } + } + + + private void StartElement(String localName) + { + XmlElement element = null; + if (_elementStack.Count == 0) + { + if (!XmlObjectSerializerImpl.MULTI_OBJECT_ELEMENT.Equals(localName)) + { + _currentTopLevelElementDocument = new XmlDocument(); + element = _currentTopLevelElementDocument.CreateElement(localName); + } + } + else + { + element = + _currentTopLevelElementDocument.CreateElement(localName); + GetCurrentElement().AppendChild(element); + } + if (element != null) + { + _elementStack.Add(element); + } + } + + private void AddAttribute(String name, String val) + { + XmlElement element = GetCurrentElement(); + if (element != null) + { + element.SetAttribute(name, val); + } + } + } + } + + public class XmlObjectSerializerImpl : XmlObjectSerializer + { + + public const String MULTI_OBJECT_ELEMENT = "MultiObject"; + public const String CONNECTORS_DTD = "connectors.dtd"; + + private readonly TextWriter _output; + + private readonly bool _multiObject; + + private readonly bool _includeHeader; + + private bool _firstObjectWritten; + + private bool _documentEnded; + + public XmlObjectSerializerImpl(TextWriter output, bool includeHeader, bool multiObject) + { + _output = output; + _includeHeader = includeHeader; + _multiObject = multiObject; + } + + + /// + /// Writes the next object to the stream. + /// + /// The object to write. + /// + /// if there is more than one object + /// and this is not configured for multi-object document. + public void WriteObject(Object obj) + { + if (_documentEnded) + { + throw new InvalidOperationException("Attempt to writeObject after the document is already closed"); + } + StringBuilder buf = new StringBuilder(); + XmlObjectEncoder encoder = new XmlObjectEncoder(buf); + String elementName = encoder.WriteObject(obj); + if (!_firstObjectWritten) + { + StartDocument(elementName); + } + else + { + if (!_multiObject) + { + throw new InvalidOperationException("Attempt to write multiple objects on a single-object document"); + } + } + Write(buf.ToString()); + _firstObjectWritten = true; + } + + public void Flush() + { + _output.Flush(); + } + + public void Close(bool closeStream) + { + if (!_documentEnded) + { + if (!_firstObjectWritten) + { + if (!_multiObject) + { + throw new InvalidOperationException("Attempt to write zero objects on a single-object document"); + } + StartDocument(null); + } + WriteEndDocument(); + _documentEnded = true; + } + if (closeStream) + { + _output.Close(); + } + } + + private void StartDocument(String firstElement) + { + if (_includeHeader) + { + String docType = _multiObject ? MULTI_OBJECT_ELEMENT : firstElement; + String line1 = "\n"; + String line2 = "\n"; + Write(line1); + Write(line2); + } + if (_multiObject) + { + String line3 = "<" + MULTI_OBJECT_ELEMENT + ">\n"; + Write(line3); + } + } + + private void WriteEndDocument() + { + if (_multiObject) + { + String line1 = "\n"; + Write(line1); + } + } + + private void Write(String str) + { + _output.Write(str); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/Server.cs b/dotnet/framework/FrameworkInternal/Server.cs new file mode 100644 index 00000000..3ec834b5 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/Server.cs @@ -0,0 +1,1154 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Net.Sockets; +using System.IO; +using System.Linq; +using System.Threading; +using System.Reflection; +using System.Security.Authentication; +using System.Text; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Server; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; + +namespace Org.IdentityConnectors.Framework.Server +{ + /// + /// Connector server interface. + /// + public abstract class ConnectorServer + { + // At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME + = "Org.IdentityConnectors.Framework.Impl.Server.ConnectorServerImpl"; + + protected internal static readonly TraceSource Logger = new TraceSource("ConnectorServer"); + + /// + /// The port to listen on; + /// + private int _port = 0; + + /// + /// Base 64 sha1 hash of the connector server key + /// + /// + private String _keyHash; + + /// + /// The number of connections to queue + /// + private int _maxConnections = 300; + + /// + /// The minimum number of worker threads + /// + private int _minWorkers = 10; + + /// + /// The maximum number of worker threads + /// + private int _maxWorkers = 100; + + /// + /// The maximum time in minutes a facade can be inactive. + /// + private int _maxFacadeLifeTime = 60; + + /// + /// The network interface address to use. + /// + private IPAddress _ifAddress = null; + + /// + /// Listen on SSL + /// + private bool _useSSL = false; + + /// + /// The server certificate to use + /// + private X509Certificate _serverCertificate = null; + + /// + /// Get the singleton instance of the . + /// + public static ConnectorServer NewInstance() + { + SafeType type = + SafeType.ForRawType(Type.GetType(IMPL_NAME, true)); + return type.CreateInstance(); + } + + private void AssertNotStarted() + { + if (IsStarted()) + { + throw new InvalidOperationException("Operation cannot be performed " + + "while server is running"); + } + } + + /// + /// Returns the port to listen on. + /// + /// The port to listen on. + public int Port + { + get + { + return _port; + } + set + { + AssertNotStarted(); + _port = value; + } + } + + /// + /// Returns the max connections to queue + /// + /// The max connections to queue + public int MaxConnections + { + get + { + return _maxConnections; + } + set + { + AssertNotStarted(); + _maxConnections = value; + } + } + + /// + /// Returns the max worker threads to allow. + /// + /// The max worker threads to allow. + public int MaxWorkers + { + get + { + return _maxWorkers; + } + set + { + AssertNotStarted(); + _maxWorkers = value; + } + } + + /// + /// Returns the min worker threads to allow. + /// + /// The min worker threads to allow. + public int MinWorkers + { + get + { + return _minWorkers; + } + set + { + AssertNotStarted(); + _minWorkers = value; + } + } + + /// + /// Returns the max inactive lifetime of + /// to allow. + /// + /// The max inactive lifetime of + /// to + /// allow. + public virtual int MaxFacadeLifeTime + { + get + { + return _maxFacadeLifeTime; + } + set + { + AssertNotStarted(); + _maxFacadeLifeTime = value; + } + } + + /// + /// Returns the network interface address to bind to. + /// + /// + /// May be null. + /// + /// The network interface address to bind to or null. + public IPAddress IfAddress + { + get + { + return _ifAddress; + } + set + { + AssertNotStarted(); + _ifAddress = value; + } + } + + /// + /// Returns true if we are to use SSL. + /// + /// true if we are to use SSL. + public bool UseSSL + { + get + { + return _useSSL; + } + set + { + AssertNotStarted(); + _useSSL = value; + } + } + + /// + /// Returns the certificate to use for the SSL connection. + /// + public X509Certificate ServerCertificate + { + get + { + return _serverCertificate; + } + set + { + AssertNotStarted(); + _serverCertificate = value; + } + } + + public String KeyHash + { + get + { + return _keyHash; + } + set + { + AssertNotStarted(); + _keyHash = value; + } + } + + /// + /// Produces a thread dump of all pending requests + /// + abstract public void DumpRequests(); + + /// + /// Gets the time when the servers was started last time. + /// + abstract public long StartTime(); + + /// + /// Starts the server. + /// + /// + /// All server settings must be configured prior + /// to calling. The following methods are required to be called: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + abstract public void Start(); + + /// + /// Stops the server gracefully. + /// + /// + /// Returns when all in-progress connections + /// have been serviced. + /// + abstract public void Stop(); + + /// + /// Return true if the server is started. + /// + /// + /// Note that started is a + /// logical state (start method has been called). It does not necessarily + /// reflect the health of the server + /// + /// true if the server is started. + abstract public bool IsStarted(); + } +} + +namespace Org.IdentityConnectors.Framework.Impl.Server +{ + + #region ConnectionProcessor + public class ConnectionProcessor + { + private class RemoteResultsHandler : ObjectStreamHandler + { + private const int PAUSE_INTERVAL = 200; + + private readonly RemoteFrameworkConnection _connection; + private long _count = 0; + public RemoteResultsHandler(RemoteFrameworkConnection conn) + { + _connection = conn; + } + + public bool Handle(Object obj) + { + try + { + OperationResponsePart part = + new OperationResponsePart(null, obj); + _connection.WriteObject(part); + _count++; + if (_count % PAUSE_INTERVAL == 0) + { + _connection.WriteObject(new OperationResponsePause()); + Object message = + _connection.ReadObject(); + return message is OperationRequestMoreData; + } + else + { + return true; + } + } + catch (IOException e) + { + throw new BrokenConnectionException(e); + } + } + } + + private readonly ConnectorServerImpl _server; + private readonly RemoteFrameworkConnection _connection; + + public ConnectionProcessor(ConnectorServerImpl server, + RemoteFrameworkConnection connection) + { + _server = server; + _connection = connection; + } + + public void Run() + { + try + { + _server.BeginRequest(); + try + { + while (true) + { + bool keepGoing = ProcessRequest(); + if (!keepGoing) + { + break; + } + } + } + finally + { + try + { + _connection.Dispose(); + } + catch (Exception e) + { + TraceUtil.TraceException(null, e); + } + } + } + catch (Exception e) + { + TraceUtil.TraceException(null, e); + } + finally + { + _server.EndRequest(); + } + } + + private bool ProcessRequest() + { + CultureInfo locale; + try + { + locale = (CultureInfo)_connection.ReadObject(); + } + catch (EndOfStreamException) + { + return false; + } + + //We can't set this because C# does not like language-neutral + //cultures for CurrentCulture - this tends to blow up + //TODO: think more about this... + //Thread.CurrentThread.CurrentCulture = locale; + Thread.CurrentThread.CurrentUICulture = locale; + + GuardedString key = (GuardedString)_connection.ReadObject(); + + bool authorized; + try + { + authorized = key.VerifyBase64SHA1Hash(_server.KeyHash); + } + finally + { + key.Dispose(); + } + Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException authException = null; + if (!authorized) + { + authException = new Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException("Remote framework key is invalid"); + } + Object requestObject = _connection.ReadObject(); + if (requestObject is HelloRequest) + { + if (authException != null) + { + HelloResponse response = + new HelloResponse(authException, null, null, null); + _connection.WriteObject(response); + } + else + { + HelloResponse response = + ProcessHelloRequest((HelloRequest)requestObject); + _connection.WriteObject(response); + } + } + else if (requestObject is OperationRequest) + { + if (authException != null) + { + OperationResponsePart part = + new OperationResponsePart(authException, null); + _connection.WriteObject(part); + } + else + { + OperationRequest opRequest = + (OperationRequest)requestObject; + OperationResponsePart part = + ProcessOperationRequest(opRequest); + _connection.WriteObject(part); + } + } + else if (requestObject is EchoMessage) + { + if (authException != null) + { + //echo message probably doesn't need auth, but + //it couldn't hurt - actually it does for test connection + EchoMessage part = + new EchoMessage(authException, null); + _connection.WriteObject(part); + } + else + { + EchoMessage message = (EchoMessage)requestObject; + Object obj = message.Object; + String xml = message.ObjectXml; + if (xml != null) + { + Console.WriteLine("xml: \n" + xml); + Object xmlClone = + SerializerUtil.DeserializeXmlObject(xml, true); + xml = + SerializerUtil.SerializeXmlObject(xmlClone, true); + } + EchoMessage message2 = new EchoMessage(obj, xml); + _connection.WriteObject(message2); + } + } + else + { + throw new Exception("Unexpected request: " + requestObject); + } + return true; + } + + private ConnectorInfoManager GetConnectorInfoManager() + { + return ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); + } + + private HelloResponse ProcessHelloRequest(HelloRequest request) + { + IList connectorInfo = null; + IList connectorKeys = null; + IDictionary serverInfo = null; + Exception exception = null; + try + { + serverInfo = new Dictionary(1); + if (request.isServerInfo()) + { + serverInfo.Add(HelloResponse.SERVER_START_TIME, _server.StartTime()); + } + if (request.isConnectorKeys()) + { + ConnectorInfoManager manager = GetConnectorInfoManager(); + IList localInfos = manager.ConnectorInfos; + connectorKeys = new List(); + foreach (ConnectorInfo localInfo in localInfos) + { + connectorKeys.Add(localInfo.ConnectorKey); + } + if (request.isConnectorInfo()) + { + connectorInfo = new List(); + foreach (ConnectorInfo localInfo in localInfos) + { + LocalConnectorInfoImpl localInfoImpl = + (LocalConnectorInfoImpl)localInfo; + RemoteConnectorInfoImpl remoteInfo = + localInfoImpl.ToRemote(); + connectorInfo.Add(remoteInfo); + } + } + } + } + catch (Exception e) + { + TraceUtil.TraceException(null, e); + exception = e; + connectorInfo = null; + } + return new HelloResponse(exception, serverInfo, connectorKeys, connectorInfo); + } + + private MethodInfo GetOperationMethod(OperationRequest request) + { + MethodInfo[] methods = + request.Operation.RawType.GetMethods(); + MethodInfo found = null; + foreach (MethodInfo m in methods) + { + if (m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper())) + { + if (found != null) + { + throw new ConnectorException("APIOperations are expected " + + "to have exactly one method of a given name: " + request.Operation); + } + found = m; + } + } + + if (found == null) + { + throw new ConnectorException("APIOperations are expected " + + "to have exactly one method of a given name: " + request.Operation + " " + methods.Length); + } + return found; + } + + private OperationResponsePart + ProcessOperationRequest(OperationRequest request) + { + Object result; + Exception exception = null; + try + { + MethodInfo method = GetOperationMethod(request); + APIOperation operation = GetAPIOperation(request); + IList arguments = request.Arguments; + IList argumentsAndStreamHandlers = + PopulateStreamHandlers(ReflectionUtil.GetParameterTypes(method), + arguments); + try + { + Object[] args = argumentsAndStreamHandlers.ToArray(); + FixupArguments(method, args); + result = method.Invoke(operation, args); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + bool anyStreams = + argumentsAndStreamHandlers.Count > arguments.Count; + if (anyStreams) + { + try + { + _connection.WriteObject(new OperationResponseEnd()); + } + catch (IOException e) + { + throw new BrokenConnectionException(e); + } + } + } + catch (BrokenConnectionException w) + { + //at this point the stream is broken - just give up + throw w.GetIOException(); + } + catch (Exception e) + { + TraceUtil.TraceException(null, e); + exception = e; + result = null; + } + return new OperationResponsePart(exception, result); + } + + private IList PopulateStreamHandlers(Type[] paramTypes, IList arguments) + { + IList rv = new List(); + bool firstStream = true; + IEnumerator argIt = arguments.GetEnumerator(); + foreach (Type paramType in paramTypes) + { + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) + { + if (!firstStream) + { + throw new InvalidOperationException("At most one stream handler is supported"); + } + ObjectStreamHandler osh = + new RemoteResultsHandler(_connection); + rv.Add(StreamHandlerUtil.AdaptFromObjectStreamHandler(paramType, osh)); + firstStream = false; + } + else + { + argIt.MoveNext(); + rv.Add(argIt.Current); + } + } + return rv; + } + + /// + /// When arguments are serialized, we loose the + /// generic-type of collections. We must fix + /// the arguments + /// + /// + /// + private void FixupArguments(MethodInfo method, + object[] args) + { + Type[] paramTypes = + ReflectionUtil.GetParameterTypes(method); + if (paramTypes.Length != args.Length) + { + throw new ArgumentException("Number of arguments does not match for method: " + method); + } + for (int i = 0; i < args.Length; i++) + { + args[i] = FixupArgument(paramTypes[i], + args[i]); + } + } + + private object FixupArgument(Type expectedType, + object argument) + { + //at some point, we might want this to be more general-purpose + //for now we just handle those cases that we need to + if (typeof(ICollection).Equals(expectedType)) + { + ICollection val = + (ICollection)argument; + return CollectionUtil.NewSet(val); + } + else + { + return argument; + } + } + + private APIOperation GetAPIOperation(OperationRequest request) + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = manager.FindConnectorInfo( + request.ConnectorKey); + if (info == null) + { + throw new Exception("No such connector: " + + request.ConnectorKey); + } + String connectorFacadeKey = request.ConnectorFacadeKey; + + ConnectorFacade facade = + ConnectorFacadeFactory.GetManagedInstance().NewInstance(info, connectorFacadeKey); + + return facade.GetOperation(request.Operation); + } + + private class BrokenConnectionException : Exception + { + + + public BrokenConnectionException(IOException ex) + : base("", ex) + { + } + + public IOException GetIOException() + { + return (IOException)InnerException; + } + } + + } + #endregion + + #region ConnectionListener + class ConnectionListener + { + /// + /// This is the size of our internal queue. + /// + /// + /// For now I have this + /// relatively small because I want the OS to manage the connect + /// queue coming in. That way it can properly turn away excessive + /// requests + /// + private const int INTERNAL_QUEUE_SIZE = 2; + + + /// + /// The server object that we are using + /// + private readonly ConnectorServerImpl _server; + + /// + /// The server socket. + /// + /// + /// This must be bound at the time + /// of creation. + /// + private readonly TcpListener _socket; + + /// + /// Pool of executors + /// + //TODO: add a thread pool + //private readonly ExecutorService _threadPool; + + /// + /// Set to indicated we need to start shutting down + /// + private bool _stopped = false; + + private Thread _thisThread; + + private readonly Object MUTEX = new Object(); + + /// + /// Creates the listener thread + /// + /// The server object + /// The socket (should already be bound) + public ConnectionListener(ConnectorServerImpl server, + TcpListener socket) + { + _server = server; + _socket = socket; + _thisThread = new Thread(Run) { Name = "ConnectionListener", IsBackground = false }; + //TODO: thread pool + /* _threadPool = + new ThreadPoolExecutor + (server.getMinWorkers(), + server.getMaxWorkers(), + 30, //idle time timeout + TimeUnit.SECONDS, + new ArrayBlockingQueue( + INTERNAL_QUEUE_SIZE, + true)); //fair*/ + } + + public void Start() + { + _thisThread.Start(); + } + + public void Run() + { + Trace.TraceInformation("Server started on port: " + _server.Port); + while (!IsStopped()) + { + try + { + TcpClient connection = null; + Stream stream = null; + try + { + connection = _socket.AcceptTcpClient(); + stream = connection.GetStream(); + if (_server.UseSSL) + { + SslStream sslStream = new SslStream(stream, false); + stream = sslStream; + sslStream.AuthenticateAsServer(_server.ServerCertificate, + false, + SslProtocols.Tls, + false); + } + + ConnectionProcessor processor = + new ConnectionProcessor(_server, + new RemoteFrameworkConnection(connection, stream)); + Thread thread = new Thread(processor.Run); + thread.IsBackground = false; + thread.Start(); + } + catch (Exception) + { + if (stream != null) + { + try { stream.Close(); } + catch (Exception) { } + } + if (connection != null) + { + try { connection.Close(); } + catch (Exception) { } + } + throw; + } + } + catch (Exception e) + { + //log the error unless it's because we've stopped + if (!IsStopped() || !(e is SocketException)) + { + TraceUtil.TraceException("Error processing request", e); + } + //wait a second before trying again + if (!IsStopped()) + { + Thread.Sleep(1000); + } + } + } + } + + private void MarkStopped() + { + lock (MUTEX) + { + _stopped = true; + } + } + + private bool IsStopped() + { + lock (MUTEX) + { + return _stopped; + } + } + + public void Shutdown() + { + if (Object.ReferenceEquals(Thread.CurrentThread, _thisThread)) + { + throw new ArgumentException("Shutdown may not be called from this thread"); + } + if (!IsStopped()) + { + //set the stopped flag so we no its a normal + //shutdown and don't log the SocketException + MarkStopped(); + //close the socket - this causes accept to throw an exception + _socket.Stop(); + //wait for the main listener thread to die so we don't + //get any new requests + _thisThread.Join(); + //TODO: shutdown thread pool + //wait for all in-progress requests to finish + //_threadPool.shutdown(); + } + } + } + #endregion + + #region RequestStats + internal class RequestStats + { + public RequestStats() + { + } + public Thread RequestThread { get; set; } + public long StartTimeMillis { get; set; } + public long RequestID { get; set; } + } + #endregion + + #region ConnectorServerImpl + public class ConnectorServerImpl : ConnectorServer + { + + private readonly IDictionary + _pendingRequests = CollectionUtil.NewIdentityDictionary(); + private ConnectionListener _listener; + private Object COUNT_LOCK = new Object(); + private Timer _timer = null; + private long _startDate = 0; + private long _requestCount = 0; + + public override bool IsStarted() + { + return _listener != null; + } + + public override long StartTime() + { + return _startDate; + } + + public void BeginRequest() + { + long requestID; + lock (COUNT_LOCK) + { + requestID = _requestCount++; + } + Thread requestThread = Thread.CurrentThread; + RequestStats stats = new RequestStats(); + stats.StartTimeMillis = + DateTimeUtil.GetCurrentUtcTimeMillis(); + stats.RequestThread = Thread.CurrentThread; + stats.RequestID = requestID; + lock (_pendingRequests) + { + _pendingRequests[stats.RequestThread] + = stats; + } + } + + public void EndRequest() + { + lock (_pendingRequests) + { + _pendingRequests.Remove(Thread.CurrentThread); + } + } + public override void DumpRequests() + { + long currentTime = DateTimeUtil.GetCurrentUtcTimeMillis(); + IDictionary + pending; + lock (_pendingRequests) + { + pending = new Dictionary(_pendingRequests); + } + StringBuilder builder = new StringBuilder(); + builder.Append("****Pending Requests Summary*****"); + foreach (RequestStats stats in pending.Values) + { + DumpStats(stats, builder, currentTime); + } + //here we purposefully use write line since + //we always want to see it. in general, don't + //use this method + Trace.WriteLine(builder.ToString()); + } + + private void DumpStats(RequestStats stats, + StringBuilder builder, + long currentTime) + { + builder.AppendLine("**Request #" + stats.RequestID + " pending for " + (currentTime - stats.StartTimeMillis) + " millis."); + StackTrace stackTrace = GetStackTrace(stats.RequestThread); + if (stackTrace == null) + { + builder.AppendLine(" "); + } + else + { + builder.AppendLine(stackTrace.ToString()); + } + } + + private static StackTrace GetStackTrace(Thread thread) + { + bool suspended = false; + try + { + thread.Suspend(); + suspended = true; + return new StackTrace(thread, true); + } + catch (ThreadStateException) + { + return null; //we missed this one + } + finally + { + if (suspended) + { + thread.Resume(); + } + } + } + + public override void Start() + { + if (IsStarted()) + { + throw new InvalidOperationException("Server is already running."); + } + if (Port == 0) + { + throw new InvalidOperationException("Port must be set prior to starting server."); + } + if (KeyHash == null) + { + throw new InvalidOperationException("Key hash must be set prior to starting server."); + } + if (UseSSL && ServerCertificate == null) + { + throw new InvalidOperationException("ServerCertificate must be set if using SSL."); + } + //make sure we are configured properly + ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); + _requestCount = 0; + /* + * the Java and .Net dates have a different starting point: zero milliseconds in Java corresponds to January 1, 1970, 00:00:00 GMT (aka “the epoch”). + * In .Net zero milliseconds* corresponds to 12:00 A.M., January 1, 0001 GMT. + * So the basic is to bridge over the reference points gap with adding (or substracting) the corresponding number of milliseconds + * such that zero milliseconds in .Net is mapped to -62135769600000L milliseconds in Java. + * This number of milliseconds corresponds to GMT zone, so do not forget to include your time zone offset into the calculations. + */ + _startDate = (DateTime.UtcNow.Ticks - 621355968000000000) / 10000; + _pendingRequests.Clear(); + TcpListener socket = + CreateServerSocket(); + ConnectionListener listener = new ConnectionListener(this, socket); + listener.Start(); + _listener = listener; + + if (MaxFacadeLifeTime > 0) + { + var statusChecker = new FacadeDisposer(new TimeSpan(0, MaxFacadeLifeTime, 0)); + // Create an inferred delegate that invokes methods for the timer. + TimerCallback tcb = statusChecker.Run; + + _timer = new Timer(tcb, null, new TimeSpan(0, MaxFacadeLifeTime, 0), + new TimeSpan(0, Math.Min(MaxFacadeLifeTime, 10), 0)); + } + } + + private TcpListener CreateServerSocket() + { + IPAddress addr = IfAddress; + + if (addr == null) + { + addr = IOUtil.GetIPAddress("0.0.0.0"); + } + TcpListener rv = new TcpListener(addr, Port); + //TODO: specify accept count + rv.Start(); + return rv; + } + + + public override void Stop() + { + if (_listener != null) + { + _listener.Shutdown(); + _listener = null; + } + _startDate = 0; + if (null != _timer) + { + _timer.Dispose(); + _timer = null; + } + ConnectorFacadeFactory.GetManagedInstance().Dispose(); + } + + internal class FacadeDisposer + { + private readonly TimeSpan _delay; + private int _sequence = 0; + + public FacadeDisposer(TimeSpan unit) + { + this._delay = unit; + } + + public void Run(Object stateInfo) + { + Logger.TraceEvent(TraceEventType.Verbose, _sequence++, + "Invoking Managed ConnectorFacade Disposer : {0:yyyy/MM/dd H:mm:ss zzz}", DateTime.Now); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetManagedInstance(); + if (factory is ManagedConnectorFacadeFactoryImpl) + { + ((ManagedConnectorFacadeFactoryImpl)factory).EvictIdle(_delay); + } + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/Test.cs b/dotnet/framework/FrameworkInternal/Test.cs new file mode 100644 index 00000000..12a68601 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/Test.cs @@ -0,0 +1,242 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Test.Common; +using Org.IdentityConnectors.Test.Common.Spi; + +namespace Org.IdentityConnectors.Framework.Impl.Test +{ + public class TestHelpersImpl : TestHelpersSpi + { + /// + /// Method for convenient testing of local connectors. + /// + public APIConfiguration CreateTestConfiguration(SafeType clazz, + Configuration config) + { + LocalConnectorInfoImpl info = new LocalConnectorInfoImpl(); + info.ConnectorConfigurationClass = SafeType.Get(config); + info.ConnectorClass = (clazz); + info.ConnectorDisplayNameKey = ("DUMMY_DISPLAY_NAME"); + info.ConnectorKey = ( + new ConnectorKey(clazz.RawType.Name + ".bundle", + "1.0", + clazz.RawType.Name)); + info.Messages = (this.CreateDummyMessages()); + APIConfigurationImpl rv = new APIConfigurationImpl(); + rv.IsConnectorPoolingSupported = ( + IsConnectorPoolingSupported(clazz)); + ConfigurationPropertiesImpl properties = + CSharpClassProperties.CreateConfigurationProperties(config); + rv.ConfigurationProperties = (properties); + rv.ConnectorInfo = (info); + rv.SupportedOperations = ( + FrameworkUtil.GetDefaultSupportedOperations(clazz)); + info.DefaultAPIConfiguration = ( + rv); + return rv; + } + + /// + /// Method for convenient testing of local connectors. + /// + public APIConfiguration CreateTestConfiguration(SafeType connectorClass, PropertyBag configData, string prefix) + { + Debug.Assert(null != connectorClass); + Type rawConnectorClass = connectorClass.RawType; + + Object[] attributes = connectorClass.RawType.GetCustomAttributes( + typeof(ConnectorClassAttribute), + false); + if (attributes.Length > 0) + { + ConnectorClassAttribute attribute = + (ConnectorClassAttribute)attributes[0]; + + Assembly assembly = IOUtil.GetAssemblyContainingType(rawConnectorClass.FullName); + + String fileName = assembly.Location; + SafeType connectorConfigurationClass = attribute.ConnectorConfigurationType; + if (connectorConfigurationClass == null) + { + String MSG = ("File " + fileName + + " contains a ConnectorInfo attribute " + + "with no connector configuration class."); + throw new ConfigurationException(MSG); + } + String connectorDisplayNameKey = + attribute.ConnectorDisplayNameKey; + if (connectorDisplayNameKey == null) + { + String MSG = ("File " + fileName + + " contains a ConnectorInfo attribute " + + "with no connector display name."); + throw new ConfigurationException(MSG); + } + LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); + rv.ConnectorClass = connectorClass; + rv.ConnectorConfigurationClass = connectorConfigurationClass; + rv.ConnectorDisplayNameKey = connectorDisplayNameKey; + rv.ConnectorCategoryKey = attribute.ConnectorCategoryKey; + rv.ConnectorKey = ( + new ConnectorKey(rawConnectorClass.Name + ".bundle", + "1.0", + rawConnectorClass.Name)); ; + APIConfigurationImpl impl = ConnectorAssemblyUtility.CreateDefaultApiConfiguration(rv); + rv.DefaultAPIConfiguration = impl; + rv.Messages = ConnectorAssemblyUtility.LoadMessages(assembly, rv, attribute.MessageCatalogPaths); + + ConfigurationPropertiesImpl configProps = (ConfigurationPropertiesImpl)impl.ConfigurationProperties; + + string fullPrefix = StringUtil.IsBlank(prefix) ? null : prefix + "."; + + foreach (ConfigurationPropertyImpl property in configProps.Properties) + { + object value = configData.GetProperty(null != fullPrefix ? fullPrefix + property.Name : property.Name, property.ValueType, property.Value); + if (value != null) + { + property.Value = value; + } + } + return impl; + } + throw new ArgumentException("ConnectorClass does not define ConnectorClassAttribute"); + } + + public void FillConfiguration(Configuration config, IDictionary configData) + { + IDictionary configDataCopy = new Dictionary(configData); + ConfigurationPropertiesImpl configProps = + CSharpClassProperties.CreateConfigurationProperties(config); + foreach (string propName in configProps.PropertyNames) + { + object value; + if (configDataCopy.TryGetValue(propName, out value)) + { + // Remove the entry from the config map, so that at the end + // the map only contains entries that were not assigned to a config property. + configDataCopy.Remove(propName); + configProps.SetPropertyValue(propName, value); + } + } + // The config map now contains entries that were not assigned to a config property. + foreach (string propName in configDataCopy.Keys) + { + Trace.TraceWarning("Configuration property {0} does not exist!", propName); + } + CSharpClassProperties.MergeIntoBean(configProps, config); + } + + private static bool IsConnectorPoolingSupported(SafeType clazz) + { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), clazz.RawType); + } + + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The result handler + /// The options - may be null - will + /// be cast to an empty OperationOptions + public SearchResult Search(SearchOp search, + ObjectClass objectClass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + SearchResult result = null; + RawSearcherImpl.RawSearch(search, objectClass, filter, new SearchResultsHandler() + { + Handle = obj => + { + return handler.Handle(obj); + }, + HandleResult = obj => + { + result = obj; + } + + }, options); + return result != null ? result : new SearchResult(); + } + + public ConnectorMessages CreateDummyMessages() + { + return new DummyConnectorMessages(); + } + + private class DummyConnectorMessages : ConnectorMessages + { + public String Format(String key, String dflt, params Object[] args) + { + StringBuilder builder = new StringBuilder(); + builder.Append(key); + builder.Append(": "); + String sep = ""; + foreach (Object arg in args) + { + builder.Append(sep); + builder.Append(arg); + sep = ", "; + } + return builder.ToString(); + } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkInternal/version.template b/dotnet/framework/FrameworkInternal/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/FrameworkInternal/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/FrameworkProtoBuf/FrameworkProtoBuf.csproj b/dotnet/framework/FrameworkProtoBuf/FrameworkProtoBuf.csproj new file mode 100755 index 00000000..ac5f4bbe --- /dev/null +++ b/dotnet/framework/FrameworkProtoBuf/FrameworkProtoBuf.csproj @@ -0,0 +1,82 @@ + + + + + + Debug + AnyCPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} + Library + Properties + Org.ForgeRock.OpenICF.Common.ProtoBuf + FrameworkProtoBuf + OpenICF Framework - Protocol Buffer Messages + v4.5.2 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\Google.ProtocolBuffers.3\lib\Google.Protobuf.dll + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkProtoBuf/protobuf/CommonObjectMessages.proto b/dotnet/framework/FrameworkProtoBuf/protobuf/CommonObjectMessages.proto new file mode 100755 index 00000000..89888f01 --- /dev/null +++ b/dotnet/framework/FrameworkProtoBuf/protobuf/CommonObjectMessages.proto @@ -0,0 +1,89 @@ +syntax = "proto3"; + +option csharp_namespace = "Org.ForgeRock.OpenICF.Common.ProtoBuf"; +package org.forgerock.openicf.common.protobuf; + +message Uid { + string value = 1; + string revision = 2; +} + +message BigDecimal { + string unscaled = 1; + int32 scale = 2; +} + +message ConnectorKey { + string bundleName = 1; + string bundleVersion = 2; + string connectorName = 3; +} + +message Locale { + string language = 1; + string country = 2; + string variant = 3; +} + +message Script { + string scriptLanguage = 1; + string scriptText = 2; +} + +message ScriptContext { + Script script = 1; + bytes scriptArguments = 2; +} + +message SearchResult { + enum CountPolicy { + NONE = 0; + ESTIMATE = 1; + EXACT = 2; + } + string pagedResultsCookie = 1; + CountPolicy totalPagedResultsPolicy = 2; + int32 totalPagedResults = 3; + int32 remainingPagedResults = 4; +} + +message SortKey { + string field = 1; + bool isAscending = 2; +} + +message SyncToken { + bytes value = 1; +} + +message SyncDelta { + enum SyncDeltaType { + CREATE_OR_UPDATE = 0; + DELETE = 1; + CREATE = 2; + UPDATE = 3; + } + SyncToken token = 1; + SyncDeltaType deltaType = 2; + Uid previousUid = 3; + string objectClass = 4; + Uid uid = 5; + bytes connectorObject = 6; + +} + +message ConnectorObject { + /*message MapFieldEntry { + //option map_entry = true; + string key = 1; + AttributeCollectionValue value = 2; + }*/ + string objectClass = 1; + bytes attributes = 2; + //repeated MapFieldEntry map_field = 2; +} + +message QualifiedUid { + string objectClass = 1; + Uid uid = 2; +} diff --git a/dotnet/framework/FrameworkProtoBuf/protobuf/OperationMessages.proto b/dotnet/framework/FrameworkProtoBuf/protobuf/OperationMessages.proto new file mode 100755 index 00000000..2b53b490 --- /dev/null +++ b/dotnet/framework/FrameworkProtoBuf/protobuf/OperationMessages.proto @@ -0,0 +1,277 @@ +syntax = "proto3"; + +option csharp_namespace = "Org.ForgeRock.OpenICF.Common.ProtoBuf"; +package org.forgerock.openicf.common.protobuf; + +import "CommonObjectMessages.proto"; + +//import "FilterMessages.proto"; + +//Interface AuthenticateOp +message AuthenticateOpRequest { + string objectClass = 1; + string username = 2; + bytes password = 3; + bytes options = 4; +} + +message AuthenticateOpResponse { + Uid uid = 1; +} + +//Interface BatchOp +message BatchOpRequest { + bool query = 1; + repeated BatchOpTask tasks = 2; + repeated string batchToken = 3; + bytes options = 4; +} + +message BatchOpTask { + string taskId = 1; + CreateOpRequest createRequest = 2; + DeleteOpRequest deleteRequest = 3; + UpdateOpRequest updateRequest = 4; +} + +message BatchOpResult { + repeated string batchToken = 1; + string taskId = 2; + bool complete = 3; + bool error = 4; + bool queryRequired = 5; + bool asynchronousResults = 6; + bool returnsResults = 7; + Uid uid = 8; + BatchEmptyResponse emptyResult = 9; +} + +message BatchEmptyResponse { + string resultMessage = 2; +} + +//Interface CreateOp +message CreateOpRequest { + string objectClass = 1; + bytes createAttributes = 2; + //repeated AttributeMessage createAttributes = 2; + bytes options = 3; +} + +message CreateOpResponse { + Uid uid = 1; +} + +//Interface ConnectorEventSubscriptionOp +message ConnectorEventSubscriptionOpRequest { + string objectClass = 1; + bytes eventFilter = 2; + bytes options = 3; +} + +message ConnectorEventSubscriptionOpResponse { + ConnectorObject connectorObject = 1; + bool completed = 2; +} + +//Interface DeleteOp +message DeleteOpRequest { + string objectClass = 1; + Uid uid = 2; + bytes options = 3; +} + +message DeleteOpResponse { +} + +//Interface GetOp +message GetOpRequest { + string objectClass = 1; + Uid uid = 2; + bytes options = 3; +} + +message GetOpResponse { + bytes connectorObject = 1; +} + +//Interface ResolveUsernameOp +message ResolveUsernameOpRequest { + string objectClass = 1; + string username = 2; + bytes options = 3; +} + +message ResolveUsernameOpResponse { + Uid uid = 1; +} + +//Interface SchemaOp +message SchemaOpRequest { +} + +message SchemaOpResponse { + bytes schema = 1; +} + +//Interface ScriptOnConnectorOp +message ScriptOnConnectorOpRequest { + ScriptContext scriptContext = 1; + bytes options = 2; +} + +message ScriptOnConnectorOpResponse { + bytes object = 1; +} + +//Interface ScriptOnResourceOp +message ScriptOnResourceOpRequest { + ScriptContext scriptContext = 1; + bytes options = 2; +} + +message ScriptOnResourceOpResponse { + bytes object = 1; +} + +//Interface SearchOp +message SearchOpRequest { + string objectClass = 1; + bytes filter = 2; + //FilterUnionMessage filter = 2; + bytes options = 3; +} + +message SearchOpResponse { + int64 sequence = 1; + SearchResult result = 2; + ConnectorObject connectorObject = 3; +} + +//Interface SyncOp +message SyncOpRequest { + message Sync { + string objectClass = 1; + SyncToken token = 2; + bytes options = 3; + } + message LatestSyncToken { + string objectClass = 1; + } + Sync sync = 1; + LatestSyncToken latestSyncToken = 2; +} + +message SyncOpResponse { + message Sync { + int64 sequence = 1; + SyncToken syncToken = 2; + SyncDelta syncDelta = 3; + } + message LatestSyncToken { + SyncToken syncToken = 1; + } + Sync sync = 1; + LatestSyncToken latestSyncToken = 2; +} + +//Interface SyncEventSubscriptionOp +message SyncEventSubscriptionOpRequest { + string objectClass = 1; + SyncToken token = 2; + bytes options = 3; +} + +message SyncEventSubscriptionOpResponse { + SyncDelta syncDelta = 1; + bool completed = 2; +} + +//Interface TestOp +message TestOpRequest { +} + +message TestOpResponse { +} + +//Interface UpdateOp +message UpdateOpRequest { + enum UpdateType { + REPLACE = 0; + ADD = 1; + REMOVE = 2; + } + string objectClass = 1; + Uid uid = 2; + UpdateType updateType = 3; + bytes replaceAttributes = 4; + //repeated AttributeMessage replaceAttributes = 4; + bytes options = 5; +} + +message UpdateOpResponse { + Uid uid = 1; +} + +//Interface ValidateApiOp +message ValidateOpRequest { +} + +message ValidateOpResponse { +} + +//Configuration Update Event +message ConfigurationChangeEvent { + bytes configurationPropertyChange = 1; +} + +//_______ + + + +message OperationRequest { + ConnectorKey connectorKey = 1; + bytes connectorFacadeKey = 2; + Locale locale = 3; + + //oneof request { + AuthenticateOpRequest authenticateOpRequest = 4; + CreateOpRequest createOpRequest = 5; + ConnectorEventSubscriptionOpRequest connectorEventSubscriptionOpRequest = 6; + DeleteOpRequest deleteOpRequest = 7; + GetOpRequest getOpRequest = 8; + ResolveUsernameOpRequest resolveUsernameOpRequest = 9; + SchemaOpRequest schemaOpRequest = 10; + ScriptOnConnectorOpRequest scriptOnConnectorOpRequest = 11; + ScriptOnResourceOpRequest scriptOnResourceOpRequest = 12; + SearchOpRequest searchOpRequest = 13; + SyncOpRequest syncOpRequest = 14; + SyncEventSubscriptionOpRequest syncEventSubscriptionOpRequest = 15; + TestOpRequest testOpRequest = 16; + UpdateOpRequest updateOpRequest = 17; + ValidateOpRequest ValidateOpRequest = 18; + ConfigurationChangeEvent configurationChangeEvent = 19; + BatchOpRequest batchOpRequest = 20; + //} +} + +message OperationResponse { + //oneof response { + AuthenticateOpResponse authenticateOpResponse = 1; + CreateOpResponse createOpResponse = 2; + ConnectorEventSubscriptionOpResponse connectorEventSubscriptionOpResponse = 3; + DeleteOpResponse deleteOpResponse = 4; + GetOpResponse GetOpResponse = 5; + ResolveUsernameOpResponse resolveUsernameOpResponse = 6; + SchemaOpResponse schemaOpResponse = 7; + ScriptOnConnectorOpResponse scriptOnConnectorOpResponse = 8; + ScriptOnResourceOpResponse scriptOnResourceOpResponse = 9; + SearchOpResponse searchOpResponse = 10; + SyncOpResponse syncOpResponse = 11; + SyncEventSubscriptionOpResponse syncEventSubscriptionOpResponse = 12; + TestOpResponse testOpResponse = 13; + UpdateOpResponse updateOpResponse = 14; + ValidateOpResponse validateOpResponse = 15; + BatchOpResult batchOpResult = 16; + //} +} diff --git a/dotnet/framework/FrameworkProtoBuf/protobuf/RPCMessages.proto b/dotnet/framework/FrameworkProtoBuf/protobuf/RPCMessages.proto new file mode 100755 index 00000000..540717fe --- /dev/null +++ b/dotnet/framework/FrameworkProtoBuf/protobuf/RPCMessages.proto @@ -0,0 +1,87 @@ +syntax = "proto3"; + +option csharp_namespace = "Org.ForgeRock.OpenICF.Common.ProtoBuf"; +package org.forgerock.openicf.common.protobuf; + +import "OperationMessages.proto"; + +//Handshake Message +message HandshakeMessage { + enum ServerType { + UNKNOWN = 0; + JAVA = 1; + DOTNET = 2; + } + string sessionId = 1; + ServerType serverType = 2; + bytes publicKey = 3; +} + +message ExceptionMessage { + message InnerCause { + string exceptionClass = 1; + string message = 2; + InnerCause cause = 3; + } + string exceptionClass = 1; + string message = 2; + InnerCause innerCause = 3; + string stackTrace = 4; +} + +message CancelOpRequest { + +} + +message ControlRequest { + enum InfoLevel { + CONNECTOR_INFO = 0; + CONTEXT_INFO = 1; + REQUESTS = 2; + } + repeated InfoLevel infoLevel = 1; + repeated int64 localRequestId = 2; + repeated int64 remoteRequestId = 3; +} + +message ControlResponse { + message StringMapFieldEntry { + //option map_entry = true; + string key = 1; + string value = 2; + } + repeated StringMapFieldEntry contextInfoMap = 1; + bytes connectorInfos = 2; + repeated int64 requestIds = 3; +} + +//Maximum 125 byte +message PingMessage { + +} + +message RPCRequest { + //oneof request { + HandshakeMessage handshakeMessage = 1; + ControlRequest controlRequest = 2; + OperationRequest operationRequest = 3; + CancelOpRequest cancelOpRequest = 4; + //} +} + +message RPCResponse { + //oneof response { + HandshakeMessage handshakeMessage = 1; + ControlResponse controlResponse = 2; + OperationResponse operationResponse = 3; + ExceptionMessage error = 4; + //} +} + +message RemoteMessage { + int64 messageId = 1; + //oneof message { + RPCRequest request = 2; + RPCResponse response = 3; + //} +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkProtoBuf/version.template b/dotnet/framework/FrameworkProtoBuf/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/FrameworkProtoBuf/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/FrameworkRPC/FrameworkRpc.csproj b/dotnet/framework/FrameworkRPC/FrameworkRpc.csproj new file mode 100755 index 00000000..33e44431 --- /dev/null +++ b/dotnet/framework/FrameworkRPC/FrameworkRpc.csproj @@ -0,0 +1,69 @@ + + + + + + Debug + AnyCPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2} + Library + Properties + Org.ForgeRock.OpenICF.Common.Rpc + FrameworkRpc + OpenICF Framework - Remote Procedure Call API + v4.5.2 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkRPC/Rpc.cs b/dotnet/framework/FrameworkRPC/Rpc.cs new file mode 100755 index 00000000..f6a786b4 --- /dev/null +++ b/dotnet/framework/FrameworkRPC/Rpc.cs @@ -0,0 +1,1105 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Org.ForgeRock.OpenICF.Common.RPC +{ + + #region AbstractLoadBalancingAlgorithm + + /// + /// A AbstractLoadBalancingAlgorithm is a base class to implementing different + /// LoadBalancingAlgorithm for multiple . + /// + public abstract class AbstractLoadBalancingAlgorithm : IRequestDistributor + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + protected internal readonly IList> RequestDistributors; + + protected internal AbstractLoadBalancingAlgorithm(IList> requestDistributors) + { + RequestDistributors = requestDistributors; + } + + /// + /// {@inheritDoc} + /// + public virtual bool Operational + { + get { return RequestDistributors.Any(e => e.Operational); } + } + + /// + /// {@inheritDoc} + /// + public virtual TR TrySubmitRequest(IRemoteRequestFactory requestFactory) + where TR : RemoteRequest + where TE : Exception + { + return TrySubmitRequest(InitialConnectionFactoryIndex, requestFactory); + } + + protected internal virtual TR TrySubmitRequest(int initialIndex, + IRemoteRequestFactory requestFactory) + where TR : RemoteRequest + where TE : Exception + { + int index = initialIndex; + int maxIndex = RequestDistributors.Count; + do + { + IRequestDistributor factory = RequestDistributors[index]; + TR result = factory.TrySubmitRequest(requestFactory); + if (null != result) + { + return result; + } + index = (index + 1)%maxIndex; + } while (index != initialIndex); + + /* + * All factories are offline so give up. + */ + return null; + } + + protected internal abstract int InitialConnectionFactoryIndex { get; } + } + + #endregion + + #region FailoverLoadBalancingAlgorithm + + /// + /// A fail-over load balancing algorithm provides fault tolerance across multiple + /// underlying s. + /// + /// This algorithm is typically used for load-balancing between data + /// centers, where there is preference to always forward connection requests to + /// the closest available data center. This algorithm contrasts with the + /// which is used for load-balancing + /// within a data center. + /// + /// + /// This algorithm selects s based on the order in + /// which they were provided during construction. More specifically, an attempt + /// to obtain a will always return the + /// + /// first + /// operational + /// + /// in the list. Applications should, + /// therefore, organize the connection factories such that the preferred + /// (usually the closest) appear before those which + /// are less preferred. + /// + /// + /// If a problem occurs that temporarily prevents connections from being obtained + /// for one of the , then this algorithm automatically + /// "fails over" to the next operational in the list. + /// If none of the are operational then a {@code null} + /// is returned to the client. + /// + /// + /// + /// + /// + public class FailoverLoadBalancingAlgorithm : AbstractLoadBalancingAlgorithm + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + public FailoverLoadBalancingAlgorithm(IList> requestDistributors) + : base(requestDistributors) + { + } + + protected internal override int InitialConnectionFactoryIndex + { + get { return 0; } + } + } + + #endregion + + #region LocalRequest + + /// + /// A LocalRequest represents a remotely requested procedure call locally. + ///

+ /// The and LocalRequest are the representation of the same + /// call on caller and receiver side. + ///

+ public abstract class LocalRequest : IDisposable + where TE : Exception + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + private readonly long _requestId; + + private readonly TP _remoteConnectionContext; + + protected internal LocalRequest(long requestId, TH socket) + { + _requestId = requestId; + _remoteConnectionContext = socket.RemoteConnectionContext; + _remoteConnectionContext.RemoteConnectionGroup.ReceiveRequest>(this); + } + + /// + /// Check if this object was {@ref Inconsistent}-ed and don't dispose. + /// + /// 'true' when object is still active or 'false' when this can be + /// disposed. + public abstract bool Check(); + + /// + /// Signs that the object state is inconsistent. + /// + public abstract void Inconsistent(); + + protected internal abstract bool TryHandleResult(TV result); + + protected internal abstract bool TryHandleError(TE error); + + protected internal abstract bool TryCancel(); + + public long RequestId + { + get { return _requestId; } + } + + public TP RemoteConnectionContext + { + get { return _remoteConnectionContext; } + } + + public void Dispose() + { + _remoteConnectionContext.RemoteConnectionGroup.RemoveRequest(RequestId); + TryCancel(); + } + + public void HandleResult(TV result) + { + _remoteConnectionContext.RemoteConnectionGroup.RemoveRequest(RequestId); + TryHandleResult(result); + } + + public void HandleError(TE error) + { + _remoteConnectionContext.RemoteConnectionGroup.RemoveRequest(RequestId); + TryHandleError(error); + } + + public virtual void HandleIncomingMessage(TH sourceConnection, Object message) + { + throw new NotSupportedException("This request does not supports"); + } + } + + #endregion + + #region MessageListener + + public interface IMessageListener + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + /// + /// + /// Invoked when the opening handshake has been completed for a specific + /// instance. + /// + /// + /// + /// the newly connected + /// + void OnConnect(TH socket); + + /// + /// + /// Invoked when has been called on a + /// particular instance. + /// + /// + /// + /// + /// + /// the being closed. + /// + /// + /// the closing code sent by the remote end-point. + /// + /// + /// the closing reason sent by the remote end-point. + /// + void OnClose(TH socket, int code, string reason); + + /// + /// + /// Invoked when the is open and an error + /// occurs processing the request. + /// + /// + /// + void OnError(Exception t); + + /// + /// + /// Invoked when has been + /// called on a particular instance. + /// + /// + /// + /// the that received a message. + /// + /// + /// the message received. + /// + void OnMessage(TH socket, byte[] data); + + /// + /// + /// Invoked when has been + /// called on a particular instance. + /// + /// + /// + /// the that received a message. + /// + /// + /// the message received. + /// + void OnMessage(TH socket, string data); + + /// + /// + /// Invoked when has been + /// called on a particular instance. + /// + /// + /// + /// the that received the ping. + /// + /// + /// the payload of the ping frame, if any. + /// + void OnPing(TH socket, byte[] bytes); + + /// + /// + /// Invoked when has been + /// called on a particular instance. + /// + /// + /// + /// the that received the pong. + /// + /// + /// the payload of the pong frame, if any. + /// + void OnPong(TH socket, byte[] bytes); + } + + #endregion + + #region RemoteConnectionContext + + /// + /// A RemoteConnectionContext is a custom context to provide application specific + /// information to create the + /// . + ///

+ /// The may depends on + /// which + /// distributes the request. Instance of this class is provided to + /// to produce the + /// before + /// + /// sending the message. + ///

+ public interface IRemoteConnectionContext + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + /// + /// Return the + ///

+ /// Return the + /// in which this instance belongs to. + ///

+ /// + /// the + /// in + /// which this instance belongs to. + /// + TG RemoteConnectionGroup { get; } + } + + #endregion + + #region RemoteConnectionGroup + + public abstract class RemoteConnectionGroup : IRequestDistributor + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + public abstract class AsyncMessageQueueRecord : IDisposable + { +#if DEBUG + private static Int32 _counter; + private Int32 _id; + + public Int32 Id + { + get + { + if (_id == 0) + { + _id = Interlocked.Increment(ref _counter); + } + return _id; + } + } +#endif + private Int32 _busy; + private bool _canFail; + private readonly HashSet _references = new HashSet(); + private readonly TaskCompletionSource _completionHandler = new TaskCompletionSource(); + + protected abstract Task DoSend(TH connection); + + public bool Accept(TH connection) + { + if (!_canFail && !_completionHandler.Task.IsCompleted) + { + return _references.Add(connection); + } + return false; + } + + /// + /// Called when ConnnectionHolder will not try to write this message any more + /// + public void Detach(TH connection) + { + if (_references.Remove(connection) && !_references.Any()) + { + NotifyFailureAndRecycle(new Exception("No Connection is available to send the message")); + } + } + + public async Task SendAsync(TimeSpan timeout) + { +#if DEBUG + Trace.TraceInformation("Sending Async Message {0}", Id); +#endif + _canFail = true; + if (!_completionHandler.Task.IsCompleted) + { + if (_busy == 1 || _references.Any()) + { + await Task.WhenAny(_completionHandler.Task, Task.Run(async () => + { + do + { + await Task.Delay(timeout); + _references.Clear(); + } while (_busy == 1); + NotifyFailureAndRecycle(new TimeoutException("WriteTimeout reached")); + })); + } + else + { + _completionHandler.SetException(new Exception("No Connection was available to send the message")); + } + } + return await _completionHandler.Task; + } + + /// + /// Called when ConnnectionHolder try to gain exclusive access to write this message. + /// + /// + public async Task AcquireAndTryComplete(TH connection) + { + if (null != connection && !_completionHandler.Task.IsCompleted && + Interlocked.Increment(ref _busy) == 1) + { + try + { + if (_references.Remove(connection)) + { +#if DEBUG + Debug.WriteLine("Sending message:{0} over connection:{1}:{2} bussy:{3}", Id, + connection.Id, connection.GetType().FullName, _busy); +#endif + await DoSend(connection); + //Sent successfully + NotifyCompleteAndRecycle(connection); +#if DEBUG + Debug.WriteLine("Sent message:{0} over connection:{1} completed:{2}", Id, + connection.Id, _completionHandler.Task.IsCompleted); +#endif + } + } + catch (Exception e) + { + if (!_references.Any()) + { + NotifyFailureAndRecycle(e); + } + } + finally + { + _busy = 0; + } + return true; + } + return _completionHandler.Task.IsCompleted; + } + + public void Dispose() + { + _completionHandler.SetCanceled(); + _references.Clear(); + Recycle(); + } + + protected virtual void NotifyFailureAndRecycle(Exception e) + { + if (_canFail && !_completionHandler.Task.IsCompleted) + { + _completionHandler.SetException(e); + _references.Clear(); + Recycle(); + } + } + + protected virtual void NotifyCompleteAndRecycle(TH connection) + { + _completionHandler.SetResult(connection); + _references.Clear(); + Recycle(); + } + + protected void Recycle() + { + } + } + + protected readonly ConcurrentDictionary RemoteRequests = + new ConcurrentDictionary(); + + protected readonly ConcurrentDictionary LocalRequests = + new ConcurrentDictionary(); + + protected readonly ConcurrentDictionary WebSockets = new ConcurrentDictionary(); + + private Int64 _messageId; + + protected RemoteConnectionGroup(string remoteSessionId) + { + RemoteSessionId = remoteSessionId; + } + + public abstract bool Operational { get; } + + public string RemoteSessionId { get; internal set; } + + protected internal abstract TP RemoteConnectionContext { get; } + + protected internal virtual Int64 NextRequestId + { + get + { + Int64 next = Interlocked.Increment(ref _messageId); + while (next == 0) + { + next = Interlocked.Increment(ref _messageId); + } + return next; + } + } + + protected internal virtual TR AllocateRequest( + IRemoteRequestFactory requestFactory) + where TR : RemoteRequest + where TE : Exception + { + Int64 newMessageId; + TR request; + Action> completionCallback = x => + { + dynamic ignored; + RemoteRequests.TryRemove(x.RequestId, out ignored); + }; + + do + { + newMessageId = NextRequestId; + request = requestFactory.CreateRemoteRequest(RemoteConnectionContext, newMessageId, completionCallback); + if (null == request) + { + break; + } + } while (!RemoteRequests.TryAdd(newMessageId, request)); + return request; + } + + public TV TrySendMessage(Func>, TV> function) + { + return function(messsage => + { + foreach (var connection in WebSockets.Keys) + { + connection.Enqueue(messsage); + } + return messsage.SendAsync(TimeSpan.FromMinutes(2)); + }); + } + + // -- Pair of methods to Submit and Receive the new Request Start -- + public virtual TR TrySubmitRequest(IRemoteRequestFactory requestFactory) + where TR : RemoteRequest + where TE : Exception + { + TR remoteRequest = AllocateRequest(requestFactory); + if (null != remoteRequest) + { + Task result = TrySendMessage(remoteRequest.SendFunction); + if (null == result) + { + dynamic ignored; + RemoteRequests.TryRemove(remoteRequest.RequestId, out ignored); + remoteRequest = null; + } + else if (remoteRequest.Promise == null) + { + Trace.TraceError("FATAL: Concurrency Problem! Prmoise can not be null if result is not null."); + } + } + return remoteRequest; + } + + public virtual TR ReceiveRequest(TR localRequest) + where TE : Exception + where TR : LocalRequest + { + TR tmp = LocalRequests.GetOrAdd(localRequest.RequestId, localRequest); + if (null != tmp && !tmp.Equals(localRequest)) + { + throw new InvalidOperationException("Request has been registered with id: " + localRequest.RequestId); + } + return localRequest; + } + + // -- Pair of methods to Submit and Receive the new Request End -- + // -- Pair of methods to Cancel pending Request Start -- + + public dynamic /*RemoteRequest*/ SubmitRequestCancel(Int64 messageId) + { + dynamic tmp; + RemoteRequests.TryRemove(messageId, out tmp); + if (null != tmp) + { + tmp.Cancel(); + } + return tmp; + } + + public dynamic /*LocalRequest*/ ReceiveRequestCancel(Int64 messageId) + { + dynamic tmp; + LocalRequests.TryRemove(messageId, out tmp); + if (null != tmp) + { + tmp.Cancel(); + } + return tmp; + } + + // -- Pair of methods to Cancel pending Request End -- + // -- Pair of methods to Communicate pending Request Start -- + public dynamic /*RemoteRequest*/ ReceiveRequestResponse(TH sourceConnection, + Int64 messageId, Object message) + { + dynamic tmp; + RemoteRequests.TryGetValue(messageId, out tmp); + if (null != tmp) + { + tmp.HandleIncomingMessage(sourceConnection, message); + } + return tmp; + } + + public dynamic /*LocalRequest*/ ReceiveRequestUpdate(TH sourceConnection, + Int64 messageId, Object message) + { + dynamic tmp; + LocalRequests.TryGetValue(messageId, out tmp); + if (null != tmp) + { + tmp.HandleIncomingMessage(sourceConnection, message); + } + return tmp; + } + + // -- Pair of methods to Communicate pending Request End -- + public dynamic /*LocalRequest*/ RemoveRequest(Int64 messageId) + { + dynamic value; + LocalRequests.TryRemove(messageId, out value); + return value; + } + } + + #endregion + + #region RemoteConnectionHolder + + /// + /// A RemoteConnectionHolder is a wrapper class for the underlying communication + /// chanel. + ///

+ /// The API is detached from the real underlying protocol and abstracted through + /// this interface. The message transmitted via this implementation should + /// trigger the appropriate method on + /// . + ///

+ public interface IRemoteConnectionHolder : IDisposable + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { +#if DEBUG + Int32 Id { get; } +#endif + + TP RemoteConnectionContext { get; } + + void Enqueue(RemoteConnectionGroup.AsyncMessageQueueRecord record); + + + /// + /// Initiates the asynchronous transmission of a binary message. This method + /// returns before the message is transmitted. Developers may use the + /// returned Future object to track progress of the transmission. + /// + /// + /// The data to be sent over the connection. + /// + /// The token that propagates the notification that operations should be canceled. + /// the Future object representing the send operation. + Task SendBytesAsync(byte[] data, CancellationToken cancellationToken); + + /// + /// Initiates the asynchronous transmission of a string message. This method + /// returns before the message is transmitted. Developers may use the + /// returned Future object to track progress of the transmission. + /// + /// + /// the data being sent + /// + /// The token that propagates the notification that operations should be canceled. + /// the Future object representing the send operation. + Task SendStringAsync(string data, CancellationToken cancellationToken); + + /// + /// Send a Ping message containing the given application data to the remote + /// endpoint. The corresponding Pong message may be picked up using the + /// MessageHandler.Pong handler. + /// + /// + /// the data to be carried in the ping request + /// + void SendPing(byte[] applicationData); + + /// + /// Allows the developer to send an unsolicited Pong message containing the + /// given application data in order to serve as a unidirectional heartbeat + /// for the session. + /// + /// + /// the application data to be carried in the pong response. + /// + void SendPong(byte[] applicationData); + } + + #endregion + + #region RemoteRequest + + /// + /// A RemoteRequest represents a locally requested procedure call executed + /// remotely. + ///

+ /// The RemoteRequest and are the representation of the same + /// call on caller and receiver side. + ///

+ public abstract class RemoteRequest : IDisposable + where TE : Exception + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + private readonly TP _context; + private readonly Int64 _requestId; + private readonly Action> _completionCallback; + + private TaskCompletionSource _promise; + private readonly CancellationToken _cancellationToken; + private readonly ReaderWriterLock _lock = new ReaderWriterLock(); + + protected RemoteRequest(TP context, Int64 requestId, + Action> completionCallback, CancellationToken cancellationToken) + { + _context = context; + _requestId = requestId; + _completionCallback = completionCallback; + _cancellationToken = cancellationToken; + } + + /// + /// Check if this object was {@ref Inconsistent}-ed and don't dispose. + /// + /// 'true' when object is still active or 'false' when this can be + /// disposed. + public abstract bool Check(); + + /// + /// Signs that the object state is inconsistent. + /// + public abstract void Inconsistent(); + + public abstract void HandleIncomingMessage(TH sourceConnection, Object message); + + protected internal abstract RemoteConnectionGroup.AsyncMessageQueueRecord CreateMessageElement( + TP remoteContext, long requestId); + + protected internal abstract void TryCancelRemote(TP remoteContext, Int64 requestId); + protected internal abstract TE CreateCancellationException(Exception cancellationException); + + public virtual Int64 RequestId + { + get { return _requestId; } + } + + public virtual Int64 RequestTime { get; internal set; } + + public virtual Task Promise + { + get { return _promise.Task; } + } + + /// + /// Invoked when the asynchronous task has completed successfully. + /// + /// + /// The result of the asynchronous task. + /// + protected internal virtual void HandleResult(TV result) + { + if (!_promise.Task.IsCompleted) + { + try + { + _promise.SetResult(result); + } + catch (InvalidOperationException) + { + //An attempt was made to transition a task to a final state when it had already completed. + } + } + } + + /// + /// Invoked when the asynchronous task has failed. + /// + /// + /// The error indicating why the asynchronous task has failed. + /// + protected internal virtual void HandleError(Exception error) + { + if (!_promise.Task.IsCompleted) + { + try + { + _promise.SetException(error); + } + catch (InvalidOperationException) + { + //An attempt was made to transition a task to a final state when it had already completed. + } + } + } + + protected internal virtual TP ConnectionContext + { + get { return _context; } + } + + + public CancellationToken CancellationToken + { + get { return _cancellationToken; } + } + + public void Dispose() + { + try + { + TryCancelRemote(_context, _requestId); + } + catch (Exception) + { + //Ignore + } + } + + public virtual Func.AsyncMessageQueueRecord, Task>, Task> + SendFunction + { + get + { + return async send => + { + TaskCompletionSource resultPromise = _promise; + if (null == resultPromise) + { + RemoteConnectionGroup.AsyncMessageQueueRecord message = + CreateMessageElement(_context, _requestId); + if (message == null) + { + throw new InvalidOperationException("RemoteRequest has empty message"); + } + + try + { + // Single thread should process it so it should not fail + _lock.AcquireWriterLock(new TimeSpan(0, 1, 0)); + IDisposable cancellationTokenRegistration = null; + try + { + if (null == _promise) + { + _promise = new TaskCompletionSource(); + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + cancellationTokenRegistration = _cancellationToken.Register(promise => + { + var p = promise as TaskCompletionSource; + if (null != p) + { + p.TrySetCanceled(); + } + Dispose(); + }, _promise); + _promise.Task.ContinueWith(x => { _completionCallback(this); }) + .ConfigureAwait(false); + if (_cancellationToken.IsCancellationRequested) + { + _promise.SetCanceled(); + } + else + { + await send(message); + } + RequestTime = DateTime.Now.Ticks; + } + } + catch (Exception) + { + _promise = null; + if (null != cancellationTokenRegistration) + { + cancellationTokenRegistration.Dispose(); + } + throw; + } + finally + { + _lock.ReleaseWriterLock(); + } + } + catch (ApplicationException) + { + // The writer lock request timed out. + } + + return null != _promise ? await _promise.Task : default(TV); + } + return await resultPromise.Task; + }; + } + } + } + + #endregion + + #region RemoteRequestFactory + + /// + /// A RemoteRequestFactory creates a new + /// aware + /// before sending in + /// . + /// + public interface IRemoteRequestFactory + where TR : RemoteRequest + where TE : Exception + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + TR CreateRemoteRequest(TP context, long requestId, Action> completionCallback); + } + + #endregion + + #region RequestDistributor + + /// + /// A RequestDistributor delivers the + /// to the connected + /// endpoint. + ///

+ /// The is used to + /// create a + /// aware which will be + /// delivered. + ///

+ /// The implementation may hold multiple transmission channels and try all to + /// deliver the message before if fails. + ///

+ /// The failed delivery signaled with null empty to avoid the expensive Throw and + /// Catch especially when many implementation are chained together. + ///

+ /// + /// + /// + public interface IRequestDistributor + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + /// + /// the factory to create the + /// + /// + /// + /// type of + /// + /// + /// The type of the task's result, or if the task + /// does not return anything (i.e. it only has side-effects). + /// + /// + /// The type of the exception thrown by the task if it fails + /// + /// new promise if succeeded otherwise {@code null}. + TR TrySubmitRequest(IRemoteRequestFactory requestFactory) + where TR : RemoteRequest + where TE : Exception; + + /// + /// Check if this implementation is operational. + /// + /// + /// {@code true} is operational and ready the submit + /// + /// otherwise {@code false}. + /// + bool Operational { get; } + } + + #endregion + + #region RoundRobinLoadBalancingAlgorithm + + /// + /// A round robin load balancing algorithm distributes + /// s across a list of + /// s one at a time. When the end of the list is + /// reached, the algorithm starts again from the beginning. + /// + /// This algorithm is typically used for load-balancing within data + /// centers, where load must be distributed equally across multiple servers. This + /// algorithm contrasts with the which is + /// used for load-balancing between data centers. + /// + /// + /// If a problem occurs that temporarily prevents connections from being obtained + /// for one of the s, then this algorithm automatically + /// "fails over" to the next operational in the list. + /// If none of the are operational then a {@code null} + /// is returned to the client. + /// + /// + /// + /// + /// + public class RoundRobinLoadBalancingAlgorithm : AbstractLoadBalancingAlgorithm + where TG : RemoteConnectionGroup + where TH : IRemoteConnectionHolder + where TP : IRemoteConnectionContext + { + private Int32 _counter; + + public RoundRobinLoadBalancingAlgorithm(IList> requestDistributors) + : base(requestDistributors) + { + } + + protected internal override int InitialConnectionFactoryIndex + { + get + { + // A round robin pool of one connection factories is unlikely in + // practice and requires special treatment. + int maxSize = RequestDistributors.Count(); + if (maxSize == 1) + { + return 0; + } + return (Interlocked.Increment(ref _counter) & 0x7fffffff)%maxSize; + } + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkRPC/version.template b/dotnet/framework/FrameworkRPC/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/FrameworkRPC/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/Async.cs b/dotnet/framework/FrameworkServer/Async.cs new file mode 100755 index 00000000..f47a35c9 --- /dev/null +++ b/dotnet/framework/FrameworkServer/Async.cs @@ -0,0 +1,663 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + + #region IAsyncConnectorFacade + + /// + /// A AsyncConnectorFacade. + /// + /// since 1.5 + public interface IAsyncConnectorFacade : ConnectorFacade, IAuthenticationAsyncApiOp, ICreateAsyncApiOp, + IDeleteAsyncApiOp, IGetAsyncApiOp, IResolveUsernameAsyncApiOp, ISchemaAsyncApiOp, IScriptOnConnectorAsyncApiOp, + IScriptOnResourceAsyncApiOp, ITestAsyncApiOp, IUpdateAsyncApiOp, + IValidateAsyncApiOp + { + } + + #endregion + + #region IAsyncConnectorInfoManager + + /// + /// An IAsyncConnectorInfoManager maintains a list of ConnectorInfo + /// instances, each of which describes a connector that is available. + /// + /// since 1.5 + public interface IAsyncConnectorInfoManager : ConnectorInfoManager + { + /// + /// Add a promise which will be fulfilled with the + /// for the given + /// {@ConnectorKey}. + /// Add a Promise which will be fulfilled immediately if the + /// is maintained + /// currently by this instance or later when it became available. + /// + /// + /// new promise + Task FindConnectorInfoAsync(ConnectorKey key); + + /// + /// Add a promise which will be fulfilled with the + /// for the given + /// {@ConnectorKeyRange}. + /// Add a Promise which will be fulfilled immediately if the + /// is maintained + /// currently by this instance or later when it became available. + /// There may be multiple ConnectorInfo matching the range. The + /// implementation can only randomly fulfill the promise. It can not grantee + /// the highest version to return because it may became available after the + /// promised added and after a lower version of ConnectorInfo became + /// available in this manager. + /// + /// + /// new promise + Task FindConnectorInfoAsync(ConnectorKeyRange keyRange); + } + + #endregion + + #region IAuthenticationAsyncApiOp + + public interface IAuthenticationAsyncApiOp : AuthenticationApiOp + { + /// + /// Most basic authentication available. + /// + /// + /// The object class to use for authenticate. Will typically be an + /// account. Must not be null. + /// + /// + /// string that represents the account or user id. + /// + /// + /// string that represents the password for the account or user. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// Uid The uid of the account that was used to authenticate + /// + /// if the credentials do not pass authentication otherwise + /// nothing. + /// + Task AuthenticateAsync(ObjectClass objectClass, String username, GuardedString password, + OperationOptions options, CancellationToken cancellationToken); + } + + #endregion + + #region DisposableAsyncConnectorInfoManager + + public abstract class DisposableAsyncConnectorInfoManager : IAsyncConnectorInfoManager, IDisposable + where T : DisposableAsyncConnectorInfoManager + { + internal Int32 IsRunning = 1; + + protected abstract void DoClose(); + + public virtual bool Running + { + get { return IsRunning != 0; } + } + + public void Dispose() + { + if (CanCloseNow()) + { + try + { + DoClose(); + } + catch (Exception) + { + //logger.ok(t, "Failed to close {0}", this); + } + // Notify CloseListeners + OnDisposed(); + } + } + + protected internal virtual bool CanCloseNow() + { + return (Interlocked.CompareExchange(ref IsRunning, 0, 1) == 1); + } + + /// + /// Adds a event handler to listen to the Disposed event on the DisposableAsyncConnectorInfoManager. + /// + private event EventHandler DisposedEvent; + + public event EventHandler Disposed + { + add + { + // check if this is still running + if (IsRunning == 1) + { + // add close listener + DisposedEvent += value; + // check the its state again + if (DisposedEvent != null && (IsRunning != 1 && DisposedEvent.GetInvocationList().Contains(value))) + { + // if this was closed during the method call - notify the + // listener + try + { + value(this, EventArgs.Empty); + } + catch (Exception) + { + // ignored + } + } + } // if this is closed - notify the listener + else + { + try + { + value(this, EventArgs.Empty); + } + catch (Exception) + { + // ignored + } + } + } + remove { DisposedEvent -= value; } + } + + protected virtual void OnDisposed() + { + try + { + var handler = DisposedEvent; + if (handler != null) handler(this, EventArgs.Empty); + } + catch (Exception) + { + //logger.ok(ignored, "CloseListener failed"); + } + } + + + public abstract IList ConnectorInfos { get; } + public abstract ConnectorInfo FindConnectorInfo(ConnectorKey key); + public abstract Task FindConnectorInfoAsync(ConnectorKey key); + public abstract Task FindConnectorInfoAsync(ConnectorKeyRange keyRange); + } + + #endregion + + #region ICreateAsyncApiOp + + public interface ICreateAsyncApiOp : CreateApiOp + { + /// + /// Create a target object based on the specified attributes. + /// The Connector framework always requires attribute + /// ObjectClass. The Connector itself may require + /// additional attributes. The API will confirm that the set contains the + /// ObjectClass attribute and that no two attributes in the set + /// have the same . + /// + /// + /// the type of object to create. Must not be null. + /// + /// + /// includes all the attributes necessary to create the target + /// object (including the ObjectClass attribute). + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// + /// the unique id for the object that is created. For instance in + /// LDAP this would be the 'dn', for a database this would be the + /// primary key, and for 'ActiveDirectory' this would be the GUID. + /// + /// + /// if ObjectClass is missing or elements of the set + /// produce duplicate values of . + /// + /// + /// if the parameter createAttributes is + /// null. + /// + /// + /// if the + /// SPI throws a native . + /// + Task CreateAsync(ObjectClass objectClass, ICollection createAttributes, + OperationOptions options, CancellationToken cancellationToken); + } + + #endregion + + #region IDeleteAsyncApiOp + + public interface IDeleteAsyncApiOp : DeleteApiOp + { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// + /// type of object to delete. + /// + /// + /// The unique id that specifies the object to delete. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// + /// if the + /// + /// does not exist on the resource. + /// + /// + /// if a problem occurs during the operation (for instance, an + /// operational timeout). + /// + Task DeleteAsync(ObjectClass objectClass, Uid uid, OperationOptions options, CancellationToken cancellationToken); + } + + #endregion + + #region IGetAsyncApiOp + + public interface IGetAsyncApiOp : GetApiOp + { + /// + /// Get a particular + /// + /// based on the . + /// + /// + /// type of object to get. + /// + /// + /// the unique id of the object that to get. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// + /// + /// based on the + /// + /// provided or null if no such object could be found. + /// + Task GetObjectAsync(ObjectClass objectClass, Uid uid, OperationOptions options, + CancellationToken cancellationToken); + } + + #endregion + + #region IResolveUsernameAsyncApiOp + + public interface IResolveUsernameAsyncApiOp : ResolveUsernameApiOp + { + /// + /// Resolve the given + /// {@link org.identityconnectors.framework.api.operations.AuthenticationApiOp + /// authentication} username to the corresponding + /// . + /// The Uid is the one that + /// + /// would return in case of a successful authentication. + /// + /// + /// The object class to use for authenticate. Will typically be an + /// account. Must not be null. + /// + /// + /// string that represents the account or user id. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// Uid The uid of the account that would be used to authenticate. + /// + /// if the username could not be resolved. + /// @since 1.5 + /// + Task ResolveUsernameAsync(ObjectClass objectClass, String username, OperationOptions options, + CancellationToken cancellationToken); + } + + #endregion + + #region ISchemaAsyncApiOp + + public interface ISchemaAsyncApiOp : SchemaApiOp + { + /// + /// Retrieve the basic schema of this + /// . + /// + /// + Task SchemaAsync(CancellationToken cancellationToken); + } + + #endregion + + #region IScriptOnConnectorAsyncApiOp + + public interface IScriptOnConnectorAsyncApiOp : ScriptOnConnectorApiOp + { + /// + /// Runs the script. + /// + /// + /// The script and arguments to run. + /// + /// + /// Additional options that control how the script is run. The + /// framework does not currently recognize any options but + /// specific connectors might. Consult the documentation for each + /// connector to identify supported options. + /// + /// + /// + /// The result of the script. The return type must be a type that the + /// framework supports for serialization. + /// + Task RunScriptOnConnectorAsync(ScriptContext request, OperationOptions options, + CancellationToken cancellationToken); + } + + #endregion + + #region IScriptOnResourceAsyncApiOp + + public interface IScriptOnResourceAsyncApiOp : ScriptOnResourceApiOp + { + /// + /// Runs a script on a specific target resource. + /// + /// + /// The script and arguments to run. + /// + /// + /// Additional options which control how the script is run. Please + /// refer to the connector documentation for supported options. + /// + /// + /// + /// The result of the script. The return type must be a type that the + /// connector framework supports for serialization. See + /// + /// for a list of supported return types. + /// + Task RunScriptOnResourceAsync(ScriptContext request, OperationOptions options, + CancellationToken cancellationToken); + } + + #endregion + + #region ITestAsyncApiOp + + public interface ITestAsyncApiOp : TestApiOp + { + /// + /// Tests the {@link org.identityconnectors.framework.api.APIConfiguration + /// Configuration} with the connector. + /// + /// + /// + /// if the configuration is not valid or the test failed. + /// + Task TestAsync(CancellationToken cancellationToken); + } + + #endregion + + #region IUpdateAsyncApiOp + + public interface IUpdateAsyncApiOp : UpdateApiOp + { + /// + /// Update the object specified by the + /// and + /// , replacing + /// the current values of each attribute with the values provided. + /// + /// For each input attribute, replace all of the current values of that + /// attribute in the target object with the values of that attribute. + /// + /// + /// If the target object does not currently contain an attribute that the + /// input set contains, then add this attribute (along with the provided + /// values) to the target object. + /// + /// + /// If the value of an attribute in the input set is {@code null}, then do + /// one of the following, depending on which is most appropriate for the + /// target: + ///
    + ///
  • + /// If possible, remove that attribute from the target object + /// entirely. + ///
  • + ///
  • + /// Otherwise, replace all of the current values of that + /// attribute in the target object with a single value of {@code null}. + ///
  • + ///
+ ///
+ ///
+ /// + /// the type of object to modify. Must not be null. + /// + /// + /// the uid of the object to modify. Must not be null. + /// + /// + /// set of new + /// + /// . the values in this set represent the new, merged values to + /// be applied to the object. This set may also include + /// {@link Org.IdentityConnectors.Framework.Common.Objects.OperationalAttributes + /// operational attributes}. Must not be null. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// + /// the + /// of the updated object in case the update changes the formation of + /// the unique identifier. + /// + /// + /// if the + /// + /// does not exist on the resource. + /// + Task UpdateAsync(ObjectClass objectClass, Uid uid, ICollection replaceAttributes, + OperationOptions options, CancellationToken cancellationToken); + + /// + /// Update the object specified by the and , + /// adding to the current values of each attribute the values provided. + /// + /// For each attribute that the input set contains, add to the current values + /// of that attribute in the target object all of the values of that + /// attribute in the input set. + /// + /// + /// NOTE that this does not specify how to handle duplicate values. The + /// general assumption for an attribute of a {@code ConnectorObject} is that + /// the values for an attribute may contain duplicates. Therefore, in general + /// simply append the provided values to the current value for each + /// attribute. + /// + /// + /// IMPLEMENTATION NOTE: for connectors that merely implement + /// and not + /// + /// this method will be simulated by fetching, merging, and calling + /// + /// . Therefore, connector implementations are encourage to implement + /// + /// from a performance and atomicity standpoint. + /// + /// + /// + /// the type of object to modify. Must not be null. + /// + /// + /// the uid of the object to modify. Must not be null. + /// + /// + /// set of deltas. The values for the attributes + /// in this set represent the values to add to attributes in the + /// object. merged. This set must not include + /// {@link Org.IdentityConnectors.Framework.Common.Objects.OperationalAttributes + /// operational attributes}. Must not be null. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// + /// + /// if the does not exist on the resource. + /// + Task AddAttributeValuesAsync(ObjectClass objclass, Uid uid, ICollection valuesToAdd, + OperationOptions options, CancellationToken cancellationToken); + + /// + /// Update the object specified by the and , + /// removing from the current values of each attribute the values provided. + /// + /// For each attribute that the input set contains, remove from the current + /// values of that attribute in the target object any value that matches one + /// of the values of the attribute from the input set. + /// + /// + /// NOTE that this does not specify how to handle unmatched values. The + /// general assumption for an attribute of a {@code ConnectorObject} is that + /// the values for an attribute are merely representational state. + /// Therefore, the implementer should simply ignore any provided value that + /// does not match a current value of that attribute in the target object. + /// Deleting an unmatched value should always succeed. + /// + /// + /// IMPLEMENTATION NOTE: for connectors that merely implement + /// and not + /// + /// this method will be simulated by fetching, merging, and calling + /// + /// . Therefore, connector implementations are encourage to implement + /// + /// from a performance and atomicity standpoint. + /// + /// + /// + /// the type of object to modify. Must not be null. + /// + /// + /// the uid of the object to modify. Must not be null. + /// + /// + /// set of deltas. The values for the attributes + /// in this set represent the values to remove from attributes in + /// the object. merged. This set must not include + /// {@link Org.IdentityConnectors.Framework.Common.Objects.OperationalAttributes + /// operational attributes}. Must not be null. + /// + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// + /// + /// + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// + /// + /// if the does not exist on the resource. + /// + Task RemoveAttributeValuesAsync(ObjectClass objclass, Uid uid, + ICollection valuesToRemove, OperationOptions options, + CancellationToken cancellationToken); + } + + #endregion + + #region IValidateAsyncApiOp + + public interface IValidateAsyncApiOp : ValidateApiOp + { + /// + /// Validates the + /// {@link org.identityconnectors.framework.api.APIConfiguration + /// configuration}. + /// + /// + /// + /// if the configuration is not valid. + /// + Task ValidateAsync(CancellationToken cancellationToken); + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/AsyncImpl.cs b/dotnet/framework/FrameworkServer/AsyncImpl.cs new file mode 100755 index 00000000..fede411f --- /dev/null +++ b/dotnet/framework/FrameworkServer/AsyncImpl.cs @@ -0,0 +1,2989 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using API = Org.IdentityConnectors.Framework.Api; +using PRB = Org.ForgeRock.OpenICF.Common.ProtoBuf; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + + #region AbstractAPIOperation + + public abstract class AbstractAPIOperation + { + public static readonly ConnectionFailedException FailedExceptionMessage = + new ConnectionFailedException("No remote Connector Server is available at this moment"); + + protected AbstractAPIOperation( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + { + RemoteConnection = Assertions.NullChecked(remoteConnection, "remoteConnection"); + ConnectorKey = Assertions.NullChecked(connectorKey, "connectorKey"); + FacadeKeyFunction = Assertions.NullChecked(facadeKeyFunction, "facadeKeyFunction"); + Timeout = Math.Max(timeout, API.APIConstants.NO_TIMEOUT); + } + + public API.ConnectorKey ConnectorKey { get; internal set; } + + public long Timeout { get; internal set; } + + protected internal + IRequestDistributor + RemoteConnection { get; internal set; } + + protected internal Func FacadeKeyFunction { get; internal set; } + + + protected internal virtual Task SubmitRequest( + AbstractRemoteOperationRequestFactory requestFactory) + where TR : AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + { + TR request = RemoteConnection.TrySubmitRequest(requestFactory); + if (null != request) + { + return request.Promise; + } + TaskCompletionSource result = new TaskCompletionSource(); + result.SetException(FailedExceptionMessage); + return result.Task; + } + } + + internal abstract class ResultBuffer + { + internal class Pair + { + public T Item { get; set; } + public TR Result { get; set; } + } + + private readonly int _timeoutMillis; + private volatile Boolean _stopped = false; + private readonly BlockingCollection _queue = new BlockingCollection(new ConcurrentQueue()); + + private readonly ConcurrentDictionary _buffer = new ConcurrentDictionary(); + + private Int64 _nextPermit = 1; + + private long _lastSequenceNumber = -1; + + protected ResultBuffer(long timeoutMillis) + { + + if (timeoutMillis == API.APIConstants.NO_TIMEOUT) + { + _timeoutMillis = int.MaxValue; + } + else if (timeoutMillis == 0) + { + _timeoutMillis = 60 * 1000; + } + else + { + _timeoutMillis = unchecked((int)timeoutMillis); + } + } + + public virtual bool Stopped + { + get + { + return _stopped; + } + } + + public virtual bool HasLast() + { + return _lastSequenceNumber > 0; + } + + public virtual bool HasAll() + { + return HasLast() && _nextPermit > _lastSequenceNumber; + } + + public virtual int Remaining + { + get + { + return _queue.Count; + } + } + + public virtual void Clear() + { + _stopped = true; + _buffer.Clear(); + Pair item; + while (_queue.TryTake(out item)) + { + // do nothing + } + } + + public virtual void ReceiveNext(long sequence, T result) + { + if (null != result) + { + if (_nextPermit == sequence) + { + Enqueue(new Pair { Item = result }); + } + else + { + _buffer[sequence] = new Pair { Item = result }; + } + } + // Feed the queue + Pair o = null; + while ((_buffer.TryRemove(_nextPermit, out o))) + { + Enqueue(o); + } + } + + public virtual void ReceiveLast(long resultCount, TR result) + { + if (0 == resultCount && null != result) + { + // Empty result set + Enqueue(new Pair { Result = result }); + _lastSequenceNumber = 0; + } + else + { + long idx = resultCount + 1; + _buffer[idx] = new Pair { Result = result }; + _lastSequenceNumber = idx; + } + + if (_lastSequenceNumber == _nextPermit) + { + // Operation finished + Enqueue(new Pair { Result = result }); + } + else + { + ReceiveNext(_nextPermit, default(T)); + } + } + + protected internal virtual void Enqueue(Pair result) + { + // Block if queue is full + if (_queue.TryAdd(result)) + { + // Let the next go through + _nextPermit++; + } + else + { + // What to do? + Trace.TraceInformation("Failed to Enqueue: "); + } + } + + protected internal abstract bool Handle(object result); + + + public void Process() + { + while (!_stopped) + { + + Pair obj; + + if (!_queue.TryTake(out obj, _timeoutMillis)) + { + // we timed out + ReceiveNext(-1L, default(T)); + if (_queue.Any()) + { + Clear(); + throw new OperationTimeoutException(); + } + } + else + { + try + { + Boolean keepGoing = Handle(obj); + if (!keepGoing) + { + // stop and wait + Clear(); + } + } + catch (Exception t) + { + Clear(); + throw new ConnectorException(t.Message, t); + } + } + } + } + } + + #endregion + + #region AbstractLocalOperationProcessor + + public abstract class AbstractLocalOperationProcessor : LocalOperationProcessor + { + protected readonly TM _requestMessage; + + protected internal AbstractLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, TM message) + : base(requestId, socket) + { + _requestMessage = message; + } + + protected internal abstract TV ExecuteOperation(API.ConnectorFacade connectorFacade, TM requestMessage); + + public virtual void Execute(API.ConnectorFacade connectorFacade) + { + try + { + HandleResult(ExecuteOperation(connectorFacade, _requestMessage)); + } + catch (Exception error) + { + HandleError(error); + } + } + } + + #endregion + + #region AbstractRemoteOperationRequestFactory + + public abstract class AbstractRemoteOperationRequestFactory : + IRemoteRequestFactory + + where TR : RemoteOperationRequest + { + private readonly IdentityConnectors.Framework.Api.ConnectorKey _connectorKey; + + private readonly Func _facadeKeyFunction; + + protected AbstractRemoteOperationRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, CancellationToken cancellationToken) + { + _connectorKey = connectorKey; + _facadeKeyFunction = facadeKeyFunction; + CancellationToken = cancellationToken; + } + + protected CancellationToken CancellationToken { get; private set; } + + protected Common.ProtoBuf.ConnectorKey CreateConnectorKey() + { + return new + Common.ProtoBuf.ConnectorKey + { + BundleName = _connectorKey.BundleName, + BundleVersion = _connectorKey.BundleVersion, + ConnectorName = _connectorKey.ConnectorName + }; + } + + protected internal ByteString CreateConnectorFacadeKey(RemoteOperationContext context) + { + return _facadeKeyFunction(context); + } + + protected internal abstract PRB.OperationRequest CreateOperationRequest(RemoteOperationContext remoteContext); + + protected internal virtual PRB.RPCRequest CreateRpcRequest(RemoteOperationContext context) + { + ByteString facadeKey = CreateConnectorFacadeKey(context); + if (null != facadeKey) + { + PRB.OperationRequest operationBuilder = CreateOperationRequest(context); + operationBuilder.ConnectorKey = CreateConnectorKey(); + operationBuilder.ConnectorFacadeKey = facadeKey; + operationBuilder.Locale = + MessagesUtil.SerializeMessage(Thread.CurrentThread.CurrentUICulture); + return new PRB.RPCRequest { OperationRequest = operationBuilder }; + } + return null; + } + + public abstract class AbstractRemoteOperationRequest : RemoteOperationRequest + { + public const string OperationExpectsMessage = "RemoteOperation[{0}] expects {1}"; + + internal readonly PRB.RPCRequest Request; + + protected AbstractRemoteOperationRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + + > completionCallback, PRB.RPCRequest requestBuilder, CancellationToken cancellationToken) + : base(context, requestId, completionCallback, cancellationToken) + { + Request = requestBuilder; + } + + protected internal abstract TM GetOperationResponseMessages(PRB.OperationResponse message); + + protected internal abstract void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + TM message); + + protected internal override PRB.RPCRequest CreateOperationRequest(RemoteOperationContext remoteContext) + { + return Request; + } + + protected internal override bool HandleResponseMessage(WebSocketConnectionHolder sourceConnection, + Object message) + { + var response = message as PRB.OperationResponse; + if (response != null) + { + TM responseMessage = GetOperationResponseMessages(response); + if (null != responseMessage) + { + try + { + HandleOperationResponseMessages(sourceConnection, responseMessage); + } + catch (Exception e) + { + Debug.WriteLine("Failed to handle the result of operation {0}", e); + HandleError(e); + } + return true; + } + } + return false; + } + } + + public abstract TR CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > + completionCallback); + } + + #endregion + + #region AuthenticationAsyncApiOpImpl + + public class AuthenticationAsyncApiOpImpl : AbstractAPIOperation, IAuthenticationAsyncApiOp + { + public AuthenticationAsyncApiOpImpl( + IRequestDistributor + remoteConnection, IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, + OperationOptions options) + { + try + { + return AuthenticateAsync(objectClass, username, password, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task AuthenticateAsync(ObjectClass objectClass, string username, GuardedString password, + OperationOptions options, CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(username, "username"); + Assertions.NullCheck(password, "password"); + + PRB.AuthenticateOpRequest requestBuilder = + new PRB.AuthenticateOpRequest + { + ObjectClass = objectClass.GetObjectClassValue(), + Username = username, + Password = MessagesUtil.SerializeLegacy(password) + }; + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + return + await + SubmitRequest( + new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { AuthenticateOpRequest = requestBuilder }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(Org.IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, + PRB.OperationRequest operationRequest, CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + return null; + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.AuthenticateOpResponse GetOperationResponseMessages( + PRB.OperationResponse message) + { + if (null != message.AuthenticateOpResponse) + { + return message.AuthenticateOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "AuthenticateOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.AuthenticateOpResponse message) + { + HandleResult(null != message.Uid + ? MessagesUtil.DeserializeMessage(message.Uid) + : null); + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.AuthenticateOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.AuthenticateOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Uid result) + { + PRB.AuthenticateOpResponse response = new PRB.AuthenticateOpResponse(); + if (null != result) + { + response.Uid = MessagesUtil.SerializeMessage(result); + } + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + AuthenticateOpResponse = response + } + }; + } + + protected internal override Uid ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.AuthenticateOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + return connectorFacade.Authenticate(objectClass, requestMessage.Username, + MessagesUtil.DeserializeLegacy(requestMessage.Password), operationOptions); + } + } + } + + #endregion + + #region CreateAsyncApiOpImpl + + public class CreateAsyncApiOpImpl : AbstractAPIOperation, ICreateAsyncApiOp + { + public CreateAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual Uid Create(ObjectClass objectClass, ICollection createAttributes, + OperationOptions options) + { + try + { + return CreateAsync(objectClass, createAttributes, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public Task CreateAsync(ObjectClass objectClass, ICollection createAttributes, + OperationOptions options, CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(createAttributes, "createAttributes"); + // check to make sure there's not a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(createAttributes) != null) + { + throw new InvalidAttributeValueException("Parameter 'createAttributes' contains a uid."); + } + + PRB.CreateOpRequest requestBuilder = new + PRB.CreateOpRequest + { + ObjectClass = objectClass.GetObjectClassValue(), + CreateAttributes = MessagesUtil.SerializeLegacy(createAttributes) + }; + + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return + SubmitRequest(new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { CreateOpRequest = requestBuilder }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalCreateRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalCreateRequest(context, requestId, completionCallback, builder, CancellationToken); + } + return null; + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + internal class InternalCreateRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalCreateRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.CreateOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.CreateOpResponse) + { + return message.CreateOpResponse; + } + Trace.TraceInformation(OperationExpectsMessage, RequestId, "CreateOpResponse"); + return null; + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.CreateOpResponse message) + { + HandleResult(null != message.Uid ? MessagesUtil.DeserializeMessage(message.Uid) : null); + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.CreateOpRequest message) + { + return new InternalCreateLocalOperationProcessor(requestId, socket, message); + } + + private class InternalCreateLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal InternalCreateLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.CreateOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Uid result) + { + PRB.CreateOpResponse response = new PRB.CreateOpResponse(); + if (null != result) + { + response.Uid = MessagesUtil.SerializeMessage(result); + } + + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + CreateOpResponse = response + } + }; + } + + protected internal override Uid ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.CreateOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + var attributes = MessagesUtil.DeserializeLegacy>(requestMessage.CreateAttributes); + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + return connectorFacade.Create(objectClass, CollectionUtil.NewSet(attributes), + operationOptions); + } + } + } + + #endregion + + #region DeleteAsyncApiOpImpl + + public class DeleteAsyncApiOpImpl : AbstractAPIOperation, IDeleteAsyncApiOp + { + public DeleteAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual void Delete(ObjectClass objectClass, Uid uid, OperationOptions options) + { + try + { + DeleteAsync(objectClass, uid, options, CancellationToken.None).Wait(); + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public Task DeleteAsync(ObjectClass objectClass, Uid uid, OperationOptions options, + CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(uid, "uid"); + + PRB.DeleteOpRequest requestBuilder = + new PRB.DeleteOpRequest + { + ObjectClass = objectClass.GetObjectClassValue(), + Uid = MessagesUtil.SerializeMessage(uid) + }; + + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + return SubmitRequest( + new InternalRequestFactory(ConnectorKey, FacadeKeyFunction, new + PRB.OperationRequest { DeleteOpRequest = requestBuilder }, cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalDeleteRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalDeleteRequest(context, requestId, completionCallback, builder, CancellationToken); + } + return null; + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalDeleteRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalDeleteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.DeleteOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.DeleteOpResponse) + { + return message.DeleteOpResponse; + } + Trace.TraceInformation(OperationExpectsMessage, RequestId, "DeleteOpResponse"); + return null; + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.DeleteOpResponse message) + { + HandleResult(null); + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.DeleteOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.DeleteOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Object result) + { + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + DeleteOpResponse = new PRB.DeleteOpResponse() + } + }; + } + + protected internal override Object ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.DeleteOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + Uid uid = MessagesUtil.DeserializeMessage(requestMessage.Uid); + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + connectorFacade.Delete(objectClass, uid, operationOptions); + return null; + } + } + } + + #endregion + + #region GetAsyncApiOpImpl + + public class GetAsyncApiOpImpl : AbstractAPIOperation, IGetAsyncApiOp + { + public GetAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual ConnectorObject GetObject(ObjectClass objectClass, Uid uid, OperationOptions options) + { + try + { + return GetObjectAsync(objectClass, uid, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public Task GetObjectAsync(ObjectClass objectClass, Uid uid, OperationOptions options, + CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(uid, "uid"); + + PRB.GetOpRequest requestBuilder = + new PRB.GetOpRequest + { + ObjectClass = objectClass.GetObjectClassValue(), + Uid = MessagesUtil.SerializeMessage(uid) + }; + + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return + SubmitRequest( + new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { GetOpRequest = requestBuilder }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.GetOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.GetOpResponse) + { + return message.GetOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "GetOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.GetOpResponse message) + { + HandleResult(null != message.ConnectorObject + ? MessagesUtil.DeserializeLegacy(message.ConnectorObject) + : null); + } + } + + // ------- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.GetOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.GetOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, ConnectorObject result) + { + PRB.GetOpResponse response = new PRB.GetOpResponse(); + if (null != result) + { + response.ConnectorObject = MessagesUtil.SerializeLegacy(result); + } + + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + GetOpResponse = response + } + }; + } + + protected internal override ConnectorObject ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.GetOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + Uid uid = MessagesUtil.DeserializeMessage(requestMessage.Uid); + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + return connectorFacade.GetObject(objectClass, uid, operationOptions); + } + } + } + + #endregion + + #region ResolveUsernameAsyncApiOpImpl + + public class ResolveUsernameAsyncApiOpImpl : AbstractAPIOperation, IResolveUsernameAsyncApiOp + { + public ResolveUsernameAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options) + { + try + { + return ResolveUsernameAsync(objectClass, username, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task ResolveUsernameAsync(ObjectClass objectClass, string username, OperationOptions options, + CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(username, "username"); + + PRB.ResolveUsernameOpRequest requestBuilder = + new PRB.ResolveUsernameOpRequest + { + ObjectClass = objectClass.GetObjectClassValue(), + Username = username + }; + + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return await + SubmitRequest( + new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { ResolveUsernameOpRequest = requestBuilder }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.ResolveUsernameOpResponse GetOperationResponseMessages( + PRB.OperationResponse message) + { + if (null != message.ResolveUsernameOpResponse) + { + return message.ResolveUsernameOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "ResolveUsernameOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.ResolveUsernameOpResponse message) + { + if (null != message.Uid) + { + HandleResult(MessagesUtil.DeserializeMessage(message.Uid)); + } + else + { + HandleResult(null); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.ResolveUsernameOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.ResolveUsernameOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Uid result) + { + PRB.ResolveUsernameOpResponse response = new PRB.ResolveUsernameOpResponse(); + if (null != result) + { + response.Uid = MessagesUtil.SerializeMessage(result); + } + + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + ResolveUsernameOpResponse = response + } + }; + } + + protected internal override Uid ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.ResolveUsernameOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + return connectorFacade.ResolveUsername(objectClass, requestMessage.Username, operationOptions); + } + } + } + + #endregion + + #region SchemaAsyncApiOpImpl + + public class SchemaAsyncApiOpImpl : AbstractAPIOperation, ISchemaAsyncApiOp + { + public SchemaAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual Schema Schema() + { + try + { + return SchemaAsync(CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task SchemaAsync(CancellationToken cancellationToken) + { + return await + SubmitRequest(new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, + new PRB.OperationRequest { SchemaOpRequest = new PRB.SchemaOpRequest() }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.SchemaOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.SchemaOpResponse) + { + return message.SchemaOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "SchemaOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.SchemaOpResponse message) + { + if (null != message.Schema) + { + HandleResult(MessagesUtil.DeserializeLegacy(message.Schema)); + } + else + { + HandleResult(null); + } + } + } + + // ------- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.SchemaOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.SchemaOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, ByteString result) + { + PRB.SchemaOpResponse response = new PRB.SchemaOpResponse(); + if (null != result) + { + response.Schema = result; + } + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + SchemaOpResponse = response + } + }; + } + + protected internal override ByteString ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.SchemaOpRequest requestMessage) + { + Schema schema = connectorFacade.Schema(); + if (null != schema) + { + return MessagesUtil.SerializeLegacy(schema); + } + return null; + } + } + } + + #endregion + + #region ScriptOnConnectorAsyncApiOpImpl + + public class ScriptOnConnectorAsyncApiOpImpl : AbstractAPIOperation, IScriptOnConnectorAsyncApiOp + { + public ScriptOnConnectorAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual Object RunScriptOnConnector(ScriptContext request, OperationOptions options) + { + try + { + return RunScriptOnConnectorAsync(request, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public Task RunScriptOnConnectorAsync(ScriptContext request, OperationOptions options, + CancellationToken cancellationToken) + { + Assertions.NullCheck(request, "request"); + + PRB.ScriptOnConnectorOpRequest requestBuilder = new PRB.ScriptOnConnectorOpRequest(); + + requestBuilder.ScriptContext = MessagesUtil.SerializeMessage(request); + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return + SubmitRequest( + new InternalRequestFactory(ConnectorKey, FacadeKeyFunction, + new PRB.OperationRequest { ScriptOnConnectorOpRequest = requestBuilder }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.ScriptOnConnectorOpResponse GetOperationResponseMessages( + PRB.OperationResponse message) + { + if (null != message.ScriptOnConnectorOpResponse) + { + return message.ScriptOnConnectorOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "ScriptOnConnectorOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.ScriptOnConnectorOpResponse message) + { + if (null != message.Object) + { + HandleResult(MessagesUtil.DeserializeLegacy(message.Object)); + } + else + { + HandleResult(null); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor( + long requestId, WebSocketConnectionHolder socket, PRB.ScriptOnConnectorOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.ScriptOnConnectorOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, ByteString result) + { + PRB.ScriptOnConnectorOpResponse response = new PRB.ScriptOnConnectorOpResponse(); + if (null != result) + { + response.Object = result; + } + + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + ScriptOnConnectorOpResponse = response + } + }; + } + + protected internal override ByteString ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.ScriptOnConnectorOpRequest requestMessage) + { + ScriptContext request = MessagesUtil.DeserializeMessage(requestMessage.ScriptContext); + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + object result = connectorFacade.RunScriptOnConnector(request, operationOptions); + if (null != result) + { + return MessagesUtil.SerializeLegacy(result); + } + return null; + } + } + } + + #endregion + + #region ScriptOnResourceAsyncApiOpImpl + + public class ScriptOnResourceAsyncApiOpImpl : AbstractAPIOperation, IScriptOnResourceAsyncApiOp + { + public ScriptOnResourceAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual object RunScriptOnResource(ScriptContext request, OperationOptions options) + { + try + { + return RunScriptOnResourceAsync(request, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task RunScriptOnResourceAsync(ScriptContext request, OperationOptions options, + CancellationToken cancellationToken) + { + Assertions.NullCheck(request, "request"); + + PRB.ScriptOnResourceOpRequest requestBuilder = new PRB.ScriptOnResourceOpRequest + { + ScriptContext = MessagesUtil.SerializeMessage(request) + }; + + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return await + SubmitRequest( + new InternalRequestFactory(ConnectorKey, FacadeKeyFunction, + new PRB.OperationRequest { ScriptOnResourceOpRequest = requestBuilder }, cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.ScriptOnResourceOpResponse GetOperationResponseMessages( + PRB.OperationResponse message) + { + if (null != message.ScriptOnResourceOpResponse) + { + return message.ScriptOnResourceOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "ScriptOnResourceOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.ScriptOnResourceOpResponse message) + { + if (null != message.Object) + { + HandleResult(MessagesUtil.DeserializeLegacy(message.Object)); + } + else + { + HandleResult(null); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor( + long requestId, WebSocketConnectionHolder socket, PRB.ScriptOnResourceOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.ScriptOnResourceOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, ByteString result) + { + PRB.ScriptOnResourceOpResponse response = new PRB.ScriptOnResourceOpResponse(); + if (null != result) + { + response.Object = result; + } + + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + ScriptOnResourceOpResponse = response + } + }; + } + + protected internal override ByteString ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.ScriptOnResourceOpRequest requestMessage) + { + ScriptContext request = MessagesUtil.DeserializeMessage(requestMessage.ScriptContext); + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + object result = connectorFacade.RunScriptOnResource(request, operationOptions); + if (null != result) + { + return MessagesUtil.SerializeLegacy(result); + } + return null; + } + } + } + + #endregion + + #region SearchAsyncApiOpImpl + + public class SearchAsyncApiOpImpl : AbstractAPIOperation, SearchApiOp + { + public SearchAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual SearchResult Search(ObjectClass objectClass, Filter filter, ResultsHandler handler, + OperationOptions options) + { + try + { + return SearchAsync(objectClass, filter, handler, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task SearchAsync(ObjectClass objectClass, Filter filter, ResultsHandler handler, + OperationOptions options, CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(handler, "handler"); + + PRB.SearchOpRequest requestBuilder = new + PRB.SearchOpRequest { ObjectClass = objectClass.GetObjectClassValue() }; + if (filter != null) + { + requestBuilder.Filter = MessagesUtil.SerializeLegacy(filter); + } + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return await + SubmitRequest( + new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { SearchOpRequest = requestBuilder }, handler, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + private readonly ResultsHandler _handler; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + ResultsHandler handler, CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + _handler = handler; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + // This is the context aware request + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, _handler, + CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + private readonly ResultsHandler _handler; + private Int64 _sequence; + private Int64 _expectedResult = -1; + private SearchResult _result; + + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + ResultsHandler handler, CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + _handler = handler; + } + + protected internal override PRB.SearchOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.SearchOpResponse) + { + return message.SearchOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "SearchOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.SearchOpResponse message) + { + if (null != message.ConnectorObject) + { + try + { + ConnectorObject co = MessagesUtil.DeserializeMessage(message.ConnectorObject); + + if (!_handler.Handle(co) && !Promise.IsCompleted) + { + HandleError(new ConnectorException("ResultsHandler stopped processing results")); + TryCancelRemote(ConnectionContext, RequestId); + } + } + finally + { + Interlocked.Increment(ref _sequence); + } + } + else + { + if (null != message.Result) + { + _result = MessagesUtil.DeserializeMessage(message.Result); + } + _expectedResult = message.Sequence; + if (_expectedResult == 0 || _sequence == _expectedResult) + { + HandleResult(_result); + } + else + { + Trace.TraceInformation("Response processed before all result has arrived"); + } + } + if (_expectedResult > 0 && _sequence == _expectedResult) + { + HandleResult(_result); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor( + long requestId, WebSocketConnectionHolder socket, PRB.SearchOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor + { + private Int32 _doContinue = 1; + private Int64 _sequence; + + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.SearchOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, PRB.SearchOpResponse response) + { + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + SearchOpResponse = response + } + }; + } + + protected internal override PRB.SearchOpResponse ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.SearchOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + Filter filter = null; + if (null != requestMessage.Filter) + { + filter = MessagesUtil.DeserializeLegacy(requestMessage.Filter); + } + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + SearchResult searchResult = connectorFacade.Search(objectClass, filter, new ResultsHandler() + { + Handle = connectorObject => + { + if (null != connectorObject) + { + PRB.SearchOpResponse result = + new PRB.SearchOpResponse + { + ConnectorObject = + MessagesUtil.SerializeMessage(connectorObject), + Sequence = Interlocked.Increment(ref _sequence) + }; + + if (TryHandleResult(result)) + { + Trace.TraceInformation("SearchResult sent in sequence:{0}", _sequence); + } + else + { + Trace.TraceInformation("Failed to send response {0}", _sequence); + } + } + return _doContinue == 1; + } + }, operationOptions); + + PRB.SearchOpResponse response = new PRB.SearchOpResponse { Sequence = _sequence }; + if (null != searchResult) + { + response.Result = MessagesUtil.SerializeMessage(searchResult); + } + return response; + } + + protected override bool TryCancel() + { + _doContinue = 0; + return base.TryCancel(); + } + } + } + + #endregion + + #region SyncAsyncApiOpImpl + + public class SyncAsyncApiOpImpl : AbstractAPIOperation, SyncApiOp + { + public SyncAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + try + { + return GetLatestSyncTokenAsync(objectClass, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public virtual SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, + OperationOptions options) + { + try + { + return SyncAsync(objectClass, token, handler, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task GetLatestSyncTokenAsync(ObjectClass objectClass, + CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + return await + SubmitRequest(new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, + new PRB.OperationRequest + { + SyncOpRequest = + new PRB.SyncOpRequest + { + LatestSyncToken = new PRB.SyncOpRequest.Types.LatestSyncToken + { + ObjectClass = objectClass.GetObjectClassValue() + } + } + }, null, cancellationToken)); + } + + public Task SyncAsync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, + OperationOptions options, CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(handler, "handler"); + PRB.SyncOpRequest.Types.Sync requestBuilder = + new PRB.SyncOpRequest.Types.Sync { ObjectClass = objectClass.GetObjectClassValue() }; + if (token != null) + { + requestBuilder.Token = MessagesUtil.SerializeMessage(token); + } + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return + SubmitRequest(new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, + new PRB.OperationRequest + { + SyncOpRequest = new PRB.SyncOpRequest { Sync = requestBuilder } + }, handler, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + private readonly SyncResultsHandler _handler; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + SyncResultsHandler handler, CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + _handler = handler; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, _handler, + CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + private readonly SyncResultsHandler _handler; + private Int64 _sequence; + private Int64 _expectedResult = -1; + private SyncToken _result; + + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + SyncResultsHandler handler, CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + _handler = handler; + } + + protected internal override PRB.SyncOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.SyncOpResponse) + { + return message.SyncOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "SyncOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.SyncOpResponse message) + { + if (null != message.LatestSyncToken) + { + if (null != message.LatestSyncToken.SyncToken) + { + HandleResult(MessagesUtil.DeserializeMessage(message.LatestSyncToken.SyncToken)); + } + else + { + HandleResult(null); + } + } + else if (null != message.Sync) + { + Trace.TraceInformation("SyncOp Response received in sequence:{0} of {1}", message.Sync.Sequence, + _sequence); + if (null != message.Sync.SyncDelta) + { + try + { + SyncDelta delta = + MessagesUtil.DeserializeMessage( + message.Sync.SyncDelta); + + if (!_handler.Handle(delta) && !Promise.IsCompleted) + { + HandleError(new ConnectorException("SyncResultsHandler stopped processing results")); + TryCancelRemote(ConnectionContext, RequestId); + } + } + finally + { + Interlocked.Increment(ref _sequence); + } + } + else + { + if (null != message.Sync.SyncToken) + { + _result = MessagesUtil.DeserializeMessage(message.Sync.SyncToken); + } + _expectedResult = message.Sync.Sequence; + if (_expectedResult == 0 || _sequence == _expectedResult) + { + HandleResult(_result); + } + else + { + Trace.TraceInformation("Response processed before all result has arrived"); + } + } + if (_expectedResult > 0 && _sequence == _expectedResult) + { + HandleResult(_result); + } + } + else + { + Trace.TraceInformation("Invalid SyncOpResponse Response:{0}", RequestId); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor( + long requestId, WebSocketConnectionHolder socket, PRB.SyncOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor + { + private Int32 _doContinue = 1; + private Int64 _sequence; + + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.SyncOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, PRB.SyncOpResponse response) + { + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + SyncOpResponse = response + } + }; + } + + protected internal override PRB.SyncOpResponse ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.SyncOpRequest requestMessage) + { + if (null != requestMessage.LatestSyncToken) + { + ObjectClass objectClass = new ObjectClass(requestMessage.LatestSyncToken.ObjectClass); + SyncToken token = connectorFacade.GetLatestSyncToken(objectClass); + + // Enable returnNullTest + PRB.SyncOpResponse.Types.LatestSyncToken builder = new + PRB.SyncOpResponse.Types.LatestSyncToken(); + if (null != token) + { + builder.SyncToken = MessagesUtil.SerializeMessage(token); + } + return new PRB.SyncOpResponse { LatestSyncToken = builder }; + } + else if (null != requestMessage.Sync) + { + ObjectClass objectClass = new ObjectClass(requestMessage.Sync.ObjectClass); + SyncToken token = MessagesUtil.DeserializeMessage(requestMessage.Sync.Token); + + OperationOptions operationOptions = null; + if (null != requestMessage.Sync.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Sync.Options); + } + + SyncToken syncResult = connectorFacade.Sync(objectClass, token, new SyncResultsHandler() + { + Handle = delta => + { + if (null != delta) + { + PRB.SyncOpResponse result = + new PRB.SyncOpResponse + { + Sync = + new PRB.SyncOpResponse.Types.Sync + { + SyncDelta = MessagesUtil.SerializeMessage(delta), + Sequence = Interlocked.Increment(ref _sequence) + } + }; + + if (TryHandleResult(result)) + { + Trace.TraceInformation("SyncResult sent in sequence:{0}", _sequence); + } + else + { + Trace.TraceInformation("Failed to send response {0}", _sequence); + } + } + return _doContinue == 1; + } + }, operationOptions); + + PRB.SyncOpResponse.Types.Sync builder = + new PRB.SyncOpResponse.Types.Sync { Sequence = _sequence }; + if (null != syncResult) + { + builder.SyncToken = MessagesUtil.SerializeMessage(syncResult); + } + + return new PRB.SyncOpResponse { Sync = builder }; + } + else + { + Trace.TraceInformation("Invalid SyncOpRequest Request:{0}", RequestId); + } + return null; + } + + + protected override bool TryCancel() + { + _doContinue = 0; + return base.TryCancel(); + } + } + } + + #endregion + + #region ConnectorEventSubscriptionApiOpImpl + + #endregion + + #region TestAsyncApiOpImpl + + public class TestAsyncApiOpImpl : AbstractAPIOperation, ITestAsyncApiOp + { + public TestAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual void Test() + { + try + { + TestAsync(CancellationToken.None).Wait(); + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task TestAsync(CancellationToken cancellationToken) + { + await SubmitRequest(new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { TestOpRequest = new PRB.TestOpRequest() }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.TestOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.TestOpResponse) + { + return message.TestOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "TestOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.TestOpResponse message) + { + HandleResult(null); + } + + public override string ToString() + { + return "Test Request {}" + RequestId; + } + } + + // ---- + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.TestOpRequest message) + { + return new TestLocalOperationProcessor(requestId, socket, message); + } + + private class TestLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal TestLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.TestOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Object response) + { + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + TestOpResponse = new PRB.TestOpResponse() + } + }; + } + + protected internal override Object ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.TestOpRequest requestMessage) + { + connectorFacade.Test(); + return null; + } + } + } + + #endregion + + #region UpdateAsyncApiOpImpl + + public class UpdateAsyncApiOpImpl : AbstractAPIOperation, IUpdateAsyncApiOp + { + public UpdateAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual Uid Update(ObjectClass objectClass, Uid uid, ICollection replaceAttributes, + OperationOptions options) + { + try + { + return UpdateAsync(objectClass, uid, replaceAttributes, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public virtual Uid AddAttributeValues(ObjectClass objectClass, Uid uid, + ICollection valuesToAdd, OperationOptions options) + { + try + { + return AddAttributeValuesAsync(objectClass, uid, valuesToAdd, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public virtual Uid RemoveAttributeValues(ObjectClass objectClass, Uid uid, + ICollection valuesToRemove, OperationOptions options) + { + try + { + return + RemoveAttributeValuesAsync(objectClass, uid, valuesToRemove, options, CancellationToken.None).Result; + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task UpdateAsync(ObjectClass objectClass, Uid uid, + ICollection replaceAttributes, OperationOptions options, + CancellationToken cancellationToken) + { + return + await + DoUpdate(objectClass, uid, PRB.UpdateOpRequest.Types.UpdateType.REPLACE, replaceAttributes, options, + cancellationToken); + } + + public async Task AddAttributeValuesAsync(ObjectClass objectClass, Uid uid, + ICollection valuesToAdd, OperationOptions options, CancellationToken cancellationToken) + { + return await DoUpdate(objectClass, uid, PRB.UpdateOpRequest.Types.UpdateType.ADD, valuesToAdd, options, + cancellationToken); + } + + public async Task RemoveAttributeValuesAsync(ObjectClass objectClass, Uid uid, + ICollection valuesToRemove, OperationOptions options, + CancellationToken cancellationToken) + { + return + await DoUpdate(objectClass, uid, PRB.UpdateOpRequest.Types.UpdateType.REMOVE, valuesToRemove, options, + cancellationToken); + } + + public async Task DoUpdate(ObjectClass objectClass, Uid uid, + PRB.UpdateOpRequest.Types.UpdateType updateType, + ICollection replaceAttributes, OperationOptions options, + CancellationToken cancellationToken) + { + UpdateImpl.ValidateInput(objectClass, uid, replaceAttributes, + !PRB.UpdateOpRequest.Types.UpdateType.REPLACE.Equals(updateType)); + PRB.UpdateOpRequest requestBuilder = + new PRB.UpdateOpRequest + { + ObjectClass = objectClass.GetObjectClassValue(), + Uid = MessagesUtil.SerializeMessage(uid), + UpdateType = updateType, + ReplaceAttributes = MessagesUtil.SerializeLegacy(replaceAttributes) + }; + + + if (options != null) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return await + SubmitRequest(new InternalRequestFactory(ConnectorKey, + FacadeKeyFunction, new PRB.OperationRequest { UpdateOpRequest = requestBuilder }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.UpdateOpResponse GetOperationResponseMessages(PRB.OperationResponse message) + { + if (null != message.UpdateOpResponse) + { + return message.UpdateOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "UpdateOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.UpdateOpResponse message) + { + if (null != message.Uid) + { + HandleResult(MessagesUtil.DeserializeMessage(message.Uid)); + } + else + { + HandleResult(null); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.UpdateOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.UpdateOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Uid result) + { + PRB.UpdateOpResponse response = new PRB.UpdateOpResponse(); + if (null != result) + { + response.Uid = MessagesUtil.SerializeMessage(result); + } + + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + UpdateOpResponse = response + } + }; + } + + protected internal override Uid ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.UpdateOpRequest requestMessage) + { + ObjectClass objectClass = new ObjectClass(requestMessage.ObjectClass); + Uid uid = MessagesUtil.DeserializeMessage(requestMessage.Uid); + + var attributes = MessagesUtil.DeserializeLegacy>(requestMessage.ReplaceAttributes); + + OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + + switch (requestMessage.UpdateType) + { + case PRB.UpdateOpRequest.Types.UpdateType.REPLACE: + return connectorFacade.Update(objectClass, uid, + CollectionUtil.NewSet(attributes), operationOptions); + case PRB.UpdateOpRequest.Types.UpdateType.ADD: + return connectorFacade.AddAttributeValues(objectClass, uid, + CollectionUtil.NewSet(attributes), operationOptions); + case PRB.UpdateOpRequest.Types.UpdateType.REMOVE: + return connectorFacade.RemoveAttributeValues(objectClass, uid, + CollectionUtil.NewSet(attributes), operationOptions); + default: + Trace.TraceInformation("Invalid UpdateOpRequest#UpdateType Request:{0}", RequestId); + break; + } + + return null; + } + } + } + + #endregion + + #region ValidateAsyncApiOpImpl + + public class ValidateAsyncApiOpImpl : AbstractAPIOperation, IValidateAsyncApiOp + { + public ValidateAsyncApiOpImpl( + IRequestDistributor + remoteConnection, API.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual void Validate() + { + try + { + ValidateAsync(CancellationToken.None).Wait(); + } + catch (AggregateException e) + { + throw e.InnerException; + } + } + + public async Task ValidateAsync(CancellationToken cancellationToken) + { + await SubmitRequest( + new InternalRequestFactory(ConnectorKey, FacadeKeyFunction, + new PRB.OperationRequest { ValidateOpRequest = new PRB.ValidateOpRequest() }, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly PRB.OperationRequest _operationRequest; + + public InternalRequestFactory(API.ConnectorKey connectorKey, + Func facadeKeyFunction, PRB.OperationRequest operationRequest, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + PRB.RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, CancellationToken); + } + else + { + return null; + } + } + + protected internal override PRB.OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, PRB.RPCRequest requestBuilder, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + } + + protected internal override PRB.ValidateOpResponse GetOperationResponseMessages( + PRB.OperationResponse message) + { + if (null != message.ValidateOpResponse) + { + return message.ValidateOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "ValidateOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + PRB.ValidateOpResponse message) + { + HandleResult(null); + } + } + + // ---- + + public static AbstractLocalOperationProcessor CreateProcessor(long requestId, + WebSocketConnectionHolder socket, PRB.ValidateOpRequest message) + { + return new ValidateLocalOperationProcessor(requestId, socket, message); + } + + private class ValidateLocalOperationProcessor : AbstractLocalOperationProcessor + { + protected internal ValidateLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + PRB.ValidateOpRequest message) + : base(requestId, socket, message) + { + } + + protected override PRB.RPCResponse CreateOperationResponse( + RemoteOperationContext remoteContext, Object result) + { + return new + PRB.RPCResponse + { + OperationResponse = new PRB.OperationResponse + { + ValidateOpResponse = new PRB.ValidateOpResponse() + } + }; + } + + protected internal override Object ExecuteOperation(API.ConnectorFacade connectorFacade, + PRB.ValidateOpRequest requestMessage) + { + connectorFacade.Validate(); + return null; + } + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/Client.cs b/dotnet/framework/FrameworkServer/Client.cs new file mode 100755 index 00000000..57104d96 --- /dev/null +++ b/dotnet/framework/FrameworkServer/Client.cs @@ -0,0 +1,598 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Org.ForgeRock.OpenICF.Common.ProtoBuf; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + + #region ClientRemoteConnectorInfoManager + + public class ClientRemoteConnectorInfoManager : ConnectionPrincipal, IRemoteConnectorInfoManager + { + private readonly RemoteDelegatingAsyncConnectorInfoManager _delegatingAsyncConnectorInfoManager; + + private readonly WebSocketWrapper[] _connections = new WebSocketWrapper[2]; + + private readonly RemoteWSFrameworkConnectionInfo _info; + + private readonly Timer _timer; + + public ClientRemoteConnectorInfoManager(RemoteWSFrameworkConnectionInfo info, + IMessageListener listener, + ConcurrentDictionary globalConnectionGroups) + : base(listener, globalConnectionGroups) + { + _delegatingAsyncConnectorInfoManager = + new RemoteDelegatingAsyncConnectorInfoManager(this); + + _info = info; + _timer = new Timer(state => + { + WebSocketConnectionHolder[] connections = state as WebSocketConnectionHolder[]; + if (null != connections) + { + for (int i = 0; i < 2; i++) + { + if (connections[i] == null || !connections[i].Operational) + { + WebSocketWrapper vr = WebSocketWrapper.Create(_info, listener, Handshake); + vr.ConnectAsync().ContinueWith((result, o) => + { + if (result.IsCompleted) + { + connections[(int)o] = result.Result; + } + else if (result.IsFaulted) + { + TraceUtil.TraceException("Failed to establish connection", result.Exception); + } + }, i); + } + } + } + }, _connections, TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(5)); + } + + protected override void DoClose() + { + _timer.Dispose(); + foreach (var connection in _connections) + { + connection.Dispose(); + } + } + + public IAsyncConnectorInfoManager AsyncConnectorInfoManager + { + get { return _delegatingAsyncConnectorInfoManager; } + } + + public IRequestDistributor + RequestDistributor + { + get { return this; } + } + + protected override void OnNewWebSocketConnectionGroup(WebSocketConnectionGroup connectionGroup) + { + Trace.TraceInformation("Activating new ConnectionGroup {0}:{1}", Identity.Name, + connectionGroup.RemoteSessionId); + _delegatingAsyncConnectorInfoManager.OnAddAsyncConnectorInfoManager(connectionGroup); + } + + private sealed class RemoteDelegatingAsyncConnectorInfoManager : DelegatingAsyncConnectorInfoManager + { + private readonly ClientRemoteConnectorInfoManager _parent; + + public RemoteDelegatingAsyncConnectorInfoManager(ClientRemoteConnectorInfoManager parent) + : base(true) + { + _parent = parent; + } + + protected override + IRequestDistributor + MessageDistributor + { + get { return _parent.RequestDistributor; } + } + + protected override void DoClose() + { + _parent.Dispose(); + } + + protected override IReadOnlyCollection Delegates + { + get { return _parent.ConnectionGroups.Values as IReadOnlyCollection; } + } + } + } + + #endregion + + #region ConnectionManagerConfig + + public class ConnectionManagerConfig + { + } + + #endregion + + #region RemoteConnectionInfoManagerFactory + + public class RemoteConnectionInfoManagerFactory : IDisposable + { + private readonly IMessageListener + _messageListener; + + protected readonly ConnectionManagerConfig managerConfig; + + protected readonly ConcurrentDictionary connectionGroups = + new ConcurrentDictionary(); + + private readonly ConcurrentDictionary + registry = + new ConcurrentDictionary(); + + private readonly Timer _timer; + + internal RemoteConnectionInfoManagerFactory( + IMessageListener + messageListener, + ConnectionManagerConfig managerConfig) + { + this._messageListener = messageListener; + this.managerConfig = managerConfig; + + + _timer = new Timer(state => + { + if (Running) + { + foreach (var connectionGroup in connectionGroups.Values) + { + Trace.TraceInformation("Check ConnectionGroup:{0} - operational={1}", connectionGroup.RemoteSessionId + , connectionGroup.Operational); + connectionGroup.CheckIsActive(); + } + } + }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(4)); + } + + public IRemoteConnectorInfoManager Connect(RemoteWSFrameworkConnectionInfo info) + { + if (Running) + { + ClientRemoteConnectorInfoManager manager; + registry.TryGetValue(info, out manager); + if (null == manager) + { + lock (registry) + { + registry.TryGetValue(info, out manager); + if (null == manager) + { + manager = new ClientRemoteConnectorInfoManager(info, MessageListener, connectionGroups); + manager.Disposed += (sender, args) => + { + ClientRemoteConnectorInfoManager ignore; + registry.TryRemove(info, out ignore); + }; + + registry.TryAdd(info, manager); + if (!Running && registry.TryRemove(info, out manager)) + { + manager.Dispose(); + throw new InvalidOperationException("RemoteConnectionInfoManagerFactory is shut down"); + } + } + } + } + return manager; + } + throw new InvalidOperationException("RemoteConnectionInfoManagerFactory is shut down"); + } + + + protected internal void doClose() + { + _timer.Dispose(); + foreach (var clientRemoteConnectorInfoManager in registry.Values) + { + clientRemoteConnectorInfoManager.Dispose(); + } + } + + protected internal virtual + IMessageListener + MessageListener + { + get { return _messageListener; } + } + + protected internal virtual ConnectionManagerConfig ManagerConfig + { + get { return managerConfig; } + } + + private Int32 _isRunning = 1; + + public virtual bool Running + { + get { return _isRunning != 0; } + } + + public void Dispose() + { + if (Interlocked.CompareExchange(ref _isRunning, 0, 1) == 0) + { + doClose(); + // Notify CloseListeners + /* + CloseListener closeListener; + while ((closeListener = closeListeners.RemoveFirst()) != null) + { + invokeCloseListener(closeListener); + }*/ + } + } + } + + #endregion + + #region IRemoteConnectorInfoManager + + public interface IRemoteConnectorInfoManager : IDisposable + { + IAsyncConnectorInfoManager AsyncConnectorInfoManager { get; } + + IRequestDistributor + RequestDistributor { get; } + } + + #endregion + + #region RemoteWSFrameworkConnectionInfo + + public class RemoteWSFrameworkConnectionInfo + { + public const string OPENICF_PROTOCOL = "v1.openicf.forgerock.org"; + + /// + /// The host to use as proxy. + /// + public const string PROXY_HOST = "http.proxyHost"; + + /// + /// The port to use for the proxy. + /// + public const string PROXY_PORT = "http.proxyPort"; + + private bool secure = false; + private Uri remoteURI; + private string encoding = "UTF-8"; + private long heartbeatInterval; + + private string proxyHost; + private int proxyPort = -1; + private string proxyPrincipal = null; + private GuardedString proxyPassword = null; + + + public Uri RemoteUri + { + set + { + Assertions.NullCheck(value, "remoteURI"); + + if ("https".Equals(value.Scheme, StringComparison.CurrentCultureIgnoreCase) || + "wss".Equals(value.Scheme, StringComparison.CurrentCultureIgnoreCase)) + { + int port = value.Port > 0 ? value.Port : 443; + secure = true; + + remoteURI = new UriBuilder(value) + { + Scheme = "wss", + Port = port + }.Uri; + } + else if ("http".Equals(value.Scheme, StringComparison.CurrentCultureIgnoreCase) || + "ws".Equals(value.Scheme, StringComparison.CurrentCultureIgnoreCase)) + { + int port = value.Port > 0 ? value.Port : 80; + remoteURI = new UriBuilder(value) + { + Scheme = "ws", + Port = port + }.Uri; + secure = false; + } + else + { + throw new ArgumentException("Unsupported protocol:" + value); + } + } + get { return remoteURI; } + } + + public virtual string Principal { get; set; } + + public virtual GuardedString Password { get; set; } + + public virtual bool Secure + { + get { return secure; } + } + + /// + /// Returns the heartbeat interval (in seconds) to use for the connection. A value + /// of zero means default 60 seconds timeout. + /// + /// the heartbeat interval (in seconds) to use for the connection. + public virtual long HeartbeatInterval + { + get { return heartbeatInterval; } + } + } + + #endregion + + #region WebSocketWrapper + + public class WebSocketWrapper : WebSocketConnectionHolder + { + private const int ReceiveChunkSize = 4096; + private const int SendChunkSize = 4096; + + private readonly ClientWebSocket _ws; + private readonly TaskCompletionSource _connectPromise; + private readonly RemoteWSFrameworkConnectionInfo _info; + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationToken _cancellationToken; + + private readonly IMessageListener + _messageListener; + + private readonly Func _handshaker; + + protected WebSocketWrapper(RemoteWSFrameworkConnectionInfo info, + IMessageListener + messageListener, + Func handshaker) + { + _messageListener = messageListener; + _handshaker = handshaker; + _ws = new ClientWebSocket(); + _connectPromise = new TaskCompletionSource(); + _ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(20); + _ws.Options.AddSubProtocol(RemoteWSFrameworkConnectionInfo.OPENICF_PROTOCOL); + string enc = + Convert.ToBase64String( + Encoding.GetEncoding("iso-8859-1") + .GetBytes(info.Principal + ":" + + IdentityConnectors.Common.Security.SecurityUtil.Decrypt(info.Password))); + _ws.Options.SetRequestHeader("Authorization", "Basic " + enc); + _info = info; + _cancellationToken = _cancellationTokenSource.Token; + } + + /// + /// Creates a new instance. + /// + /// The URI of the WebSocket server. + /// + /// + /// + public static WebSocketWrapper Create(RemoteWSFrameworkConnectionInfo info, + IMessageListener + messageListener, + Func handshaker) + { + return new WebSocketWrapper(info, messageListener, handshaker); + } + + + private async void SendMessageAsync(byte[] message) + { + if (_ws.State != WebSocketState.Open) + { + throw new Exception("Connection is not open."); + } + + await WriteMessageAsync(message, WebSocketMessageType.Binary); + } + + private async void SendMessageAsync(string message) + { + if (_ws.State != WebSocketState.Open) + { + throw new Exception("Connection is not open."); + } + + var messageBuffer = Encoding.UTF8.GetBytes(message); + await WriteMessageAsync(messageBuffer, WebSocketMessageType.Text); + } + + private static Int32 count = 0; + + public async Task ConnectAsync() + { + Trace.TraceInformation("Client make attempt to connect {0}", count++); + _ws.ConnectAsync(_info.RemoteUri, _cancellationToken).ContinueWith(task => + { + if (task.IsCompleted) + { + StartListen(); + RunInTask(WriteMessageAsync); + RunInTask(() => _messageListener.OnConnect(this)); + } + else + { + _connectPromise.SetException(task.Exception ?? new Exception("Failed to Connect Remote Server")); + } + }, CancellationToken.None).ConfigureAwait(false); + return await _connectPromise.Task; + } + + private async void StartListen() + { + var buffer = new byte[ReceiveChunkSize]; + try + { + while (_ws.State == WebSocketState.Open) + { + StringBuilder stringResult = null; + MemoryStream binaryResult = null; + + WebSocketReceiveResult result; + do + { + result = await _ws.ReceiveAsync(new ArraySegment(buffer), _cancellationToken); + + if (result.MessageType == WebSocketMessageType.Close) + { + await + _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + RunInTask(() => _messageListener.OnClose(this, 1000, null)); + } + else if (result.MessageType == WebSocketMessageType.Binary) + { + if (null == binaryResult) + { + binaryResult = new MemoryStream(); + } + binaryResult.Write(buffer, 0, result.Count); + } + else + { + var str = Encoding.UTF8.GetString(buffer, 0, result.Count); + if (null == stringResult) + { + stringResult = new StringBuilder(); + } + stringResult.Append(str); + } + } while (!result.EndOfMessage); + + if (result.MessageType == WebSocketMessageType.Binary) + { + if (binaryResult != null) + RunInTask(() => _messageListener.OnMessage(this, binaryResult.ToArray())); + } + else + { + if (stringResult != null) + RunInTask(() => _messageListener.OnMessage(this, stringResult.ToString())); + } + } + } + catch (Exception e) + { + TraceUtil.TraceException("Client processing exception", e); + RunInTask(() => _messageListener.OnError(e)); + } + finally + { + _ws.Dispose(); + } + } + + private static void RunInTask(Action action) + { + Task.Factory.StartNew(action); + } + + protected override async Task WriteMessageAsync(byte[] entry, WebSocketMessageType messageType) + { + var messageBuffer = entry; + var messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / SendChunkSize); + + for (var i = 0; i < messagesCount; i++) + { + var offset = (SendChunkSize * i); + var count = SendChunkSize; + var lastMessage = ((i + 1) == messagesCount); + + if ((count * (i + 1)) > messageBuffer.Length) + { + count = messageBuffer.Length - offset; + } + + await + _ws.SendAsync(new ArraySegment(messageBuffer, offset, count), messageType, + lastMessage, _cancellationToken); + } + } + + private RemoteOperationContext _context; + + protected override void Handshake(HandshakeMessage message) + { + _context = _handshaker(this, message); + + if (null != _context) + { + _connectPromise.TrySetResult(this); + } + else + { + _connectPromise.TrySetException(new ConnectorException( + "Failed Application HandShake")); + TryClose(); + } + } + + protected override void TryClose() + { + } + + public override bool Operational + { + get { return _ws.State == WebSocketState.Open; } + } + + public override RemoteOperationContext RemoteConnectionContext + { + get { return _context; } + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/ConnectorEventSubscriptionApiOpImpl.cs b/dotnet/framework/FrameworkServer/ConnectorEventSubscriptionApiOpImpl.cs new file mode 100755 index 00000000..49c55f43 --- /dev/null +++ b/dotnet/framework/FrameworkServer/ConnectorEventSubscriptionApiOpImpl.cs @@ -0,0 +1,649 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; +using Org.ForgeRock.OpenICF.Common.ProtoBuf; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using OBJ = Org.IdentityConnectors.Framework.Common.Objects; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + + #region ConnectorEventSubscriptionApiOpImpl + + public class ConnectorEventSubscriptionApiOpImpl : AbstractAPIOperation, IConnectorEventSubscriptionApiOp + { + public ConnectorEventSubscriptionApiOpImpl( + IRequestDistributor + remoteConnection, Org.IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual OBJ.ISubscription Subscribe(OBJ.ObjectClass objectClass, Filter eventFilter, + IObserver handler, + OBJ.OperationOptions operationOptions) + { + CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + Task promise = + TrySubscribe(objectClass, eventFilter, handler, operationOptions, CancellationToken.None); + + promise.ContinueWith((task, state) => + { + var observer = state as IObserver; + if (task.IsFaulted) + { + if (null != observer) + { + if (task.Exception != null) observer.OnError(task.Exception); + } + } + else if (task.IsCanceled) + { + //Ignore + } + else + { + if (null != observer) + { + observer.OnCompleted(); + } + } + }, handler, cancellationTokenSource.Token); + + return new SubscriptionImpl(promise, cancellationTokenSource); + } + + + public virtual Task TrySubscribe(OBJ.ObjectClass objectClass, Filter eventFilter, + IObserver handler, + OBJ.OperationOptions options, + CancellationToken cancellationToken) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(handler, "handler"); + ConnectorEventSubscriptionOpRequest requestBuilder = + new ConnectorEventSubscriptionOpRequest {ObjectClass = objectClass.GetObjectClassValue()}; + if (eventFilter != null) + { + requestBuilder.EventFilter = MessagesUtil.SerializeLegacy(eventFilter); + } + + if (options != null && options.Options.Any()) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return + SubmitRequest( + new InternalRequestFactory(ConnectorKey, FacadeKeyFunction, + new OperationRequest + { + ConnectorEventSubscriptionOpRequest = requestBuilder + }, handler, + cancellationToken)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly OperationRequest _operationRequest; + private readonly IObserver _handler; + + public InternalRequestFactory(Org.IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, OperationRequest operationRequest, + IObserver handler, + CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + _handler = handler; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, _handler, + CancellationToken); + } + return null; + + } + + protected internal override OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + private readonly IObserver _handler; + private Int32 _confirmed = 0; + + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, RPCRequest requestBuilder, + IObserver handler, + CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + _handler = handler; + } + + protected internal override ConnectorEventSubscriptionOpResponse GetOperationResponseMessages( + OperationResponse message) + { + if (null != message.ConnectorEventSubscriptionOpResponse) + { + return message.ConnectorEventSubscriptionOpResponse; + } + Trace.TraceInformation(OperationExpectsMessage, RequestId, "ConnectorEventSubscriptionOpResponse"); + return null; + + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + ConnectorEventSubscriptionOpResponse message) + { + if (null != _handler && null != message.ConnectorObject) + { + Org.IdentityConnectors.Framework.Common.Objects.ConnectorObject delta = + MessagesUtil.DeserializeMessage + (message.ConnectorObject); + try + { + _handler.OnNext(delta); + } + catch (Exception) + { + if (!Promise.IsCompleted) + { + HandleError(new ConnectorException("ResultsHandler stopped processing results")); + TryCancelRemote(ConnectionContext, RequestId); + } + } + } + else if (message.Completed && message.Completed) + { + HandleResult(null); + Trace.TraceInformation("Subscription is completed"); + } + else if (Interlocked.CompareExchange(ref _confirmed, 0, 1) == 0) + { + Trace.TraceInformation("Subscription has been made successfully on remote side"); + } + } + } + + // ---- + + public static + AbstractLocalOperationProcessor + CreateProcessor(long requestId, WebSocketConnectionHolder socket, + ConnectorEventSubscriptionOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor, + IObserver + { + private OBJ.ISubscription _subscription; + + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + ConnectorEventSubscriptionOpRequest message) + : base(requestId, socket, message) + { + } + + protected override RPCResponse CreateOperationResponse(RemoteOperationContext remoteContext, + ConnectorEventSubscriptionOpResponse result) + { + return + new RPCResponse + { + OperationResponse = new OperationResponse + { + ConnectorEventSubscriptionOpResponse = result + } + }; + } + + public override void Execute(ConnectorFacade connectorFacade) + { + try + { + TryHandleResult(ExecuteOperation(connectorFacade, _requestMessage)); + } + catch (Exception error) + { + HandleError(error); + } + } + + protected internal override ConnectorEventSubscriptionOpResponse ExecuteOperation( + ConnectorFacade connectorFacade, ConnectorEventSubscriptionOpRequest requestMessage) + { + OBJ.ObjectClass objectClass = new OBJ.ObjectClass(requestMessage.ObjectClass); + + Filter token = null; + if (null != requestMessage.EventFilter) + { + token = MessagesUtil.DeserializeLegacy(requestMessage.EventFilter); + } + + OBJ.OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + + _subscription = connectorFacade.Subscribe(objectClass, token, this, operationOptions); + + return new ConnectorEventSubscriptionOpResponse(); + } + + + protected override bool TryCancel() + { + _subscription.Dispose(); + return base.TryCancel(); + } + + public void OnNext(Org.IdentityConnectors.Framework.Common.Objects.ConnectorObject value) + { + if (null != value) + { + if (!TryHandleResult(new + ConnectorEventSubscriptionOpResponse + { + ConnectorObject = MessagesUtil.SerializeMessage(value) + })) + { + OnError(new Exception("Failed to Handle OnNext event.")); + } + } + } + + public void OnError(Exception error) + { + try + { + byte[] responseMessage = MessagesUtil.CreateErrorResponse(RequestId, error).ToByteArray(); + TrySendBytes(responseMessage, true); + } + catch (Exception t) + { + Trace.TraceInformation + ("Operation encountered an exception and failed to send the exception response {0}", t.Message); + } + } + + public void OnCompleted() + { + TryHandleResult(new ConnectorEventSubscriptionOpResponse {Completed = true}); + } + } + } + + #endregion + + #region SyncEventSubscriptionApiOpImpl + + public class SyncEventSubscriptionApiOpImpl : AbstractAPIOperation, ISyncEventSubscriptionApiOp + { + public SyncEventSubscriptionApiOpImpl( + IRequestDistributor + remoteConnection, Org.IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, long timeout) + : base(remoteConnection, connectorKey, facadeKeyFunction, timeout) + { + } + + public virtual OBJ.ISubscription Subscribe(OBJ.ObjectClass objectClass, OBJ.SyncToken eventFilter, + IObserver handler, OBJ.OperationOptions operationOptions) + { + CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + Task promise = + TrySubscribe(objectClass, eventFilter, handler, operationOptions); + + promise.ContinueWith((task, state) => + { + var observer = state as IObserver; + if (task.IsFaulted) + { + if (null != observer) + { + if (task.Exception != null) observer.OnError(task.Exception); + } + } + else if (task.IsCanceled) + { + //Ignore + } + else + { + if (null != observer) + { + observer.OnCompleted(); + } + } + }, handler, cancellationTokenSource.Token); + + return new SubscriptionImpl(promise, cancellationTokenSource); + } + + + public virtual Task TrySubscribe(OBJ.ObjectClass objectClass, OBJ.SyncToken eventFilter, + IObserver handler, OBJ.OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(handler, "handler"); + SyncEventSubscriptionOpRequest requestBuilder = new + SyncEventSubscriptionOpRequest {ObjectClass = objectClass.GetObjectClassValue()}; + if (eventFilter != null) + { + requestBuilder.Token = + MessagesUtil.SerializeMessage(eventFilter); + } + + if (options != null && options.Options.Any()) + { + requestBuilder.Options = MessagesUtil.SerializeLegacy(options); + } + + return + SubmitRequest( + new InternalRequestFactory(ConnectorKey, FacadeKeyFunction, + new OperationRequest {SyncEventSubscriptionOpRequest = requestBuilder}, handler, + CancellationToken.None)); + } + + private class InternalRequestFactory : AbstractRemoteOperationRequestFactory + { + private readonly OperationRequest _operationRequest; + private readonly IObserver _handler; + + public InternalRequestFactory(Org.IdentityConnectors.Framework.Api.ConnectorKey connectorKey, + Func facadeKeyFunction, OperationRequest operationRequest, + IObserver handler, CancellationToken cancellationToken) + : base(connectorKey, facadeKeyFunction, cancellationToken) + { + _operationRequest = operationRequest; + _handler = handler; + } + + public override InternalRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + RPCRequest builder = CreateRpcRequest(context); + if (null != builder) + { + return new InternalRequest(context, requestId, completionCallback, builder, _handler, + CancellationToken); + } + else + { + return null; + } + } + + protected internal override OperationRequest CreateOperationRequest( + RemoteOperationContext remoteContext) + { + return _operationRequest; + } + } + + private class InternalRequest : + AbstractRemoteOperationRequestFactory.AbstractRemoteOperationRequest + + { + private readonly IObserver _handler; + private Int32 _confirmed = 0; + + public InternalRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, RPCRequest requestBuilder, + IObserver handler, CancellationToken cancellationToken) + : base(context, requestId, completionCallback, requestBuilder, cancellationToken) + { + _handler = handler; + } + + protected internal override SyncEventSubscriptionOpResponse GetOperationResponseMessages( + OperationResponse message) + { + if (null != message.SyncEventSubscriptionOpResponse) + { + return message.SyncEventSubscriptionOpResponse; + } + else + { + Trace.TraceInformation(OperationExpectsMessage, RequestId, "HasSyncEventSubscriptionOpResponse"); + return null; + } + } + + protected internal override void HandleOperationResponseMessages(WebSocketConnectionHolder sourceConnection, + SyncEventSubscriptionOpResponse message) + { + if (null != _handler && null != message.SyncDelta) + { + OBJ.SyncDelta delta = MessagesUtil.DeserializeMessage(message.SyncDelta); + try + { + _handler.OnNext(delta); + } + catch (Exception) + { + if (!Promise.IsCompleted) + { + HandleError(new ConnectorException("ResultsHandler stopped processing results")); + TryCancelRemote(ConnectionContext, RequestId); + } + } + } + else if (message.Completed && message.Completed) + { + HandleResult(null); + Trace.TraceInformation("Subscription is completed"); + } + else if (Interlocked.CompareExchange(ref _confirmed, 0, 1) == 0) + { + Trace.TraceInformation("Subscription has been made successfully on remote side"); + } + } + } + + // ---- + + public static AbstractLocalOperationProcessor + CreateProcessor(long requestId, WebSocketConnectionHolder socket, SyncEventSubscriptionOpRequest message) + { + return new InternalLocalOperationProcessor(requestId, socket, message); + } + + private class InternalLocalOperationProcessor : + AbstractLocalOperationProcessor, + IObserver + { + private OBJ.ISubscription _subscription; + + protected internal InternalLocalOperationProcessor(long requestId, WebSocketConnectionHolder socket, + SyncEventSubscriptionOpRequest message) + : base(requestId, socket, message) + { + } + + protected override RPCResponse CreateOperationResponse(RemoteOperationContext remoteContext, + SyncEventSubscriptionOpResponse result) + { + return new + RPCResponse + { + OperationResponse = new OperationResponse + { + SyncEventSubscriptionOpResponse = result + } + }; + } + + public override void Execute(ConnectorFacade connectorFacade) + { + try + { + TryHandleResult(ExecuteOperation(connectorFacade, _requestMessage)); + } + catch (Exception error) + { + HandleError(error); + } + } + + protected internal override SyncEventSubscriptionOpResponse ExecuteOperation( + ConnectorFacade connectorFacade, SyncEventSubscriptionOpRequest requestMessage) + { + OBJ.ObjectClass objectClass = new OBJ.ObjectClass(requestMessage.ObjectClass); + + OBJ.SyncToken token = null; + if (null != requestMessage.Token) + { + token = MessagesUtil.DeserializeMessage(requestMessage.Token); + } + + OBJ.OperationOptions operationOptions = null; + if (null != requestMessage.Options) + { + operationOptions = MessagesUtil.DeserializeLegacy(requestMessage.Options); + } + + _subscription = connectorFacade.Subscribe(objectClass, token, this, operationOptions); + + return new SyncEventSubscriptionOpResponse(); + } + + + protected override bool TryCancel() + { + _subscription.Dispose(); + return base.TryCancel(); + } + + public void OnNext(OBJ.SyncDelta value) + { + if (null != value) + { + if (!TryHandleResult(new + SyncEventSubscriptionOpResponse + { + SyncDelta = MessagesUtil.SerializeMessage(value) + })) + { + OnError(new Exception("Failed to Handle OnNext event.")); + } + } + } + + public void OnError(Exception error) + { + try + { + byte[] responseMessage = MessagesUtil.CreateErrorResponse(RequestId, error).ToByteArray(); + TrySendBytes(responseMessage, true); + } + catch (Exception t) + { + Trace.TraceInformation + ("Operation encountered an exception and failed to send the exception response {0}", t.Message); + } + } + + public void OnCompleted() + { + TryHandleResult(new SyncEventSubscriptionOpResponse {Completed = true}); + } + } + } + + #endregion + + internal class SubscriptionImpl : OBJ.ISubscription + { + private readonly CancellationTokenSource _cancellationTokenSource; + private readonly Task _childTask; + + public SubscriptionImpl(Task childTask, CancellationTokenSource cancellationTokenSource) + { + _childTask = childTask; + _cancellationTokenSource = cancellationTokenSource; + } + + public void Dispose() + { + if (!_cancellationTokenSource.IsCancellationRequested) + { + _cancellationTokenSource.Cancel(); + _cancellationTokenSource.Dispose(); + } + } + + public bool Unsubscribed + { + get { return _childTask.IsCompleted; } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/Framework.cs b/dotnet/framework/FrameworkServer/Framework.cs new file mode 100755 index 00000000..c4d1b12b --- /dev/null +++ b/dotnet/framework/FrameworkServer/Framework.cs @@ -0,0 +1,670 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.ForgeRock.OpenICF.Framework.Remote; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using RemoteConnectorInfoImpl = Org.ForgeRock.OpenICF.Framework.Remote.RemoteConnectorInfoImpl; + +namespace Org.ForgeRock.OpenICF.Framework +{ + + #region ConnectorFramework + + public class ConnectorFramework : IDisposable + { + public const string RemoteLibraryMissingException = "Remote Connection Library is not initialised"; + + private RemoteConnectionInfoManagerFactory _remoteConnectionInfoManagerFactory; + private ConnectionManagerConfig _connectionManagerConfig = new ConnectionManagerConfig(); + + private Int32 _isRunning = 1; + + public bool Running + { + get { return _isRunning == 1; } + } + + public virtual void Dispose() + { + if (Interlocked.CompareExchange(ref _isRunning, 0, 1) == 1) + { + // Notify CloseListeners + RemoteConnectionInfoManagerFactory factory = RemoteConnectionInfoManagerFactory; + if (null != factory) + { + //logger.ok("Closing RemoteConnectionInfoManagerFactory"); + factory.Dispose(); + } + + // We need to complete all pending Promises + /*while (remoteManagerCache.Any()) + { + foreach (AsyncRemoteLegacyConnectorInfoManager manager in remoteManagerCache.Values) + { + manager.Dispose(); + } + }*/ + + // We need to complete all pending Promises + foreach (AsyncLocalConnectorInfoManager manager in _localConnectorInfoManagerCache.Values) + { + manager.Dispose(); + } + _localConnectorInfoManagerCache.Clear(); + } + } + + //// + public ConnectorFacade NewInstance(APIConfiguration config) + { + ConnectorFacade ret; + APIConfigurationImpl impl = (APIConfigurationImpl) config; + AbstractConnectorInfo connectorInfo = impl.ConnectorInfo; + if (connectorInfo is LocalConnectorInfoImpl) + { + LocalConnectorInfoImpl localInfo = (LocalConnectorInfoImpl) connectorInfo; + try + { + // create a new Provisioner. + ret = new LocalConnectorFacadeImpl(localInfo, impl); + } + catch (Exception ex) + { + string connector = impl.ConnectorInfo.ConnectorKey.ToString(); + Debug.WriteLine("Failed to create new connector facade: {0}, {1}", connector, config); + TraceUtil.TraceException("Failed to create new connector facade", ex); + throw; + } + } + else if (connectorInfo is Org.IdentityConnectors.Framework.Impl.Api.Remote.RemoteConnectorInfoImpl) + { + ret = new Org.IdentityConnectors.Framework.Impl.Api.Remote.RemoteConnectorFacadeImpl(impl); + } + else if (connectorInfo is Org.ForgeRock.OpenICF.Framework.Remote.RemoteConnectorInfoImpl) + { + ret = new RemoteAsyncConnectorFacade(impl); + } + else + { + throw new System.ArgumentException("Unknown ConnectorInfo type"); + } + return ret; + } + + private readonly ConcurrentDictionary _managedFacadeCache = + new ConcurrentDictionary(); + + private Timer _scheduledManagedFacadeCacheTimer; + + public virtual ConnectorFacade NewManagedInstance(ConnectorInfo connectorInfo, string config) + { + return NewManagedInstance(connectorInfo, config, null); + } + + public virtual ConnectorFacade NewManagedInstance(ConnectorInfo connectorInfo, string config, + IConfigurationPropertyChangeListener changeListener) + { + ConnectorFacade facade; + _managedFacadeCache.TryGetValue(config, out facade); + if (null == facade) + { + // new ConnectorFacade creation must remain cheap operation + facade = NewInstance(connectorInfo, config, changeListener); + if (facade is LocalConnectorFacadeImpl) + { + ConnectorFacade ret = _managedFacadeCache.GetOrAdd(facade.ConnectorFacadeKey, facade); + if (null != ret) + { + Trace.TraceInformation("ConnectorFacade found in cache"); + facade = ret; + } + else + { + lock (_managedFacadeCache) + { + if (null == _scheduledManagedFacadeCacheTimer) + { + _scheduledManagedFacadeCacheTimer = new Timer(state => + { + foreach (var connectorFacade in _managedFacadeCache) + { + LocalConnectorFacadeImpl value = + connectorFacade.Value as LocalConnectorFacadeImpl; + if (null != value && value.IsUnusedFor(TimeSpan.FromHours(2))) + { + ConnectorFacade ignore; + _managedFacadeCache.TryRemove(connectorFacade.Key, out ignore); + if (ignore == value) + { + Trace.TraceInformation( + "LocalConnectorFacade is disposed after 120min inactivity"); + value.Dispose(); + } + } + } + }, _managedFacadeCache, TimeSpan.FromHours(2), TimeSpan.FromHours(2)); + } + } + } + } + } + return facade; + } + + public ConnectorFacade NewInstance(ConnectorInfo connectorInfo, string config) + { + return NewInstance(connectorInfo, config, null); + } + + public ConnectorFacade NewInstance(ConnectorInfo connectorInfo, string config, + IConfigurationPropertyChangeListener changeListener) + { + ConnectorFacade ret; + if (connectorInfo is LocalConnectorInfoImpl) + { + try + { + // create a new Provisioner. + ret = new LocalConnectorFacadeImpl((LocalConnectorInfoImpl) connectorInfo, config, changeListener); + } + catch (Exception) + { + Debug.WriteLine("Failed to create new connector facade: {0}, {1}", connectorInfo.ConnectorKey, + config); + throw; + } + } + else if (connectorInfo is Org.IdentityConnectors.Framework.Impl.Api.Remote.RemoteConnectorInfoImpl) + { + ret = + new RemoteConnectorFacadeImpl( + (Org.IdentityConnectors.Framework.Impl.Api.Remote.RemoteConnectorInfoImpl) connectorInfo, config, + changeListener); + } + else if (connectorInfo is Org.ForgeRock.OpenICF.Framework.Remote.RemoteConnectorInfoImpl) + { + Assertions.NullCheck(connectorInfo, "connectorInfo"); + APIConfigurationImpl configuration = + (APIConfigurationImpl) + SerializerUtil.DeserializeBase64Object(Assertions.NullChecked(config, "configuration")); + configuration.ConnectorInfo = (Remote.RemoteConnectorInfoImpl) connectorInfo; + + configuration.ChangeListener = changeListener; + ret = NewInstance(configuration); + } + else + { + throw new System.ArgumentException("Unknown ConnectorInfo type"); + } + return ret; + } + + // ------ LocalConnectorFramework Implementation Start ------ + + private readonly ConcurrentDictionary _localConnectorInfoManagerCache = + new ConcurrentDictionary(); + + public virtual AsyncLocalConnectorInfoManager LocalManager + { + get { return GetLocalConnectorInfoManager("default"); } + } + + public virtual AsyncLocalConnectorInfoManager GetLocalConnectorInfoManager( + String connectorBundleParentClassLoader) + { + String key = connectorBundleParentClassLoader ?? "default"; + AsyncLocalConnectorInfoManager manager; + _localConnectorInfoManagerCache.TryGetValue(key, out manager); + return manager ?? (_localConnectorInfoManagerCache.GetOrAdd(key, new AsyncLocalConnectorInfoManager())); + } + + // ------ LocalConnectorFramework Implementation End ------ + + // ------ Legacy RemoteConnectorInfoManager Support ------ + /* private readonly ConcurrentDictionary, AsyncRemoteLegacyConnectorInfoManager> remoteManagerCache = new ConcurrentDictionary, AsyncRemoteLegacyConnectorInfoManager>(); + + public virtual AsyncRemoteLegacyConnectorInfoManager getRemoteManager(RemoteFrameworkConnectionInfo info) + { + if (null == info) + { + return null; + } + if (Running) + { + Pair key = Pair.Of(info.ToLower(CultureInfo.GetCultureInfo("en"))), info.Port); + AsyncRemoteLegacyConnectorInfoManager rv = remoteManagerCache[key]; + if (rv == null) + { + lock (remoteManagerCache) + { + rv = remoteManagerCache[key]; + if (rv == null) + { + rv = new AsyncRemoteLegacyConnectorInfoManager(info, scheduler); + rv.addCloseListener((x)={remoteManagerCache.Remove(key);}); + if (!Running && remoteManagerCache.Remove(key) != null) + { + rv.close(); + throw new IllegalStateException("ConnectorFramework is shut down"); + } + } + remoteManagerCache[key] = rv; + } + } + return rv; + } + else + { + throw new InvalidOperationException("ConnectorFramework is shut down"); + } + }*/ + + // ------ RemoteConnectorFramework Implementation Start ------ + + public virtual AsyncRemoteConnectorInfoManager GetRemoteManager(RemoteWSFrameworkConnectionInfo info) + { + if (null == info) + { + return null; + } + return new AsyncRemoteConnectorInfoManager(RemoteConnectionInfoManagerFactory.Connect(info)); + } + + public virtual LoadBalancingConnectorInfoManager GetRemoteManager( + LoadBalancingAlgorithmFactory loadBalancingAlgorithmFactory) + { + if (null != loadBalancingAlgorithmFactory && + loadBalancingAlgorithmFactory.AsyncRemoteConnectorInfoManager.Any()) + { + return new LoadBalancingConnectorInfoManager(loadBalancingAlgorithmFactory); + } + return null; + } + + public const String RemoteLibraryMissingExceptionMsg = "Remote Connection Library is not initialised"; + + public virtual ConnectorFacade NewInstance(ConnectorInfo connectorInfo, + Func transformer) + { + if (null != _remoteConnectionInfoManagerFactory) + { + return null; + } + throw new System.NotSupportedException(RemoteLibraryMissingExceptionMsg); + } + + public virtual Task NewInstanceAsync(ConnectorKey key, + Func transformer) + { + if (null != _remoteConnectionInfoManagerFactory) + { + return null; + } + throw new System.NotSupportedException(RemoteLibraryMissingExceptionMsg); + } + + // ------ RemoteConnectorFramework Implementation End ------ + + public virtual RemoteConnectionInfoManagerFactory RemoteConnectionInfoManagerFactory + { + get + { + lock (this) + { + if (null == _remoteConnectionInfoManagerFactory && Running) + { + OpenICFServerAdapter listener = new OpenICFServerAdapter(this, ConnectionInfoManager, true); + try + { + _remoteConnectionInfoManagerFactory = new RemoteConnectionInfoManagerFactory(listener, + ConnectionManagerConfig); + } + catch (Exception e) + { + TraceUtil.TraceException("RemoteConnectionInfoManagerFactory is not available", e); + //remoteConnectionInfoManagerFactory = new RemoteConnectionInfoManagerFactoryAnonymousInnerClassHelper(this, listener, Client.ConnectionManagerConfig, e); + } + //_remoteConnectionInfoManagerFactory.AddCloseListener( new CloseListenerAnonymousInnerClassHelper(this)); + } + return _remoteConnectionInfoManagerFactory; + } + } + } + + protected internal virtual IAsyncConnectorInfoManager ConnectionInfoManager + { + get { return LocalManager; } + } + + public virtual ConnectionManagerConfig ConnectionManagerConfig + { + get { return _connectionManagerConfig; } + set { _connectionManagerConfig = value ?? new ConnectionManagerConfig(); } + } + } + + #endregion + + #region DelegatingAsyncConnectorInfoManager + + public abstract class DelegatingAsyncConnectorInfoManager : + DisposableAsyncConnectorInfoManager + { + protected readonly ConcurrentDictionary delegates = + new ConcurrentDictionary(); + + private readonly ConcurrentDictionary, Boolean> + _deferredRangePromiseCacheList; + + private readonly ConcurrentDictionary, Boolean> _deferredKeyPromiseCacheList; + + private readonly bool _allowDeferred; + + public delegate void CloseListener(DelegatingAsyncConnectorInfoManager connectorInfoManager); + + /// + /// Adds a event handler to listen to the Disposed event on the DelegatingAsyncConnectorInfoManager. + /// + public event EventHandler Disposed; + + protected DelegatingAsyncConnectorInfoManager(bool allowDeferred) + { + _allowDeferred = allowDeferred; + if (allowDeferred) + { + _deferredRangePromiseCacheList = + new ConcurrentDictionary, Boolean>(); + + _deferredKeyPromiseCacheList = new ConcurrentDictionary, Boolean>(); + Disposed += (sender, args) => + { + DelegatingAsyncConnectorInfoManager outerInstance = sender as DelegatingAsyncConnectorInfoManager; + if (null != outerInstance) + { + foreach ( + Pair promise in + outerInstance._deferredRangePromiseCacheList.Keys) + { + promise.Second.Shutdown(); + } + outerInstance._deferredRangePromiseCacheList.Clear(); + + foreach ( + Pair promise in + outerInstance._deferredKeyPromiseCacheList.Keys) + { + promise.Second.Shutdown(); + } + outerInstance._deferredKeyPromiseCacheList.Clear(); + } + }; + } + else + { + _deferredRangePromiseCacheList = null; + _deferredKeyPromiseCacheList = null; + } + } + + protected abstract + IRequestDistributor + MessageDistributor { get; } + + + protected virtual IReadOnlyCollection Delegates + { + get { return delegates.Keys as IReadOnlyCollection; } + } + + protected bool AddAsyncConnectorInfoManager(IAsyncConnectorInfoManager @delegate) + { + if (null != @delegate && delegates.TryAdd(@delegate, true)) + { + Trace.TraceInformation("Add AsyncConnectorInfoManager to delegates"); + OnAddAsyncConnectorInfoManager(@delegate); + return true; + } + return false; + } + + protected internal virtual bool RemoveAsyncConnectorInfoManager(IAsyncConnectorInfoManager @delegate) + { + bool ignore; + return null != @delegate && delegates.TryRemove(@delegate, out ignore); + } + + protected internal virtual void OnAddAsyncConnectorInfoManager(IAsyncConnectorInfoManager @delegate) + { + if (_allowDeferred) + { + foreach (Pair promise in _deferredRangePromiseCacheList.Keys) + { + promise.Second.Add(@delegate.FindConnectorInfoAsync(promise.First)); + } + foreach (Pair promise in _deferredKeyPromiseCacheList.Keys) + { + promise.Second.Add(@delegate.FindConnectorInfoAsync(promise.First)); + } + } + } + + private sealed class DeferredPromise : TaskCompletionSource + { + private Int32 _remaining; + + public DeferredPromise(bool neverFail) + { + _remaining = neverFail ? 1 : 0; + } + + public void Shutdown() + { + if (!Task.IsCompleted) + SetException(new InvalidOperationException("AsyncConnectorInfoManager is shut down!")); + } + + internal bool Add(Task promise) + { + Interlocked.Increment(ref _remaining); + promise.ContinueWith(task => + { + if (task.IsCompleted) + { + TrySetResult(task.Result); + } + else + { + if (Interlocked.Decrement(ref _remaining) == 0 && !Task.IsCompleted) + { + if (task.IsFaulted) + { + TrySetException(task.Exception ?? new Exception()); + } + else + { + TrySetCanceled(); + } + } + } + }); + return !Task.IsCompleted; + } + } + + public override async Task FindConnectorInfoAsync(ConnectorKey key) + { + if (!Running) + { + TaskCompletionSource promise = new TaskCompletionSource(); + promise.SetException(new InvalidOperationException("AsyncConnectorInfoManager is shut down!")); + return await promise.Task; + } + else + { + IEnumerator safeDelegates = Delegates.GetEnumerator(); + DeferredPromise promise = new DeferredPromise(_allowDeferred); + Pair entry = Pair.Of(key, promise); + + if (_allowDeferred) + { + _deferredKeyPromiseCacheList.TryAdd(entry, true); + } + + bool pending = true; + while (pending && safeDelegates.MoveNext()) + { + pending = promise.Add(safeDelegates.Current.FindConnectorInfoAsync(key)); + } + + if (_allowDeferred && Running) + { + if (pending) + { + promise.Task.ContinueWith(task => + { + bool ignore; + _deferredKeyPromiseCacheList.TryRemove(entry, out ignore); + }).ConfigureAwait(false); + } + else + { + bool ignore; + _deferredKeyPromiseCacheList.TryRemove(entry, out ignore); + } + } + else if (!Running) + { + promise.Shutdown(); + } + + return await + promise.Task.ContinueWith( + task => new RemoteConnectorInfoImpl(MessageDistributor, + (RemoteConnectorInfoImpl) task.Result)); + } + } + + public override async Task FindConnectorInfoAsync(ConnectorKeyRange keyRange) + { + if (!Running) + { + throw new InvalidOperationException("AsyncConnectorInfoManager is shut down!"); + } + if (keyRange.BundleVersionRange.Empty) + { + TaskCompletionSource result = new TaskCompletionSource(); + result.SetException(new ArgumentException("ConnectorBundle VersionRange is Empty")); + return await result.Task; + } + if (keyRange.BundleVersionRange.Exact) + { + return await FindConnectorInfoAsync(keyRange.ExactConnectorKey); + } + IEnumerator safeDelegates = Delegates.GetEnumerator(); + + DeferredPromise promise = new DeferredPromise(_allowDeferred); + Pair entry = + Pair.Of(keyRange, promise); + + if (_allowDeferred) + { + _deferredRangePromiseCacheList.TryAdd(entry, true); + } + + bool pending = true; + while (pending && safeDelegates.MoveNext()) + { + pending = promise.Add(safeDelegates.Current.FindConnectorInfoAsync(keyRange)); + } + if (_allowDeferred && Running) + { + if (pending) + { + promise.Task.ContinueWith((task, state) => + { + bool ignore; + _deferredRangePromiseCacheList.TryRemove( + (Pair) state, out ignore); + }, entry).ConfigureAwait(false); + } + else + { + bool ignore; + _deferredRangePromiseCacheList.TryRemove(entry, out ignore); + } + } + else if (!Running) + { + promise.Shutdown(); + } + + return await + promise.Task.ContinueWith( + task => new RemoteConnectorInfoImpl(MessageDistributor, + (RemoteConnectorInfoImpl) task.Result)); + } + + public override IList ConnectorInfos + { + get + { + List keys = new List(); + List result = new List(); + foreach (IAsyncConnectorInfoManager group in Delegates) + { + foreach (ConnectorInfo info in group.ConnectorInfos) + { + if (!keys.Contains(info.ConnectorKey)) + { + keys.Add(info.ConnectorKey); + result.Add(info); + } + } + } + return result; + } + } + + public override ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + return Delegates.Select(@group => @group.FindConnectorInfo(key)).FirstOrDefault(result => null != result); + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/FrameworkServer.csproj b/dotnet/framework/FrameworkServer/FrameworkServer.csproj new file mode 100755 index 00000000..e2d3b5bb --- /dev/null +++ b/dotnet/framework/FrameworkServer/FrameworkServer.csproj @@ -0,0 +1,109 @@ + + + + + + Debug + AnyCPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568} + Library + Properties + Org.ForgeRock.OpenICF.Framework.Remote + FrameworkServer + OpenICF Framework - Connector Server + v4.5.2 + 512 + False + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\BouncyCastle.Crypto.1.8.0-beta4\lib\net40\crypto.dll + + + ..\packages\Google.ProtocolBuffers.3\lib\Google.Protobuf.dll + + + + + + + + + + + + + + + + + + + {f140e8da-52b4-4159-992a-9da10ea8eefb} + Common + + + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + FrameworkInternal + + + {5a9e8c5b-4d41-4e3e-9680-6c195bfad47a} + FrameworkProtoBuf + + + {b85c5a35-e3a2-4b04-9693-795e57d66de2} + FrameworkRpc + + + {8b24461b-456a-4032-89a1-cd418f7b5b62} + Framework + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/Local.cs b/dotnet/framework/FrameworkServer/Local.cs new file mode 100755 index 00000000..499d018f --- /dev/null +++ b/dotnet/framework/FrameworkServer/Local.cs @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Org.IdentityConnectors.Framework.Impl.Api.Local; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + public class AsyncLocalConnectorInfoManager : + ManagedAsyncConnectorInfoManager + { + public virtual void AddConnectorAssembly(ICollection connectorAssemblyFileInfos) + { + foreach (FileInfo assemblyFile in connectorAssemblyFileInfos) + { + Assembly lib = Assembly.LoadFrom(assemblyFile.ToString()); + AddConnectorAssembly(lib); + } + } + + public virtual void AddConnectorAssembly(Assembly connectorAssembly) + { + foreach (var connectorInfo in ConnectorAssemblyUtility.ProcessAssembly(connectorAssembly)) + { + AddConnectorInfo(connectorInfo as LocalConnectorInfoImpl); + } + } + + protected internal override bool CanCloseNow() + { + DoClose(); + return false; + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/Remote.cs b/dotnet/framework/FrameworkServer/Remote.cs new file mode 100755 index 00000000..a67859c8 --- /dev/null +++ b/dotnet/framework/FrameworkServer/Remote.cs @@ -0,0 +1,2408 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.EC; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Security; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Spi; +using OBJ = Org.IdentityConnectors.Framework.Common.Objects; +using API = Org.IdentityConnectors.Framework.Api; +using PRB = Org.ForgeRock.OpenICF.Common.ProtoBuf; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + + #region AsyncRemoteConnectorInfoManager + + /// + /// An AsyncRemoteConnectorInfoManager. + /// + /// Since 1.5 + public class AsyncRemoteConnectorInfoManager : DelegatingAsyncConnectorInfoManager + { + protected readonly IDisposable RemoteDisposable; + + private readonly + IRequestDistributor + _messageDistributor; + + public AsyncRemoteConnectorInfoManager(IDisposable remoteDisposable, + IRequestDistributor + messageDistributor) + : base(false) + { + RemoteDisposable = remoteDisposable; + _messageDistributor = messageDistributor; + } + + public AsyncRemoteConnectorInfoManager(IRemoteConnectorInfoManager loadBalancingAlgorithm) + : this(loadBalancingAlgorithm, loadBalancingAlgorithm.RequestDistributor) + { + AddAsyncConnectorInfoManager(loadBalancingAlgorithm.AsyncConnectorInfoManager); + } + + protected AsyncRemoteConnectorInfoManager(LoadBalancingAlgorithmFactory loadBalancingAlgorithmFactory) + : this( + null, + loadBalancingAlgorithmFactory.NewInstance( + loadBalancingAlgorithmFactory.AsyncRemoteConnectorInfoManager.Select( + manager => manager.MessageDistributor))) + { + if ( + loadBalancingAlgorithmFactory.AsyncRemoteConnectorInfoManager.SelectMany( + @delegate => @delegate.Delegates).Any(am => !AddAsyncConnectorInfoManager(am))) + { + throw new ArgumentException("Possible circular or repeated remote in LoadBalancing tree"); + } + } + + + protected override + IRequestDistributor + MessageDistributor + { + get { return _messageDistributor; } + } + + protected override void DoClose() + { + if (null != RemoteDisposable) + { + try + { + RemoteDisposable.Dispose(); + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to close underlying remote connection", e); + } + } + } + } + + #endregion + + /* + #region AsyncRemoteLegacyConnectorInfoManager + +/// +/// @since 1.5 +/// +public class AsyncRemoteLegacyConnectorInfoManager : ManagedAsyncConnectorInfoManager +{ + + protected internal readonly ConnectorEventHandler handler = new ConnectorEventHandlerAnonymousInnerClassHelper(); + + private class ConnectorEventHandlerAnonymousInnerClassHelper : ConnectorEventHandler + { + public ConnectorEventHandlerAnonymousInnerClassHelper() + { + } + public virtual void handleEvent(ConnectorEvent @event) + { + if (ConnectorEvent.CONNECTOR_REGISTERED.Equals(@event.Topic)) + { + ConnectorInfo connectorInfo = outerInstance.@delegate.findConnectorInfo((ConnectorKey) @event.Source); + addConnectorInfo((RemoteConnectorInfoImpl) connectorInfo); + } + } + } + private readonly RemoteConnectorInfoManagerImpl @delegate; + + private readonly ScheduledFuture future; + +//JAVA TO C# CONVERTER WARNING: 'final' parameters are not allowed in .NET: +//ORIGINAL LINE: public AsyncRemoteLegacyConnectorInfoManager(final RemoteFrameworkConnectionInfo info, final ScheduledExecutorService scheduler) + public AsyncRemoteLegacyConnectorInfoManager(RemoteFrameworkConnectionInfo info, ScheduledExecutorService scheduler) + { + this.@delegate = new RemoteConnectorInfoManagerImpl(info, false); + @delegate.addConnectorEventHandler(handler); + long heartbeatInterval = info.HeartbeatInterval; + if (heartbeatInterval <= 0) + { + heartbeatInterval = 60L; + } + try + { + future = scheduler.scheduleAtFixedRate(@delegate, 0, heartbeatInterval, TimeUnit.SECONDS); + logger.info("Legacy ConnectorServer Heartbeat scheduled to {0} by {1} seconds", info, heartbeatInterval); + } + catch (RejectedExecutionException e) + { + throw new ConnectorException(e.Message, e); + } + } + + protected internal virtual void doClose() + { + future.cancel(true); + @delegate.deleteConnectorEventHandler(handler); + base.doClose(); + } + + public override IList ConnectorInfos + { + get + { + return @delegate.ConnectorInfos; + } + } + + public override ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + return @delegate.FindConnectorInfo(key); + } + +} + #endregion + */ + + #region ConnectionPrincipal + + public abstract class ConnectionPrincipal : GenericPrincipal, IDisposable + , IRequestDistributor + { + public const string DefaultName = "anonymous"; + + protected internal Int32 isRunning = 1; + + protected readonly ConcurrentDictionary ConnectionGroups = + new ConcurrentDictionary(); + + private readonly ConcurrentDictionary _globalConnectionGroups; + + private readonly IMessageListener + _listener; + + public event EventHandler Disposed; + + protected ConnectionPrincipal( + IMessageListener listener, + ConcurrentDictionary globalConnectionGroups) + : base(new GenericIdentity(DefaultName), new[] { "connector" }) + { + _listener = listener; + _globalConnectionGroups = globalConnectionGroups; + } + + + public virtual RemoteOperationContext Handshake(WebSocketConnectionHolder webSocketConnection, + PRB.HandshakeMessage message) + { + WebSocketConnectionGroup newConnectionGroup = new WebSocketConnectionGroup(message.SessionId); + WebSocketConnectionGroup connectionGroup = _globalConnectionGroups.GetOrAdd(message.SessionId, + newConnectionGroup); + if (newConnectionGroup == connectionGroup) + { + ConnectionGroups.TryAdd(message.SessionId, connectionGroup); + try + { + OnNewWebSocketConnectionGroup(connectionGroup); + } + catch (Exception ignore) + { +#if DEBUG + System.Text.StringBuilder sb = new System.Text.StringBuilder("Failed to notify onNewWebSocketConnectionGroup - "); + TraceUtil.ExceptionToString(sb, ignore, String.Empty); + Debug.WriteLine(sb.ToString()); +#endif + } + } + return connectionGroup.Handshake(this, webSocketConnection, message); + } + + protected virtual void OnNewWebSocketConnectionGroup(WebSocketConnectionGroup connectionGroup) + { + } + + public virtual IMessageListener + OperationMessageListener + { + get { return _listener; } + } + + public virtual TR TrySubmitRequest( + IRemoteRequestFactory + + requestFactory) + where TR : + RemoteRequest + where TE : Exception + { + return + (from e in ConnectionGroups.Values where e.Operational select e.TrySubmitRequest(requestFactory)) + .FirstOrDefault(result => null != result); + } + + public virtual bool Operational + { + get { return isRunning == 1 && ConnectionGroups.Values.Any(e => e.Operational); } + } + + /// + /// Closes this manager and releases any system resources associated with it. + /// If the manager is already closed then invoking this method has no effect. + /// + protected abstract void DoClose(); + + public void Dispose() + { + if (Interlocked.CompareExchange(ref isRunning, 0, 1) == 1) + { + DoClose(); + // Notify CloseListeners + OnDisposed(); + } + } + + protected virtual void OnDisposed() + { + var handler = Disposed; + if (handler != null) handler(this, EventArgs.Empty); + } + } + + #endregion + + #region FailoverLoadBalancingAlgorithmFactory + + public class FailoverLoadBalancingAlgorithmFactory : LoadBalancingAlgorithmFactory + { + protected override + IRequestDistributor + CreateLoadBalancer( + IList> + delegates) + { + return + new FailoverLoadBalancingAlgorithm + (delegates); + } + } + + #endregion + + #region LoadBalancingAlgorithmFactory + + public abstract class LoadBalancingAlgorithmFactory + { + private readonly IList _remoteConnectorInfoManagers = + new List(); + + public virtual void AddAsyncRemoteConnectorInfoManager( + AsyncRemoteConnectorInfoManager remoteConnectorInfoManager) + { + if (null != remoteConnectorInfoManager) _remoteConnectorInfoManagers.Add(remoteConnectorInfoManager); + } + + public virtual ICollection AsyncRemoteConnectorInfoManager + { + get { return _remoteConnectorInfoManagers; } + } + + public virtual IRequestDistributor + NewInstance( + IEnumerable + > + parameter) + { + IList> + delegates = parameter.Where(dist => null != dist).ToList(); + if (delegates.Count == 0) + { + throw new ArgumentException("The LoadBalancing delegates is empty"); + } + return CreateLoadBalancer(delegates); + } + + protected abstract + IRequestDistributor + CreateLoadBalancer( + IList> + delegates); + } + + #endregion + + #region LoadBalancingConnectorFacadeContext + + /// + /// A LoadBalancingConnectorFacadeContext gives contextual information about the + /// current executing + /// + /// + public interface ILoadBalancingConnectorFacadeContext + { + /// + /// Loads the and + /// class in order + /// to determine the proper default configuration parameters. + /// + /// default APIConfiguration + API.APIConfiguration ApiConfiguration { get; } + + /// + /// Gets the principal name of executing + /// . + /// + /// + /// name of + /// + /// + string PrincipalName { get; } + + /// + /// Get the RemoteOperationContext of executing + /// + /// + /// + /// context of + /// + /// + RemoteOperationContext RemoteOperationContext { get; } + } + + #endregion + + #region LoadBalancingConnectorInfoManager + + /// Since 1.5 + public class LoadBalancingConnectorInfoManager : AsyncRemoteConnectorInfoManager + { + public LoadBalancingConnectorInfoManager(LoadBalancingAlgorithmFactory loadBalancingAlgorithmFactory) + : base(loadBalancingAlgorithmFactory) + { + } + + public virtual Task NewInstance(API.ConnectorKey key, + Func transformer) + { + return FindConnectorInfoAsync(key).ContinueWith((task, state) => + { + if (task.Result is RemoteConnectorInfoImpl) + { + //return new RemoteAsyncConnectorFacade((RemoteConnectorInfoImpl) task.Result, transformer); + return default(API.ConnectorFacade); + } + else + { + throw new ArgumentException("Invalid RemoteConnectorInfoImpl"); + } + }, transformer); + } + } + + #endregion + + #region ManagedAsyncConnectorInfoManager + + public class ManagedAsyncConnectorInfoManager : DisposableAsyncConnectorInfoManager + where TV : API.ConnectorInfo + where TC : ManagedAsyncConnectorInfoManager + { + private readonly ConcurrentDictionary> _managedConnectorInfos = + new ConcurrentDictionary>(); + + private class ConnectorKeyComparator : IComparer + { + /// + /// Descending ConnectorKey Comparator. + /// + /// + /// the first object to be compared. + /// + /// + /// the second object to be compared. + /// + /// + /// a negative integer, zero, or a positive + /// integer as the first argument is less than, + /// equal to, or greater than the second. + /// + public virtual int Compare(API.ConnectorKey left, API.ConnectorKey right) + { + int result = String.Compare(left.BundleName, right.BundleName, StringComparison.Ordinal); + if (result != 0) + { + return result * -1; + } + result = String.Compare(left.ConnectorName, right.ConnectorName, StringComparison.Ordinal); + if (result != 0) + { + return result * -1; + } + return String.Compare(left.BundleVersion, right.BundleVersion, StringComparison.Ordinal) * -1; + } + } + + private readonly ConcurrentDictionary>, Boolean> + _rangePromiseCacheList = + new ConcurrentDictionary>, Boolean>(); + + public const String ClosedExceptionMsg = "AsyncConnectorInfoManager is shut down!"; + + + protected override void DoClose() + { + foreach (ConnectorEntry entry in _managedConnectorInfos.Values) + { + entry.Shutdown(); + } + _managedConnectorInfos.Clear(); + foreach ( + Pair> entry in _rangePromiseCacheList.Keys) + { + entry.Second.SetException( + new InvalidOperationException("ManagedAsyncConnectorInfoManager is shutting down!")); + } + _rangePromiseCacheList.Clear(); + } + + private Int32 _revision; + + public virtual bool IsChanged(int lastRevision) + { + return lastRevision != _revision; + } + + protected internal virtual void AddConnectorInfo(TV connectorInfo) + { + ConnectorEntry entry = _managedConnectorInfos.GetOrAdd(connectorInfo.ConnectorKey, + new ConnectorEntry()); + + Trace.TraceInformation("Add new ConnectorInfo: {0}", connectorInfo.ConnectorKey); + + entry.ConnectorInfo = connectorInfo; + Interlocked.Increment(ref _revision); + + foreach ( + var rangeEntry in _rangePromiseCacheList.Keys.Where(x => x.First.IsInRange(connectorInfo.ConnectorKey))) + { + rangeEntry.Second.SetResult(connectorInfo); + } + } + + public override IList ConnectorInfos + { + get + { + List resultList = new List(_managedConnectorInfos.Count); + resultList.AddRange( + (from entry in _managedConnectorInfos.Values + where null != entry.ConnectorInfo + select entry.ConnectorInfo).Select(dummy => (API.ConnectorInfo)dummy)); + return resultList; + } + } + + public override API.ConnectorInfo FindConnectorInfo(API.ConnectorKey key) + { + ConnectorEntry entry; + _managedConnectorInfos.TryGetValue(key, out entry); + if (null != entry) + { + return entry.ConnectorInfo; + } + return null; + } + + public override Task FindConnectorInfoAsync(ConnectorKeyRange keyRange) + { + if (IsRunning == 0) + { + var result = new TaskCompletionSource(); + result.SetException(new InvalidOperationException("AsyncConnectorInfoManager is shut down!")); + return result.Task; + } + else + { + if (keyRange.BundleVersionRange.Empty) + { + var result = new TaskCompletionSource(); + result.SetException(new ArgumentException("ConnectorBundle VersionRange is Empty")); + return result.Task; + } + else if (keyRange.BundleVersionRange.Exact) + { + return FindConnectorInfoAsync(keyRange.ExactConnectorKey); + } + else + { + Pair> cacheEntry = + Pair>.Of(keyRange, + new TaskCompletionSource()); + + cacheEntry.Second.Task.ContinueWith((_, e) => + { + bool ignore; + Pair> key = + e as Pair>; + if (null != key) + _rangePromiseCacheList.TryRemove(key, out ignore); + }, cacheEntry); + _rangePromiseCacheList.TryAdd(cacheEntry, true); + + foreach (KeyValuePair> entry in _managedConnectorInfos) + { + API.ConnectorInfo connectorInfo = entry.Value.ConnectorInfo; + if (null != connectorInfo && keyRange.IsInRange(connectorInfo.ConnectorKey)) + { + cacheEntry.Second.SetResult(connectorInfo); + return cacheEntry.Second.Task; + } + } + + if (IsRunning != 1) + { + bool ignore; + _rangePromiseCacheList.TryRemove(cacheEntry, out ignore); + var result = new TaskCompletionSource(); + result.SetException(new InvalidOperationException(ClosedExceptionMsg)); + return result.Task; + } + return cacheEntry.Second.Task; + } + } + } + + public override async Task FindConnectorInfoAsync(API.ConnectorKey key) + { + if (IsRunning != 1) + { + throw new InvalidOperationException("AsyncConnectorInfoManager is shut down!"); + } + + TaskCompletionSource promise = new TaskCompletionSource(); + ConnectorEntry entry = _managedConnectorInfos.GetOrAdd(key, new ConnectorEntry()); + entry.AddOrFirePromise(promise); + if (IsRunning != 1 && !promise.Task.IsCompleted) + { + promise.SetException(new InvalidOperationException(ClosedExceptionMsg)); + } + return await promise.Task; + } + } + + + internal class ConnectorEntry where TV : API.ConnectorInfo + { + private TV _connectorInfo; + + private readonly ConcurrentQueue> _listeners = + new ConcurrentQueue>(); + + internal virtual TV ConnectorInfo + { + set + { + _connectorInfo = value; + TaskCompletionSource listener; + while (_listeners.TryDequeue(out listener)) + { + Trace.TraceInformation("Complete TaskSource:{0}", listener.Task.Id); + listener.SetResult(_connectorInfo); + } + } + get { return _connectorInfo; } + } + + internal virtual void Shutdown() + { + TaskCompletionSource listener; + while (_listeners.TryDequeue(out listener)) + { + //listener.SetException(CLOSED_EXCEPTION); + } + } + + internal virtual void AddOrFirePromise(TaskCompletionSource listener) + { + API.ConnectorInfo registered = _connectorInfo; + if (null != registered && !listener.Task.IsCompleted) + { + listener.SetResult(registered); + } + else + { + _listeners.Enqueue(listener); + API.ConnectorInfo registeredAfter = _connectorInfo; + if (null != registeredAfter && !listener.Task.IsCompleted) + { + listener.SetResult(registeredAfter); + } + } + } + } + + #endregion + + #region MessagesUtil + + public class MessagesUtil + { + public static PRB.RemoteMessage CreateErrorResponse(long messageId, Exception error) + { + PRB.RPCResponse builder = new PRB.RPCResponse + { + Error = FromException(error, 4) + }; + var message = CreateRemoteMessage(messageId); + message.Response = builder; + return message; + } + + public static PRB.ExceptionMessage FromException(Exception error, int depth) + { + PRB.ExceptionMessage builder = new PRB.ExceptionMessage + { + ExceptionClass = error.GetType().FullName, + StackTrace = error.StackTrace, + Message = error.Message + }; + + PRB.ExceptionMessage.Types.InnerCause cause = FromCause(error.InnerException, depth); + if (null != cause) + { + builder.InnerCause = cause; + } + + return builder; + } + + private static PRB.ExceptionMessage.Types.InnerCause FromCause(Exception error, int depth) + { + if (null != error && depth > 0) + { + PRB.ExceptionMessage.Types.InnerCause builder = new + PRB.ExceptionMessage.Types.InnerCause + { + ExceptionClass = error.GetType().FullName, + Message = error.Message + }; + + PRB.ExceptionMessage.Types.InnerCause cause = FromCause(error.InnerException, --depth); + if (null != cause) + { + builder.Cause = cause; + } + return builder; + } + return null; + } + + public static Exception FromExceptionMessage(PRB.ExceptionMessage exceptionMessage) + { + string message = null; + try + { + string throwableClass = exceptionMessage.ExceptionClass ?? typeof(ConnectorException).FullName; + message = exceptionMessage.Message ?? ""; + string stackTrace = !String.IsNullOrEmpty(exceptionMessage.StackTrace) + ? exceptionMessage.StackTrace + : null; + + return new RemoteWrappedException(throwableClass, message, GetCause(exceptionMessage.InnerCause), + stackTrace); + } + catch (Exception t) + { + return + new ConnectorException( + !String.IsNullOrEmpty(message) ? message : "Failed to process ExceptionMessage response", t); + } + } + + private static RemoteWrappedException GetCause(PRB.ExceptionMessage.Types.InnerCause cause) + { + if (null != cause) + { + string throwableClass = cause.ExceptionClass ?? typeof(ConnectorException).FullName; + string message = cause.Message ?? ""; + RemoteWrappedException originalCause = cause.Cause != null ? GetCause(cause.Cause) : null; + return new RemoteWrappedException(throwableClass, message, originalCause, null); + } + return null; + } + + public static PRB.Uid FromUid(PRB.Uid uid) + { + PRB.Uid builder = new PRB.Uid + { + Value = uid.Value + }; + if (null != uid.Revision) + { + builder.Revision = uid.Value; + } + return builder; + } + + public static PRB.RemoteMessage CreateResponse(long messageId, PRB.RPCResponse builderForValue) + { + var message = CreateRemoteMessage(messageId); + message.Response = builderForValue; + return message; + } + + public static PRB.RemoteMessage CreateRequest(int messageId, PRB.RPCRequest builderForValue) + { + var message = CreateRemoteMessage(messageId); + message.Request = builderForValue; + return message; + } + + public static PRB.RemoteMessage CreateRemoteMessage(long messageId) + { + PRB.RemoteMessage builder = new PRB.RemoteMessage + { + MessageId = 0 + }; + if (0 != messageId) + { + builder.MessageId = messageId; + } + return builder; + } + + [Obsolete] + public static T DeserializeLegacy(ByteString byteString) + { + if (null == byteString || byteString.IsEmpty) + { + return default(T); + } + return (T)SerializerUtil.DeserializeBinaryObject(byteString.ToByteArray()); + } + + [Obsolete] + public static ByteString SerializeLegacy(object source) + { + if (null != source) + { + return ByteString.CopyFrom(SerializerUtil.SerializeBinaryObject(source)); + } + return ByteString.Empty; + } + + public static T DeserializeMessage(Object source) + { + if (source == null) + { + return default(T); + } + var type = typeof(T); + // UID + if (typeof(IdentityConnectors.Framework.Common.Objects.Uid) == type) + { + var from = source as PRB.Uid; + if (null != from) + { + if (String.IsNullOrEmpty(from.Revision)) + { + return (T)(object)new IdentityConnectors.Framework.Common.Objects.Uid(from.Value); + } + return (T)(object)new IdentityConnectors.Framework.Common.Objects.Uid(from.Value, from.Revision); + } + } + //ConnectorKey + if (typeof(API.ConnectorKey) == type) + { + var from = source as Common.ProtoBuf.ConnectorKey; + if (null != from) + { + return (T)(object)new API.ConnectorKey(from.BundleName, from.BundleVersion, from.ConnectorName); + } + } + //ScriptContext + if (typeof(OBJ.ScriptContext) == type) + { + var from = source as Common.ProtoBuf.ScriptContext; + if (null != from) + { + var options = DeserializeLegacy>(from.ScriptArguments); + IDictionary arguments = + CollectionUtil.NewDictionary(options); + return + (T) + (object) + new OBJ.ScriptContext(from.Script.ScriptLanguage, from.Script.ScriptText, + arguments ?? new Dictionary()); + } + } + //SearchResult + if (typeof(OBJ.SearchResult) == type) + { + var from = source as Common.ProtoBuf.SearchResult; + if (null != from) + { + OBJ.SearchResult.CountPolicy policy = OBJ.SearchResult.CountPolicy.NONE; + + switch (from.TotalPagedResultsPolicy) + { + case PRB.SearchResult.Types.CountPolicy.EXACT: + { + policy = OBJ.SearchResult.CountPolicy.EXACT; + break; + } + case PRB.SearchResult.Types.CountPolicy.ESTIMATE: + { + policy = OBJ.SearchResult.CountPolicy.ESTIMATE; + break; + } + default: policy = OBJ.SearchResult.CountPolicy.NONE; + break; + } + return (T)(object)new OBJ.SearchResult(from.PagedResultsCookie, policy, from.TotalPagedResults, from.RemainingPagedResults); + } + } + //ConnectorObject + if (typeof(OBJ.ConnectorObject) == type) + { + var from = source as Common.ProtoBuf.ConnectorObject; + if (null != from) + { + ICollection attsObj = DeserializeLegacy>(from.Attributes); + ICollection atts = + CollectionUtil.NewSet(attsObj); + return (T)(object)new OBJ.ConnectorObject(new OBJ.ObjectClass(from.ObjectClass), atts); + } + } + //Locale/CultureInfo + if (typeof(CultureInfo) == type) + { + var from = source as Common.ProtoBuf.Locale; + if (null != from) + { + return + (T) + (object) + new Org.IdentityConnectors.Common.Locale(from.Language, from.Country, from.Variant) + .ToCultureInfo(); + } + } + //SyncToken + if (typeof(OBJ.SyncToken) == type) + { + var from = source as Common.ProtoBuf.SyncToken; + if (null != from) + { + return (T)(object)new OBJ.SyncToken(DeserializeLegacy(from.Value)); + } + } + //SyncDelta + if (typeof(IdentityConnectors.Framework.Common.Objects.SyncDelta) == type) + { + var from = source as Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta; + if (null != from) + { + OBJ.SyncDeltaBuilder builder = new OBJ.SyncDeltaBuilder(); + builder.Token = DeserializeMessage(from.Token); + switch (from.DeltaType) + { + case Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.CREATE: + builder.DeltaType = OBJ.SyncDeltaType.CREATE; + break; + case Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.CREATE_OR_UPDATE: + builder.DeltaType = OBJ.SyncDeltaType.CREATE_OR_UPDATE; + break; + case Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.UPDATE: + builder.DeltaType = OBJ.SyncDeltaType.UPDATE; + break; + case Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.DELETE: + builder.DeltaType = OBJ.SyncDeltaType.DELETE; + break; + } + if (from.PreviousUid != null) + { + builder.PreviousUid = + DeserializeMessage(from.PreviousUid); + } + if (!String.IsNullOrEmpty(from.ObjectClass)) + { + builder.ObjectClass = new OBJ.ObjectClass(from.ObjectClass); + } + if (from.Uid != null) + { + builder.Uid = DeserializeMessage(from.Uid); + } + if (!from.ConnectorObject.IsEmpty) + { + ICollection attsObj = DeserializeLegacy>(from.ConnectorObject); + ICollection atts = + CollectionUtil.NewSet(attsObj); + builder.Object = new OBJ.ConnectorObject(builder.ObjectClass, atts); + } + return (T)(object)builder.Build(); + } + } + + throw new NotImplementedException("From not supported"); + } + + public static T SerializeMessage(Object source) + { + if (source == null) + { + return default(T); + } + var type = typeof(T); + // UID + if (typeof(PRB.Uid) == type) + { + var from = source as IdentityConnectors.Framework.Common.Objects.Uid; + if (null != from) + { + var to = new PRB.Uid + { + Value = from.GetUidValue() + }; + if (null != from.Revision) to.Revision = from.Revision; + return (T)(object)to; + } + } + //ConnectorKey + if (typeof(Common.ProtoBuf.ConnectorKey) == type) + { + var from = source as API.ConnectorKey; + if (null != from) + { + return (T)(object)new Common.ProtoBuf.ConnectorKey + { + BundleName = from.BundleName, + BundleVersion = from.BundleVersion, + ConnectorName = from.ConnectorName + }; + } + } + //ScriptContext + if (typeof(Common.ProtoBuf.ScriptContext) == type) + { + var from = source as OBJ.ScriptContext; + if (null != from) + { + return (T)(object)new Common.ProtoBuf.ScriptContext + { + Script = new Common.ProtoBuf.Script + { + ScriptLanguage = from.ScriptLanguage, + ScriptText = from.ScriptText + }, + ScriptArguments = SerializeLegacy(from.ScriptArguments) + }; + } + } + //SearchResult + if (typeof(Common.ProtoBuf.SearchResult) == type) + { + var from = source as OBJ.SearchResult; + if (null != from) + { + PRB.SearchResult.Types.CountPolicy policy; + switch (from.TotalPagedResultsPolicy) + { + case OBJ.SearchResult.CountPolicy.EXACT: + { + policy = PRB.SearchResult.Types.CountPolicy.EXACT; + break; + } + case OBJ.SearchResult.CountPolicy.ESTIMATE: + { + policy = PRB.SearchResult.Types.CountPolicy.ESTIMATE; + break; + } + default: + policy = PRB.SearchResult.Types.CountPolicy.NONE; + break; + } + + return + (T) + (object) + new Common.ProtoBuf.SearchResult + { + PagedResultsCookie = from.PagedResultsCookie, + TotalPagedResultsPolicy = policy, + TotalPagedResults = from.TotalPagedResults, + RemainingPagedResults = from.RemainingPagedResults + }; + } + } + //ConnectorObject + if (typeof(Common.ProtoBuf.ConnectorObject) == type) + { + var from = source as OBJ.ConnectorObject; + if (null != from) + { + return (T)(object)new Common.ProtoBuf.ConnectorObject + { + ObjectClass = from.ObjectClass.GetObjectClassValue(), + Attributes = SerializeLegacy(from.GetAttributes()) + }; + } + } + //Locale/CultureInfo + if (typeof(Common.ProtoBuf.Locale) == type) + { + var from = source as CultureInfo; + if (null != from) + { + Org.IdentityConnectors.Common.Locale locale = Org.IdentityConnectors.Common.Locale.FindLocale(from); + return (T)(object)new PRB.Locale + { + Language = locale.Language, + Country = locale.Country, + Variant = locale.Variant + }; + } + } + //SyncToken + if (typeof(Common.ProtoBuf.SyncToken) == type) + { + var from = source as OBJ.SyncToken; + if (null != from) + { + return (T)(object)new Common.ProtoBuf.SyncToken + { + Value = SerializeLegacy(from.Value) + }; + } + } + //SyncDelta + if (typeof(Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta) == type) + { + var from = source as IdentityConnectors.Framework.Common.Objects.SyncDelta; + if (null != from) + { + Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta builder = + new Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta(); + builder.Token = SerializeMessage(from.Token); + + + switch (from.DeltaType) + { + case OBJ.SyncDeltaType.CREATE: + builder.DeltaType = + Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.CREATE; + break; + case OBJ.SyncDeltaType.CREATE_OR_UPDATE: + builder.DeltaType = + Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.CREATE_OR_UPDATE; + break; + case OBJ.SyncDeltaType.UPDATE: + builder.DeltaType = + Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.UPDATE; + break; + case OBJ.SyncDeltaType.DELETE: + builder.DeltaType = + Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta.Types.SyncDeltaType.DELETE; + break; + } + if (null != from.Uid) + { + builder.Uid = SerializeMessage(from.Uid); + } + if (null != from.ObjectClass) + { + builder.ObjectClass = from.ObjectClass.GetObjectClassValue(); + } + if (null != from.Object) + { + builder.ConnectorObject = SerializeLegacy(from.Object.GetAttributes()); + } + if (null != from.PreviousUid) + { + builder.PreviousUid = SerializeMessage(from.PreviousUid); + } + + return (T)(object)builder; + } + } + throw new NotImplementedException("To not supported"); + } + } + + #endregion + + #region OpenICFServerAdapter + + public class OpenICFServerAdapter : + IMessageListener + { + public static TraceSource traceSource = new TraceSource("OpenICFServerAdapter"); + //private readonly KeyPair keyPair = SecurityUtil.generateKeyPair(); + + private readonly ConnectorFramework _connectorFramework; + private readonly PRB.HandshakeMessage _handshakeMessage; + private readonly IAsyncConnectorInfoManager _connectorInfoManager; + + public OpenICFServerAdapter(ConnectorFramework framework, IAsyncConnectorInfoManager defaultConnectorInfoManager, + bool isClient) + { + Client = isClient; + _connectorFramework = Assertions.NullChecked(framework, "connectorFramework"); + _connectorInfoManager = Assertions.NullChecked(defaultConnectorInfoManager, "connectorInfoManager"); + _handshakeMessage = new PRB.HandshakeMessage + { + SessionId = Guid.NewGuid().ToString(), + ServerType = PRB.HandshakeMessage.Types.ServerType.DOTNET + }; + } + + public virtual void OnClose(WebSocketConnectionHolder socket, int code, string reason) + { + Trace.TraceInformation("{0} onClose({1},{2}) ", LoggerName(), Convert.ToString(code), + Convert.ToString(reason)); + } + + public virtual void OnConnect(WebSocketConnectionHolder socket) + { + if (Client) + { + Trace.TraceInformation("Client onConnect() - Send 'HandshakeMessage({0})'", + _handshakeMessage.SessionId); + PRB.RemoteMessage requestBuilder = MessagesUtil.CreateRequest(0, + new PRB.RPCRequest + { + HandshakeMessage = _handshakeMessage + }); + socket.SendBytesAsync(requestBuilder.ToByteArray(), CancellationToken.None); + } + else + { + Trace.TraceInformation("Server onConnect()"); + } + } + + public virtual void OnError(Exception t) + { + //traceSource.TraceVerbose("Socket error {0}", t); + TraceUtil.TraceException("Socket error", t); + } + + public virtual void OnMessage(WebSocketConnectionHolder socket, string data) + { + Trace.TraceWarning("String message is ignored: {0}", data); + } + + public virtual void OnMessage(WebSocketConnectionHolder socket, byte[] bytes) + { +#if DEBUG + Debug.WriteLine("{0} onMessage(socket[{1}], {2}:bytes)", LoggerName(), socket.Id, bytes.Length); +#else + Trace.TraceInformation("{0} onMessage({1}:bytes)", LoggerName(), bytes.Length); +#endif + try + { + PRB.RemoteMessage message = PRB.RemoteMessage.Parser.ParseFrom(bytes); + + if (null != message.Request) + { + if (null != message.Request.HandshakeMessage) + { + if (Client) + { + Trace.TraceInformation("Error = The client must send the Handshake first"); + } + else + { + ProcessHandshakeRequest(socket, message.MessageId, message.Request.HandshakeMessage); + } + } + else if (socket.HandHooked) + { + if (null != message.Request.OperationRequest) + { + ProcessOperationRequest(socket, message.MessageId, message.Request.OperationRequest); + } + else if (null != message.Request.CancelOpRequest) + { + ProcessCancelOpRequest(socket, message.MessageId, message.Request.CancelOpRequest); + } + else if (null != message.Request.ControlRequest) + { + ProcessControlRequest(socket, message.MessageId, message.Request.ControlRequest); + } + else + { + HandleRequestMessage(socket, message.MessageId, message.Request); + } + } + else + { + HandleRequestMessage(socket, message.MessageId, message.Request); + } + } + else if (null != message.Response) + { + if (null != message.Response.HandshakeMessage) + { + if (Client) + { + ProcessHandshakeResponse(socket, message.MessageId, message.Response.HandshakeMessage); + } + else + { + Trace.TraceInformation("Error = The server must send the Handshake response"); + } + } + else if (null != message.Response.Error) + { + ProcessExceptionMessage(socket, message.MessageId, message.Response.Error); + } + else if (socket.HandHooked) + { + if (null != message.Response.OperationResponse) + { + ProcessOperationResponse(socket, message.MessageId, message.Response.OperationResponse); + } + else if (null != message.Response.ControlResponse) + { + ProcessControlResponse(socket, message.MessageId, message.Response.ControlResponse); + } + else + { + HandleResponseMessage(socket, message.MessageId, message.Response); + } + } + else + { + HandleResponseMessage(socket, message.MessageId, message.Response); + } + } + else + { + HandleRemoteMessage(socket, message); + } + } + catch (InvalidProtocolBufferException e) + { + Trace.TraceWarning("{0} failed parse message {1}", LoggerName(), e); + } + catch (Exception t) + { + Trace.TraceInformation("{0} Unhandled exception {1}", LoggerName(), t); + } + } + + protected virtual void HandleRemoteMessage(WebSocketConnectionHolder socket, PRB.RemoteMessage message) + { + if (socket.HandHooked) + { + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage( + MessagesUtil.CreateErrorResponse(message.MessageId, new ConnectorException("Unknown RemoteMessage"))); + } + } + + protected virtual void HandleRequestMessage(WebSocketConnectionHolder socket, long messageId, + PRB.RPCRequest message) + { + if (socket.HandHooked) + { + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage( + MessagesUtil.CreateErrorResponse(messageId, new ConnectorException("Unknown Request message"))); + } + else + { + socket.SendBytesAsync( + MessagesUtil.CreateErrorResponse(messageId, + new ConnectorException("Connection received request before handshake")).ToByteArray(), + CancellationToken.None); + } + } + + protected internal virtual void HandleResponseMessage(WebSocketConnectionHolder socket, long messageId, + PRB.RPCResponse message) + { + if (socket.HandHooked) + { + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage( + MessagesUtil.CreateErrorResponse(messageId, new ConnectorException("Unknown Request message"))); + } + else + { + socket.SendBytesAsync( + MessagesUtil.CreateErrorResponse(messageId, + new ConnectorException("Connection received response before handshake")).ToByteArray(), + CancellationToken.None); + } + } + + public virtual void OnPing(WebSocketConnectionHolder socket, byte[] bytes) + { + // Nothing to do, pong response has been sent + Trace.TraceInformation("{0} onPing()", LoggerName()); + try + { +#if DEBUG + PRB.PingMessage message = PRB.PingMessage.Parser.ParseFrom(bytes); +#endif + } + catch (InvalidProtocolBufferException e) + { + Trace.TraceWarning("{0} failed parse message {1}", LoggerName(), e); + } + } + + public virtual void OnPong(WebSocketConnectionHolder socket, byte[] bytes) + { + // Confirm ping response! + Trace.TraceInformation("{0} onPong()", LoggerName()); + try + { +#if DEBUG + PRB.PingMessage message = PRB.PingMessage.Parser.ParseFrom(bytes); +#endif + } + catch (InvalidProtocolBufferException e) + { + Trace.TraceWarning("{0} failed parse message {1}", LoggerName(), e); + } + } + + protected internal virtual bool Client { get; private set; } + + // Handshake Operations + + public virtual void ProcessHandshakeRequest(WebSocketConnectionHolder socket, long messageId, + PRB.HandshakeMessage message) + { + PRB.RemoteMessage responseBuilder = MessagesUtil.CreateResponse(messageId, new + PRB.RPCResponse { HandshakeMessage = _handshakeMessage }); + socket.SendBytesAsync(responseBuilder.ToByteArray(), CancellationToken.None); + // Set Operational + socket.ReceiveHandshake(message); + Trace.TraceInformation("{0} accept Handshake ({1})", LoggerName(), message.SessionId); + } + + public virtual void ProcessHandshakeResponse(WebSocketConnectionHolder socket, long messageId, + PRB.HandshakeMessage message) + { + // Set Operational + socket.ReceiveHandshake(message); + + Trace.TraceInformation("{0} accept Handshake ({1})", LoggerName(), message.SessionId); + } + + // Control Operations + + public virtual void ProcessControlRequest(WebSocketConnectionHolder socket, long messageId, + PRB.ControlRequest message) + { + PRB.ControlResponse builder = new PRB.ControlResponse(); + if (message.InfoLevel.Contains(PRB.ControlRequest.Types.InfoLevel.CONNECTOR_INFO)) + { + if (_connectorInfoManager.ConnectorInfos.Any()) + { + IList connectorInfos = + _connectorInfoManager.ConnectorInfos.Select( + ci => toRemote((AbstractConnectorInfo)ci) + ).ToList(); + ByteString response = MessagesUtil.SerializeLegacy(connectorInfos); + builder.ConnectorInfos = response; + } + } + socket.RemoteConnectionContext.RemoteConnectionGroup.ProcessControlRequest(message); + PRB.RemoteMessage responseBuilder = MessagesUtil.CreateResponse(messageId, + new PRB.RPCResponse + { + ControlResponse = builder + }); + + socket.SendBytesAsync(responseBuilder.ToByteArray(), CancellationToken.None); + Trace.TraceInformation("{0} accept ControlRequest ({1})", LoggerName(), message); + } + + private Org.IdentityConnectors.Framework.Impl.Api.Remote.RemoteConnectorInfoImpl toRemote( + AbstractConnectorInfo source) + { + Org.IdentityConnectors.Framework.Impl.Api.Remote.RemoteConnectorInfoImpl rv = new Org.IdentityConnectors. + Framework.Impl.Api.Remote.RemoteConnectorInfoImpl + { + ConnectorDisplayNameKey = source.ConnectorDisplayNameKey, + ConnectorKey = source.ConnectorKey, + DefaultAPIConfiguration = source.DefaultAPIConfiguration, + Messages = source.Messages + }; + return rv; + } + + public virtual void ProcessControlResponse(WebSocketConnectionHolder socket, long messageId, + PRB.ControlResponse message) + { + socket.RemoteConnectionContext.RemoteConnectionGroup.ReceiveRequestResponse(socket, messageId, message); + + Trace.TraceInformation("{0} accept ControlResponse", LoggerName()); + } + + public virtual void ProcessOperationRequest(WebSocketConnectionHolder socket, long messageId, + PRB.OperationRequest message) + { + Debug.WriteLine("IN Request({0}:{1})", messageId, + socket.RemoteConnectionContext.RemotePrincipal.Identity.Name); + + String connectorFacadeKey = message.ConnectorFacadeKey.ToStringUtf8(); + + if (null != message.ConfigurationChangeEvent) + { + ICollection rawChanges = + MessagesUtil.DeserializeLegacy>(message.ConfigurationChangeEvent + .ConfigurationPropertyChange); + IList changes = + CollectionUtil.NewList(rawChanges); + socket.RemoteConnectionContext.RemoteConnectionGroup + .NotifyConfigurationChangeListener(connectorFacadeKey, changes); + } + else + { + Org.ForgeRock.OpenICF.Common.ProtoBuf.ConnectorKey connectorKey = message.ConnectorKey; + + API.ConnectorInfo info = FindConnectorInfo(connectorKey); + if (info == null) + { + PRB.RemoteMessage response = MessagesUtil.CreateErrorResponse(messageId, + new ConnectorException("Connector not found: " + connectorKey + " ")); + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage(response); + return; + } + + try + { + try + { + if (null != message.Locale) + { + CultureInfo local = MessagesUtil.DeserializeMessage(message.Locale); + Thread.CurrentThread.CurrentUICulture = local; + } + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to set request CultureInfo", e); + } + + API.ConnectorFacade connectorFacade = NewInstance(socket, info, connectorFacadeKey); + + if (null != message.AuthenticateOpRequest) + { + AuthenticationAsyncApiOpImpl.CreateProcessor(messageId, socket, message.AuthenticateOpRequest) + .Execute(connectorFacade); + } + else if (null != message.CreateOpRequest) + { + CreateAsyncApiOpImpl.CreateProcessor(messageId, socket, message.CreateOpRequest) + .Execute(connectorFacade); + } + else if (null != message.ConnectorEventSubscriptionOpRequest) + { + ConnectorEventSubscriptionApiOpImpl.CreateProcessor(messageId, socket, + message.ConnectorEventSubscriptionOpRequest).Execute(connectorFacade); + } + else if (null != message.DeleteOpRequest) + { + DeleteAsyncApiOpImpl.CreateProcessor(messageId, socket, message.DeleteOpRequest) + .Execute(connectorFacade); + } + else if (null != message.GetOpRequest) + { + GetAsyncApiOpImpl.CreateProcessor(messageId, socket, message.GetOpRequest) + .Execute(connectorFacade); + } + else if (null != message.ResolveUsernameOpRequest) + { + ResolveUsernameAsyncApiOpImpl.CreateProcessor(messageId, socket, + message.ResolveUsernameOpRequest) + .Execute(connectorFacade); + } + else if (null != message.SchemaOpRequest) + { + SchemaAsyncApiOpImpl.CreateProcessor(messageId, socket, message.SchemaOpRequest) + .Execute(connectorFacade); + } + else if (null != message.ScriptOnConnectorOpRequest) + { + ScriptOnConnectorAsyncApiOpImpl.CreateProcessor(messageId, socket, + message.ScriptOnConnectorOpRequest).Execute(connectorFacade); + } + else if (null != message.ScriptOnResourceOpRequest) + { + ScriptOnResourceAsyncApiOpImpl.CreateProcessor(messageId, socket, + message.ScriptOnResourceOpRequest) + .Execute(connectorFacade); + } + else if (null != message.SearchOpRequest) + { + SearchAsyncApiOpImpl.CreateProcessor(messageId, socket, message.SearchOpRequest) + .Execute(connectorFacade); + } + else if (null != message.SyncOpRequest) + { + SyncAsyncApiOpImpl.CreateProcessor(messageId, socket, message.SyncOpRequest) + .Execute(connectorFacade); + } + else if (null != message.SyncEventSubscriptionOpRequest) + { + SyncEventSubscriptionApiOpImpl.CreateProcessor(messageId, socket, + message.SyncEventSubscriptionOpRequest).Execute(connectorFacade); + } + else if (null != message.TestOpRequest) + { + TestAsyncApiOpImpl.CreateProcessor(messageId, socket, message.TestOpRequest) + .Execute(connectorFacade); + } + else if (null != message.UpdateOpRequest) + { + UpdateAsyncApiOpImpl.CreateProcessor(messageId, socket, message.UpdateOpRequest) + .Execute(connectorFacade); + } + else if (null != message.ValidateOpRequest) + { + ValidateAsyncApiOpImpl.CreateProcessor(messageId, socket, message.ValidateOpRequest) + .Execute(connectorFacade); + } + else + { + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage( + MessagesUtil.CreateErrorResponse(messageId, + new ConnectorException("Unknown OperationRequest"))); + } + } + catch (Exception t) + { + TraceUtil.TraceException("Failed handle OperationRequest " + messageId, t); + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage( + MessagesUtil.CreateErrorResponse(messageId, t)); + } + } + } + + public virtual void ProcessOperationResponse(WebSocketConnectionHolder socket, long messageId, + PRB.OperationResponse message) + { + Debug.WriteLine("IN Response({0}:{1})", messageId, + socket.RemoteConnectionContext.RemotePrincipal.Identity.Name); + socket.RemoteConnectionContext.RemoteConnectionGroup.ReceiveRequestResponse(socket, messageId, message); + } + + public virtual void ProcessExceptionMessage(WebSocketConnectionHolder socket, long messageId, + PRB.ExceptionMessage message) + { + socket.RemoteConnectionContext.RemoteConnectionGroup.ReceiveRequestResponse(socket, messageId, message); + } + + public virtual void ProcessCancelOpRequest(WebSocketConnectionHolder socket, long messageId, + PRB.CancelOpRequest message) + { + socket.RemoteConnectionContext.RemoteConnectionGroup.ReceiveRequestCancel(messageId); + } + + /* + protected internal virtual Encryptor initialiseEncryptor() + { + HandshakeMessage message = null; + // Create Encryptor + if (message.hasPublicKey()) + { + PublicKey publicKey = SecurityUtil.createPublicKey(message.PublicKey.toByteArray()); + try + { + Encryptor encryptor = new ECIESEncryptor(keyPair.Private, publicKey); + } + catch (InvalidKeyException) + { + sbyte[] error = null; + // socket.sendBytes(error); + } + } + return null; + }*/ + + protected internal virtual string LoggerName() + { + return Client ? "Client" : "Server"; + } + + public virtual API.ConnectorFacade NewInstance(WebSocketConnectionHolder socket, API.ConnectorInfo connectorInfo, + string config) + { + return _connectorFramework.NewManagedInstance(connectorInfo, config, + new RemoteConfigurationChangeListener(socket, connectorInfo, config)); + } + + public virtual API.ConnectorInfo FindConnectorInfo(Org.ForgeRock.OpenICF.Common.ProtoBuf.ConnectorKey key) + { + return + _connectorInfoManager.FindConnectorInfo(new API.ConnectorKey(key.BundleName, key.BundleVersion, + key.ConnectorName)); + } + + private class RemoteConfigurationChangeListener : API.IConfigurationPropertyChangeListener + { + private readonly WebSocketConnectionHolder socket; + private readonly API.ConnectorInfo connectorInfo; + private readonly string config; + + public RemoteConfigurationChangeListener(WebSocketConnectionHolder socket, API.ConnectorInfo connectorInfo, + string config) + { + this.socket = socket; + this.connectorInfo = connectorInfo; + this.config = config; + } + + public virtual void ConfigurationPropertyChange(IList changes) + { + try + { + PRB.RemoteMessage request = MessagesUtil.CreateRequest(0, new PRB.RPCRequest + { + OperationRequest = new PRB.OperationRequest + { + ConnectorFacadeKey = ByteString.CopyFromUtf8(config), + ConfigurationChangeEvent = new PRB.ConfigurationChangeEvent + { + ConfigurationPropertyChange = MessagesUtil.SerializeLegacy(changes) + } + } + }); + + socket.RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage(request); + } + catch (Exception t) + { + TraceUtil.TraceException("Failed to send ConfigurationChangeEvent event: " + connectorInfo, t); + } + } + } + } + + #endregion + + #region ReferenceCountedObject + + /// + /// An object which is lazily created when first referenced, and destroyed when + /// the last reference is released. + /// + /// The type of referenced object. + public abstract class ReferenceCountedObject + { + /// + /// A reference to the reference counted object which will automatically be + /// released during garbage collection. + /// + public sealed class Reference + { + private readonly ReferenceCountedObject _outerInstance; + + /// + /// The value will be accessed by the finalizer thread so it needs to be + /// volatile in order to ensure that updates are published. + /// + internal TR Value; + + internal Reference(ReferenceCountedObject outerInstance, TR value) + { + _outerInstance = outerInstance; + Value = value; + } + + /// + /// Returns the referenced object. + /// + /// The referenced object. + /// + /// If the referenced object has already been released. + /// + public TR Get() + { + if (Value == null) + { + throw new NullReferenceException(); // Fail-fast. + } + return Value; + } + + /// + /// Decrements the reference count for the reference counted object if + /// this reference refers to the reference counted instance. If the + /// reference count drops to zero then the referenced object will be + /// destroyed. + /// + public void Release() + { + TR instanceToRelease = default(TR); + lock (_outerInstance._lock) + { + if (Value != null) + { + if (Value.Equals(_outerInstance._instance) && --_outerInstance._refCount == 0) + { + // This was the last reference. + instanceToRelease = Value; + _outerInstance._instance = default(TR); + } + + /* + * Force NPE for subsequent get() attempts and prevent + * multiple releases. + */ + Value = default(TR); + } + } + if (instanceToRelease != null) + { + _outerInstance.DestroyInstance(instanceToRelease); + } + } + + /// + /// Provide a finalizer because reference counting is intended for + /// expensive rarely created resources which should not be accidentally + /// left around. + /// + ~Reference() + { + Release(); + } + } + + private T _instance; + private readonly object _lock = new object(); + private int _refCount; + + /// + /// Creates a new referenced object whose reference count is initially zero. + /// + protected internal ReferenceCountedObject() + { + // Nothing to do. + } + + /// + /// Returns a reference to the reference counted object. + /// + /// A reference to the reference counted object. + public Reference Acquire() + { + lock (_lock) + { + if (_refCount++ == 0) + { + Debug.Assert(_instance == null); + _instance = NewInstance(); + } + Debug.Assert(_instance != null); + return new Reference(this, _instance); + } + } + + protected internal virtual bool Null + { + get { return _instance == null; } + } + + /// + /// Returns a reference to the provided object or, if it is {@code null}, a + /// reference to the reference counted object. + /// + /// + /// The object to be referenced, or {@code null} if the reference + /// counted object should be used. + /// + /// + /// A reference to the provided object or, if it is {@code null}, a + /// reference to the reference counted object. + /// + public Reference AcquireIfNull(T value) + { + return value != null ? new Reference(this, value) : Acquire(); + } + + /// + /// Invoked when a reference is released and the reference count will become + /// zero. Implementations should release any resources associated with the + /// resource and should not return until the resources have been released. + /// + /// + /// The instance to be destroyed. + /// + protected internal abstract void DestroyInstance(T instance); + + /// + /// Invoked when a reference is acquired and the current reference count is + /// zero. Implementations should create a new instance as fast as possible. + /// + /// The new instance. + protected internal abstract T NewInstance(); + } + + #endregion + + #region RemoteAsyncConnectorFacade + + public class RemoteAsyncConnectorFacade : AbstractConnectorFacade, IAsyncConnectorFacade + { + private readonly ConcurrentDictionary _facadeKeys; + + private readonly IAuthenticationAsyncApiOp _authenticationApiOp; + private readonly ICreateAsyncApiOp _createApiOp; + private readonly IConnectorEventSubscriptionApiOp _connectorEventSubscriptionApiOp; + private readonly IDeleteAsyncApiOp _deleteApiOp; + private readonly IGetAsyncApiOp _getApiOp; + private readonly IResolveUsernameAsyncApiOp _resolveUsernameApiOp; + private readonly ISchemaAsyncApiOp _schemaApiOp; + private readonly IScriptOnConnectorAsyncApiOp _scriptOnConnectorApiOp; + private readonly IScriptOnResourceAsyncApiOp _scriptOnResourceApiOp; + private readonly SearchApiOp _searchApiOp; + private readonly SyncApiOp _syncApiOp; + private readonly ISyncEventSubscriptionApiOp _syncEventSubscriptionApiOp; + private readonly ITestAsyncApiOp _testApiOp; + private readonly IUpdateAsyncApiOp _updateApiOp; + private readonly IValidateAsyncApiOp _validateApiOp; + + + private class InnerFacadeContext : ILoadBalancingConnectorFacadeContext + { + private readonly API.ConnectorInfo _connectorInfo; + private readonly RemoteOperationContext _context; + + public InnerFacadeContext(API.ConnectorInfo connectorInfo, RemoteOperationContext context) + { + _connectorInfo = connectorInfo; + _context = context; + } + + public API.APIConfiguration ApiConfiguration + { + get { return _connectorInfo.CreateDefaultAPIConfiguration(); } + } + + public string PrincipalName + { + get { return _context.RemotePrincipal.Identity.Name; } + } + + public RemoteOperationContext RemoteOperationContext + { + get { return _context; } + } + } + + protected internal RemoteAsyncConnectorFacade(APIConfigurationImpl configuration, + Func transformer) + : base(configuration) + { + if (configuration.ConnectorInfo is RemoteConnectorInfoImpl) + { + IRequestDistributor + remoteConnection = + Assertions.NullChecked( + ((RemoteConnectorInfoImpl)configuration.ConnectorInfo).messageDistributor, + "messageDistributor"); + + API.ConnectorKey connectorKey = GetAPIConfiguration().ConnectorInfo.ConnectorKey; + Func facadeKeyFunction; + if (null != transformer) + { + _facadeKeys = new ConcurrentDictionary(); + facadeKeyFunction = (value) => + { + ByteString facadeKey; + _facadeKeys.TryGetValue(value.RemotePrincipal.Identity.Name, out facadeKey); + if (null == facadeKey) + { + API.ConnectorInfo connectorInfo = value.RemoteConnectionGroup.FindConnectorInfo(connectorKey); + if (null != connectorInfo) + { + // Remote server has the ConnectorInfo + try + { + API.APIConfiguration fullConfiguration = + transformer( + new InnerFacadeContext(connectorInfo, value)); + if (null != fullConfiguration) + { + string connectorFacadeKey = + (SerializerUtil.SerializeBase64Object(fullConfiguration)); + facadeKey = ByteString.CopyFromUtf8(connectorFacadeKey); + if (null != GetAPIConfiguration().ChangeListener) + { + value.RemoteConnectionGroup.AddConfigurationChangeListener( + connectorFacadeKey, + GetAPIConfiguration().ChangeListener); + } + _facadeKeys.TryAdd(value.RemotePrincipal.Identity.Name, facadeKey); + } + } + catch (Exception t) + { + TraceUtil.TraceException(TraceLevel.Warning, + "Failed to build APIConfiguration for {0}", t, + value.RemotePrincipal.Identity.Name); + } + } + else + { + Trace.TraceInformation( + "Can not execute Operation on {0} because ConnectorInfo [{1}] is not installed", + value.RemotePrincipal.Identity.Name, connectorKey); + } + } + return facadeKey; + }; + } + else + { + _facadeKeys = null; + ByteString facadeKey = ByteString.CopyFromUtf8(ConnectorFacadeKey); + facadeKeyFunction = context => + { + context.RemoteConnectionGroup.FindConnectorInfo(GetAPIConfiguration().ConnectorInfo.ConnectorKey); + if (null != GetAPIConfiguration().ChangeListener) + { + context.RemoteConnectionGroup.AddConfigurationChangeListener(ConnectorFacadeKey, + GetAPIConfiguration().ChangeListener); + } + return facadeKey; + }; + } + + // initialise operations + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _authenticationApiOp = CreateLogging(SafeType.Get(), new AuthenticationAsyncApiOpImpl(remoteConnection, connectorKey, + facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _authenticationApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _createApiOp = CreateLogging(SafeType.Get(), new CreateAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _createApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _connectorEventSubscriptionApiOp = CreateLogging(SafeType.Get(), new ConnectorEventSubscriptionApiOpImpl(remoteConnection, + connectorKey, + facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _connectorEventSubscriptionApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _deleteApiOp = CreateLogging(SafeType.Get(), new DeleteAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _deleteApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _resolveUsernameApiOp = CreateLogging(SafeType.Get(), new ResolveUsernameAsyncApiOpImpl(remoteConnection, connectorKey, + facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _resolveUsernameApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _schemaApiOp = CreateLogging(SafeType.Get(), new SchemaAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _schemaApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _scriptOnConnectorApiOp = CreateLogging(SafeType.Get(), new ScriptOnConnectorAsyncApiOpImpl(remoteConnection, connectorKey, + facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _scriptOnConnectorApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _scriptOnResourceApiOp = CreateLogging(SafeType.Get(), new ScriptOnResourceAsyncApiOpImpl(remoteConnection, connectorKey, + facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _scriptOnResourceApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _searchApiOp = CreateLogging(SafeType.Get(), new SearchAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + _getApiOp = CreateLogging(SafeType.Get(), new GetAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _searchApiOp = null; + _getApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _syncApiOp = CreateLogging(SafeType.Get(), new SyncAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _syncApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _syncEventSubscriptionApiOp = CreateLogging(SafeType.Get(), new SyncEventSubscriptionApiOpImpl(remoteConnection, connectorKey, + facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _syncEventSubscriptionApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _testApiOp = CreateLogging(SafeType.Get(), new TestAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _testApiOp = null; + } + if (configuration.IsSupportedOperation(SafeType.Get())) + { + _updateApiOp = CreateLogging(SafeType.Get(), new UpdateAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + _updateApiOp = null; + } + _validateApiOp = CreateLogging(SafeType.Get(), new ValidateAsyncApiOpImpl(remoteConnection, connectorKey, facadeKeyFunction, GetAPIConfiguration().GetTimeout(SafeType.Get()))); + } + else + { + throw new InvalidParameterException("Unsupported ConnectorInfo type"); + } + } + + + protected T CreateLogging(SafeType api, T target) + { + T ret = target; + if (LOGGINGPROXY_ENABLED) + { + ret = (T)Proxy.NewProxyInstance(typeof(T), new LoggingProxy(api, target)); + } + return ret; + } + + public RemoteAsyncConnectorFacade(RemoteConnectorInfoImpl firstConnectorInfo, + Func transformer) + : this((APIConfigurationImpl)firstConnectorInfo.CreateDefaultAPIConfiguration(), transformer) + { + } + + public RemoteAsyncConnectorFacade(APIConfigurationImpl configuration) + : this(configuration, null) + { + } + + protected internal virtual T GetAsyncOperationCheckSupported() where T : APIOperation + { + T op = (T)GetOperationImplementation(SafeType.ForRawType(typeof(T))); + + // check if this operation is supported. + if (null == op) + { + throw new NotSupportedException(String.Format(Msg, typeof(T))); + } + return op; + } + + protected override APIOperation GetOperationImplementation(SafeType api) + { + if (typeof(AuthenticationApiOp).IsAssignableFrom(api.RawType)) + { + return _authenticationApiOp; + } + if (typeof(CreateApiOp).IsAssignableFrom(api.RawType)) + { + return _createApiOp; + } + if (typeof(IConnectorEventSubscriptionApiOp).IsAssignableFrom(api.RawType)) + { + return _connectorEventSubscriptionApiOp; + } + if (typeof(DeleteApiOp).IsAssignableFrom(api.RawType)) + { + return _deleteApiOp; + } + if (typeof(GetApiOp).IsAssignableFrom(api.RawType)) + { + return _getApiOp; + } + if (typeof(ResolveUsernameApiOp).IsAssignableFrom(api.RawType)) + { + return _resolveUsernameApiOp; + } + if (typeof(SchemaApiOp).IsAssignableFrom(api.RawType)) + { + return _schemaApiOp; + } + if (typeof(ScriptOnConnectorApiOp).IsAssignableFrom(api.RawType)) + { + return _scriptOnConnectorApiOp; + } + if (typeof(ScriptOnResourceApiOp).IsAssignableFrom(api.RawType)) + { + return _scriptOnResourceApiOp; + } + if (typeof(SearchApiOp).IsAssignableFrom(api.RawType)) + { + return _searchApiOp; + } + if (typeof(SyncApiOp).IsAssignableFrom(api.RawType)) + { + return _syncApiOp; + } + if (typeof(ISyncEventSubscriptionApiOp).IsAssignableFrom(api.RawType)) + { + return _syncEventSubscriptionApiOp; + } + if (typeof(TestApiOp).IsAssignableFrom(api.RawType)) + { + return _testApiOp; + } + if (typeof(UpdateApiOp).IsAssignableFrom(api.RawType)) + { + return _updateApiOp; + } + if (typeof(ValidateApiOp).IsAssignableFrom(api.RawType)) + { + return _validateApiOp; + } + return null; + } + + public async Task AuthenticateAsync( + OBJ.ObjectClass objectClass, + string username, GuardedString password, OBJ.OperationOptions options, CancellationToken cancellationToken) + { + return await GetAsyncOperationCheckSupported() + .AuthenticateAsync(objectClass, username, password, options, cancellationToken); + } + + public async Task CreateAsync(OBJ.ObjectClass objectClass, + ICollection createAttributes, OBJ.OperationOptions options, + CancellationToken cancellationToken) + { + return await GetAsyncOperationCheckSupported() + .CreateAsync(objectClass, createAttributes, options, cancellationToken); + } + + public async Task DeleteAsync(OBJ.ObjectClass objectClass, IdentityConnectors.Framework.Common.Objects.Uid uid, + OBJ.OperationOptions options, CancellationToken cancellationToken) + { + await + GetAsyncOperationCheckSupported() + .DeleteAsync(objectClass, uid, options, cancellationToken); + } + + public async Task GetObjectAsync(OBJ.ObjectClass objectClass, + IdentityConnectors.Framework.Common.Objects.Uid uid, OBJ.OperationOptions options, + CancellationToken cancellationToken) + { + return + await + GetAsyncOperationCheckSupported() + .GetObjectAsync(objectClass, uid, options, cancellationToken); + } + + public async Task ResolveUsernameAsync( + OBJ.ObjectClass objectClass, + string username, OBJ.OperationOptions options, CancellationToken cancellationToken) + { + return await GetAsyncOperationCheckSupported() + .ResolveUsernameAsync(objectClass, username, options, cancellationToken); + } + + public async Task SchemaAsync(CancellationToken cancellationToken) + { + return await GetAsyncOperationCheckSupported().SchemaAsync(cancellationToken); + } + + public async Task RunScriptOnConnectorAsync(OBJ.ScriptContext request, OBJ.OperationOptions options, + CancellationToken cancellationToken) + { + return await GetAsyncOperationCheckSupported() + .RunScriptOnConnectorAsync(request, options, cancellationToken); + } + + public async Task RunScriptOnResourceAsync(OBJ.ScriptContext request, OBJ.OperationOptions options, + CancellationToken cancellationToken) + { + return await GetAsyncOperationCheckSupported() + .RunScriptOnResourceAsync(request, options, cancellationToken); + } + + public Task TestAsync(CancellationToken cancellationToken) + { + return GetAsyncOperationCheckSupported() + .TestAsync(cancellationToken); + } + + public Task UpdateAsync(OBJ.ObjectClass objectClass, + IdentityConnectors.Framework.Common.Objects.Uid uid, ICollection replaceAttributes, + OBJ.OperationOptions options, CancellationToken cancellationToken) + { + return GetAsyncOperationCheckSupported() + .UpdateAsync(objectClass, uid, replaceAttributes, options, cancellationToken); + } + + public Task AddAttributeValuesAsync( + OBJ.ObjectClass objectClass, + IdentityConnectors.Framework.Common.Objects.Uid uid, ICollection valuesToAdd, + OBJ.OperationOptions options, CancellationToken cancellationToken) + { + return GetAsyncOperationCheckSupported() + .AddAttributeValuesAsync(objectClass, uid, valuesToAdd, options, cancellationToken); + } + + public Task RemoveAttributeValuesAsync( + OBJ.ObjectClass objectClass, + IdentityConnectors.Framework.Common.Objects.Uid uid, ICollection valuesToRemove, + OBJ.OperationOptions options, CancellationToken cancellationToken) + { + return GetAsyncOperationCheckSupported() + .RemoveAttributeValuesAsync(objectClass, uid, valuesToRemove, options, cancellationToken); + } + + public Task ValidateAsync(CancellationToken cancellationToken) + { + return GetAsyncOperationCheckSupported() + .ValidateAsync(cancellationToken); + } + } + + #endregion + + #region RemoteConnectorInfoImpl + + public class RemoteConnectorInfoImpl : AbstractConnectorInfo + { + internal readonly + IRequestDistributor + messageDistributor; + + public RemoteConnectorInfoImpl( + IRequestDistributor + remoteConnection, AbstractConnectorInfo copyFrom) + { + messageDistributor = Assertions.NullChecked(remoteConnection, "remoteConnection"); + Assertions.NullCheck(copyFrom, "copyFrom"); + ConnectorDisplayNameKey = copyFrom.ConnectorDisplayNameKey; + ConnectorKey = copyFrom.ConnectorKey; + Messages = copyFrom.Messages; + ConnectorCategoryKey = copyFrom.ConnectorCategoryKey; + DefaultAPIConfiguration = copyFrom.DefaultAPIConfiguration; + } + } + + #endregion + + #region RoundRobinLoadBalancingAlgorithmFactory + + public class RoundRobinLoadBalancingAlgorithmFactory : LoadBalancingAlgorithmFactory + { + protected override + IRequestDistributor + CreateLoadBalancer( + IList> + delegates) + { + return + new RoundRobinLoadBalancingAlgorithm + (delegates); + } + } + + #endregion + + #region SecurityUtil + + /// + /// + /// since 1.5 + public class SecurityUtil + { + public static AsymmetricCipherKeyPair GenerateKeyPair() + { + try + { + var ecP = CustomNamedCurves.GetByName(NamedCurve.secp256r1.ToString()); + var ecParams = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + keyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, new SecureRandom())); + return keyPairGenerator.GenerateKeyPair(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + Console.Write(e.StackTrace); + } + return null; + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/Rpc.cs b/dotnet/framework/FrameworkServer/Rpc.cs new file mode 100755 index 00000000..ef90759d --- /dev/null +++ b/dotnet/framework/FrameworkServer/Rpc.cs @@ -0,0 +1,941 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.WebSockets; +using System.Security.Principal; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; +using Org.ForgeRock.OpenICF.Common.ProtoBuf; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Impl.Api; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + + #region LocalOperationProcessor + + public abstract class LocalOperationProcessor : + LocalRequest + { + private int _inconsistencyCounter = 0; + + protected WebSocketConnectionHolder ReverseConnection; + + protected internal LocalOperationProcessor(long requestId, WebSocketConnectionHolder socket) + : base(requestId, socket) + { + } + + protected abstract RPCResponse CreateOperationResponse(RemoteOperationContext remoteContext, + TV result); + + public override Boolean Check() + { + Boolean valid = _inconsistencyCounter < 3; + if (!valid) + { + Trace.TraceInformation( + "LocalRequest:{0} -> inconsistent with remote server, trying to cancel local process.", + RequestId); + Dispose(); + } + return valid; + } + + public override void Inconsistent() + { + _inconsistencyCounter++; + } + + protected override bool TryHandleResult(TV result) + { + try + { + byte[] responseMessage = + new RemoteMessage + { + MessageId = RequestId, + Response = CreateOperationResponse(RemoteConnectionContext, result) + }.ToByteArray(); + + return null != TrySendBytes(responseMessage); + } + catch (ConnectorIOException e) + { + // May not be complete / failed to send response + TraceUtil.TraceException("Operation complete successfully but failed to send result", e); + } + catch (Exception e) + { + TraceUtil.TraceException("Operation complete successfully but failed to build result message", e); + } + return false; + } + + protected override bool TryHandleError(Exception error) + { + byte[] responseMessage = MessagesUtil.CreateErrorResponse(RequestId, error).ToByteArray(); + try + { + return null != TrySendBytes(responseMessage, true); + } + catch (ConnectorIOException e) + { + TraceUtil.TraceException("Operation complete unsuccessfully and failed to send error", e); + } + catch (Exception e) + { + TraceUtil.TraceException("Operation complete unsuccessfully and failed to build result message", e); + } + return false; + } + + protected WebSocketConnectionHolder TrySendBytes(byte[] responseMessage) + { + return TrySendBytes(responseMessage, false); + } + + protected WebSocketConnectionHolder TrySendBytes(byte[] responseMessage, bool useAnyConnection) + { + return TrySendMessageNow(responseMessage); + } + + private WebSocketConnectionHolder TrySendMessageNow(byte[] responseMessage) + { + return + RemoteConnectionContext.RemoteConnectionGroup.TrySendMessage( + connection => + connection(new WebSocketConnectionHolder.InternalAsyncMessageQueueRecord(responseMessage)) + .Result); + } + + protected override bool TryCancel() + { + return false; + } + } + + #endregion + + #region RemoteOperationContext + + public class RemoteOperationContext : + IRemoteConnectionContext + { + private readonly WebSocketConnectionGroup _connectionGroup; + + private readonly IPrincipal _connectionPrincipal; + + protected internal RemoteOperationContext(IPrincipal connectionPrincipal, + WebSocketConnectionGroup connectionGroup) + { + _connectionPrincipal = connectionPrincipal; + _connectionGroup = connectionGroup; + } + + public virtual WebSocketConnectionGroup RemoteConnectionGroup + { + get { return _connectionGroup; } + } + + public virtual IPrincipal RemotePrincipal + { + get { return _connectionPrincipal; } + } + } + + #endregion + + #region RemoteOperationRequest + + public abstract class RemoteOperationRequest : + RemoteRequest + { + + protected int InconsistencyCounter = 0; + + protected RemoteOperationRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > + completionCallback, CancellationToken cancellationToken) + : base(context, requestId, completionCallback, cancellationToken) + { + } + + protected internal abstract bool HandleResponseMessage(WebSocketConnectionHolder sourceConnection, + Object message); + + protected internal abstract RPCRequest CreateOperationRequest(RemoteOperationContext remoteContext); + + public override Boolean Check() + { + Boolean valid = InconsistencyCounter < 3; + if (!valid) + { + Trace.TraceInformation( + "RemoteRequest:{0} -> inconsistent with remote server, set failed local process.", + RequestId); + HandleError( + new ConnectorException( + "Operation finished on remote server with unknown result")); + } + return valid; + } + + public override void Inconsistent() + { + InconsistencyCounter++; + } + + public override void HandleIncomingMessage(WebSocketConnectionHolder sourceConnection, Object message) + { + var exceptionMessage = message as ExceptionMessage; + if (exceptionMessage != null) + { + HandleExceptionMessage(exceptionMessage); + } + else + { + if (message != null) + { + if (!HandleResponseMessage(sourceConnection, message)) + { +#if DEBUG + Debug.WriteLine("Request {0} has unknown response message type:{1}", RequestId, this.GetType().Name); +#endif + HandleError(new ConnectorException("Unknown response message type:" + message.GetType())); + } + } + } + } + + protected override + RemoteConnectionGroup. + AsyncMessageQueueRecord CreateMessageElement(RemoteOperationContext remoteContext, long requestId) + { + return new WebSocketConnectionHolder.InternalAsyncMessageQueueRecord(new RemoteMessage + { + MessageId = requestId, + Request = CreateOperationRequest(remoteContext) + }.ToByteArray()); + } + + protected override void TryCancelRemote(RemoteOperationContext remoteContext, long requestId) + { + byte[] cancelMessage = + new RemoteMessage + { + MessageId = requestId, + Request = new RPCRequest + { + CancelOpRequest = new CancelOpRequest() + } + }.ToByteArray(); + + TrySendBytes(cancelMessage); + } + + protected override Exception CreateCancellationException(Exception cancellationException) + { + var canceledException = cancellationException as OperationCanceledException; + if (canceledException != null) + { + return cancellationException; + } + if (null != cancellationException) + { + OperationCanceledException exception = new OperationCanceledException(cancellationException.Message, + cancellationException); + return exception; + } + + return new OperationCanceledException("Operation is cancelled #" + RequestId); + } + + protected internal virtual void TrySendBytes(byte[] cancelMessage) + { + if (null == ConnectionContext.RemoteConnectionGroup.TrySendMessage(async connection => + { + await connection(new WebSocketConnectionHolder.InternalAsyncMessageQueueRecord(cancelMessage)); + return true; + })) + { + // Failed to send remote message + throw new ConnectorIOException("Transport layer is not operational"); + } + } + + protected internal virtual void HandleExceptionMessage(ExceptionMessage exceptionMessage) + { + try + { + HandleError(MessagesUtil.FromExceptionMessage(exceptionMessage)); + } + catch (Exception e) + { + Trace.TraceInformation("Exception received but failed to handle it: {0}:{1}", RequestId, e.Message); + } + } + } + + #endregion + + #region WebSocketConnectionGroup + + public class WebSocketConnectionGroup : + RemoteConnectionGroup, + IAsyncConnectorInfoManager + { + private DateTime _lastActivity = DateTime.Now; + + private readonly Encryptor _encryptor = null; + + private RemoteOperationContext _operationContext; + + private readonly HashSet _principals = new HashSet(StringComparer.OrdinalIgnoreCase); + + private readonly ConcurrentDictionary + _configurationChangeListeners = + new ConcurrentDictionary(); + + public delegate void WebSocketConnectionGroupEventListenerOnDispose(WebSocketConnectionHolder disposeable); + + private readonly EventHandler _closeListener = (sender, args) => + { + WebSocketConnectionHolder connection = sender as WebSocketConnectionHolder; + if (null != connection) + { + foreach (var p in connection.RemoteConnectionContext.RemoteConnectionGroup.WebSockets) + { + if (connection.Equals(p.Key)) + { + String ignore; + connection.RemoteConnectionContext.RemoteConnectionGroup.WebSockets.TryRemove(p.Key, out ignore); + } + } + } + }; + + private readonly RemoteConnectorInfoManager _delegate; + + public WebSocketConnectionGroup(string remoteSessionId) + : base(remoteSessionId) + { + _delegate = new RemoteConnectorInfoManager(this); + } + + public virtual RemoteOperationContext Handshake(IPrincipal connectionPrincipal, + WebSocketConnectionHolder webSocketConnection, HandshakeMessage message) + { + if (null == _operationContext) + { + lock (this) + { + if (null == _operationContext) + { + _operationContext = new RemoteOperationContext(connectionPrincipal, this); + } + } + } + if (RemoteSessionId.Equals(message.SessionId)) + { + WebSockets.TryAdd(webSocketConnection, connectionPrincipal.Identity.Name); + webSocketConnection.Disposed += _closeListener; + if (null != connectionPrincipal.Identity.Name) + _principals.Add(connectionPrincipal.Identity.Name); + //This is not thread-safe, it could yield true for cuncurrent threads + if (webSocketConnection.Equals(WebSockets.Keys.First())) + { + ControlMessageRequestFactory requestFactory = new ControlMessageRequestFactory(); + requestFactory.InfoLevels.Add(ControlRequest.Types.InfoLevel.CONNECTOR_INFO); + TrySubmitRequest(requestFactory); + } + } + return _operationContext; + } + + public virtual void PrincipalIsShuttingDown(IPrincipal connectionPrincipal) + { + string name = connectionPrincipal.Identity.Name; + if (_principals.Remove(name)) + { + Shutdown(); + foreach (var entry in WebSockets) + { + if (name.Equals(entry.Value, StringComparison.CurrentCultureIgnoreCase)) + { + String ignore; + WebSockets.TryRemove(entry.Key, out ignore); + } + } + } + } + + protected void Shutdown() + { + if (!_principals.Any()) + { + // Gracefully close all request and shut down this group. + foreach ( + var local in + LocalRequests.Select(entry => entry.Value) + .OfType + ()) + { + local.Dispose(); + } + foreach ( + var remote in + RemoteRequests.Select(entry => entry.Value) + .OfType + ()) + { + remote.Dispose(); + } + _delegate.Dispose(); + } + } + + public bool TrySendMessage(IMessage message) + { + byte[] messageBytes = message.ToByteArray(); + return true.Equals(TrySendMessage(connection => + { + connection(new WebSocketConnectionHolder.InternalAsyncMessageQueueRecord(messageBytes)).Wait(); + return true; + })); + } + + + protected override RemoteOperationContext RemoteConnectionContext + { + get { return _operationContext; } + } + + public override bool Operational + { + get { return WebSockets.Keys.Any(e => e.Operational); } + } + + public virtual Encryptor Encryptor + { + get { return _encryptor; } + } + + // --- AsyncConnectorInfoManager implementation --- + + public virtual Task FindConnectorInfoAsync(Org.IdentityConnectors.Framework.Api.ConnectorKey key) + { + return _delegate.FindConnectorInfoAsync(key); + } + + public virtual Task FindConnectorInfoAsync(ConnectorKeyRange keyRange) + { + return _delegate.FindConnectorInfoAsync(keyRange); + } + + public virtual IList ConnectorInfos + { + get { return _delegate.ConnectorInfos; } + } + + public virtual ConnectorInfo FindConnectorInfo(Org.IdentityConnectors.Framework.Api.ConnectorKey key) + { + return _delegate.FindConnectorInfo(key); + } + + // --- AsyncConnectorInfoManager implementation --- + + public void AddConfigurationChangeListener(String key, IConfigurationPropertyChangeListener listener) + { + if (!String.IsNullOrEmpty(key)) + { + if (null != listener) + { + _configurationChangeListeners.TryAdd(key, listener); + } + else + { + IConfigurationPropertyChangeListener ignore; + _configurationChangeListeners.TryRemove(key, out ignore); + } + } + } + + public void NotifyConfigurationChangeListener(string key, IList change) + { + if (null != key && null != change) + { + IConfigurationPropertyChangeListener listener; + _configurationChangeListeners.TryGetValue(key, out listener); + if (null != listener) + { + try + { + listener.ConfigurationPropertyChange(change); + } + catch (Exception e) + { +#if DEBUG + StringBuilder builder = new StringBuilder("Failed to notify connfiguration change - "); + TraceUtil.ExceptionToString(builder, e, String.Empty); + Debug.WriteLine(builder.ToString()); +#endif + } + } + } + } + + public virtual bool CheckIsActive() + { + bool operational = Operational; + + if (new TimeSpan(DateTime.Now.Ticks - _lastActivity.Ticks).TotalHours > 0 && !Operational) + { + // 1 hour inactivity -> Shutdown + _principals.Clear(); + Shutdown(); + WebSockets.Clear(); + } + else + { + foreach (var local in LocalRequests.Values) + { + local.Check(); + } + + foreach (var remote in RemoteRequests.Values) + { + remote.Check(); + } + + if (operational) + { + ControlMessageRequestFactory requestFactory = new ControlMessageRequestFactory(); + TrySubmitRequest(requestFactory); + } + } + return operational || !RemoteRequests.IsEmpty || !LocalRequests.IsEmpty; + } + + public void ProcessControlRequest(ControlRequest message) + { + _lastActivity = DateTime.Now; + foreach (var entry in RemoteRequests) + { + if (!message.LocalRequestId.Contains(entry.Key)) + { + //Remote request is exists locally but remotely nothing match. + // 1. ControlRequest was sent before LocalRequest was created so it normal. + // 2. Request on remote side is completed so we must Complete the RemoteRequest(Fail) unless the local request is still processing. + entry.Value.Inconsistent(); + } + } + + foreach (var entry in LocalRequests) + { + if (!message.RemoteRequestId.Contains(entry.Key)) + { + //Local request exists locally but remotely nothing match. + // 1. ControlRequest was sent before RemoteRequest was created so it normal. + // 2. Request on remote side is Cancelled/Terminated so it can safely Cancel here. + entry.Value.Inconsistent(); + } + } + } + + // -- Static Classes + + private sealed class RemoteConnectorInfoManager : + ManagedAsyncConnectorInfoManager + { + private readonly + IRequestDistributor + _outerInstance; + + public RemoteConnectorInfoManager( + IRequestDistributor + remoteConnection) + { + _outerInstance = remoteConnection; + } + + internal void AddOtherConnectorInfo(T1 connectorInfo) where T1 : AbstractConnectorInfo + { + AddConnectorInfo(new RemoteConnectorInfoImpl(_outerInstance, connectorInfo)); + } + + internal void AddAll(ICollection connectorInfos) where T1 : AbstractConnectorInfo + { + foreach (var connectorInfo in connectorInfos) + { + AddConnectorInfo(new RemoteConnectorInfoImpl(_outerInstance, connectorInfo)); + } + } + } + + private sealed class ControlMessageRequestFactory : + IRemoteRequestFactory + + { + public readonly List InfoLevels = new List(); + + public ControlMessageRequest CreateRemoteRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback) + { + return new ControlMessageRequest(context, requestId, completionCallback, InfoLevels); + } + } + + private class ControlMessageRequest : RemoteOperationRequest + { + private readonly List _infoLevels; + + public ControlMessageRequest(RemoteOperationContext context, long requestId, + Action + < + RemoteRequest + > completionCallback, + List infoLevels) + : base(context, requestId, completionCallback, CancellationToken.None) + { + _infoLevels = infoLevels; + } + + protected internal override RPCRequest CreateOperationRequest(RemoteOperationContext remoteContext) + { + ControlRequest builder = new ControlRequest(); + foreach (ControlRequest.Types.InfoLevel infoLevel in _infoLevels) + { + builder.InfoLevel.Add(infoLevel); + } + builder.LocalRequestId.Add(remoteContext.RemoteConnectionGroup.LocalRequests.Keys); + builder.RemoteRequestId.Add(remoteContext.RemoteConnectionGroup.RemoteRequests.Keys); + return new RPCRequest + { + ControlRequest = builder, + }; + } + + protected internal override bool HandleResponseMessage(WebSocketConnectionHolder sourceConnection, + Object message) + { + var controlResponse = message as ControlResponse; + if (controlResponse != null) + { + var response = MessagesUtil.DeserializeLegacy>(controlResponse.ConnectorInfos); + if (null != response && response.Any()) + { + foreach (var o in response) + { + var connectorInfo = o as AbstractConnectorInfo; + if (null != connectorInfo) + { + sourceConnection.RemoteConnectionContext.RemoteConnectionGroup._delegate + .AddOtherConnectorInfo(connectorInfo); + } + } + } + HandleResult(true); + } + else + { + return false; + } + return true; + } + } + } + + #endregion + + #region WebSocketConnectionHolder + + public abstract class WebSocketConnectionHolder : + IRemoteConnectionHolder + { +#if DEBUG + private static Int32 _counter; + private Int32 _id; + + public Int32 Id + { + get + { + if (_id == 0) + { + _id = Interlocked.Increment(ref _counter); + } + return _id; + } + } +#endif + + private readonly + ConcurrentDictionary + < + RemoteConnectionGroup. + AsyncMessageQueueRecord, Boolean> _messageQueue = + new ConcurrentDictionary + < + RemoteConnectionGroup + . + AsyncMessageQueueRecord, Boolean>(); + + private readonly AutoResetEvent _onMessageToSendEvent = new AutoResetEvent(true); + + /// + /// Adds a event handler to listen to the Disposed event on the WebSocketConnectionHolder. + /// + private event EventHandler DisposedEvent; + + public event EventHandler Disposed + { + add + { + // check if this is still running + if (Operational) + { + // add close listener + DisposedEvent += value; + // check the its state again + if (DisposedEvent != null && (!Operational && DisposedEvent.GetInvocationList().Contains(value))) + { + // if this was closed during the method call - notify the + // listener + try + { + value(this, EventArgs.Empty); + } + catch (Exception) + { + // ignored + } + } + } // if this is closed - notify the listener + else + { + try + { + value(this, EventArgs.Empty); + } + catch (Exception) + { + // ignored + } + } + } + remove { DisposedEvent -= value; } + } + + protected abstract Task WriteMessageAsync(byte[] entry, WebSocketMessageType messageType); + + protected async void WriteMessageAsync() + { + while (Operational) + { + do + { + foreach (var entry in _messageQueue) + { + if (await entry.Key.AcquireAndTryComplete(this)) + { + bool ignore; + _messageQueue.TryRemove(entry.Key, out ignore); +#if DEBUG + Debug.WriteLine("Dequeue from {2} Message:{1}, Pending:{0}", _messageQueue.Count, + entry.Key.Id, Id); +#endif + } + } + } while (_messageQueue.Any()); + + _onMessageToSendEvent.WaitOne(TimeSpan.FromMinutes(1)); + } +#if DEBUG + Debug.WriteLine("Finish Writting messages over Socket:{0}", Id); +#endif + foreach (var asyncQueueRecord in _messageQueue.Keys) + { + asyncQueueRecord.Detach(this); + } + } + + public virtual bool ReceiveHandshake(HandshakeMessage message) + { + if (null == RemoteConnectionContext) + { + Handshake(message); +#if DEBUG + Debug.WriteLine("New Connection accepted {0}:{1}", GetType().FullName, Id); +#endif + } + return HandHooked; + } + + public virtual bool HandHooked + { + get { return null != RemoteConnectionContext; } + } + + public void Dispose() + { + TryClose(); + _onMessageToSendEvent.Set(); + OnDisposed(); + } + + protected void OnDisposed() + { + try + { + var handler = DisposedEvent; + if (handler != null) handler(this, EventArgs.Empty); + } + catch (Exception e) + { +#if DEBUG + StringBuilder builder = new StringBuilder("DisposedEvent failed - "); + TraceUtil.ExceptionToString(builder, e, String.Empty); + Debug.WriteLine(builder.ToString()); +#endif + } + } + + protected abstract void Handshake(HandshakeMessage message); + + protected abstract void TryClose(); + + public abstract bool Operational { get; } + + public abstract RemoteOperationContext RemoteConnectionContext { get; } + + public void Enqueue( + RemoteConnectionGroup. + AsyncMessageQueueRecord record) + { + if (Operational && record.Accept(this)) + { + if (_messageQueue.TryAdd(record, true) && !Operational) + { + bool ignore; + _messageQueue.TryRemove(record, out ignore); + record.Detach(this); + } + else + { +#if DEBUG + Debug.WriteLine("Enqueue Socket:{0} Message:{1}", Id, record.Id); +#endif + _onMessageToSendEvent.Set(); + } + } + } + + + public class InternalAsyncMessageQueueRecord : + RemoteConnectionGroup. + AsyncMessageQueueRecord + { + private readonly WebSocketMessageType _messageType; + private readonly byte[] _message; + + public override string ToString() + { +#if DEBUG + return String.Format("{1} Message - Id:[{2}] size:{0}", _message.Length, _messageType, Id); +#else + return String.Format("{1} Message - size:{0}", _message.Length, _messageType); +#endif + } + + public InternalAsyncMessageQueueRecord(byte[] message) + { + _messageType = WebSocketMessageType.Binary; + _message = message; + } + + public InternalAsyncMessageQueueRecord(string message) + { + _messageType = WebSocketMessageType.Text; + _message = Encoding.UTF8.GetBytes(message); + } + + protected override Task DoSend(WebSocketConnectionHolder connection) + { +#if DEBUG + Debug.WriteLine("WebSocket: {0} writes {1} bytes of message: {2}", connection.Id, _message.Length, Id); +#endif + return connection.WriteMessageAsync(_message, _messageType); + } + } + + + public Task SendBytesAsync(byte[] data, CancellationToken cancellationToken) + { + var record = new InternalAsyncMessageQueueRecord(data); + Enqueue(record); + return record.SendAsync(TimeSpan.FromMinutes(1)); + } + + public Task SendStringAsync(string data, CancellationToken cancellationToken) + { + var record = new InternalAsyncMessageQueueRecord(data); + Enqueue(record); + return record.SendAsync(TimeSpan.FromMinutes(1)); + } + + public void SendPing(byte[] applicationData) + { + throw new NotImplementedException(); + } + + public void SendPong(byte[] applicationData) + { + throw new NotImplementedException(); + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/packages.config b/dotnet/framework/FrameworkServer/packages.config new file mode 100755 index 00000000..097afb81 --- /dev/null +++ b/dotnet/framework/FrameworkServer/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkServer/version.template b/dotnet/framework/FrameworkServer/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/FrameworkServer/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/FrameworkServerTests/FrameworkServerTests.csproj b/dotnet/framework/FrameworkServerTests/FrameworkServerTests.csproj new file mode 100755 index 00000000..2d97d9cf --- /dev/null +++ b/dotnet/framework/FrameworkServerTests/FrameworkServerTests.csproj @@ -0,0 +1,155 @@ + + + + + Debug + AnyCPU + {46365523-FA23-4AD4-9DB8-B0E195F00571} + Library + Properties + Org.ForgeRock.OpenICF.Framework.Remote + FrameworkServerTests + OpenICF Framework - Connector Server Tests + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + UnitTest + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Google.ProtocolBuffers.3\lib\Google.Protobuf.dll + + + + + + False + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + False + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + False + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + + + False + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + + + + + + + + + + + + + {f140e8da-52b4-4159-992a-9da10ea8eefb} + Common + + + {8b24461b-456a-4032-89a1-cd418f7b5b62} + Framework + + + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + FrameworkInternal + + + {E6A207D2-E083-41BF-B522-D9D3EC09323E} + TestCommon + + + {b85c5a35-e3a2-4b04-9693-795e57d66de2} + FrameworkRpc + + + {5b47befd-c60b-4e80-943e-a7151ceea568} + FrameworkServer + + + {d1771e11-c7d3-43fd-9d87-46f1231846f1} + WcfServiceLibrary + + + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} + ConnectorServerService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkServerTests/WcfServiceTests.cs b/dotnet/framework/FrameworkServerTests/WcfServiceTests.cs new file mode 100755 index 00000000..01b3a0f8 --- /dev/null +++ b/dotnet/framework/FrameworkServerTests/WcfServiceTests.cs @@ -0,0 +1,635 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Reactive.Linq; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading; +using NUnit.Framework; +using Org.ForgeRock.OpenICF.Framework.ConnectorServerService; +using Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Test.Common; + +namespace Org.ForgeRock.OpenICF.Framework.Remote +{ + [TestFixture] + [Explicit] + public class ExplicitServerTest : ServerTestBase + { + protected override Action Start(ClientAuthenticationValidator validator, EndpointAddress serviceAddress) + { + return null; + } + + protected override int FreePort + { + get { return 8759; } + } + } + + [TestFixture] + public class NonWcfServerTest : ServerTestBase + { + protected override Action Start(ClientAuthenticationValidator validator, EndpointAddress serviceAddress) + { + VtortConnectorServiceHost host = new VtortConnectorServiceHost(validator, serviceAddress.Uri); + host.Open(); + + return () => host.Close(); + } + } + + + [TestFixture] + public class WcfServerTest : ServerTestBase + { + protected override Action Start(ClientAuthenticationValidator validator, EndpointAddress serviceAddress) + { + ServiceHost host = new ConnectorServiceHost(validator, serviceAddress.Uri); + + CustomBinding binding = new CustomBinding(); + binding.Elements.Add(new ByteStreamMessageEncodingBindingElement()); + + HttpTransportBindingElement transport = new HttpTransportBindingElement + { + WebSocketSettings = + { + TransportUsage = WebSocketTransportUsage.Always, + CreateNotificationOnConnection = true, + SubProtocol = "v1.openicf.forgerock.org" + }, + Realm = "openicf", + AuthenticationScheme = AuthenticationSchemes.Basic, + }; + + binding.Elements.Add(transport); + host.AddServiceEndpoint(typeof (IWebSocketService), binding, ""); + + host.Open(); + + return () => { host.Close(); }; + } + } + + + [TestFixture] + public abstract class ServerTestBase + { + protected static readonly ConnectorKey TestConnectorKey = new ConnectorKey("TestBundleV1.Connector", "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + protected static readonly ConnectorKey TestStatefulConnectorKey = new ConnectorKey("TestBundleV1.Connector", + "1.0.0.0", + "org.identityconnectors.testconnector.TstStatefulConnector"); + + protected static readonly ConnectorKey TestPoolableStatefulConnectorKey = + new ConnectorKey("TestBundleV1.Connector", "1.0.0.0", + "org.identityconnectors.testconnector.TstStatefulPoolableConnector"); + + private Action _close; + private ConnectorFramework _clientConnectorFramework; + private RemoteWSFrameworkConnectionInfo _connectionInfo; + + + protected virtual int FreePort + { + get { return 8000; } + } + + private static int FreeTcpPort() + { + //netsh http add urlacl url=http://+:8000/openicf user=DOMAIN\user + TcpListener l = new TcpListener(IPAddress.Loopback, 0); + l.Start(); + int port = ((IPEndPoint) l.LocalEndpoint).Port; + l.Stop(); + return port; + } + + protected abstract Action Start(ClientAuthenticationValidator validator, EndpointAddress serviceAddress); + + [TestFixtureSetUp] + public void Init() + { + try + { + ConnectorFramework serverConnectorFramework = new ConnectorFramework(); + ConnectorServerService.ConnectorServerService.InitializeConnectors(serverConnectorFramework.LocalManager); + + foreach (var connectorInfo in serverConnectorFramework.LocalManager.ConnectorInfos) + { + Trace.TraceInformation("Found Connector {0}", connectorInfo.ConnectorKey); + } + + int freePort = FreePort; + EndpointAddress serviceAddress = + new EndpointAddress(String.Format("http://localhost:{0}/openicf", freePort)); + + var secureString = new GuardedString(); + "changeit".ToCharArray().ToList().ForEach(p => secureString.AppendChar(p)); + + ClientAuthenticationValidator validator = new ClientAuthenticationValidator(); + validator.Add(new SingleTenantPrincipal(serverConnectorFramework), secureString.GetBase64SHA1Hash()); + + _close = Start(validator, serviceAddress); + _close += () => serverConnectorFramework.Dispose(); + + // ---------- + + _clientConnectorFramework = new ConnectorFramework(); + + _connectionInfo = new RemoteWSFrameworkConnectionInfo + { + RemoteUri = new Uri(String.Format("http://localhost.fiddler:{0}/openicf", freePort)), + Principal = ConnectionPrincipal.DefaultName, + Password = secureString + }; + } + catch (Exception e) + { + TraceUtil.TraceException("Failed", e); + throw; + } + } + + [TestFixtureTearDown] + public void Dispose() + { + if (null != _close) + { + _close(); + } + } + + [SetUp] + public void InitTest() + { + /* ... */ + } + + [TearDown] + public void DisposeTest() + { + /* ... */ + } + + public ConnectorFramework ConnectorFramework + { + get { return _clientConnectorFramework; } + } + + protected AsyncRemoteConnectorInfoManager ConnectorInfoManager + { + get { return ConnectorFramework.GetRemoteManager(_connectionInfo); } + } + + protected ConnectorFacade ConnectorFacade + { + get { return GetConnectorFacade(false, false); } + } + + protected ConnectorFacade GetConnectorFacade(Boolean caseIgnore, + Boolean returnNullTest) + { + return ConnectorInfoManager.FindConnectorInfoAsync(TestStatefulConnectorKey) + .ContinueWith(task => + { + if (task.IsCompleted) + { + var info = task.Result; + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + ConfigurationProperties props = api.ConfigurationProperties; + + props.GetProperty("randomString").Value = StringUtil.RandomString(); + props.GetProperty("caseIgnore").Value = caseIgnore; + props.GetProperty("returnNullTest").Value = returnNullTest; + props.GetProperty("failValidation").Value = false; + props.GetProperty("testObjectClass").Value = + new[] {ObjectClass.ACCOUNT_NAME, ObjectClass.GROUP_NAME}; + api.ProducerBufferSize = 0; + return ConnectorFramework.NewInstance(api); + } + task.Wait(); + return null; + }).Result; + } + + + [Test] + public void TestRequiredConnectorInfo() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + Assert.IsNotNull(manager); + + var result = manager.FindConnectorInfoAsync(TestConnectorKey); + Assert.IsTrue(result.Wait(TimeSpan.FromMinutes(5))); + Assert.IsNotNull(result.Result); + + result = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + Assert.IsTrue(result.Wait(TimeSpan.FromMinutes(5))); + Assert.IsNotNull(result.Result); + + result = manager.FindConnectorInfoAsync(TestPoolableStatefulConnectorKey); + Assert.IsTrue(result.Wait(TimeSpan.FromMinutes(5))); + Assert.IsNotNull(result.Result); + } + + [Test] + public void TestValidate() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + var task = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + Assert.IsTrue(task.Wait(TimeSpan.FromMinutes(5))); + + ConnectorInfo info = task.Result; + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("failValidation"); + property.Value = false; + + ConnectorFacade facade = ConnectorFramework.NewInstance(api); + facade.Validate(); + property.Value = true; + facade = ConnectorFramework.NewInstance(api); + try + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (Exception e) + { + TraceUtil.TraceException("Test Exception", e); + Assert.AreEqual("validation failed en", e.Message); + } + try + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("es"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (RemoteWrappedException e) + { + Assert.AreEqual("validation failed es", e.Message); + } + + // call test and also test that locale is propagated + // properly + try + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); + facade.Test(); + Assert.Fail("exception expected"); + } + catch (Exception e) + { + TraceUtil.TraceException("Test Exception", e); + Assert.AreEqual("test failed en", e.Message); + } + } + + [Test] + public virtual void TestConfigurationUpdate() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + var taskA = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + var taskB = manager.FindConnectorInfoAsync(TestPoolableStatefulConnectorKey); + Assert.IsTrue(taskA.Wait(TimeSpan.FromMinutes(5))); + Assert.IsTrue(taskB.Wait(TimeSpan.FromMinutes(5))); + + ConnectorInfo[] infos = + { + taskA.Result, + taskB.Result + }; + foreach (ConnectorInfo info in infos) + { + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + props.GetProperty("randomString").Value = StringUtil.RandomString(); + api.ProducerBufferSize = 0; + + var listener = new ConfigurationPropertyChangeListener(); + api.ChangeListener = listener; + + ConnectorFacade facade = ConnectorFramework.NewInstance(api); + + ScriptContextBuilder builder = new ScriptContextBuilder + { + ScriptLanguage = "Boo", + ScriptText = "connector.Update()" + }; + + facade.RunScriptOnConnector(builder.Build(), null); + + for (int i = 0; (i < 25 && null == listener.Changes); i++) + { + Thread.Sleep(1000); + } + Assert.NotNull(listener.Changes); + Assert.AreEqual(listener.Changes.Count, 1); + Assert.AreEqual(listener.Changes.First().Value, "change"); + } + } + + internal class ConfigurationPropertyChangeListener : IConfigurationPropertyChangeListener + { + internal IList Changes { get; set; } + + public virtual void ConfigurationPropertyChange(IList changes) + { + Changes = changes; + } + } + + [Test] + public void TestNullOperations() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + var task = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + Assert.IsTrue(task.Wait(TimeSpan.FromMinutes(5))); + + ConnectorFacade facade = GetConnectorFacade(true, true); + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + facade.Test(); + Assert.IsNull(facade.Schema()); + + var guardedString = new GuardedString(); + "Passw0rd".ToCharArray().ToList().ForEach(p => guardedString.AppendChar(p)); + + Uid uid = facade.Create(ObjectClass.ACCOUNT, + CollectionUtil.NewSet(new Name("CREATE_01"), ConnectorAttributeBuilder.BuildPassword(guardedString)), + optionsBuilder.Build()); + Assert.IsNull(uid); + + Uid resolvedUid = facade.ResolveUsername(ObjectClass.ACCOUNT, "CREATE_01", optionsBuilder.Build()); + Assert.IsNull(resolvedUid); + + + Uid authenticatedUid = facade.Authenticate(ObjectClass.ACCOUNT, "CREATE_01", guardedString, + optionsBuilder.Build()); + Assert.IsNull(authenticatedUid); + + SyncToken token = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.IsNull(token); + + SyncToken lastToken = facade.Sync(ObjectClass.ACCOUNT, new SyncToken(-1), + new SyncResultsHandler {Handle = delta => true}, optionsBuilder.Build()); + + Assert.IsNull(lastToken); + + SearchResult searchResult = facade.Search(ObjectClass.ACCOUNT, null, + new ResultsHandler {Handle = connectorObject => true}, optionsBuilder.Build()); + + Assert.IsNull(searchResult); + + Uid updatedUid = facade.Update(ObjectClass.ACCOUNT, new Uid("1"), + CollectionUtil.NewSet(ConnectorAttributeBuilder.BuildLockOut(true)), optionsBuilder.Build()); + Assert.IsNull(updatedUid); + + ConnectorObject co = facade.GetObject(ObjectClass.ACCOUNT, new Uid("1"), optionsBuilder.Build()); + Assert.IsNull(co); + + + ScriptContextBuilder contextBuilder = new ScriptContextBuilder + { + ScriptLanguage = "Boo", + ScriptText = "arg" + }; + contextBuilder.AddScriptArgument("arg", "test"); + + object o = facade.RunScriptOnConnector(contextBuilder.Build(), optionsBuilder.Build()); + Assert.AreEqual(o, "test"); + o = facade.RunScriptOnResource(contextBuilder.Build(), optionsBuilder.Build()); + Assert.IsNull(o); + } + + [Test] + public void TestOperations() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + var task = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + Assert.IsTrue(task.Wait(TimeSpan.FromMinutes(5))); + + ConnectorFacade facade = ConnectorFacade; + facade.Test(); + Assert.IsNotNull(facade.Schema()); + + var guardedString = new GuardedString(); + "Passw0rd".ToCharArray().ToList().ForEach(p => guardedString.AppendChar(p)); + + Uid uid1 = facade.Create(ObjectClass.ACCOUNT, + CollectionUtil.NewSet(new Name("CREATE_01"), ConnectorAttributeBuilder.BuildPassword(guardedString)), + null); + Assert.IsNotNull(uid1); + + Uid uid2 = facade.Create(ObjectClass.ACCOUNT, + CollectionUtil.NewSet(new Name("CREATE_02"), ConnectorAttributeBuilder.BuildPassword(guardedString)), + null); + + Assert.AreNotEqual(uid1, uid2); + + Uid resolvedUid = facade.ResolveUsername(ObjectClass.ACCOUNT, "CREATE_01", null); + Assert.AreEqual(uid1, resolvedUid); + + Uid authenticatedUid = facade.Authenticate(ObjectClass.ACCOUNT, "CREATE_01", guardedString, null); + Assert.AreEqual(uid1, authenticatedUid); + + try + { + guardedString = new GuardedString(); + "wrongPassw0rd".ToCharArray().ToList().ForEach(p => guardedString.AppendChar(p)); + facade.Authenticate(ObjectClass.ACCOUNT, "CREATE_01", guardedString, null); + Assert.Fail("This should fail"); + } + catch (Exception e) + { + Assert.AreEqual("Invalid Password", e.Message); + } + + SyncToken token = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.AreEqual(token.Value, 2); + + IList changes = new List(); + Int32? index = null; + + SyncToken lastToken = facade.Sync(ObjectClass.ACCOUNT, new SyncToken(-1), new SyncResultsHandler + { + Handle = delta => + { + Int32? previous = index; + index = (Int32?) delta.Token.Value; + if (null != previous) + { + Assert.IsTrue(previous < index); + } + changes.Add(delta); + return true; + } + }, null); + + Assert.AreEqual(changes.Count, 2); + Assert.AreEqual(facade.GetObject(ObjectClass.ACCOUNT, uid1, null).Uid, uid1); + Assert.AreEqual(token, lastToken); + + IList connectorObjects = new List(); + facade.Search(ObjectClass.ACCOUNT, + FilterBuilder.Or(FilterBuilder.EqualTo(new Name("CREATE_02")), + FilterBuilder.StartsWith(new Name("CREATE"))), new ResultsHandler + { + Handle = + connectorObject => + { + connectorObjects.Add(connectorObject); + return true; + } + }, null); + Assert.AreEqual(connectorObjects.Count, 2); + + connectorObjects = new List(); + facade.Search(ObjectClass.ACCOUNT, null, new ResultsHandler + { + Handle = + connectorObject => + { + connectorObjects.Add(connectorObject); + return true; + } + }, null); + Assert.AreEqual(connectorObjects.Count, 2); + + Uid updatedUid = facade.Update(ObjectClass.ACCOUNT, uid1, + CollectionUtil.NewSet(ConnectorAttributeBuilder.BuildLockOut(true)), null); + ConnectorObject co = facade.GetObject(ObjectClass.ACCOUNT, updatedUid, null); + var isLockedOut = ConnectorAttributeUtil.IsLockedOut(co); + Assert.IsTrue(isLockedOut != null && (bool) isLockedOut); + + facade.Delete(ObjectClass.ACCOUNT, updatedUid, null); + Assert.IsNull(facade.GetObject(ObjectClass.ACCOUNT, updatedUid, null)); + } + + [Test] + public void TestScriptOperations() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + var task = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + Assert.IsTrue(task.Wait(TimeSpan.FromMinutes(5))); + + ConnectorFacade facade = ConnectorFacade; + + ScriptContextBuilder contextBuilder = new ScriptContextBuilder + { + ScriptLanguage = "Boo", + ScriptText = "arg", + }; + contextBuilder.AddScriptArgument("arg", "test"); + + object o = facade.RunScriptOnConnector(contextBuilder.Build(), null); + Assert.AreEqual(o, "test"); + o = facade.RunScriptOnResource(contextBuilder.Build(), null); + Assert.AreEqual(o, "test"); + } + + [Test] + public void TestSubscriptionOperation() + { + IAsyncConnectorInfoManager manager = ConnectorInfoManager; + var task = manager.FindConnectorInfoAsync(TestStatefulConnectorKey); + Assert.IsTrue(task.Wait(TimeSpan.FromMinutes(5))); + + ConnectorFacade facade = ConnectorFacade; + ToListResultsHandler handler = new ToListResultsHandler(); + CountdownEvent cde = new CountdownEvent(1); + var localFacade = facade; + var connectorObjectObservable = + Observable.Create(o => localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, null)); + + + var subscription = connectorObjectObservable.Subscribe( + co => + { + Console.WriteLine(@"Connector Event received:{0}", co.Uid.GetUidValue()); + handler.ResultsHandler.Handle(co); + }, + ex => + { + cde.Signal(); + Assert.AreEqual(handler.Objects.Count, 10, "Uncompleted subscription"); + }); + + + cde.Wait(new TimeSpan(0, 0, 25)); + subscription.Dispose(); + Assert.AreEqual(10, handler.Objects.Count); + + handler = new ToListResultsHandler(); + cde = new CountdownEvent(1); + + var syncDeltaObservable = + Observable.Create(o => localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, null)); + + IDisposable[] subscriptions = new IDisposable[1]; + subscriptions[0] = syncDeltaObservable.Subscribe( + delta => + { + Console.WriteLine(@"Sync Event received:{0}", delta.Token.Value); + if (((int?) delta.Token.Value) > 2) + { + subscriptions[0].Dispose(); + cde.Signal(); + } + handler.ResultsHandler.Handle(delta.Object); + }, + ex => + { + cde.Signal(); + Assert.Fail("Failed Subscription {0}", ex); + }); + + cde.Wait(new TimeSpan(0, 0, 25)); + for (int i = 0; i < 5 && !(handler.Objects.Count > 2); i++) + { + Console.WriteLine(@"Wait for result handler thread to complete: {0}", i); + Thread.Sleep(200); // Wait to complete all other threads + } + Assert.IsTrue(handler.Objects.Count < 10 && handler.Objects.Count > 2); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkServerTests/packages.config b/dotnet/framework/FrameworkServerTests/packages.config new file mode 100755 index 00000000..4b8fa985 --- /dev/null +++ b/dotnet/framework/FrameworkServerTests/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkServerTests/version.template b/dotnet/framework/FrameworkServerTests/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/FrameworkServerTests/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/CollectionUtilTests.cs b/dotnet/framework/FrameworkTests/CollectionUtilTests.cs new file mode 100644 index 00000000..beb0fc5f --- /dev/null +++ b/dotnet/framework/FrameworkTests/CollectionUtilTests.cs @@ -0,0 +1,161 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + [TestFixture] + public class CollectionUtilTests + { + [Test] + public void TestEquals() + { + Assert.IsTrue(CollectionUtil.Equals(null, null)); + Assert.IsFalse(CollectionUtil.Equals(null, "str")); + Assert.IsTrue(CollectionUtil.Equals("str", "str")); + + byte[] arr1 = new byte[] { 1, 2, 3 }; + byte[] arr2 = new byte[] { 1, 2, 3 }; + byte[] arr3 = new byte[] { 1, 2, 4 }; + byte[] arr4 = new byte[] { 1, 2 }; + int[] arr5 = new int[] { 1, 2, 3 }; + + Assert.IsTrue(CollectionUtil.Equals(arr1, arr2)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr3)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr4)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr5)); + + IList list1 = new List(); + IList list2 = new List(); + list1.Add(arr1); + list2.Add(arr2); + + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); + + list2.Add(arr2); + Assert.IsFalse(CollectionUtil.Equals(list1, list2)); + + list1.Add(arr1); + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); + + list1.Add(arr1); + list2.Add(arr3); + Assert.IsFalse(CollectionUtil.Equals(list1, list2)); + + IDictionary map1 = new Dictionary(); + IDictionary map2 = new Dictionary(); + map1["key1"] = arr1; + map2["key1"] = arr2; + Assert.IsTrue(CollectionUtil.Equals(map1, map2)); + map2["key2"] = arr2; + Assert.IsFalse(CollectionUtil.Equals(map1, map2)); + map1["key2"] = arr1; + Assert.IsTrue(CollectionUtil.Equals(map1, map2)); + map1["key2"] = arr3; + Assert.IsFalse(CollectionUtil.Equals(map1, map2)); + + ICollection set1 = new HashSet(); + ICollection set2 = new HashSet(); + set1.Add("val"); + set2.Add("val"); + Assert.IsTrue(CollectionUtil.Equals(set1, set2)); + set2.Add("val2"); + Assert.IsFalse(CollectionUtil.Equals(set1, set2)); + set1.Add("val2"); + Assert.IsTrue(CollectionUtil.Equals(set1, set2)); + } + [Test] + public void TestHashCode() + { + Assert.AreEqual(0, CollectionUtil.GetHashCode(null)); + Assert.AreEqual("str".GetHashCode(), CollectionUtil.GetHashCode("str")); + + byte[] arr1 = new byte[] { 1, 2, 3 }; + byte[] arr2 = new byte[] { 1, 2, 3 }; + byte[] arr3 = new byte[] { 1, 2, 4 }; + byte[] arr4 = new byte[] { 1, 2 }; + int[] arr5 = new int[] { 1, 2, 3 }; + + Assert.AreEqual(CollectionUtil.GetHashCode(arr1), + CollectionUtil.GetHashCode(arr2)); + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr3)); + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr4)); + Assert.IsTrue(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr5)); + + List list1 = new List(); + List list2 = new List(); + list1.Add(arr1); + list2.Add(arr2); + + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list2.Add(arr2); + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list1.Add(arr1); + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list1.Add(arr1); + list2.Add(arr3); + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + Dictionary map1 = new Dictionary(); + Dictionary map2 = new Dictionary(); + map1["key1"] = arr1; + map2["key1"] = arr2; + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map2["key2"] = arr2; + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map1["key2"] = arr1; + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map1["key2"] = arr3; + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + + HashSet set1 = new HashSet(); + HashSet set2 = new HashSet(); + set1.Add("val"); + set2.Add("val"); + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + set2.Add("val2"); + Assert.IsFalse(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + set1.Add("val2"); + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/ConnectorAttributeUtilTests.cs b/dotnet/framework/FrameworkTests/ConnectorAttributeUtilTests.cs new file mode 100644 index 00000000..e063be28 --- /dev/null +++ b/dotnet/framework/FrameworkTests/ConnectorAttributeUtilTests.cs @@ -0,0 +1,371 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using NUnit.Framework; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace FrameworkTests +{ + #region ConnectorAttributesAccessorTest + [TestFixture] + public class ConnectorAttributesAccessorTest + { + [Test] + public virtual void TestGetUid() + { + Assert.AreEqual(Testable.GetUid(), new Uid("UID001")); + } + + [Test] + public virtual void TestGetName() + { + Assert.AreEqual(Testable.GetName(), new Name("NAME001")); + } + + [Test] + public virtual void TestGetEnabled() + { + Assert.IsFalse(Testable.GetEnabled(true)); + } + + [Test] + public virtual void testGetPassword() + { + SecureString secret = new SecureString(); + "Passw0rd".ToCharArray().ToList().ForEach(secret.AppendChar); + Assert.AreEqual(new GuardedString(secret), Testable.GetPassword()); + } + + [Test] + public virtual void TestFindList() + { + Assert.AreEqual(Testable.FindList("attributeFloatMultivalue"), CollectionUtil.NewList(new float?[3] { 24F, 25F, null })); + } + + [Test] + public virtual void TestListAttributeNames() + { + Assert.IsTrue(Testable.ListAttributeNames().Contains("attributeboolean")); + } + + [Test] + public virtual void TestHasAttribute() + { + Assert.IsTrue(Testable.HasAttribute("attributeboolean")); + } + + [Test] + public virtual void TestFindString() + { + Assert.AreEqual(Testable.FindString("attributeString"), "retipipiter"); + } + + [Test] + public virtual void TestFindStringList() + { + Assert.AreEqual(Testable.FindStringList("attributeStringMultivalue"), CollectionUtil.NewList(new String[2] { "value1", "value2" })); + } + + [Test] + public virtual void TestFindCharacter() + { + Assert.AreEqual((char)Testable.FindCharacter("attributechar"), 'a'); + Assert.AreEqual((char)Testable.FindCharacter("attributecharacter"), 'd'); + } + + [Test] + public virtual void TestFindInteger() + { + Assert.AreEqual((int)Testable.FindInteger("attributeint"), 26); + Assert.AreEqual((int)Testable.FindInteger("attributeinteger"), 29); + } + + [Test] + public virtual void TestFindLong() + { + Assert.AreEqual((long)Testable.FindLong("attributelongp"), 11L); + Assert.AreEqual((long)Testable.FindLong("attributelong"), 14L); + } + + [Test] + public virtual void TestFindDouble() + { + Assert.AreEqual(Testable.FindDouble("attributedoublep"), double.MinValue); + Assert.AreEqual(Testable.FindDouble("attributedouble"), 17D); + } + + [Test] + public virtual void TestFindFloat() + { + Assert.AreEqual(Testable.FindFloat("attributefloatp"), 20F); + Assert.AreEqual(Testable.FindFloat("attributefloat"), 23F); + } + + [Test] + public virtual void TestFindBoolean() + { + var findBoolean = Testable.FindBoolean("attributebooleanp"); + Assert.IsTrue(findBoolean != null && (bool)findBoolean); + var boolean = Testable.FindBoolean("attributeboolean"); + Assert.IsFalse(boolean != null && (bool)boolean); + } + + [Test] + public virtual void TestFindByte() + { + Assert.AreEqual((byte)Testable.FindByte("attributebytep"), (byte)48); + Assert.AreEqual((byte)Testable.FindByte("attributebyte"), (byte)51); + } + + [Test] + public virtual void TestFindByteArray() + { + Assert.AreEqual(Testable.FindByteArray("attributeByteArray"), System.Text.Encoding.UTF8.GetBytes("array")); + } + + [Test] + public virtual void TestFindBigDecimal() + { + Assert.AreEqual(Testable.FindBigDecimal("attributeBigDecimal"), new BigDecimal(new BigInteger("1"), 0)); + } + + [Test] + public virtual void FestFindBigInteger() + { + Assert.AreEqual(Testable.FindBigInteger("attributeBigInteger"), new BigInteger("1")); + } + + [Test] + public virtual void TestFindGuardedByteArray() + { + var expected = new GuardedByteArray(); + System.Text.Encoding.UTF8.GetBytes("array").ToList().ForEach(expected.AppendByte); + Assert.AreEqual(expected, Testable.FindGuardedByteArray("attributeGuardedByteArray")); + } + + [Test] + public virtual void TestFindGuardedString() + { + SecureString secret = new SecureString(); + "secret".ToCharArray().ToList().ForEach(secret.AppendChar); + Assert.AreEqual(new GuardedString(secret), Testable.FindGuardedString("attributeGuardedString")); + } + + [Test] + public void TestFindDictionary() + { + Assert.AreEqual(SampleMap,Testable.FindDictionary("attributeMap")); + } + + + private ConnectorAttributesAccessor Testable + { + get + { + ICollection attributes = new HashSet(); + attributes.Add(new Uid("UID001")); + attributes.Add(new Name("NAME001")); + attributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + SecureString password = new SecureString(); + "Passw0rd".ToCharArray().ToList().ForEach(p => password.AppendChar(p)); + attributes.Add(ConnectorAttributeBuilder.BuildPassword(password)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeString", "retipipiter")); + attributes.Add(ConnectorAttributeBuilder.Build("attributeStringMultivalue", "value1", "value2")); + + attributes.Add(ConnectorAttributeBuilder.Build("attributelongp", 11L)); + attributes.Add(ConnectorAttributeBuilder.Build("attributelongpMultivalue", 12L, 13L)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeLong", new long?(14L))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeLongMultivalue", Convert.ToInt64(15), Convert.ToInt64(16), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributechar", 'a')); + attributes.Add(ConnectorAttributeBuilder.Build("attributecharMultivalue", 'b', 'c')); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeCharacter", new char?('d'))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeCharacterMultivalue", new char?('e'), new char?('f'), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributedoublep", double.MinValue)); + attributes.Add(ConnectorAttributeBuilder.Build("attributedoublepMultivalue", double.Epsilon, double.MaxValue)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeDouble", new double?(17D))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeDoubleMultivalue", new double?(18D), new double?(19D), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributefloatp", 20F)); + attributes.Add(ConnectorAttributeBuilder.Build("attributefloatpMultivalue", 21F, 22F)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeFloat", new float?(23F))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeFloatMultivalue", new float?(24F), new float?(25F), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeint", 26)); + attributes.Add(ConnectorAttributeBuilder.Build("attributeintMultivalue", 27, 28)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeInteger", new int?(29))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeIntegerMultivalue", new int?(30), new int?(31), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributebooleanp", true)); + attributes.Add(ConnectorAttributeBuilder.Build("attributebooleanpMultivalue", true, false)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeBoolean", Convert.ToBoolean(false))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeBooleanMultivalue", Convert.ToBoolean(true), Convert.ToBoolean(false), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributebytep", (byte)48)); + attributes.Add(ConnectorAttributeBuilder.Build("attributebytepMultivalue", (byte)49, (byte)50)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeByte", new byte?((byte)51))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeByteMultivalue", new byte?((byte)52), new byte?((byte)53), null)); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeByteArray", System.Text.Encoding.UTF8.GetBytes("array"))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeByteArrayMultivalue", System.Text.Encoding.UTF8.GetBytes("item1"), System.Text.Encoding.UTF8.GetBytes("item2"))); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeBigDecimal", new BigDecimal(new BigInteger("1"), 0))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeBigDecimalMultivalue", new BigDecimal(new BigInteger("0"), 0), new BigDecimal(new BigInteger("10"), 0))); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeBigInteger", new BigInteger("1"))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeBigIntegerMultivalue", new BigInteger("0"), new BigInteger("10"))); + + GuardedByteArray array = new GuardedByteArray(); + GuardedByteArray item1 = new GuardedByteArray(); + GuardedByteArray item2 = new GuardedByteArray(); + System.Text.Encoding.UTF8.GetBytes("array").ToList().ForEach(p => array.AppendByte(p)); + System.Text.Encoding.UTF8.GetBytes("item1").ToList().ForEach(p => item1.AppendByte(p)); + System.Text.Encoding.UTF8.GetBytes("item2").ToList().ForEach(p => item2.AppendByte(p)); + attributes.Add(ConnectorAttributeBuilder.Build("attributeGuardedByteArray", array)); + attributes.Add(ConnectorAttributeBuilder.Build("attributeGuardedByteArrayMultivalue", item1, item2)); + + SecureString secret = new SecureString(); + SecureString secret1 = new SecureString(); + SecureString secret2 = new SecureString(); + "secret".ToCharArray().ToList().ForEach(p => secret.AppendChar(p)); + "secret1".ToCharArray().ToList().ForEach(p => secret1.AppendChar(p)); + "secret2".ToCharArray().ToList().ForEach(p => secret2.AppendChar(p)); + attributes.Add(ConnectorAttributeBuilder.Build("attributeGuardedString", new GuardedString(secret))); + attributes.Add(ConnectorAttributeBuilder.Build("attributeGuardedStringMultivalue", new GuardedString(secret1), new GuardedString(secret2))); + + attributes.Add(ConnectorAttributeBuilder.Build("attributeMap", SampleMap)); + attributes.Add(ConnectorAttributeBuilder.Build("attributeMapMultivalue", SampleMap, SampleMap)); + return new ConnectorAttributesAccessor(attributes); + } + } + + private IDictionary SampleMap + { + get + { + IDictionary ret = CollectionUtil.NewDictionary("string", "String", "number", 43, "trueOrFalse", true, "nullValue", null, "collection", CollectionUtil.NewList(new string[2] { "item1", "item2" })); + ret["object"] = CollectionUtil.NewDictionary("key1", "value1", "key2", "value2"); + return ret; + } + } + } + #endregion + #region ConnectorAttributeUtilTests + [TestFixture] + public class ConnectorAttributeUtilTests + { + + [Test] + public void TestNamesEqual() + { + Assert.IsTrue(ConnectorAttributeUtil.NamesEqual("givenName", "givenname")); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestGetSingleValue() + { + object TEST_VALUE = 1L; + ConnectorAttribute attr = ConnectorAttributeBuilder.Build("long", TEST_VALUE); + object value = ConnectorAttributeUtil.GetSingleValue(attr); + Assert.AreEqual(TEST_VALUE, value); + + // test null + attr = ConnectorAttributeBuilder.Build("long"); + value = ConnectorAttributeUtil.GetSingleValue(attr); + Assert.IsNull(value); + // test empty + attr = ConnectorAttributeBuilder.Build("long", new List()); + value = ConnectorAttributeUtil.GetSingleValue(attr); + Assert.IsNull(value); + // test illegal argument exception + ConnectorAttributeUtil.GetSingleValue(ConnectorAttributeBuilder.Build("bob", 1, 2, 3)); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestDictionaryIntegerAttribute() + { + Dictionary map = new Dictionary(); + map[1] = "NOK"; + + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.AddValue(map); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestDictionaryShortAttribute() + { + Dictionary map1 = new Dictionary(); + map1["string"] = "NOK"; + + Dictionary map2 = new Dictionary(); + map2["map1"] = map1; + map2["list"] = new List { 1, 2, 3, (short)5 }; + + Dictionary map3 = new Dictionary(); + map3["map2"] = map2; + + Dictionary map4 = new Dictionary(); + map4["map3"] = map3; + + ConnectorAttributeBuilder.Build("map", map4); + } + + [Test] + public void TestDictionaryAttribute() + { + Dictionary map1 = new Dictionary(); + map1["string"] = "OK"; + + Dictionary map2 = new Dictionary(); + map2["map1"] = map1; + map2["list"] = new List { 1, 2, 3 }; + + Dictionary map3 = new Dictionary(); + map3["map2"] = map2; + + Dictionary map4 = new Dictionary(); + map4["map3"] = map3; + + ConnectorAttributeBuilder.Build("map", map4); + } + } + #endregion +} diff --git a/dotnet/framework/FrameworkTests/ConnectorFacadeExceptionTests.cs b/dotnet/framework/FrameworkTests/ConnectorFacadeExceptionTests.cs new file mode 100644 index 00000000..af2130be --- /dev/null +++ b/dotnet/framework/FrameworkTests/ConnectorFacadeExceptionTests.cs @@ -0,0 +1,122 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using NUnit.Framework; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Test.Common; +using Org.IdentityConnectors.Common; + +namespace FrameworkTests +{ + [TestFixture] + public class ConnectorFacadeExceptionTests + { + /// + /// The test specific exception that is intended to be distinguished from the exceptions the framework + /// might throw. + /// + private class EUTestException : Exception + { + } + + private class SpyConnector : Connector, TestOp + { + #region Member variables + private static StackTrace _stackTrace; + #endregion + + #region Properties + /// + /// Gets the stack trace of the last call to the method performed on + /// any instance of this class. + /// + public static StackTrace StackTrace + { + get + { + return _stackTrace; + } + } + #endregion + + #region Connector Members + public void Init(Configuration configuration) + { + } + #endregion + + #region IDisposable Members + public void Dispose() + { + } + #endregion + + #region TestOp Members + public void Test() + { + //do not insert file info, as the stack trace of the exception and the dumped stack trace + //will always differ in the line numbers + _stackTrace = new StackTrace(false); + throw new EUTestException(); + } + #endregion + } + + private class MockConfiguration : AbstractConfiguration + { + public override void Validate() + { + } + } + + /// + /// Tests whether the implementation let the exception - thrown by a connector - + /// propagate to the caller with the call stack representation from the method that throws the exception. + /// + /// The current implementation uses reflection that can hide the original + /// exception. See for more information. + [Test] + public void TestStackTraceOfExceptionThrownByConnectorFacade() + { + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + Configuration config = new MockConfiguration(); + ConnectorFacade facade = factory.NewInstance( + TestHelpers.CreateTestConfiguration(SafeType.Get(), config)); + + try + { + facade.Test(); + + Assert.Fail("Exception was not thrown"); + } + catch (EUTestException eutex) + { + ExceptionUtilTestHelpers.AssertStackTrace(eutex, SpyConnector.StackTrace); + } + } + } +} diff --git a/dotnet/framework/FrameworkTests/ConnectorFacadeTests.cs b/dotnet/framework/FrameworkTests/ConnectorFacadeTests.cs new file mode 100644 index 00000000..17b596b1 --- /dev/null +++ b/dotnet/framework/FrameworkTests/ConnectorFacadeTests.cs @@ -0,0 +1,663 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System; +using System.Collections.Generic; + +using NUnit.Framework; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Test.Common; + +namespace FrameworkTests +{ + + [TestFixture] + public class ConnectorFacadeTests + { + + [SetUp] + public void setup() + { + // always reset the call patterns.. + MockConnector.Reset(); + } + + private class TestOperationPattern + { + /// + /// Simple call back to make the 'facade' calls. + /// + public Action MakeCall; + + /// + /// Given the list of calls determine if they match expected values based + /// on the calls made in the method. + /// + public Action> CheckCalls; + } + + /// + /// Test the pattern of the common operations. + /// + private void TestCallPattern(TestOperationPattern pattern) + { + TestCallPattern(pattern, SafeType.Get()); + } + + private void TestCallPattern(TestOperationPattern pattern, + SafeType clazz) + { + Configuration config = new MockConfiguration(false); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + // **test only** + APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, config); + ConnectorFacade facade; + facade = factory.NewInstance(impl); + // make the call on the connector facade.. + pattern.MakeCall(facade); + // check the call structure.. + IList calls = MockConnector.GetCallPattern(); + // check the call pattern.. + Assert.AreEqual("Init", calls[0].MethodName); + calls.RemoveAt(0); + pattern.CheckCalls(calls); + Assert.AreEqual("Dispose", calls[0].MethodName); + calls.RemoveAt(0); + Assert.IsTrue(calls.Count == 0); + } + + /// + /// Tests that if an SPI operation is not implemented that the API will throw + /// an . + /// + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void UnsupportedOperationTest() + { + Configuration config = new MockConfiguration(false); + SafeType clazz = SafeType.Get(); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, + config); + ConnectorFacade facade; + facade = factory.NewInstance(impl); + facade.Authenticate(ObjectClass.ACCOUNT, "fadf", new GuardedString(), null); + } + + [Test] + public void RunScriptOnConnectorCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.RunScriptOnConnector( + new ScriptContextBuilder("lang", "script").Build(), + null); + }, + CheckCalls = calls => + { + Assert.AreEqual("RunScriptOnConnector", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void RunScriptOnResourceCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.RunScriptOnResource( + new ScriptContextBuilder("lang", "script").Build(), + null); + }, + CheckCalls = calls => + { + Assert.AreEqual("RunScriptOnResource", GetAndRemoveMethodName(calls)); + } + }); + } + + /// + /// Test the call pattern to get the schema. + /// + [Test] + public void SchemaCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Schema(); + }, + CheckCalls = calls => + { + Assert.AreEqual("Schema", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void AuthenticateCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Authenticate(ObjectClass.ACCOUNT, "dfadf", new GuardedString(), null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Authenticate", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void AuthenticateAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Authenticate(ObjectClass.ALL, "dfadf", new GuardedString(), null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void ResolveUsernameCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.ResolveUsername(ObjectClass.ACCOUNT, "dfadf", null); + }, + CheckCalls = calls => + { + Assert.AreEqual("ResolveUsername", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ResolveUsernameAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.ResolveUsername(ObjectClass.ALL, "dfadf", null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void CreateCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = CollectionUtil.NewReadOnlySet(); + facade.Create(ObjectClass.ACCOUNT, attrs, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Create", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CreateWithOutObjectClassPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + facade.Create(null, attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void CreateAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = CollectionUtil.NewReadOnlySet(); + facade.Create(ObjectClass.ALL, attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + [ExpectedException(typeof(InvalidAttributeValueException))] + public void CreateDuplicatConnectorAttributesPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("abc", 1)); + attrs.Add(ConnectorAttributeBuilder.Build("abc", 2)); + facade.Create(ObjectClass.ACCOUNT, attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void UpdateCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("accountid")); + facade.Update(ObjectClass.ACCOUNT, NewUid(0), attrs, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Update", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void UpdateAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("accountid")); + facade.Update(ObjectClass.ALL, NewUid(0), attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void DeleteCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Delete(ObjectClass.ACCOUNT, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Delete", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void DeleteAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Delete(ObjectClass.ALL, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void SearchCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + ResultsHandler rh = new ResultsHandler() + { + Handle = obj => { + return true; + } + + }; + // call the search method.. + facade.Search(ObjectClass.ACCOUNT, null, rh, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("CreateFilterTranslator", GetAndRemoveMethodName(calls)); + Assert.AreEqual("ExecuteQuery", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void SearchAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + ResultsHandler rh = new ResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the search method.. + facade.Search(ObjectClass.ALL, null, rh, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void GetCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + // call the search method.. + facade.GetObject(ObjectClass.ACCOUNT, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.AreEqual("CreateFilterTranslator", GetAndRemoveMethodName(calls)); + Assert.AreEqual("ExecuteQuery", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void GetAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + // call the search method.. + facade.GetObject(ObjectClass.ALL, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public virtual void GetLatestSyncTokenCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // call the getLatestSyncToken method.. + facade.GetLatestSyncToken(ObjectClass.ALL); + }, + CheckCalls = calls => + { + Assert.AreEqual("GetLatestSyncToken", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public virtual void GetLatestSyncTokenAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // call the getLatestSyncToken method.. + facade.GetLatestSyncToken(ObjectClass.ALL); + }, + CheckCalls = calls => + { + Assert.AreEqual("GetLatestSyncToken", GetAndRemoveMethodName(calls)); + } + }); + } + + + [Test] + public virtual void SyncCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + SyncResultsHandler rh = new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the sync method.. + facade.Sync(ObjectClass.ACCOUNT, new SyncToken(1), rh, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Sync", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public virtual void SyncAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + SyncResultsHandler rh = new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the sync method.. + facade.Sync(ObjectClass.ALL, new SyncToken(1), rh, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Sync", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(ConnectorException), ExpectedMessage="Sync '__ALL__' operation requires.*", MatchType = MessageMatch.Regex)] + public virtual void SyncAllCallFailPattern() { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + SyncResultsHandler rh = new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the sync method.. + var builder = new OperationOptionsBuilder(); + builder.Options["FAIL_DELETE"] = true; + facade.Sync(ObjectClass.ALL, new SyncToken(1), rh, builder.Build()); + }, + CheckCalls = calls => + { + Assert.AreEqual("Sync", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void TestOpCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Test(); + }, + CheckCalls = calls => + { + Assert.AreEqual("Test", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void UpdateMergeTests() + { + ConnectorAttribute expected, actual; + Configuration config = new MockConfiguration(false); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + SafeType clazz = SafeType.Get(); + // **test only** + APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, config); + impl.SetTimeout(SafeType.Get(), APIConstants.NO_TIMEOUT); + impl.SetTimeout(SafeType.Get(), APIConstants.NO_TIMEOUT); + impl.SetTimeout(SafeType.Get(), APIConstants.NO_TIMEOUT); + ConnectorFacade facade = factory.NewInstance(impl); + // sniff test to make sure we can get an object.. + ConnectorObject obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); + Assert.AreEqual(NewUid(1), obj.Uid); + // ok lets add an attribute that doesn't exist.. + String ADDED = "somthing to add to the object"; + String ATTR_NAME = "added"; + ICollection addAttrSet; + addAttrSet = CollectionUtil.NewSet((IEnumerable)obj.GetAttributes()); + addAttrSet.Add(ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED)); + Name name = obj.Name; + addAttrSet.Remove(name); + Uid uid = facade.AddAttributeValues(ObjectClass.ACCOUNT, obj.Uid, ConnectorAttributeUtil.FilterUid(addAttrSet), null); + // get back the object and see if there are the same.. + addAttrSet.Add(name); + ConnectorObject addO = new ConnectorObject(ObjectClass.ACCOUNT, addAttrSet); + obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); + Assert.AreEqual(addO, obj); + // attempt to add on to an existing attribute.. + addAttrSet.Remove(name); + uid = facade.AddAttributeValues(ObjectClass.ACCOUNT, obj.Uid, ConnectorAttributeUtil.FilterUid(addAttrSet), null); + // get the object back out and check on it.. + obj = facade.GetObject(ObjectClass.ACCOUNT, uid, null); + expected = ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED, ADDED); + actual = obj.GetAttributeByName(ATTR_NAME); + Assert.AreEqual(expected, actual); + // attempt to delete a value from an attribute.. + ICollection deleteAttrs = CollectionUtil.NewSet((IEnumerable)addO.GetAttributes()); + deleteAttrs.Remove(name); + uid = facade.RemoveAttributeValues(ObjectClass.ACCOUNT, addO.Uid, ConnectorAttributeUtil.FilterUid(deleteAttrs), null); + obj = facade.GetObject(ObjectClass.ACCOUNT, uid, null); + expected = ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED); + actual = obj.GetAttributeByName(ATTR_NAME); + Assert.AreEqual(expected, actual); + // attempt to delete an attribute that doesn't exist.. + ICollection nonExist = new HashSet(); + nonExist.Add(NewUid(1)); + nonExist.Add(ConnectorAttributeBuilder.Build("does not exist", "asdfe")); + uid = facade.RemoveAttributeValues(ObjectClass.ACCOUNT, addO.Uid, ConnectorAttributeUtil.FilterUid(nonExist), null); + obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); + Assert.IsTrue(obj.GetAttributeByName("does not exist") == null); + } + + static Uid NewUid(int id) + { + return new Uid(Convert.ToString(id)); + } + + static string GetAndRemoveMethodName(IList calls) + { + string result = calls[0].MethodName; + calls.RemoveAt(0); + return result; + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/ConnectorInfoManagerTests.cs b/dotnet/framework/FrameworkTests/ConnectorInfoManagerTests.cs new file mode 100755 index 00000000..c29baf9e --- /dev/null +++ b/dotnet/framework/FrameworkTests/ConnectorInfoManagerTests.cs @@ -0,0 +1,1150 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.Collections; +using NUnit.Framework; +using System.Collections.Generic; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using ICF = Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Server; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using System.Threading; +using System.Globalization; +using System.Net.Security; +using System.Reactive.Linq; +using System.Security.Cryptography.X509Certificates; +using Org.IdentityConnectors.Test.Common; + +namespace FrameworkTests +{ + [TestFixture] + public class ConnectorInfoManagerTests + { + protected static ConnectorInfo FindConnectorInfo + (ConnectorInfoManager manager, + string version, + string connectorName) + { + foreach (ConnectorInfo info in manager.ConnectorInfos) + { + ConnectorKey key = info.ConnectorKey; + if (version.Equals(key.BundleVersion) && + connectorName.Equals(key.ConnectorName)) + { + //intentionally ineffecient to test + //more code + return manager.FindConnectorInfo(key); + } + } + return null; + } + + [TearDown] + public void TearDown() + { + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestClassLoading() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info1 = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + ConnectorInfo info2 = + FindConnectorInfo(manager, + "2.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + Assert.IsNotNull(info2); + + ConnectorFacade facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(info1.CreateDefaultAPIConfiguration()); + + ConnectorFacade facade2 = + ConnectorFacadeFactory.GetInstance().NewInstance(info2.CreateDefaultAPIConfiguration()); + + ICollection attrs = new HashSet(); + Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue()); + Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue()); + } + + [Test] + public void TestAPIConfiguration() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("tstField"); + + Assert.IsNotNull(property); + ICollection> operations = + property.Operations; + Assert.AreEqual(1, operations.Count); + Assert.IsTrue(operations.Contains(SafeType.Get())); + + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); + Assert.AreEqual("Help for test field.", property.GetHelpMessage(null)); + Assert.AreEqual("Display for test field.", property.GetDisplayName(null)); + Assert.AreEqual("Group for test field.", property.GetGroup(null)); + Assert.AreEqual("Test Framework Value", + info.Messages.Format("TEST_FRAMEWORK_KEY", "empty")); + + CultureInfo eslocale = new CultureInfo("es"); + Thread.CurrentThread.CurrentUICulture = eslocale; + Assert.AreEqual("tstField.help_es", property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es", property.GetDisplayName(null)); + + CultureInfo esESlocale = new CultureInfo("es-ES"); + Thread.CurrentThread.CurrentUICulture = esESlocale; + Assert.AreEqual("tstField.help_es-ES", property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es-ES", property.GetDisplayName(null)); + + CultureInfo esARlocale = new CultureInfo("es-AR"); + Thread.CurrentThread.CurrentUICulture = esARlocale; + Assert.AreEqual("tstField.help_es", property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es", property.GetDisplayName(null)); + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + // call the various create/update/delete commands.. + facade.Schema(); + } + + [Test] + public void TestValidate() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("failValidation"); + property.Value = false; + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + facade.Validate(); + property.Value = true; + facade = facf.NewInstance(api); + try + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (ConnectorException e) + { + Assert.AreEqual("validation failed en", e.Message); + } + try + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("es"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (ConnectorException e) + { + Assert.AreEqual("validation failed es", e.Message); + } + } + + /// + /// Main purpose of this is to test searching with + /// many results and that we can properly handle + /// stopping in the middle of this. + /// + /// + /// There's a bunch of + /// code in the remote stuff that is there to handle this + /// in particular that we want to excercise. + /// + [Test] + public void TestSearchWithManyResults() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + api.ProducerBufferSize = 0; + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + + //1000 is several times the remote size between pauses + property.Value = 1000; + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + IList results = new List(); + + SearchResult searchResult = facade.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + Handle = + obj => + { + results.Add(obj); + return true; + } + }, null); + + Assert.AreEqual(1000, results.Count); + Assert.AreEqual(0, searchResult.RemainingPagedResults); + for (int i = 0; i < results.Count; i++) + { + ConnectorObject obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + results.Clear(); + + searchResult = facade.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + Handle = obj => + { + if (results.Count < 500) + { + results.Add(obj); + return true; + } + else + { + return false; + } + } + }, null); + + Assert.AreEqual(500, results.Count); + Assert.IsTrue(500 == searchResult.RemainingPagedResults || 401 == searchResult.RemainingPagedResults); + for (int i = 0; i < results.Count; i++) + { + ConnectorObject obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + } + /// + /// Main purpose of this is to test sync with + /// many results and that we can properly handle + /// stopping in the middle of this. + /// + /// + /// There's a bunch of + /// code in the remote stuff that is there to handle this + /// in particular that we want to excercise. + /// + [Test] + public void TestSyncWithManyResults() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + + //1000 is several times the remote size between pauses + property.Value = (1000); + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.AreEqual("mylatest", latest.Value); + IList results = new List(); + + facade.Sync(ObjectClass.ACCOUNT, null, new SyncResultsHandler() + { + Handle = obj => + { + results.Add(obj); + return true; + } + }, null); + + Assert.AreEqual(1000, results.Count); + for (int i = 0; i < results.Count; i++) + { + SyncDelta obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + results.Clear(); + + facade.Sync(ObjectClass.ACCOUNT, + null, new SyncResultsHandler() + { + Handle = obj => + { + if (results.Count < 500) + { + results.Add(obj); + return true; + } + else + { + return false; + } + } + } + , null); + + Assert.AreEqual(500, results.Count); + for (int i = 0; i < results.Count; i++) + { + SyncDelta obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + } + + [Test] + public void TestSyncTokenResults() + { + foreach (ConnectorFacade facade in CreateStateFulFacades()) + { + Uid uid = facade.Create(ObjectClass.ACCOUNT, CollectionUtil.NewReadOnlySet(), null); + + SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.AreEqual(uid.GetUidValue(), latest.Value); + + for (int i = 0; i < 10; i++) + { + SyncToken lastToken = facade.Sync(ObjectClass.ACCOUNT, null, new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }, null); + Assert.IsNotNull(lastToken); + Assert.AreEqual(lastToken.Value, latest.Value); + } + } + } + + [Test] + public void TestSubscriptionOperation() + { + foreach (ConnectorFacade facade in CreateStateFulFacades()) + { + if (facade is LocalConnectorFacadeImpl) + { + ToListResultsHandler handler = new ToListResultsHandler(); + CountdownEvent cde = new CountdownEvent(1); + var localFacade = facade; + var connectorObjectObservable = + Observable.Create(o => localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, null)); + + var subscription = connectorObjectObservable.Subscribe( + co => + { + Console.WriteLine("Connector Event received:{0}", co.Uid.GetUidValue()); + handler.ResultsHandler.Handle(co); + }, + ex => + { + cde.Signal(); + Assert.AreEqual(handler.Objects.Count, 10, "Uncompleted subscription"); + }); + + + cde.Wait(new TimeSpan(0, 0, 25)); + subscription.Dispose(); + Assert.AreEqual(10, handler.Objects.Count); + + handler = new ToListResultsHandler(); + cde = new CountdownEvent(1); + + connectorObjectObservable = + Observable.Create( + o => + localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, + OperationOptionsBuilder.Create().SetOption("doComplete", true).Build())); + + bool failed = false; + subscription = connectorObjectObservable.Subscribe( + co => + { + Console.WriteLine("Connector Event received:{0}", co.Uid.GetUidValue()); + handler.ResultsHandler.Handle(co); + }, + ex => + { + cde.Signal(); + failed = true; + }, () => + { + cde.Signal(); + }); + + + cde.Wait(new TimeSpan(0, 0, 25)); + subscription.Dispose(); + Assert.IsFalse(failed); + Assert.AreEqual(10, handler.Objects.Count); + + handler = new ToListResultsHandler(); + cde = new CountdownEvent(1); + + var syncDeltaObservable = + Observable.Create(o => localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, null)); + + IDisposable[] subscriptions = new IDisposable[1]; + subscriptions[0] = syncDeltaObservable.Subscribe( + delta => + { + Console.WriteLine("Sync Event received:{0}", delta.Token.Value); + if (((int?)delta.Token.Value) > 2) + { + subscriptions[0].Dispose(); + cde.Signal(); + } + handler.ResultsHandler.Handle(delta.Object); + }, + ex => + { + cde.Signal(); + Assert.Fail("Failed Subscription", ex); + }); + + cde.Wait(new TimeSpan(0, 0, 25)); + for (int i = 0; i < 5 && !(handler.Objects.Count > 2); i++) + { + Console.WriteLine("Wait for result handler thread to complete: {0}", i); + Thread.Sleep(200); // Wait to complete all other threads + } + Assert.IsTrue(handler.Objects.Count < 10 && handler.Objects.Count > 2); + } + } + } + + [Test] + public void TestConnectionPooling() + { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info1 = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + //reset connection count + { + //trigger TstConnection.init to be called + APIConfiguration config2 = + info1.CreateDefaultAPIConfiguration(); + config2.ConfigurationProperties.GetProperty("resetConnectionCount").Value = (true); + ConnectorFacade facade2 = + ConnectorFacadeFactory.GetInstance().NewInstance(config2); + facade2.Schema(); //force instantiation + } + + APIConfiguration config = + info1.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle = (0); + config.ConnectorPoolConfiguration.MaxIdle = (0); + + ConnectorFacade facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.SetOption("testPooling", "true"); + OperationOptions options = builder.Build(); + ICollection attrs = CollectionUtil.NewReadOnlySet(); + Assert.AreEqual("1", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("2", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("3", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("4", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + config = + info1.CreateDefaultAPIConfiguration(); + config.ConnectorPoolConfiguration.MinIdle = (1); + config.ConnectorPoolConfiguration.MaxIdle = (2); + facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + } + + [Test] + public void TestScripting() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + ScriptContextBuilder builder = new ScriptContextBuilder(); + builder.AddScriptArgument("arg1", "value1"); + builder.AddScriptArgument("arg2", "value2"); + builder.ScriptLanguage = ("BOO"); + + //test that they can run the script and access the + //connector object + { + String SCRIPT = + "connector.concat(arg1,arg2)"; + builder.ScriptText = (SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), + null); + + Assert.AreEqual("value1value2", result); + } + + //test that they can access a class in the class loader + { + String SCRIPT = + "import org.identityconnectors.testconnector\n" + + "TstConnector.GetVersion()"; + builder.ScriptText = (SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), + null); + Assert.AreEqual("1.0", result); + } + + //test that they cannot access a class in internal + { + Type clazz = typeof(ConfigurationPropertyImpl); + + String SCRIPT = + "import " + clazz.Namespace + "\n" + + clazz.Name + "()"; + builder.ScriptText = (SCRIPT); + try + { + facade.RunScriptOnConnector(builder.Build(), + null); + Assert.Fail("exception expected"); + } + catch (Exception e) + { + String msg = e.Message; + String expectedMessage = + "Namespace '" + clazz.Namespace + "' not found"; + Assert.IsTrue( + msg.Contains(expectedMessage), + "Unexpected message: " + msg); + } + } + + // test that they can access a class in common + { + Type clazz = typeof(ConnectorAttributeBuilder); + String SCRIPT = + "import " + clazz.Namespace + "\n" + + clazz.Name + ".Build(\"myattr\")"; + builder.ScriptText = (SCRIPT); + ConnectorAttribute attr = (ConnectorAttribute)facade.RunScriptOnConnector(builder.Build(), null); + Assert.AreEqual("myattr", attr.Name); + } + } + + [Test] + public void TestConnectorContext() + { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle = 0; + config.ConnectorPoolConfiguration.MaxIdle = 0; + + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + ICollection attrs = CollectionUtil.NewReadOnlySet(); + string uid = facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + + config = info1.CreateDefaultAPIConfiguration(); + config.ConnectorPoolConfiguration.MinIdle = 1; + config.ConnectorPoolConfiguration.MaxIdle = 2; + facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + uid = facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + } + + [Test] + public void TestAttributeTypeMap() + { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle = 0; + config.ConnectorPoolConfiguration.MaxIdle = 0; + + ConnectorFacade facade = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + HashSet createAttributes = new HashSet(); + IDictionary mapAttribute = new Dictionary(); + mapAttribute["email"] = "foo@example.com"; + mapAttribute["primary"] = true; + mapAttribute["usage"] = new List() { "home", "work" }; + createAttributes.Add(ConnectorAttributeBuilder.Build("emails", mapAttribute)); + + Uid uid = facade.Create(ObjectClass.ACCOUNT, createAttributes, null); + Assert.AreEqual(uid.GetUidValue(), "foo@example.com"); + + ConnectorObject co = facade.GetObject(ObjectClass.ACCOUNT, new Uid("0"), null); + object value = ConnectorAttributeUtil.GetSingleValue(co.GetAttributeByName("emails")); + Assert.IsTrue(value is IDictionary); + Assert.IsTrue(((IDictionary)value)["usage"] is IList); + } + + [Test] + public void TestPagedSearch() + { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulPoolableConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + config.ProducerBufferSize = 0; + + config.ConnectorPoolConfiguration.MinIdle = 1; + config.ConnectorPoolConfiguration.MaxIdle = 2; + + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.PageSize = 10; + builder.SetSortKeys(new ICF.SortKey(Name.NAME, true)); + + SearchResult searchResult = null; + ISet UIDs = new HashSet(); + + int iteration = 0; + do + { + + if (null != searchResult) + { + builder.PagedResultsCookie = searchResult.PagedResultsCookie; + } + + int size = 0; + searchResult = facade1.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + + Handle = obj => + { + if (size >= 10) + { + Assert.Fail("More then 10 objects was handled!"); + } + size++; + if (UIDs.Contains(obj.Uid)) + { + Assert.Fail("Duplicate Entry in results"); + } + return UIDs.Add(obj.Uid); + } + }, builder.Build()); + iteration++; + Assert.IsNotNull(searchResult); + Assert.AreEqual(searchResult.RemainingPagedResults, 100 - (iteration * 10)); + Assert.AreEqual(searchResult.TotalPagedResultsPolicy, SearchResult.CountPolicy.EXACT); + Assert.AreEqual(searchResult.TotalPagedResults, 100); + + } while (searchResult.PagedResultsCookie != null); + + // Search with paged results offset + + builder = new OperationOptionsBuilder(); + builder.PageSize = 10; + builder.PagedResultsOffset = 5; + builder.SetSortKeys(new ICF.SortKey(Name.NAME, true)); + + searchResult = null; + + UIDs.Clear(); + Filter filter = FilterBuilder.EqualTo(ConnectorAttributeBuilder.BuildEnabled(true)); + + iteration = 0; + do + { + + if (null != searchResult) + { + builder.PagedResultsCookie = searchResult.PagedResultsCookie; + } + + int size = 0; + searchResult = facade1.Search(ObjectClass.ACCOUNT, filter, new ResultsHandler() + { + Handle = obj => + { + if (size >= 10) + { + Assert.Fail("More then 10 objects was handled!"); + } + size++; + if (UIDs.Contains(obj.Uid)) + { + Assert.Fail("Duplicate Entry in results"); + } + return UIDs.Add(obj.Uid); + } + }, builder.Build()); + iteration++; + Assert.IsNotNull(searchResult); + Assert.AreEqual(searchResult.RemainingPagedResults, Math.Max(50 - (iteration * 15), 0)); + + } while (searchResult.PagedResultsCookie != null); + } + + [Test] + public void TestTimeout() + { + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + config.SetTimeout(SafeType.ForRawType(typeof(CreateApiOp)), 5000); + config.SetTimeout(SafeType.ForRawType(typeof(SearchApiOp)), 5000); + ConfigurationProperties props = config.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + // 1000 is several times the remote size between pauses + property.Value = 2; + OperationOptionsBuilder opBuilder = new OperationOptionsBuilder(); + opBuilder.SetOption("delay", 10000); + + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + ICollection attrs = CollectionUtil.NewReadOnlySet(); + try + { + facade1.Create(ObjectClass.ACCOUNT, attrs, opBuilder.Build()).GetUidValue(); + Assert.Fail("expected timeout"); + } + catch (OperationTimeoutException) + { + // expected + } + //catch (RemoteWrappedException e) + //{ + // Assert.IsTrue(e.Is(typeof(OperationTimeoutException))); + //} + + try + { + facade1.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + Handle = obj => + { + return true; + } + }, opBuilder.Build()); + Assert.Fail("expected timeout"); + } + catch (OperationTimeoutException) + { + // expected + } + //catch (RemoteWrappedException e) + //{ + // Assert.IsTrue(e.Is(typeof(OperationTimeoutException))); + //} + } + + [Test] + public void TestMVCCControl() + { + + foreach (ConnectorFacade facade in CreateStateFulFacades()) + { + + + Uid uid = facade.Create(ObjectClass.ACCOUNT, CollectionUtil.NewReadOnlySet(), null); + + + if (facade is LocalConnectorFacadeImpl) + { + try + { + facade.Delete(ObjectClass.ACCOUNT, uid, null); + } + catch (PreconditionRequiredException) + { + // Expected + } + catch (Exception) + { + Assert.Fail("Expecting PreconditionRequiredException"); + } + try + { + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), "0"), null); + } + catch (PreconditionFailedException) + { + // Expected + } + catch (Exception) + { + Assert.Fail("Expecting PreconditionFailedException"); + } + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), uid.GetUidValue()), null); + } + else + { + try + { + facade.Delete(ObjectClass.ACCOUNT, uid, null); + } + catch (RemoteWrappedException e) + { + if (!e.Is(typeof(PreconditionRequiredException))) + { + Assert.Fail("Expecting PreconditionRequiredException"); + } + } + catch (Exception) + { + Assert.Fail("Expecting RemoteWrappedException"); + } + try + { + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), "0"), null); + } + catch (RemoteWrappedException e) + { + if (!e.Is(typeof(PreconditionFailedException))) + { + Assert.Fail("Expecting PreconditionFailedException"); + } + } + catch (Exception) + { + Assert.Fail("Expecting RemoteWrappedException"); + } + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), uid.GetUidValue()), null); + } + } + } + + public IList CreateStateFulFacades() + { + IList test = new List(2); + + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulConnector"); + Assert.IsNotNull(info); + + APIConfiguration config = info.CreateDefaultAPIConfiguration(); + + test.Add(ConnectorFacadeFactory.GetInstance().NewInstance(config)); + + info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulPoolableConnector"); + Assert.IsNotNull(info); + + config = info.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle = 0; + config.ConnectorPoolConfiguration.MaxIdle = 0; + + test.Add(ConnectorFacadeFactory.GetInstance().NewInstance(config)); + + return test; + } + + protected virtual ConnectorInfoManager GetConnectorInfoManager() + { + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + ConnectorInfoManager manager = fact.GetLocalManager(); + return manager; + } + + protected virtual void ShutdownConnnectorInfoManager() + { + ConnectorFacadeFactory.GetInstance().Dispose(); + } + } + + [TestFixture] + public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests + { + + private ConnectorServer _server; + + protected override ConnectorInfoManager GetConnectorInfoManager() + { + TestUtil.InitializeLogging(); + + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 58758; +#else + const int PORT = 58759; +#endif + _server = ConnectorServer.NewInstance(); + _server.Port = PORT; + _server.IfAddress = (IOUtil.GetIPAddress("127.0.0.1")); + _server.KeyHash = str.GetBase64SHA1Hash(); + _server.Start(); + //while ( true ) { + // Thread.Sleep(1000); + //} + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + + RemoteFrameworkConnectionInfo connInfo = new + RemoteFrameworkConnectionInfo("127.0.0.1", PORT, str); + + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); + + return manager; + } + + protected override void ShutdownConnnectorInfoManager() + { + if (_server != null) + { + _server.Stop(); + _server = null; + } + } + + [Test, Explicit] + [Category("LongRunning")] + public virtual void TestFacadeEviction() + { + ConnectorServer server = ConnectorServer.NewInstance(); + try + { + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 58760; +#else + const int PORT = 58761; +#endif + + server.MaxFacadeLifeTime = 1; + server.Port = PORT; + server.IfAddress = (IOUtil.GetIPAddress("127.0.0.1")); + server.KeyHash = str.GetBase64SHA1Hash(); + server.Start(); + + RemoteFrameworkConnectionInfo connInfo = + new RemoteFrameworkConnectionInfo("127.0.0.1", PORT, str, false, null, 0); + ConnectorInfoManager remoteManager = + ConnectorInfoManagerFactory.GetInstance().GetRemoteManager(connInfo); + + ConnectorInfo remoteInfo = + FindConnectorInfo(remoteManager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); + + ConnectorFacade remoteFacade = ConnectorFacadeFactory.GetInstance(). + NewInstance(remoteInfo.CreateDefaultAPIConfiguration()); + + ManagedConnectorFacadeFactoryImpl managedFactory = + (ManagedConnectorFacadeFactoryImpl)ConnectorFacadeFactory.GetManagedInstance(); + + // Assert it's empty + Assert.IsNull(managedFactory.Find(remoteFacade.ConnectorFacadeKey)); + remoteFacade.Schema(); + // Assert it has one item + Assert.IsNotNull(managedFactory.Find(remoteFacade.ConnectorFacadeKey)); + Thread.Sleep(new TimeSpan(0, 2, 0)); + // Assert it's empty + Assert.IsNull(managedFactory.Find(remoteFacade.ConnectorFacadeKey)); + } + finally + { + server.Stop(); + } + } + } + + internal class MyCertificateValidationCallback + { + public bool Validate(Object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + Trace.TraceInformation("validating: " + certificate.Subject); + return true; + } + } + + [TestFixture] + public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests + { + + //To generate test certificate do the following: + // + //makecert -r -pe -n "CN=localhost" -ss TestCertificateStore -sr currentuser -sky exchange + // + //In MMC, go to the certificate, export + private ConnectorServer _server; + private const String CERT_PATH = "../../../server.pfx"; + protected override ConnectorInfoManager GetConnectorInfoManager() + { + TestUtil.InitializeLogging(); + + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 58762; +#else + const int PORT = 58761; +#endif + + /*X509Store store = new X509Store("TestCertificateStore", + StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); + X509Certificate certificate = store.Certificates[0]; + store.Close();*/ + + X509Certificate2 certificate = new + X509Certificate2(CERT_PATH, + "changeit"); + //Trace.TraceInformation("certificate: "+certificate); + _server = ConnectorServer.NewInstance(); + _server.Port = PORT; + _server.KeyHash = str.GetBase64SHA1Hash(); + _server.IfAddress = (IOUtil.GetIPAddress("localhost")); + _server.UseSSL = true; + _server.ServerCertificate = certificate; + _server.Start(); + //while ( true ) { + // Thread.Sleep(1000); + //} + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + MyCertificateValidationCallback + callback = new MyCertificateValidationCallback(); + RemoteFrameworkConnectionInfo connInfo = new + RemoteFrameworkConnectionInfo("localhost", + PORT, + str, + true, + callback.Validate, + 60000); + + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); + + return manager; + } + + protected override void ShutdownConnnectorInfoManager() + { + if (_server != null) + { + _server.Stop(); + _server = null; + } + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/ExceptionUtilTests.cs b/dotnet/framework/FrameworkTests/ExceptionUtilTests.cs new file mode 100644 index 00000000..5159c149 --- /dev/null +++ b/dotnet/framework/FrameworkTests/ExceptionUtilTests.cs @@ -0,0 +1,114 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using NUnit.Framework; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Impl; + +namespace FrameworkTests +{ + /// + /// Contains helper function to verify the output of . + /// + internal class ExceptionUtilTestHelpers + { + /// + /// Tests whether the is originated from the same function as in which the + /// was captured. + /// + /// The exception to test. + /// The captured stack trace to test against. + public static void AssertStackTrace(Exception exception, StackTrace stackTrace) + { + Trace.TraceInformation("Stack trace from the failing method: {0}", stackTrace.ToString()); + Trace.TraceInformation("Exception stack trace: {0}", exception.StackTrace); + + string fullST = stackTrace.ToString(); + int newLinePos = fullST.IndexOf(Environment.NewLine); + string failingMethodExpected = fullST.Substring(0, (newLinePos == -1) ? fullST.Length : newLinePos); + + newLinePos = exception.StackTrace.IndexOf(Environment.NewLine); + string failingMethodActual = exception.StackTrace.Substring(0, (newLinePos == -1) ? fullST.Length : newLinePos); + + //check if the first line of the stack trace fetched from the failing method is contained in the + //first line of the exception's stack trace, i.e. the method which threw the exception is in the + //stack trace of the exception + Assert.That(failingMethodActual.Contains(failingMethodExpected)); + } + } + + [TestFixture] + public class ExceptionUtilTests + { + [Test] + public void TestPreserveStackTrace() + { + StackTrace stackTrace = null; + try + { + try + { + Action bar = () => + { + try + { + //delegate to the failing method + Action foo = () => + { + stackTrace = new StackTrace(false); + throw new InvalidOperationException(); + }; + + foo(); + + Assert.Fail("Exception was not thrown - 1"); + } + catch (InvalidOperationException iopex) + { + //wrap the exception to make sure that there is an + //inner exception that can be re-thrown later on + throw new ArgumentException(string.Empty, iopex); + }; + }; + + bar(); + + Assert.Fail("Exception was not thrown - 2"); + } + catch (ArgumentException aex) + { + //preserve the stack trace of the nested exception and re-throw it + ExceptionUtil.PreserveStackTrace(aex.InnerException); + throw aex.InnerException; + } + + Assert.Fail("Exception was not thrown - 3"); + } + catch (InvalidOperationException iopex) + { + ExceptionUtilTestHelpers.AssertStackTrace(iopex, stackTrace); + } + } + } +} diff --git a/dotnet/framework/FrameworkTests/FilterTranslatorTests.cs b/dotnet/framework/FrameworkTests/FilterTranslatorTests.cs new file mode 100644 index 00000000..173646c8 --- /dev/null +++ b/dotnet/framework/FrameworkTests/FilterTranslatorTests.cs @@ -0,0 +1,642 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +namespace FrameworkTests +{ + + internal class AllFiltersTranslator : AbstractFilterTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { + return "( & " + leftExpression + " " + rightExpression + " )"; + } + + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { + return "( | " + leftExpression + " " + rightExpression + " )"; + } + + protected override String CreateContainsExpression(ContainsFilter filter, bool not) + { + String rv = "( CONTAINS " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + String rv = "( ENDS-WITH " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + + protected override String CreateEqualsExpression(EqualsFilter filter, bool not) + { + String rv = "( = " + filter.GetAttribute().Name + " [" + filter.GetAttribute().Value[0] + "] )"; + return Not(rv, not); + } + + protected override String CreateGreaterThanExpression(GreaterThanFilter filter, bool not) + { + String rv = "( > " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + + protected override String CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) + { + String rv = "( >= " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + + protected override String CreateLessThanExpression(LessThanFilter filter, bool not) + { + String rv = "( < " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + + protected override String CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) + { + String rv = "( <= " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + + protected override String CreateStartsWithExpression(StartsWithFilter filter, bool not) + { + String rv = "( STARTS-WITH " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); + } + protected override String CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) + { + String rv = "( CONTAINS-ALL-VALUES " + filter.GetAttribute() + " )"; + return Not(rv, not); + } + private String Not(String orig, bool not) + { + if (not) + { + return "( ! " + orig + " )"; + } + else + { + return orig; + } + } + } + + /// + /// Everything but Or + /// + internal class NoOrTranslator : AllFiltersTranslator + { + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { + return null; + } + } + /// + /// Everything but EndsWith + /// + internal class NoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + return null; + } + } + /// + /// Everything but EndsWith,Or + /// + internal class NoEndsWithNoOrTranslator : AllFiltersTranslator + { + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + return null; + } + } + + /// + /// Everything but And + /// + internal class NoAndTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { + return null; + } + } + /// + /// Everything but And + /// + internal class NoAndNoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + return null; + } + + } + /// + /// Everything but And + /// + internal class NoAndNoOrNoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + return null; + } + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { + return null; + } + + } + [TestFixture] + public class FilterTranslatorTests + { + + /// + /// Test all operations when everything is fully implemented. + /// + /// + /// Test not normalization as well. + /// + [Test] + public void TestBasics() + { + ConnectorAttribute attribute = + ConnectorAttributeBuilder.Build("att-name", "att-value"); + ConnectorAttribute attribute2 = + ConnectorAttributeBuilder.Build("att-name2", "att-value2"); + AllFiltersTranslator translator = new + AllFiltersTranslator(); + + { + Filter filter = + FilterBuilder.Contains(attribute); + String expected = "( CONTAINS att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.EndsWith(attribute); + String expected = "( ENDS-WITH att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.EqualTo(attribute); + String expected = "( = att-name [att-value] )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.GreaterThan(attribute); + String expected = "( > att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(attribute); + String expected = "( >= att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.LessThan(attribute); + String expected = "( < att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.LessThanOrEqualTo(attribute); + String expected = "( <= att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.StartsWith(attribute); + String expected = "( STARTS-WITH att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.ContainsAllValues(attribute); + String expected = "( CONTAINS-ALL-VALUES " + attribute + " )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! " + expected + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + //and + { + Filter left = + FilterBuilder.Contains(attribute); + Filter right = + FilterBuilder.Contains(attribute2); + String expectedLeft = "( CONTAINS att-name att-value )"; + String expectedRight = "( CONTAINS att-name2 att-value2 )"; + Filter filter = + FilterBuilder.And(left, right); + String expected = + "( & " + expectedLeft + " " + expectedRight + " )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expectedLeft = "( ! " + expectedLeft + " )"; + expectedRight = "( ! " + expectedRight + " )"; + expected = + "( | " + expectedLeft + " " + expectedRight + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + } + + //or + { + Filter left = + FilterBuilder.Contains(attribute); + Filter right = + FilterBuilder.Contains(attribute2); + String expectedLeft = "( CONTAINS att-name att-value )"; + String expectedRight = "( CONTAINS att-name2 att-value2 )"; + Filter filter = + FilterBuilder.Or(left, right); + String expected = + "( | " + expectedLeft + " " + expectedRight + " )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expectedLeft = "( ! " + expectedLeft + " )"; + expectedRight = "( ! " + expectedRight + " )"; + expected = + "( & " + expectedLeft + " " + expectedRight + " )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + //double-negative + { + Filter filter = + FilterBuilder.Contains(attribute); + filter = FilterBuilder.Not(filter); + filter = FilterBuilder.Not(filter); + String expected = "( CONTAINS att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + } + + /// + /// (a OR b) AND ( c OR d) needs to become + /// (a AND c) OR ( a AND d) OR (b AND c) OR (b AND d) is + /// OR is not implemented. + /// + /// + /// Otherwise it should stay + /// as-is. + /// + [Test] + public void TestDistribution() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( & ( | ( CONTAINS a a ) ( CONTAINS b b ) ) ( | ( CONTAINS c c ) ( CONTAINS d d ) ) )"; + String actual = + TranslateSingle(new AllFiltersTranslator(), filter); + + Assert.AreEqual(expected, actual); + + IList results = + new NoOrTranslator().Translate(filter); + Assert.AreEqual(4, results.Count); + + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS c c ) )", + results[0]); + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS d d ) )", + results[1]); + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS c c ) )", + results[2]); + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS d d ) )", + results[3]); + } + + //test simplification + //-no leaf + [Test] + public void TestSimplifyNoLeaf() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoEndsWithTranslator(), filter); + Assert.AreEqual(expected, actual); + + } + //-no leaf + no or + [Test] + public void TestSimplifyNoLeafNoOr() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoEndsWithNoOrTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS a a )", + results[0]); + Assert.AreEqual("( CONTAINS b b )", + results[1]); + + } + + //-no and + [Test] + public void TestSimplifyNoAnd() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoAndTranslator(), filter); + Assert.AreEqual(expected, actual); + } + + //-no and+no leaf + [Test] + public void TestSimplifyNoAndNoLeaf() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoAndNoEndsWithTranslator(), filter); + Assert.AreEqual(expected, actual); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + expected = "( | ( CONTAINS c c ) ( CONTAINS d d ) )"; + actual = + TranslateSingle(new NoAndNoEndsWithTranslator(), filter); + Assert.AreEqual(expected, actual); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("d", "d")); + + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoAndNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(0, results.Count); + } + + //-no and, no or, no leaf + [Test] + public void TestSimplifyNoAndNoOrNoLeaf() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoAndNoOrNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS a a )", + results[0]); + Assert.AreEqual("( CONTAINS b b )", + results[1]); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + results = + new NoAndNoOrNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS c c )", + results[0]); + Assert.AreEqual("( CONTAINS d d )", + results[1]); + } + + private static String TranslateSingle(AbstractFilterTranslator translator, + Filter filter) + { + IList translated = + translator.Translate(filter); + Assert.AreEqual(1, translated.Count); + return translated[0]; + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/FrameworkTests.csproj b/dotnet/framework/FrameworkTests/FrameworkTests.csproj new file mode 100644 index 00000000..175b9506 --- /dev/null +++ b/dotnet/framework/FrameworkTests/FrameworkTests.csproj @@ -0,0 +1,191 @@ + + + + + {32804F5A-812C-4FA6-835C-BDAE5B24D355} + Debug + AnyCPU + Library + FrameworkTests + FrameworkTests + FrameworkTests + v4.5.2 + + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + false + + + bin\Release\ + true + pdbonly + True + False + TRACE + false + + + + + + + 4.0 + + + + 4.0 + + + False + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + False + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + False + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + + + False + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + + + + 4.0 + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + Always + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + {E6A207D2-E083-41BF-B522-D9D3EC09323E} + TestCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/GuardedByteArrayTests.cs b/dotnet/framework/FrameworkTests/GuardedByteArrayTests.cs new file mode 100644 index 00000000..381d44e2 --- /dev/null +++ b/dotnet/framework/FrameworkTests/GuardedByteArrayTests.cs @@ -0,0 +1,102 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Serializer; +using NUnit.Framework; + +namespace FrameworkTests +{ + [TestFixture] + public class GuardedByteArrayTests + { + [Test] + public void TestBasics() + { + GuardedByteArray ss = new GuardedByteArray(); + ss.AppendByte(0x00); + ss.AppendByte(0x01); + ss.AppendByte(0x02); + byte[] decrypted = DecryptToByteArray(ss); + Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02 }, decrypted); + String hash = ss.GetBase64SHA1Hash(); + Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); + ss.AppendByte(0x03); + Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); + } + [Test] + public void TestRange() + { + + for (byte i = 0; i < 0xFF; i++) + { + byte expected = i; + GuardedByteArray gba = new GuardedByteArray(); + gba = (GuardedByteArray)SerializerUtil.CloneObject(gba); + gba.AppendByte(i); + gba.Access(new GuardedByteArray.LambdaAccessor(clearChars => + { + int v = (byte)clearChars[0]; + Assert.AreEqual(expected, v); + })); + + } + } + + [Test] + public void TestEquals() + { + GuardedByteArray arr1 = new GuardedByteArray(); + GuardedByteArray arr2 = new GuardedByteArray(); + Assert.AreEqual(arr1, arr2); + arr2.AppendByte(0x02); + Assert.AreNotEqual(arr1, arr2); + arr1.AppendByte(0x02); + Assert.AreEqual(arr1, arr2); + } + + + /// + /// Highly insecure method! Do not do this in production + /// code. + /// + /// + /// This is only for test purposes + /// + private byte[] DecryptToByteArray(GuardedByteArray str) + { + byte[] result = null; + str.Access(new GuardedByteArray.LambdaAccessor( + array => + { + result = new byte[array.Length]; + for (int i = 0; i < array.Length; i++) + { + result[i] = array[i]; + } + })); + return result; + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/GuardedStringTests.cs b/dotnet/framework/FrameworkTests/GuardedStringTests.cs new file mode 100644 index 00000000..0d0f0136 --- /dev/null +++ b/dotnet/framework/FrameworkTests/GuardedStringTests.cs @@ -0,0 +1,105 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Text; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Serializer; +using NUnit.Framework; + +namespace FrameworkTests +{ + [TestFixture] + public class GuardedStringTests + { + [Test] + public void TestBasics() + { + GuardedString ss = new GuardedString(); + ss.AppendChar('f'); + ss.AppendChar('o'); + ss.AppendChar('o'); + ss.AppendChar('b'); + ss.AppendChar('a'); + ss.AppendChar('r'); + String decrypted = DecryptToString(ss); + Assert.AreEqual("foobar", decrypted); + String hash = ss.GetBase64SHA1Hash(); + Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); + ss.AppendChar('2'); + Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); + } + [Test] + public void TestUnicode() + { + + for (int i = 0; i < 0xFFFF; i++) + { + int expected = i; + char c = (char)i; + GuardedString gs = new GuardedString(); + gs = (GuardedString)SerializerUtil.CloneObject(gs); + gs.AppendChar(c); + gs.Access(new GuardedString.LambdaAccessor(clearChars => + { + int v = (int)clearChars[0]; + Assert.AreEqual(expected, v); + })); + + } + } + + [Test] + public void TestEquals() + { + GuardedString str1 = new GuardedString(); + GuardedString str2 = new GuardedString(); + Assert.AreEqual(str1, str2); + str2.AppendChar('2'); + Assert.AreNotEqual(str1, str2); + str1.AppendChar('2'); + Assert.AreEqual(str1, str2); + } + + /// + /// Highly insecure method! Do not do this in production + /// code. + /// + /// + /// This is only for test purposes + /// + private String DecryptToString(GuardedString str) + { + StringBuilder buf = new StringBuilder(); + str.Access(new GuardedString.LambdaAccessor( + array => + { + for (int i = 0; i < array.Length; i++) + { + buf.Append(array[i]); + } + })); + return buf.ToString(); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/LocaleTests.cs b/dotnet/framework/FrameworkTests/LocaleTests.cs new file mode 100644 index 00000000..6f139239 --- /dev/null +++ b/dotnet/framework/FrameworkTests/LocaleTests.cs @@ -0,0 +1,210 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using System.Globalization; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + [TestFixture] + public class LocaleTests + { + + + [Test] + public void TestJava2CSharp() + { + HashSet + cultures = new HashSet(CultureInfo.GetCultures(CultureTypes.AllCultures)); + + TestJavaLocale(cultures, new Locale("ar", "", ""), "Arabic"); + TestJavaLocale(cultures, new Locale("be", "", ""), "Belarusian"); + TestJavaLocale(cultures, new Locale("bg", "", ""), "Bulgarian"); + TestJavaLocale(cultures, new Locale("ca", "", ""), "Catalan"); + TestJavaLocale(cultures, new Locale("cs", "", ""), "Czech"); + TestJavaLocale(cultures, new Locale("da", "", ""), "Danish"); + TestJavaLocale(cultures, new Locale("de", "", ""), "German"); + TestJavaLocale(cultures, new Locale("el", "", ""), "Greek"); + TestJavaLocale(cultures, new Locale("en", "", ""), "English"); + TestJavaLocale(cultures, new Locale("es", "", ""), "Spanish"); + TestJavaLocale(cultures, new Locale("et", "", ""), "Estonian"); + TestJavaLocale(cultures, new Locale("fi", "", ""), "Finnish"); + TestJavaLocale(cultures, new Locale("fr", "", ""), "French"); + TestJavaLocale(cultures, new Locale("hr", "", ""), "Croatian"); + TestJavaLocale(cultures, new Locale("hu", "", ""), "Hungarian"); + TestJavaLocale(cultures, new Locale("is", "", ""), "Icelandic"); + TestJavaLocale(cultures, new Locale("it", "", ""), "Italian"); + TestJavaLocale(cultures, new Locale("iw", "", ""), "Hebrew"); + TestJavaLocale(cultures, new Locale("ja", "", ""), "Japanese"); + TestJavaLocale(cultures, new Locale("ko", "", ""), "Korean"); + TestJavaLocale(cultures, new Locale("lt", "", ""), "Lithuanian"); + TestJavaLocale(cultures, new Locale("lv", "", ""), "Latvian"); + TestJavaLocale(cultures, new Locale("mk", "", ""), "Macedonian"); + TestJavaLocale(cultures, new Locale("nl", "", ""), "Dutch"); + TestJavaLocale(cultures, new Locale("no", "", ""), "Norwegian"); + TestJavaLocale(cultures, new Locale("pl", "", ""), "Polish"); + TestJavaLocale(cultures, new Locale("pt", "", ""), "Portuguese"); + TestJavaLocale(cultures, new Locale("ro", "", ""), "Romanian"); + TestJavaLocale(cultures, new Locale("ru", "", ""), "Russian"); + TestJavaLocale(cultures, new Locale("sk", "", ""), "Slovak"); + TestJavaLocale(cultures, new Locale("sl", "", ""), "Slovenian"); + TestJavaLocale(cultures, new Locale("sq", "", ""), "Albanian"); + TestJavaLocale(cultures, new Locale("sr", "", ""), "Serbian"); + TestJavaLocale(cultures, new Locale("sv", "", ""), "Swedish"); + TestJavaLocale(cultures, new Locale("th", "", ""), "Thai"); + TestJavaLocale(cultures, new Locale("tr", "", ""), "Turkish"); + TestJavaLocale(cultures, new Locale("uk", "", ""), "Ukrainian"); + TestJavaLocale(cultures, new Locale("vi", "", ""), "Vietnamese"); + TestJavaLocale(cultures, new Locale("zh", "", ""), "Chinese"); + TestJavaLocale(cultures, new Locale("ar", "AE", ""), "Arabic (United Arab Emirates)"); + TestJavaLocale(cultures, new Locale("ar", "BH", ""), "Arabic (Bahrain)"); + TestJavaLocale(cultures, new Locale("ar", "DZ", ""), "Arabic (Algeria)"); + TestJavaLocale(cultures, new Locale("ar", "EG", ""), "Arabic (Egypt)"); + TestJavaLocale(cultures, new Locale("ar", "IQ", ""), "Arabic (Iraq)"); + TestJavaLocale(cultures, new Locale("ar", "JO", ""), "Arabic (Jordan)"); + TestJavaLocale(cultures, new Locale("ar", "KW", ""), "Arabic (Kuwait)"); + TestJavaLocale(cultures, new Locale("ar", "LB", ""), "Arabic (Lebanon)"); + TestJavaLocale(cultures, new Locale("ar", "LY", ""), "Arabic (Libya)"); + TestJavaLocale(cultures, new Locale("ar", "MA", ""), "Arabic (Morocco)"); + TestJavaLocale(cultures, new Locale("ar", "OM", ""), "Arabic (Oman)"); + TestJavaLocale(cultures, new Locale("ar", "QA", ""), "Arabic (Qatar)"); + TestJavaLocale(cultures, new Locale("ar", "SA", ""), "Arabic (Saudi Arabia)"); + TestJavaLocale(cultures, + new Locale("ar", "SD", ""), + "Arabic (Sudan)", + new Locale("ar")); + TestJavaLocale(cultures, new Locale("ar", "SY", ""), "Arabic (Syria)"); + TestJavaLocale(cultures, new Locale("ar", "TN", ""), "Arabic (Tunisia)"); + TestJavaLocale(cultures, new Locale("ar", "YE", ""), "Arabic (Yemen)"); + TestJavaLocale(cultures, new Locale("be", "BY", ""), "Belarusian (Belarus)"); + TestJavaLocale(cultures, new Locale("bg", "BG", ""), "Bulgarian (Bulgaria)"); + TestJavaLocale(cultures, new Locale("ca", "ES", ""), "Catalan (Spain)"); + TestJavaLocale(cultures, new Locale("cs", "CZ", ""), "Czech (Czech Republic)"); + TestJavaLocale(cultures, new Locale("da", "DK", ""), "Danish (Denmark)"); + TestJavaLocale(cultures, new Locale("de", "AT", ""), "German (Austria)"); + TestJavaLocale(cultures, new Locale("de", "CH", ""), "German (Switzerland)"); + TestJavaLocale(cultures, new Locale("de", "DE", ""), "German (Germany)"); + TestJavaLocale(cultures, new Locale("de", "LU", ""), "German (Luxembourg)"); + TestJavaLocale(cultures, new Locale("el", "GR", ""), "Greek (Greece)"); + TestJavaLocale(cultures, new Locale("en", "AU", ""), "English (Australia)"); + TestJavaLocale(cultures, new Locale("en", "CA", ""), "English (Canada)"); + TestJavaLocale(cultures, new Locale("en", "GB", ""), "English (United Kingdom)"); + TestJavaLocale(cultures, new Locale("en", "IE", ""), "English (Ireland)"); + TestJavaLocale(cultures, new Locale("en", "IN", ""), "English (India)"); + TestJavaLocale(cultures, new Locale("en", "NZ", ""), "English (New Zealand)"); + TestJavaLocale(cultures, new Locale("en", "US", ""), "English (United States)"); + TestJavaLocale(cultures, new Locale("en", "ZA", ""), "English (South Africa)"); + TestJavaLocale(cultures, new Locale("es", "AR", ""), "Spanish (Argentina)"); + TestJavaLocale(cultures, new Locale("es", "BO", ""), "Spanish (Bolivia)"); + TestJavaLocale(cultures, new Locale("es", "CL", ""), "Spanish (Chile)"); + TestJavaLocale(cultures, new Locale("es", "CO", ""), "Spanish (Colombia)"); + TestJavaLocale(cultures, new Locale("es", "CR", ""), "Spanish (Costa Rica)"); + TestJavaLocale(cultures, new Locale("es", "DO", ""), "Spanish (Dominican Republic)"); + TestJavaLocale(cultures, new Locale("es", "EC", ""), "Spanish (Ecuador)"); + TestJavaLocale(cultures, new Locale("es", "ES", ""), "Spanish (Spain)"); + TestJavaLocale(cultures, new Locale("es", "GT", ""), "Spanish (Guatemala)"); + TestJavaLocale(cultures, new Locale("es", "HN", ""), "Spanish (Honduras)"); + TestJavaLocale(cultures, new Locale("es", "MX", ""), "Spanish (Mexico)"); + TestJavaLocale(cultures, new Locale("es", "NI", ""), "Spanish (Nicaragua)"); + TestJavaLocale(cultures, new Locale("es", "PA", ""), "Spanish (Panama)"); + TestJavaLocale(cultures, new Locale("es", "PE", ""), "Spanish (Peru)"); + TestJavaLocale(cultures, new Locale("es", "PR", ""), "Spanish (Puerto Rico)"); + TestJavaLocale(cultures, new Locale("es", "PY", ""), "Spanish (Paraguay)"); + TestJavaLocale(cultures, new Locale("es", "SV", ""), "Spanish (El Salvador)"); + TestJavaLocale(cultures, new Locale("es", "UY", ""), "Spanish (Uruguay)"); + TestJavaLocale(cultures, new Locale("es", "VE", ""), "Spanish (Venezuela)"); + TestJavaLocale(cultures, new Locale("et", "EE", ""), "Estonian (Estonia)"); + TestJavaLocale(cultures, new Locale("fi", "FI", ""), "Finnish (Finland)"); + TestJavaLocale(cultures, new Locale("fr", "BE", ""), "French (Belgium)"); + TestJavaLocale(cultures, new Locale("fr", "CA", ""), "French (Canada)"); + TestJavaLocale(cultures, new Locale("fr", "CH", ""), "French (Switzerland)"); + TestJavaLocale(cultures, new Locale("fr", "FR", ""), "French (France)"); + TestJavaLocale(cultures, new Locale("fr", "LU", ""), "French (Luxembourg)"); + TestJavaLocale(cultures, new Locale("hi", "IN", ""), "Hindi (India)"); + TestJavaLocale(cultures, new Locale("hr", "HR", ""), "Croatian (Croatia)"); + TestJavaLocale(cultures, new Locale("hu", "HU", ""), "Hungarian (Hungary)"); + TestJavaLocale(cultures, new Locale("is", "IS", ""), "Icelandic (Iceland)"); + TestJavaLocale(cultures, new Locale("it", "CH", ""), "Italian (Switzerland)"); + TestJavaLocale(cultures, new Locale("it", "IT", ""), "Italian (Italy)"); + TestJavaLocale(cultures, new Locale("iw", "IL", ""), "Hebrew (Israel)"); + TestJavaLocale(cultures, new Locale("ja", "JP", ""), "Japanese (Japan)"); + TestJavaLocale(cultures, new Locale("ko", "KR", ""), "Korean (South Korea)"); + TestJavaLocale(cultures, new Locale("lt", "LT", ""), "Lithuanian (Lithuania)"); + TestJavaLocale(cultures, new Locale("lv", "LV", ""), "Latvian (Latvia)"); + TestJavaLocale(cultures, new Locale("mk", "MK", ""), "Macedonian (Macedonia)"); + TestJavaLocale(cultures, new Locale("nl", "BE", ""), "Dutch (Belgium)"); + TestJavaLocale(cultures, new Locale("nl", "NL", ""), "Dutch (Netherlands)"); + TestJavaLocale(cultures, new Locale("no", "NO", ""), "Norwegian (Norway)"); + TestJavaLocale(cultures, new Locale("no", "NO", "NY"), "Norwegian (Norway,Nynorsk)"); + TestJavaLocale(cultures, new Locale("pl", "PL", ""), "Polish (Poland)"); + TestJavaLocale(cultures, new Locale("pt", "BR", ""), "Portuguese (Brazil)"); + TestJavaLocale(cultures, new Locale("pt", "PT", ""), "Portuguese (Portugal)"); + TestJavaLocale(cultures, new Locale("ro", "RO", ""), "Romanian (Romania)"); + TestJavaLocale(cultures, new Locale("ru", "RU", ""), "Russian (Russia)"); + TestJavaLocale(cultures, new Locale("sk", "SK", ""), "Slovak (Slovakia)"); + TestJavaLocale(cultures, new Locale("sl", "SI", ""), "Slovenian (Slovenia)"); + TestJavaLocale(cultures, new Locale("sq", "AL", ""), "Albanian (Albania)"); + TestJavaLocale(cultures, new Locale("sr", "BA", ""), + "Serbian (Bosnia and Herzegovina)", + new Locale("sr")); + TestJavaLocale(cultures, new Locale("sr", "CS", ""), + "Serbian (Serbia and Montenegro)", + new Locale("sr")); + TestJavaLocale(cultures, new Locale("sv", "SE", ""), "Swedish (Sweden)"); + TestJavaLocale(cultures, new Locale("th", "TH", ""), "Thai (Thailand)"); + TestJavaLocale(cultures, new Locale("th", "TH", "TH"), + "Thai (Thailand,TH)", + new Locale("th", "TH")); + TestJavaLocale(cultures, new Locale("tr", "TR", ""), "Turkish (Turkey)"); + TestJavaLocale(cultures, new Locale("uk", "UA", ""), "Ukrainian (Ukraine)"); + TestJavaLocale(cultures, new Locale("vi", "VN", ""), "Vietnamese (Vietnam)"); + TestJavaLocale(cultures, new Locale("zh", "CN", ""), "Chinese (China)"); + TestJavaLocale(cultures, new Locale("zh", "HK", ""), "Chinese (Hong Kong)"); + TestJavaLocale(cultures, new Locale("zh", "TW", ""), "Chinese (Taiwan)"); + + //foreach (CultureInfo info in cultures) { + // Console.WriteLine("remaining: "+info+" "+info.DisplayName+" "+info.TwoLetterISOLanguageName); + //} + } + private void TestJavaLocale(HashSet cultures, + Locale original, + String display) + { + TestJavaLocale(cultures, original, display, null); + } + private void TestJavaLocale(HashSet cultures, + Locale original, + String display, + Locale expected) + { + if (expected == null) + { + expected = original; + } + CultureInfo cinfo = original.ToCultureInfo(); + Locale actual = Locale.FindLocale(cinfo); + Assert.AreEqual(expected, actual, display + " (" + original + ") " + " didn't map"); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/MockConnector.cs b/dotnet/framework/FrameworkTests/MockConnector.cs new file mode 100644 index 00000000..b802801a --- /dev/null +++ b/dotnet/framework/FrameworkTests/MockConnector.cs @@ -0,0 +1,379 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System; +using System.Collections.Generic; + +using NUnit.Framework; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace FrameworkTests +{ + + public class MockConnector : Connector, SchemaOp + { + + /// + /// Represents a call to a connector method. + /// + public class Call + { + + private readonly string methodName; + private readonly object[] args; + + public Call(string methodName, params object[] args) + { + this.methodName = methodName; + this.args = args; + } + + public string MethodName + { + get + { + return methodName; + } + } + + public object[] Arguments + { + get + { + return this.args; + } + } + } + + // need to keep track of when methods are called an their parameters.. + private static IList callPattern = new List(); + + private Configuration _config; + + public void Dispose() + { + AddCall("Dispose"); + } + + public Schema Schema() + { + AddCall("Schema"); + return null; + } + + public void Init(Configuration cfg) + { + _config = cfg; + AddCall("Init", cfg); + } + + public Configuration getConfiguration() + { + return _config; + } + + /// + /// Clear the call pattern. + /// + public static void Reset() + { + callPattern.Clear(); + } + + /// + /// Get the current call pattern. + /// + public static IList GetCallPattern() + { + return CollectionUtil.NewList(callPattern); + } + + /// + /// Adds the call to the internal call pattern. + /// + public static void AddCall(string methodName, params object[] args) + { + callPattern.Add(new Call(methodName, args)); + } + } + + public class MockAllOpsConnector : MockConnector, CreateOp, + DeleteOp, UpdateOp, SearchOp, UpdateAttributeValuesOp, AuthenticateOp, + ResolveUsernameOp, TestOp, ScriptOnConnectorOp, ScriptOnResourceOp, SyncOp + { + + public object RunScriptOnConnector(ScriptContext request, + OperationOptions options) + { + Assert.IsNotNull(request); + Assert.IsNotNull(options); + AddCall("RunScriptOnConnector", request, options); + return null; + } + + public object RunScriptOnResource(ScriptContext request, + OperationOptions options) + { + Assert.IsNotNull(request); + Assert.IsNotNull(options); + AddCall("RunScriptOnResource", request, options); + return null; + } + + public Uid Create(ObjectClass oclass, ICollection attrs, + OperationOptions options) + { + Assert.IsNotNull(attrs); + AddCall("Create", attrs); + return null; + } + + public void Delete(ObjectClass objClass, Uid uid, + OperationOptions options) + { + Assert.IsNotNull(uid); + Assert.IsNotNull(objClass); + AddCall("Delete", objClass, uid); + } + + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, + OperationOptions options) + { + Assert.IsNotNull(objclass); + Assert.IsNotNull(attrs); + AddCall("Update", objclass, attrs); + return null; + } + + public Uid AddAttributeValues(ObjectClass objclass, Uid uid, + ICollection valuesToAdd, OperationOptions options) + { + AddCall("AddAttributeValues", objclass, valuesToAdd); + return null; + } + + public Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, + ICollection valuesToRemove, OperationOptions options) + { + AddCall("RemoveAttributeValues", objclass, valuesToRemove); + return null; + } + + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, + OperationOptions options) + { + Assert.IsNotNull(oclass); + Assert.IsNotNull(options); + AddCall("CreateFilterTranslator", oclass, options); + // no translation - ok since this is just for tests + return new MockFilterTranslator(); + } + + public void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + Assert.IsNotNull(oclass); + Assert.IsNotNull(handler); + Assert.IsNotNull(options); + AddCall("ExecuteQuery", oclass, query, handler, options); + if (null != options.PageSize && options.PageSize > 0) + { + // This is a pages search request + ((SearchResultsHandler)handler).HandleResult(new SearchResult("TOKEN==", 100)); + } + } + + public Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, + OperationOptions options) + { + Assert.IsNotNull(username); + Assert.IsNotNull(password); + AddCall("Authenticate", username, password); + return null; + } + + public Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options) + { + Assert.IsNotNull(username); + AddCall("ResolveUsername", username); + return null; + } + + public void Test() + { + AddCall("Test"); + } + + public void Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) + { + Assert.IsNotNull(objectClass); + Assert.IsNotNull(token); + Assert.IsNotNull(handler); + Assert.IsNotNull(options); + AddCall("Sync", objectClass, token, handler, options); + if (ObjectClass.ALL.Equals(objectClass)) + { + if (null != CollectionUtil.GetValue(options.Options, "FAIL_DELETE", null)) + { + //Require ObjectClass when delta is 'delete' + var builder = new SyncDeltaBuilder(); + builder.DeltaType = SyncDeltaType.DELETE; + builder.Uid = new Uid("DELETED"); + builder.Token = new SyncToken(99); + handler.Handle(builder.Build()); + } + else + { + ((SyncTokenResultsHandler)handler).HandleResult(new SyncToken(100)); + } + } + } + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + Assert.IsNotNull(objectClass); + AddCall("GetLatestSyncToken", objectClass); + return new SyncToken(0); + } + } + + public class MockUpdateConnector : Connector, UpdateOp, SearchOp + { + + private Configuration _cfg; + + public void Dispose() + { + // nothing to do this is a mock connector.. + } + + public void Init(Configuration cfg) + { + _cfg = cfg; + } + + public Configuration GetConfiguration() + { + return _cfg; + } + + private static IList objects = new List(); + + static MockUpdateConnector() + { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + for (int i = 0; i < 100; i++) + { + bld.SetUid(Convert.ToString(i)); + bld.SetName(Convert.ToString(i)); + objects.Add(bld.Build()); + } + } + + /// + /// This will do a basic replace. + /// + /// + /// + /// + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) + { + string val = ConnectorAttributeUtil.GetAsStringValue(uid); + int idx = Convert.ToInt32(val); + //.Get out the object.. + ConnectorObject baseObject = objects[idx]; + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.Add(baseObject); + bld.AddAttributes(attrs); + ConnectorObject obj = bld.Build(); + objects[idx] = obj; + return obj.Uid; + } + + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + //no translation - ok since this is just for tests + return new MockFilterTranslator(); + } + + /// + /// Simply return everything don't bother optimizing. + /// + /// + /// + public void ExecuteQuery(ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options) + { + foreach (ConnectorObject obj in objects) + { + if (!handler.Handle(obj)) + { + break; + } + } + } + } + + class MockFilterTranslator : AbstractFilterTranslator + { + } + + public class MockConfiguration : AbstractConfiguration + { + + private readonly bool fail; + + public MockConfiguration() + { + } + + /// + /// Determines if this configuration will fail validation. + /// + public MockConfiguration(bool failvalidation) + { + this.fail = failvalidation; + } + + public bool Fail + { + get; + set; + } + + public override void Validate() + { + if (fail) + { + throw new ConfigurationException(); + } + } + + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/ObjectClassUtilTests.cs b/dotnet/framework/FrameworkTests/ObjectClassUtilTests.cs new file mode 100644 index 00000000..2c75c3c9 --- /dev/null +++ b/dotnet/framework/FrameworkTests/ObjectClassUtilTests.cs @@ -0,0 +1,45 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace FrameworkTests +{ + [TestFixture] + public class ObjectClassUtilTests + { + + [Test] + public void TestIsSpecial() + { + Assert.IsTrue(ObjectClassUtil.IsSpecial(ObjectClass.ACCOUNT)); + Assert.IsFalse(ObjectClassUtil.IsSpecial(new ObjectClass("o"))); + } + + [Test] + public void TestNamesEqual() + { + Assert.IsTrue(ObjectClassUtil.NamesEqual("ACCOUNT", "account")); + } + } +} diff --git a/dotnet/framework/FrameworkTests/ObjectNormalizerFacadeTests.cs b/dotnet/framework/FrameworkTests/ObjectNormalizerFacadeTests.cs new file mode 100644 index 00000000..21377a6d --- /dev/null +++ b/dotnet/framework/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -0,0 +1,250 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +namespace FrameworkTests +{ + [TestFixture] + public class ObjectNormalizerFacadeTests + { + public class MyAttributeNormalizer : AttributeNormalizer + { + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + { + if (attribute.Is("foo")) + { + String val = ConnectorAttributeUtil.GetStringValue(attribute); + return ConnectorAttributeBuilder.Build("foo", val.Trim()); + } + else + { + return attribute; + } + } + } + + private ConnectorAttribute CreateTestAttribute() + { + return ConnectorAttributeBuilder.Build("foo", " bar "); + } + + private ConnectorAttribute CreateNormalizedTestAttribute() + { + return ConnectorAttributeBuilder.Build("foo", "bar"); + } + + private ObjectNormalizerFacade CreateTestNormalizer() + { + ObjectNormalizerFacade facade = new + ObjectNormalizerFacade(ObjectClass.ACCOUNT, + new MyAttributeNormalizer()); + return facade; + } + + private void AssertNormalizedFilter(Filter expectedNormalizedFilter, + Filter filter) + { + ObjectNormalizerFacade facade = + CreateTestNormalizer(); + filter = facade.NormalizeFilter(filter); + String expectedXml = SerializerUtil.SerializeXmlObject(expectedNormalizedFilter, false); + String actualXml = SerializerUtil.SerializeXmlObject(filter, false); + Assert.AreEqual(expectedXml, actualXml); + } + + + + [Test] + public void TestEndsWith() + { + Filter expected = + FilterBuilder.EndsWith(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.EndsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestStartsWith() + { + Filter expected = + FilterBuilder.StartsWith(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.StartsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestContains() + { + Filter expected = + FilterBuilder.Contains(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.Contains(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestEqualTo() + { + Filter expected = + FilterBuilder.EqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.EqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestGreaterThanOrEqualTo() + { + Filter expected = + FilterBuilder.GreaterThanOrEqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestLessThanOrEqualTo() + { + Filter expected = + FilterBuilder.LessThanOrEqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.LessThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestLessThan() + { + Filter expected = + FilterBuilder.LessThan(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.LessThan(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestGreaterThan() + { + Filter expected = + FilterBuilder.GreaterThan(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.GreaterThan(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestAnd() + { + Filter expected = + FilterBuilder.And(FilterBuilder.Contains(CreateNormalizedTestAttribute()), + FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.And(FilterBuilder.Contains(CreateTestAttribute()), + FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestOr() + { + Filter expected = + FilterBuilder.Or(FilterBuilder.Contains(CreateNormalizedTestAttribute()), + FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.Or(FilterBuilder.Contains(CreateTestAttribute()), + FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestNot() + { + Filter expected = + FilterBuilder.Not(FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.Not(FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestContainsAllValues() + { + Filter expected = + FilterBuilder.ContainsAllValues(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.ContainsAllValues(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } + [Test] + public void TestConnectorObject() + { + ConnectorObjectBuilder builder = + new ConnectorObjectBuilder(); + builder.SetName("myname"); + builder.SetUid("myuid"); + builder.AddAttribute(CreateTestAttribute()); + ConnectorObject v1 = builder.Build(); + ConnectorObject v2 = CreateTestNormalizer().NormalizeObject(v1); + builder = + new ConnectorObjectBuilder(); + builder.SetName("myname"); + builder.SetUid("myuid"); + builder.AddAttribute(CreateNormalizedTestAttribute()); + ConnectorObject expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + } + + [Test] + public void TestSyncDelta() + { + ConnectorObjectBuilder objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateTestAttribute()); + ConnectorObject obj = objbuilder.Build(); + + SyncDeltaBuilder builder = + new SyncDeltaBuilder(); + builder.DeltaType = (SyncDeltaType.DELETE); + builder.Token = (new SyncToken("mytoken")); + builder.Uid = (new Uid("myuid")); + builder.Object = (obj); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); + builder = + new SyncDeltaBuilder(); + builder.DeltaType = (SyncDeltaType.DELETE); + builder.Token = (new SyncToken("mytoken")); + builder.Uid = (new Uid("myuid")); + objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateNormalizedTestAttribute()); + builder.Object = objbuilder.Build(); + SyncDelta expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + + } + + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/ObjectPoolTests.cs b/dotnet/framework/FrameworkTests/ObjectPoolTests.cs new file mode 100644 index 00000000..957c1404 --- /dev/null +++ b/dotnet/framework/FrameworkTests/ObjectPoolTests.cs @@ -0,0 +1,291 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Threading; +using NUnit.Framework; + +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Impl.Api.Local; + +namespace FrameworkTests +{ + [TestFixture] + public class ObjectPoolTests + { + private class MyTestConnection + { + private bool _isGood = true; + + public void Test() + { + if (!_isGood) + { + throw new ConnectorException("Connection is bad"); + } + } + + public void Dispose() + { + _isGood = false; + } + + public bool IsGood + { + get + { + return _isGood; + } + } + } + + private class MyTestConnectionFactory : ObjectPoolHandler + { + + private bool _createBadConnection = false; + private int _totalCreatedConnections = 0; + + public MyTestConnection MakeObject() + { + _totalCreatedConnections++; + MyTestConnection rv = new MyTestConnection(); + if (_createBadConnection) + { + rv.Dispose(); + } + return rv; + } + public void TestObject(MyTestConnection obj) + { + obj.Test(); + } + public void DisposeObject(MyTestConnection obj) + { + obj.Dispose(); + } + + public int TotalCreatedConnections + { + get + { + return _totalCreatedConnections; + } + } + + public bool CreateBadConnection + { + set + { + _createBadConnection = value; + } + } + + public ObjectPoolConfiguration Validate(ObjectPoolConfiguration original) + { + return original; + } + + public void Shutdown() + { + } + } + + private class MyTestThread + { + private readonly ObjectPool _pool; + private readonly int _numIterations; + private Exception _exception; + private Thread _thisThread; + public MyTestThread(ObjectPool pool, + int numIterations) + { + _pool = pool; + _numIterations = numIterations; + _thisThread = new Thread(Run); + } + + public void Start() + { + _thisThread.Start(); + } + + public void Run() + { + try + { + for (int i = 0; i < _numIterations; i++) + { + MyTestConnection con = + _pool.BorrowObject(); + Thread.Sleep(300); + _pool.ReturnObject(con); + } + } + catch (Exception e) + { + _exception = e; + } + } + public void Shutdown() + { + _thisThread.Join(); + if (_exception != null) + { + throw _exception; + } + } + + } + + [Test] + public void TestWithManyThreads() + { + int NUM_ITERATIONS = 10; + int NUM_THREADS = 10; + int MAX_CONNECTIONS = NUM_THREADS - 3; //make sure we get some waiting + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects = (MAX_CONNECTIONS); + config.MaxIdle = (MAX_CONNECTIONS); + config.MinIdle = (MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis = (60 * 1000); + config.MaxWait = (60 * 1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact, config); + + MyTestThread[] threads = new MyTestThread[NUM_THREADS]; + for (int i = 0; i < threads.Length; i++) + { + threads[i] = new MyTestThread(pool, NUM_ITERATIONS); + threads[i].Start(); + } + + foreach (MyTestThread thread in threads) + { + thread.Shutdown(); + } + + //these should be the same since we never + //should have disposed anything + Assert.AreEqual(MAX_CONNECTIONS, fact.TotalCreatedConnections); + ObjectPool.Statistics stats = pool.GetStatistics(); + Assert.AreEqual(0, stats.NumActive); + Assert.AreEqual(MAX_CONNECTIONS, stats.NumIdle); + + pool.Shutdown(); + stats = pool.GetStatistics(); + Assert.AreEqual(0, stats.NumActive); + Assert.AreEqual(0, stats.NumIdle); + + } + + [Test] + public void TestBadConnection() + { + int MAX_CONNECTIONS = 3; + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects = (MAX_CONNECTIONS); + config.MaxIdle = (MAX_CONNECTIONS); + config.MinIdle = (MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis = (60 * 1000); + config.MaxWait = (60 * 1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact, config); + + //borrow first connection and return + MyTestConnection conn = pool.BorrowObject(); + Assert.AreEqual(1, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(1, fact.TotalCreatedConnections); + + //re-borrow same connection and return + conn = pool.BorrowObject(); + Assert.AreEqual(1, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(1, fact.TotalCreatedConnections); + + //dispose and make sure we get a new connection + conn.Dispose(); + conn = pool.BorrowObject(); + Assert.AreEqual(2, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(2, fact.TotalCreatedConnections); + } + + [Test] + public void TestIdleCleanup() + { + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects = (3); + config.MaxIdle = (2); + config.MinIdle = (1); + config.MinEvictableIdleTimeMillis = (3000); + config.MaxWait = (60 * 1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact, config); + + MyTestConnection conn1 = (MyTestConnection)pool.BorrowObject(); + MyTestConnection conn2 = (MyTestConnection)pool.BorrowObject(); + MyTestConnection conn3 = (MyTestConnection)pool.BorrowObject(); + + Assert.AreEqual(3, fact.TotalCreatedConnections); + pool.ReturnObject(conn1); + Assert.AreEqual(1, pool.GetStatistics().NumIdle); + pool.ReturnObject(conn2); + Assert.AreEqual(2, pool.GetStatistics().NumIdle); + pool.ReturnObject(conn3); + Assert.AreEqual(2, pool.GetStatistics().NumIdle); + Assert.AreEqual(false, conn1.IsGood); + Assert.AreEqual(true, conn2.IsGood); + Assert.AreEqual(true, conn3.IsGood); + Thread.Sleep(((int)(config.MinEvictableIdleTimeMillis + 1000))); + MyTestConnection conn4 = (MyTestConnection)pool.BorrowObject(); + Assert.AreSame(conn3, conn4); + Assert.AreEqual(false, conn1.IsGood); + Assert.AreEqual(false, conn2.IsGood); + Assert.AreEqual(true, conn3.IsGood); + Assert.AreEqual(true, conn4.IsGood); + } + + [Test] + public void TestCreateBadConnection() + { + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + fact.CreateBadConnection = (true); + + ObjectPool pool = new ObjectPool(fact, new ObjectPoolConfiguration()); + try + { + pool.BorrowObject(); + Assert.Fail("expected exception"); + } + catch (ConnectorException e) + { + Assert.AreEqual("Connection is bad", e.Message); + } + } + } +} diff --git a/dotnet/framework/FrameworkTests/ObjectSerializationTests.cs b/dotnet/framework/FrameworkTests/ObjectSerializationTests.cs new file mode 100755 index 00000000..2e0a4a4f --- /dev/null +++ b/dotnet/framework/FrameworkTests/ObjectSerializationTests.cs @@ -0,0 +1,1313 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ +using System; +using System.IO; +using System.Text; +using NUnit.Framework; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; + +namespace FrameworkTests +{ + [TestFixture] + public class ObjectSerializationTests + { + [Test] + public void TestBoolean() + { + bool v1 = true; + bool v2 = (bool)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = false; + v2 = (bool)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + [Test] + public void TestCharacter() + { + char v1 = 'h'; + char v2 = (char)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + [Test] + public void TestInteger() + { + int v1 = 12345; + int v2 = (int)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Int32.MinValue; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Int32.MaxValue; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = -1; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestLong() + { + long v1 = 12345; + long v2 = (long)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Int64.MinValue; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Int64.MaxValue; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = -1; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestFloat() + { + float v1 = 1.1F; + float v2 = (float)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + + v1 = Single.Epsilon; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Single.NaN; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Single.NegativeInfinity; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Single.PositiveInfinity; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Single.MinValue; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Single.MaxValue; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestDouble() + { + double v1 = 1.1; + double v2 = (double)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + + v1 = Double.Epsilon; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Double.NaN; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Double.NegativeInfinity; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Double.PositiveInfinity; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Double.MaxValue; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = Double.MinValue; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestString() + { + string v1 = "abcd"; + string v2 = (string)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestURI() + { + Uri v1 = new Uri("mailto:java-net@java.sun.com"); + Uri v2 = (Uri)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestFile() + { + FileName v1 = new FileName("c:/foo.txt"); + FileName v2 = (FileName)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestBigInteger() + { + BigInteger v1 = new BigInteger("983423442347324324324"); + BigInteger v2 = (BigInteger)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestBigDecimal() + { + BigDecimal v1 = new BigDecimal(new BigInteger("9847324324324"), 45); + BigDecimal v2 = (BigDecimal)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestByteArray() + { + byte[] v1 = { 1, 2, 3 }; + byte[] v2 = (byte[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual(1, v2[0]); + Assert.AreEqual(2, v2[1]); + Assert.AreEqual(3, v2[2]); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestByte() + { + byte v1 = 51; + byte v2 = (byte)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestClasses() + { + Assert.AreEqual(typeof(bool), + CloneObject(typeof(bool))); + Assert.AreEqual(typeof(bool?), + CloneObject(typeof(bool?))); + Assert.AreEqual(typeof(bool[][]), + CloneObject(typeof(bool[][]))); + Assert.AreEqual(typeof(bool?[][]), + CloneObject(typeof(bool?[][]))); + Assert.AreEqual(typeof(char), + CloneObject(typeof(char))); + Assert.AreEqual(typeof(char?), + CloneObject(typeof(char?))); + //if this fails, we have added a new type and we need to add + //a serializer for it + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedConfigTypes()), + (ICollection)CloneObject(FrameworkUtil.GetAllSupportedConfigTypes()))); + //if this fails, we have added a new type and we need to add + //a serializer for it + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), + (ICollection)CloneObject(FrameworkUtil.GetAllSupportedAttributeTypes()))); + ICollection apiOperations = + new HashSet(); + foreach (SafeType op in FrameworkUtil.AllAPIOperations()) + { + apiOperations.Add(op.RawType); + } + //if this fails, need to add to the OperationMappings class + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(apiOperations), + (ICollection)CloneObject(apiOperations))); + + } + + [Test] + public void TestArrays() + { + int[] v1 = { 1, 2, 3 }; + int[] v2 = (int[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual(1, v2[0]); + Assert.AreEqual(2, v2[1]); + Assert.AreEqual(3, v2[2]); + } + + + [Test] + public void TestObjectArrays() + { + object[] v1 = { "1", "2", "3" }; + object[] v2 = (object[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual("1", v2[0]); + Assert.AreEqual("2", v2[1]); + Assert.AreEqual("3", v2[2]); + } + + [Test] + public void TestDictionary() + { + IDictionary map = new Dictionary(); + map["key1"] = "val1"; + map["key2"] = "val2"; + IDictionary map2 = (IDictionary)CloneObject(map); + Assert.AreEqual("val1", map["key1"]); + Assert.AreEqual("val2", map["key2"]); + + IDictionary map3 = new Dictionary(); + map3["key1"] = "val1"; + map3["key2"] = "val2"; + IDictionary map4 = (IDictionary)CloneObject(map3); + Assert.AreEqual("val1", map4["key1"]); + Assert.AreEqual("val2", map4["key2"]); + } + [Test] + public void TestCaseInsensitiveMap() + { + HashSet set = new HashSet(); + set.Add(ConnectorAttributeBuilder.Build("foo1")); + set.Add(ConnectorAttributeBuilder.Build("foo2")); + IDictionary map = ConnectorAttributeUtil.ToMap(set); + Assert.IsTrue(map.ContainsKey("Foo1")); + Assert.IsTrue(map.ContainsKey("Foo2")); + IDictionary map2 = (IDictionary)CloneObject(map); + Assert.IsTrue(map2.ContainsKey("Foo1")); + Assert.IsTrue(map2.ContainsKey("Foo2")); + } + + [Test] + public void TestList() + { + IList v1 = new List(); + v1.Add("val1"); + v1.Add("val2"); + IList v2 = (IList)CloneObject(v1); + Assert.AreEqual(2, v2.Count); + Assert.AreEqual("val1", v2[0]); + Assert.AreEqual("val2", v2[1]); + + IList v3 = new List(); + v3.Add("val1"); + v3.Add("val2"); + + IList v4 = (IList)CloneObject(v3); + Assert.AreEqual(2, v4.Count); + Assert.AreEqual("val1", v4[0]); + Assert.AreEqual("val2", v4[1]); + } + + [Test] + public void TestSet() + { + //underneath the covers, this creates an + //ICollection - we need to test that ICollection + //becomes a Set + ICollection v1 = + CollectionUtil.NewReadOnlySet("val1", "val2"); + HashSet v2 = (HashSet)CloneObject(v1); + Assert.AreEqual(2, v2.Count); + Assert.IsTrue(v2.Contains("val1")); + Assert.IsTrue(v2.Contains("val2")); + + HashSet v3 = new HashSet(); + v3.Add("val1"); + v3.Add("val2"); + + HashSet v4 = (HashSet)CloneObject(v3); + Assert.AreEqual(2, v4.Count); + Assert.IsTrue(v4.Contains("val1")); + Assert.IsTrue(v4.Contains("val2")); + } + + [Test] + public void TestCaseInsensitiveSet() + { + ICollection v1 = CollectionUtil.NewCaseInsensitiveSet(); + v1.Add("foo"); + v1.Add("foo2"); + ICollection v2 = (ICollection)CloneObject(v1); + Assert.IsTrue(v2.Contains("Foo")); + Assert.IsTrue(v2.Contains("Foo2")); + } + + [Test] + public void TestCultureInfo() + { + //use this one since it uses all 3 fields of locale + CultureInfo v1 = new CultureInfo("nn-NO"); + CultureInfo v2 = (CultureInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectPoolConfiguration() + { + ObjectPoolConfiguration v1 = new ObjectPoolConfiguration(); + v1.MaxObjects = 1; + v1.MaxIdle = 2; + v1.MaxWait = 3; + v1.MinEvictableIdleTimeMillis = 4; + v1.MinIdle = 5; + ObjectPoolConfiguration v2 = + (ObjectPoolConfiguration)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + + Assert.AreEqual(v1, v2); + + Assert.AreEqual(1, v2.MaxObjects); + Assert.AreEqual(2, v2.MaxIdle); + Assert.AreEqual(3, v2.MaxWait); + Assert.AreEqual(4, v2.MinEvictableIdleTimeMillis); + Assert.AreEqual(5, v2.MinIdle); + } + [Test] + public void TestConfigurationProperty() + { + ConfigurationPropertyImpl v1 = new ConfigurationPropertyImpl(); + v1.Order = (1); + v1.IsConfidential = (true); + v1.IsRequired = true; + v1.Name = ("foo"); + v1.HelpMessageKey = ("help key"); + v1.DisplayMessageKey = ("display key"); + v1.GroupMessageKey = ("group key"); + v1.Value = ("bar"); + v1.ValueType = (typeof(string)); + v1.Operations = FrameworkUtil.AllAPIOperations(); + + ConfigurationPropertyImpl v2 = (ConfigurationPropertyImpl) + CloneObject(v1); + Assert.AreEqual(1, v2.Order); + Assert.IsTrue(v2.IsConfidential); + Assert.IsTrue(v2.IsRequired); + Assert.AreEqual("foo", v2.Name); + Assert.AreEqual("help key", v2.HelpMessageKey); + Assert.AreEqual("display key", v2.DisplayMessageKey); + Assert.AreEqual("group key", v2.GroupMessageKey); + Assert.AreEqual("bar", v2.Value); + Assert.AreEqual(typeof(string), v2.ValueType); + Assert.IsTrue(CollectionUtil.Equals( + FrameworkUtil.AllAPIOperations(), v2.Operations)); + } + + [Test] + public void TestConfigurationProperties() + { + ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); + prop1.Order = (1); + prop1.IsConfidential = (true); + prop1.Name = ("foo"); + prop1.HelpMessageKey = ("help key"); + prop1.DisplayMessageKey = ("display key"); + prop1.GroupMessageKey = ("group key"); + prop1.Value = ("bar"); + prop1.ValueType = (typeof(string)); + prop1.Operations = null; + + ConfigurationPropertiesImpl v1 = new ConfigurationPropertiesImpl(); + v1.Properties = (CollectionUtil.NewReadOnlyList(prop1)); + v1.SetPropertyValue("foo", "bar"); + + ConfigurationPropertiesImpl v2 = (ConfigurationPropertiesImpl) + CloneObject(v1); + Assert.AreEqual("bar", v2.GetProperty("foo").Value); + } + + [Test] + public void TestAPIConfiguration() + { + ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); + prop1.Order = (1); + prop1.IsConfidential = (true); + prop1.Name = ("foo"); + prop1.HelpMessageKey = ("help key"); + prop1.DisplayMessageKey = ("display key"); + prop1.GroupMessageKey = ("group key"); + prop1.Value = ("bar"); + prop1.ValueType = (typeof(string)); + prop1.Operations = null; + + ConfigurationPropertiesImpl props1 = new ConfigurationPropertiesImpl(); + props1.Properties = (CollectionUtil.NewReadOnlyList(prop1)); + + APIConfigurationImpl v1 = new APIConfigurationImpl(); + v1.ConnectorPoolConfiguration = (new ObjectPoolConfiguration()); + v1.ConfigurationProperties = (props1); + v1.IsConnectorPoolingSupported = (true); + v1.ProducerBufferSize = (200); + v1.SupportedOperations = (FrameworkUtil.AllAPIOperations()); + IDictionary, int> map = + CollectionUtil.NewDictionary, int>(SafeType.Get(), 6); + v1.TimeoutMap = (map); + + APIConfigurationImpl v2 = (APIConfigurationImpl) + CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.IsNotNull(v2.ConnectorPoolConfiguration); + Assert.IsNotNull(v2.ConfigurationProperties); + Assert.AreEqual(v1.ConnectorPoolConfiguration, v2.ConnectorPoolConfiguration); + Assert.AreEqual(v1.ConfigurationProperties, v2.ConfigurationProperties); + Assert.IsTrue(v2.IsConnectorPoolingSupported); + Assert.AreEqual(200, v2.ProducerBufferSize); + Assert.IsTrue(CollectionUtil.SetsEqual( + FrameworkUtil.AllAPIOperations(), + v2.SupportedOperations)); + Assert.AreEqual(map, v2.TimeoutMap); + } + + [Test] + public void TestConnectorMessages() + { + ConnectorMessagesImpl v1 = new ConnectorMessagesImpl(); + IDictionary defaultMap = new Dictionary(); + defaultMap["key1"] = "val1"; + IDictionary> messages = + new Dictionary>(); + messages[new CultureInfo("en")] = defaultMap; + messages[new CultureInfo("")] = defaultMap; + v1.Catalogs = (messages); + + ConnectorMessagesImpl v2 = (ConnectorMessagesImpl) + CloneObject(v1); + Assert.IsTrue( + CollectionUtil.SetsEqual(messages[new CultureInfo("")], + v2.Catalogs[new CultureInfo("")])); + Assert.IsTrue( + CollectionUtil.SetsEqual(messages[new CultureInfo("en")], + v2.Catalogs[new CultureInfo("en")])); + } + + [Test] + public void TestRemoteConnectorInfo() + { + RemoteConnectorInfoImpl v1 = new RemoteConnectorInfoImpl(); + v1.Messages = (new ConnectorMessagesImpl()); + v1.ConnectorKey = (new ConnectorKey("my bundle", + "my version", + "my connector")); + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties = (new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties = (configProperties); + v1.DefaultAPIConfiguration = (apiImpl); + v1.ConnectorDisplayNameKey = ("mykey"); + v1.ConnectorCategoryKey = ("LDAP"); + + RemoteConnectorInfoImpl v2 = (RemoteConnectorInfoImpl) + CloneObject(v1); + + Assert.IsNotNull(v2.Messages); + Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); + Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); + Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); + Assert.AreEqual("mykey", v2.ConnectorDisplayNameKey); + Assert.AreEqual("LDAP", v2.ConnectorCategoryKey); + Assert.IsNotNull(v2.DefaultAPIConfiguration); + } + + [Test] + public void TestAttribute() + { + ConnectorAttribute v1 = ConnectorAttributeBuilder.Build("foo", "val1", "val2"); + ConnectorAttribute v2 = (ConnectorAttribute)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestAttributeInfo() + { + + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name = ("foo"); + builder.ValueType = (typeof(String)); + builder.Required = (true); + builder.Readable = (true); + builder.Creatable = (true); + builder.Updateable = (true); + builder.MultiValued = (true); + builder.ReturnedByDefault = false; + ConnectorAttributeInfo v1 = builder.Build(); + ConnectorAttributeInfo v2 = (ConnectorAttributeInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.AreEqual("foo", v2.Name); + Assert.AreEqual(typeof(String), v2.ValueType); + Assert.IsTrue(v2.IsMultiValued); + Assert.IsTrue(v2.IsReadable); + Assert.IsTrue(v2.IsRequired); + Assert.IsTrue(v2.IsUpdateable); + Assert.IsTrue(v2.IsCreatable); + Assert.IsFalse(v2.IsReturnedByDefault); + + builder.InfoFlags = AllFlags(); + v1 = builder.Build(); + v2 = (ConnectorAttributeInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + private ConnectorAttributeInfo.Flags AllFlags() + { + ConnectorAttributeInfo.Flags flags = + ConnectorAttributeInfo.Flags.NONE; + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) + { + ConnectorAttributeInfo.Flags f = + (ConnectorAttributeInfo.Flags)e; + flags |= f; + } + return flags; + } + + [Test] + public void TestConnectorObject() + { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("fooToo"); + + ConnectorObject v1 = bld.Build(); + ConnectorObject v2 = (ConnectorObject)CloneObject(v1); + + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestName() + { + Name v1 = new Name("Test"); + Name v2 = (Name)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectClass() + { + ObjectClass v1 = new ObjectClass("test"); + ObjectClass v2 = (ObjectClass)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectClassInfo() + { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name = ("foo"); + builder.ValueType = (typeof(String)); + builder.Required = (true); + builder.Readable = (true); + builder.Updateable = (true); + builder.MultiValued = (true); + ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); + obld.ObjectType = ObjectClass.ACCOUNT_NAME; + obld.IsContainer = true; + obld.AddAttributeInfo(builder.Build()); + ObjectClassInfo v1 = obld.Build(); + ObjectClassInfo v2 = (ObjectClassInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.IsTrue(v2.IsContainer); + } + + [Test] + public void TestUid() + { + Uid v1 = new Uid("test"); + Uid v2 = (Uid)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void testOperationOptionInfo() + { + OperationOptionInfo v1 = + new OperationOptionInfo("name", typeof(int?)); + OperationOptionInfo v2 = + (OperationOptionInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSchema() + { + OperationOptionInfo opInfo = + new OperationOptionInfo("name", typeof(int?)); + ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); + bld.ObjectType = ObjectClass.ACCOUNT_NAME; + ObjectClassInfo info = bld.Build(); + ICollection temp = CollectionUtil.NewSet(info); + IDictionary, ICollection> map = + new Dictionary, ICollection>(); + map[SafeType.Get()] = temp; + ICollection temp2 = CollectionUtil.NewSet(opInfo); + IDictionary, ICollection> map2 = + new Dictionary, ICollection>(); + map2[SafeType.Get()] = temp2; + Schema v1 = new Schema(CollectionUtil.NewSet(info), + CollectionUtil.NewSet(opInfo), + map, + map2); + Schema v2 = (Schema)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.AreEqual(info, v2.ObjectClassInfo.First()); + Assert.AreEqual(1, v2.SupportedObjectClassesByOperation.Count); + Assert.AreEqual(1, v2.SupportedOptionsByOperation.Count); + Assert.AreEqual(1, v2.OperationOptionInfo.Count); + } + + [Test] + public void TestContainsFilter() + { + ContainsFilter v1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter v2 = (ContainsFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestAndFilter() + { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); + AndFilter v1 = new AndFilter(left1, right1); + AndFilter v2 = (AndFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Left; + ContainsFilter right2 = (ContainsFilter)v2.Right; + Assert.AreEqual(left1.GetAttribute(), left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(), right2.GetAttribute()); + } + + [Test] + public void TestEndsWithFilter() + { + EndsWithFilter v1 = new EndsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + EndsWithFilter v2 = (EndsWithFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestEqualsFilter() + { + EqualsFilter v1 = new EqualsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + EqualsFilter v2 = (EqualsFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestGreaterThanFilter() + { + GreaterThanFilter v1 = new GreaterThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + GreaterThanFilter v2 = (GreaterThanFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestGreaterThanOrEqualFilter() + { + GreaterThanOrEqualFilter v1 = new GreaterThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + GreaterThanOrEqualFilter v2 = (GreaterThanOrEqualFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestLessThanFilter() + { + LessThanFilter v1 = new LessThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + LessThanFilter v2 = (LessThanFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestLessThanOrEqualFilter() + { + LessThanOrEqualFilter v1 = new LessThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + LessThanOrEqualFilter v2 = (LessThanOrEqualFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestNotFilter() + { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + NotFilter v1 = new NotFilter(left1); + NotFilter v2 = (NotFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Filter; + Assert.AreEqual(left1.GetAttribute(), left2.GetAttribute()); + } + + [Test] + public void TestOrFilter() + { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); + OrFilter v1 = new OrFilter(left1, right1); + OrFilter v2 = (OrFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Left; + ContainsFilter right2 = (ContainsFilter)v2.Right; + Assert.AreEqual(left1.GetAttribute(), left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(), right2.GetAttribute()); + } + + [Test] + public void TestStartsWithFilter() + { + StartsWithFilter v1 = new StartsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + StartsWithFilter v2 = (StartsWithFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestContainsAllValuesFilter() + { + ContainsAllValuesFilter v1 = new ContainsAllValuesFilter(ConnectorAttributeBuilder.Build("foo", "bar", "foo")); + ContainsAllValuesFilter v2 = (ContainsAllValuesFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + + [Test] + public void TestPresenceFilter() + { + PresenceFilter v1 = new PresenceFilter("foo"); + PresenceFilter v2 = (PresenceFilter)CloneObject(v1); + Assert.AreEqual(v1.Name, v2.Name); + } + + [Test] + public void TestExtendedMatchFilter() + { + ExtendedMatchFilter v1 = new ExtendedMatchFilter("bar", ConnectorAttributeBuilder.Build("foo", "a", "b")); + ExtendedMatchFilter v2 = (ExtendedMatchFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + Assert.AreEqual(v1.Operator, v2.Operator); + } + + + [Test] + public void TestExceptions() + { + { + AlreadyExistsException v1 = new AlreadyExistsException("ex"); + AlreadyExistsException v2 = (AlreadyExistsException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + ConfigurationException v1 = new ConfigurationException("ex"); + ConfigurationException v2 = (ConfigurationException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + ConnectionBrokenException v1 = new ConnectionBrokenException("ex"); + ConnectionBrokenException v2 = (ConnectionBrokenException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + ConnectionFailedException v1 = new ConnectionFailedException("ex"); + ConnectionFailedException v2 = (ConnectionFailedException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + ConnectorException v1 = new ConnectorException("ex"); + ConnectorException v2 = (ConnectorException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + { + ConnectorIOException v1 = new ConnectorIOException("ex"); + ConnectorIOException v2 = (ConnectorIOException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + { + ConnectorSecurityException v1 = new ConnectorSecurityException("ex"); + ConnectorSecurityException v2 = (ConnectorSecurityException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + InvalidCredentialException v1 = new InvalidCredentialException("ex"); + InvalidCredentialException v2 = (InvalidCredentialException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + InvalidPasswordException v1 = new InvalidPasswordException("ex"); + InvalidPasswordException v2 = (InvalidPasswordException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + PasswordExpiredException v1 = new PasswordExpiredException("ex"); + v1.Uid = (new Uid("myuid")); + PasswordExpiredException v2 = (PasswordExpiredException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + Assert.AreEqual("myuid", v2.Uid.GetUidValue()); + } + + { + OperationTimeoutException v1 = new OperationTimeoutException("ex"); + OperationTimeoutException v2 = (OperationTimeoutException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + PermissionDeniedException v1 = new PermissionDeniedException("ex"); + PermissionDeniedException v2 = (PermissionDeniedException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + UnknownUidException v1 = new UnknownUidException("ex"); + UnknownUidException v2 = (UnknownUidException)CloneObject(v1); + Assert.AreEqual("ex", v2.Message); + } + + { + ArgumentException v1 = new ArgumentException("my msg"); + ArgumentException v2 = (ArgumentException)CloneObject(v1); + Assert.AreEqual("my msg", v2.Message); + } + + { + ArgumentNullException v1 = new ArgumentNullException(null, "my msg 1"); + ArgumentException v2 = (ArgumentException)CloneObject(v1); + Assert.AreEqual("my msg 1", v2.Message); + } + + { + Exception v1 = new Exception("my msg2"); + Exception v2 = (Exception)CloneObject(v1); + Assert.AreEqual("my msg2", v2.Message); + } + + } + + [Test] + public void TestHelloRequest() + { + HelloRequest v1 = new HelloRequest(HelloRequest.CONNECTOR_INFO); + HelloRequest v2 = (HelloRequest)CloneObject(v1); + Assert.IsNotNull(v2); + Assert.AreEqual(HelloRequest.CONNECTOR_INFO, v2.GetInfoLevel()); + } + + [Test] + public void TestHelloResponse() + { + Exception ex = new Exception("foo"); + IDictionary serverInfo = new Dictionary(1); + serverInfo.Add(HelloResponse.SERVER_START_TIME, DateTimeUtil.GetCurrentUtcTimeMillis()); + ConnectorKey key = new ConnectorKey("my bundle", "my version", "my connector"); + RemoteConnectorInfoImpl info = new RemoteConnectorInfoImpl(); + info.Messages = (new ConnectorMessagesImpl()); + info.ConnectorKey = (key); + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties = (new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties = (configProperties); + info.DefaultAPIConfiguration = (apiImpl); + info.ConnectorDisplayNameKey = ("mykey"); + info.ConnectorCategoryKey = (""); + + + HelloResponse v1 = new HelloResponse(ex, serverInfo, CollectionUtil.NewReadOnlyList(key), CollectionUtil.NewReadOnlyList(info)); + HelloResponse v2 = (HelloResponse)CloneObject(v1); + Assert.IsNotNull(v2.Exception); + Assert.IsNotNull(v2.ServerInfo[HelloResponse.SERVER_START_TIME]); + Assert.IsNotNull(v2.ConnectorKeys.First()); + Assert.IsNotNull(v2.ConnectorInfos.First()); + } + + [Test] + public void TestOperationRequest() + { + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties = (new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties = (configProperties); + + IList args = new List(); + args.Add("my arg"); + OperationRequest v1 = new + OperationRequest(new ConnectorKey("my bundle", + "my version", + "my connector"), + SerializerUtil.SerializeBase64Object(apiImpl), + SafeType.Get(), + "mymethodName", + args); + OperationRequest v2 = (OperationRequest)CloneObject(v1); + Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); + Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); + Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); + Assert.IsNotNull(v2.ConnectorFacadeKey); + Assert.AreEqual(SafeType.Get(), v2.Operation); + Assert.AreEqual("mymethodName", v2.OperationMethodName); + Assert.IsTrue( + CollectionUtil.Equals( + args, v2.Arguments)); + } + + [Test] + public void TestOperationResponseEnd() + { + OperationResponseEnd v1 = new OperationResponseEnd(); + OperationResponseEnd v2 = (OperationResponseEnd)CloneObject(v1); + Assert.IsNotNull(v2); + } + [Test] + public void TestOperationResponsePart() + { + Exception ex = new Exception("foo", new ArgumentException("Cause")); + OperationResponsePart v1 = new OperationResponsePart(ex, "bar"); + OperationResponsePart v2 = (OperationResponsePart)CloneObject(v1); + Assert.IsNotNull(v2.Exception); + Assert.AreEqual("bar", v2.Result); + } + + [Test] + public void TestOperationResponsePause() + { + OperationResponsePause v1 = new OperationResponsePause(); + OperationResponsePause v2 = (OperationResponsePause)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestOperationRequestMoreData() + { + OperationRequestMoreData v1 = new OperationRequestMoreData(); + OperationRequestMoreData v2 = (OperationRequestMoreData)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestOperationRequestStopData() + { + OperationRequestStopData v1 = new OperationRequestStopData(); + OperationRequestStopData v2 = (OperationRequestStopData)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestEchoMessage() + { + EchoMessage v1 = new EchoMessage("test", "xml"); + EchoMessage v2 = (EchoMessage)CloneObject(v1); + Assert.AreEqual("test", v2.Object); + Assert.AreEqual("xml", v2.ObjectXml); + } + + [Test] + public void TestOperationOptions() + { + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.SetOption("foo", "bar"); + builder.SetOption("foo2", "bar2"); + OperationOptions v1 = builder.Build(); + OperationOptions v2 = (OperationOptions)CloneObject(v1); + Assert.AreEqual(2, v2.Options.Count); + Assert.AreEqual("bar", v2.Options["foo"]); + Assert.AreEqual("bar2", v2.Options["foo2"]); + } + + [Test] + public void TestSearchResult() + { + SearchResult v1 = new SearchResult("FE00", SearchResult.CountPolicy.ESTIMATE, 100, 10); + SearchResult v2 = (SearchResult)CloneObject(v1); + Assert.AreEqual(v1.PagedResultsCookie, v2.PagedResultsCookie); + Assert.AreEqual(v1.TotalPagedResultsPolicy, v2.TotalPagedResultsPolicy); + Assert.AreEqual(v1.TotalPagedResults, v2.TotalPagedResults); + Assert.AreEqual(v1.RemainingPagedResults, v2.RemainingPagedResults); + } + + + [Test] + public void TestScript() + { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptLanguage = "language"; + builder.ScriptText = "text"; + Script v1 = builder.Build(); + Script v2 = (Script)CloneObject(v1); + Assert.AreEqual("language", v2.ScriptLanguage); + Assert.AreEqual("text", v2.ScriptText); + } + + [Test] + public void TestScriptContext() + { + ScriptContextBuilder builder = new ScriptContextBuilder(); + builder.ScriptLanguage = ("language"); + builder.ScriptText = ("text"); + builder.AddScriptArgument("foo", "bar"); + builder.AddScriptArgument("foo2", "bar2"); + ScriptContext v1 = builder.Build(); + ScriptContext v2 = (ScriptContext)CloneObject(v1); + Assert.AreEqual(2, v2.ScriptArguments.Count); + Assert.AreEqual("bar", v2.ScriptArguments["foo"]); + Assert.AreEqual("bar2", v2.ScriptArguments["foo2"]); + Assert.AreEqual("language", v2.ScriptLanguage); + Assert.AreEqual("text", v2.ScriptText); + } + [Test] + public void TestSyncDeltaType() + { + SyncDeltaType v1 = SyncDeltaType.DELETE; + SyncDeltaType v2 = (SyncDeltaType)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSyncToken() + { + SyncToken v1 = new SyncToken("mytoken"); + SyncToken v2 = (SyncToken)CloneObject(v1); + Assert.AreEqual(v1.Value, v2.Value); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSyncDelta() + { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("name"); + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.PreviousUid = (new Uid("mypreviousuid")); + builder.Uid = (new Uid("myuid")); + builder.DeltaType = (SyncDeltaType.CREATE_OR_UPDATE); + builder.Token = (new SyncToken("mytoken")); + builder.Object = (bld.Build()); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = (SyncDelta)CloneObject(v1); + Assert.AreEqual(new Uid("mypreviousuid"), v2.PreviousUid); + Assert.AreEqual(new Uid("foo"), v2.Uid); + Assert.AreEqual(new SyncToken("mytoken"), v2.Token); + Assert.AreEqual(SyncDeltaType.CREATE_OR_UPDATE, v2.DeltaType); + Assert.AreEqual(v1, v2); + + builder = new SyncDeltaBuilder(); + builder.DeltaType = SyncDeltaType.DELETE; + builder.Token = new SyncToken("mytoken"); + builder.ObjectClass = ObjectClass.ACCOUNT; + builder.Uid = new Uid("foo"); + v1 = builder.Build(); + v2 = (SyncDelta)CloneObject(v1); + Assert.AreEqual(ObjectClass.ACCOUNT, v2.ObjectClass); + Assert.AreEqual(new Uid("foo"), v2.Uid); + Assert.AreEqual(new SyncToken("mytoken"), v2.Token); + Assert.AreEqual(SyncDeltaType.DELETE, v2.DeltaType); + Assert.AreEqual(v1, v2); + + + + } + + [Test] + public void TestNull() + { + Object v1 = null; + Object v2 = CloneObject(v1); + Assert.IsNull(v2); + } + + [Test] + public void TestGuardedByteArray() + { + GuardedByteArray v1 = new GuardedByteArray(); + v1.AppendByte(0x00); + v1.AppendByte(0x01); + v1.AppendByte(0x02); + GuardedByteArray v2 = (GuardedByteArray)CloneObject(v1); + Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02 }, DecryptToByteArray(v2)); + } + + [Test] + public void TestGuardedString() + { + GuardedString v1 = new GuardedString(); + v1.AppendChar('f'); + v1.AppendChar('o'); + v1.AppendChar('o'); + v1.AppendChar('b'); + v1.AppendChar('a'); + v1.AppendChar('r'); + GuardedString v2 = (GuardedString)CloneObject(v1); + Assert.AreEqual("foobar", DecryptToString(v2)); + } + + [Test] + public void TestQualifiedUid() + { + QualifiedUid v1 = new QualifiedUid(new ObjectClass("myclass"), + new Uid("myuid")); + QualifiedUid v2 = (QualifiedUid)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.AreEqual("myclass", v2.ObjectClass.GetObjectClassValue()); + Assert.AreEqual("myuid", v2.Uid.GetUidValue()); + } + + /// + /// Highly insecure method! Do not do this in production + /// code. + /// + /// + /// This is only for test purposes + /// + private String DecryptToString(GuardedString str) + { + StringBuilder buf = new StringBuilder(); + str.Access(new GuardedString.LambdaAccessor( + array => + { + for (int i = 0; i < array.Length; i++) + { + buf.Append(array[i]); + } + })); + return buf.ToString(); + } + + private byte[] DecryptToByteArray(GuardedByteArray bytes) + { + byte[] result = null; + bytes.Access(new GuardedByteArray.LambdaAccessor( + array => + { + result = new byte[array.Length]; + for (int i = 0; i < array.Length; i++) + { + result[i] = array[i]; + } + })); + return result; + } + + protected virtual Object CloneObject(Object o) + { + return SerializerUtil.CloneObject(o); + } + } + [TestFixture] + public class XmlSerializationTests : ObjectSerializationTests + { + protected override Object CloneObject(Object o) + { + String xml = SerializerUtil.SerializeXmlObject(o, true); + Console.WriteLine(xml); + o = SerializerUtil.DeserializeXmlObject(xml, true); + + //pass through a list to make sure dtd correctly defines all xml objects + List list = new List(); + list.Add(o); + xml = SerializerUtil.SerializeXmlObject(list, true); + Console.WriteLine(xml); + IList rv = (IList)SerializerUtil.DeserializeXmlObject(xml, true); + return rv[0]; + } + + [Test] + public void TestMultiObject() + { + ObjectSerializerFactory factory = ObjectSerializerFactory.GetInstance(); + StringWriter sw = new StringWriter(); + XmlObjectSerializer ser = factory.NewXmlSerializer(sw, true, true); + ser.WriteObject("foo"); + ser.WriteObject("bar"); + ser.Close(true); + String xml = sw.ToString(); + Console.WriteLine(xml); + IList results = new List(); + factory.DeserializeXmlStream(new StringReader(xml), + o => + { + results.Add(o); + return true; + }, + true); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("foo", results[0]); + Assert.AreEqual("bar", results[1]); + } + + [Test] + public void TestWriter() + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.OmitXmlDeclaration = true; + XmlWriter writer = XmlWriter.Create(Console.Out, settings); + + // Write the book element. + writer.WriteStartElement("book"); + writer.WriteEndElement(); + writer.Close(); + // Write the title element. + //writer.WriteStartElement("title"); + //writer.WriteString("Pride And Prejudice<<"); + //writer.WriteEndElement(); + + // Write the close tag for the root element. + //writer.WriteEndElement(); + + // Write the XML and close the writer. + //writer.Close(); + + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml b/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml new file mode 100644 index 00000000..f080732c --- /dev/null +++ b/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/converter/config.xml b/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/converter/config.xml new file mode 100755 index 00000000..889cce4f --- /dev/null +++ b/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/converter/config.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml b/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml new file mode 100644 index 00000000..b9c4303d --- /dev/null +++ b/dotnet/framework/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/PropertyBagTests.cs b/dotnet/framework/FrameworkTests/PropertyBagTests.cs new file mode 100644 index 00000000..05426ea5 --- /dev/null +++ b/dotnet/framework/FrameworkTests/PropertyBagTests.cs @@ -0,0 +1,134 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Org.IdentityConnectors.Test.Common; +using Org.IdentityConnectors.Common; + +namespace FrameworkTests +{ + [TestFixture] + public class PropertyBagTests + { + private PropertyBag bag = null; + + [SetUp] + public void SetUp() + { + bag = CreateBag(); + } + + [Test] + public void TestGetProperty() + { + Assert.AreEqual("value1", bag.GetProperty("key1")); + Assert.IsNull(bag.GetProperty("key2")); + + Assert.AreEqual((int?)1, bag.GetProperty("key3")); + Assert.AreEqual((long?)1, bag.GetProperty("key5")); + + // try not existing + try + { + bag.GetProperty("key4"); + Assert.Fail("Get Property must fail for unexisting property"); + } + catch (ArgumentException) + { + } + + // Try cast + try + { + bag.GetProperty("key3"); + Assert.Fail("Get Property with incompatible type must fail on InvalidCastException"); + } + catch (InvalidCastException) + { + } + } + + [Test] + public void TestGetPropertyWithDef() + { + Assert.AreEqual("value1", bag.GetProperty("key1", "def")); + Assert.IsNull(bag.GetProperty("key2", "def")); + Assert.AreEqual("def", bag.GetProperty("key4", "def")); + Assert.IsNull(bag.GetProperty("key4", null)); + } + + [Test] + public void TestGetStringProperty() + { + Assert.AreEqual("value1", bag.GetStringProperty("key1")); + Assert.IsNull(bag.GetStringProperty("key2")); + // Try cast + try + { + bag.GetStringProperty("key3"); + Assert.Fail("Get Property with incompatible type must fail on InvalidCastException"); + } + catch (InvalidCastException) + { + } + + // try not existing + try + { + bag.GetStringProperty("key4"); + Assert.Fail("Get String Property must fail for unexisting property"); + } + catch (ArgumentException) + { + } + } + + [Test] + public void TestToDictionary() + { + var properties = bag.ToDictionary(); + Assert.That(CollectionUtil.DictionariesEqual(properties, CreateDictionary()), + "ToDictionary must return the same properties as it was created with" ); + } + + private static PropertyBag CreateBag() + { + var properties = CreateDictionary(); + return new PropertyBag(properties); + } + + private static Dictionary CreateDictionary() + { + var properties = new Dictionary + { + {"key1", "value1"}, + {"key2", null}, + {"key3", (int?) 1}, + {"key5", (long?) 1} + }; + return properties; + } + } +} diff --git a/dotnet/framework/FrameworkTests/ProxyTests.cs b/dotnet/framework/FrameworkTests/ProxyTests.cs new file mode 100644 index 00000000..968986bb --- /dev/null +++ b/dotnet/framework/FrameworkTests/ProxyTests.cs @@ -0,0 +1,96 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using NUnit.Framework; +using System.Reflection; +using Org.IdentityConnectors.Common.Proxy; +using System.Collections.Generic; + +namespace FrameworkTests +{ + public interface MyTestInterface + { + string TestProperty { get; set; } + + String TestTwoArgs(string val1, string val2); + void TestVoid(IList list); + int TestPrimitive(int arg); + DateTime TestStruct(DateTime arg); + } + + [TestFixture] + public class ProxyTests + { + public class MyHandler : InvocationHandler + { + private string _testProperty; + + public Object Invoke(Object proxy, MethodInfo method, Object[] args) + { + if (method.Name.Equals("TestTwoArgs")) + { + return "" + method.Name + " " + args[0] + " " + args[1]; + } + else if (method.Name.Equals("TestVoid")) + { + IList arg = (IList)args[0]; + arg.Add("my void result"); + return null; + } + else if (method.Name.Equals("get_TestProperty")) + { + return _testProperty; + } + else if (method.Name.Equals("set_TestProperty")) + { + _testProperty = (string)args[0]; + return null; + } + else + { + return args[0]; + } + } + } + + [Test] + public void TestProxy() + { + InvocationHandler handler = new MyHandler(); + + MyTestInterface inter = + (MyTestInterface)Proxy.NewProxyInstance(typeof(MyTestInterface), + handler); + Assert.AreEqual("TestTwoArgs foo bar", + inter.TestTwoArgs("foo", "bar")); + IList temp = new List(); + inter.TestVoid(temp); + Assert.AreEqual("my void result", temp[0]); + Assert.AreEqual(10, inter.TestPrimitive(10)); + Assert.AreEqual(1000L, inter.TestStruct(new DateTime(1000)).Ticks); + inter.TestProperty = "my property"; + Assert.AreEqual("my property", inter.TestProperty); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/SafeTypeTest.cs b/dotnet/framework/FrameworkTests/SafeTypeTest.cs new file mode 100644 index 00000000..441b629a --- /dev/null +++ b/dotnet/framework/FrameworkTests/SafeTypeTest.cs @@ -0,0 +1,46 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using NUnit.Framework; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api.Operations; +namespace FrameworkTests +{ + [TestFixture] + public class SafeTypeTest + { + [Test] + public void TestSafeType() + { + //compile-time type safe + SafeType op = + SafeType.Get(); + Assert.AreEqual(typeof(ScriptOnResourceApiOp), op.RawType); + //runtime type safe. needed for marshalling code, etc + op = + SafeType.ForRawType(typeof(SchemaApiOp)); + Assert.AreEqual(typeof(SchemaApiOp), op.RawType); + } + } +} diff --git a/dotnet/framework/FrameworkTests/ScriptTests.cs b/dotnet/framework/FrameworkTests/ScriptTests.cs new file mode 100644 index 00000000..697ea8fe --- /dev/null +++ b/dotnet/framework/FrameworkTests/ScriptTests.cs @@ -0,0 +1,146 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Framework.Common.Objects; +using NUnit.Framework; + +namespace FrameworkTests +{ + /// + /// Description of ScriptTests. + /// + [TestFixture] + public class ScriptTests + { + [Test] + public void testBooScripting() + { + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("BOO"); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0], "x", false); + IDictionary vals = new Dictionary(); + vals["x"] = 1; + Assert.AreEqual(1, exe.Execute(vals)); + vals["x"] = 2; + Assert.AreEqual(2, exe.Execute(vals)); + } + [Test] + public void testShellScripting() + { + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("Shell"); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0], "echo bob", false); + IDictionary vals = new Dictionary(); + Assert.AreEqual(0, ((IDictionary)exe.Execute(vals))["exitCode"]); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void testUnsupported() + { + ScriptExecutorFactory.NewInstance("fadsflkj"); + } + + [Test] + public void testBasic() + { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptLanguage = "Groovy"; + builder.ScriptText = "print 'foo'"; + Script s1 = builder.Build(); + Assert.AreEqual("Groovy", s1.ScriptLanguage); + Assert.AreEqual("print 'foo'", s1.ScriptText); + builder = new ScriptBuilder(); + builder.ScriptLanguage = "Groovy"; + builder.ScriptText = "print 'foo'"; + Script s2 = builder.Build(); + Assert.AreEqual(s1, s2); + Assert.AreEqual(s1.GetHashCode(), s2.GetHashCode()); + } + + [Test] + public void testLanguageNotBlank() + { + try + { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptText = "print 'foo'"; + builder.Build(); + Assert.Fail(); + } + catch (ArgumentException) + { + // OK. + } + + try + { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptText = "print 'foo'"; + builder.ScriptLanguage = ""; + builder.Build(); + Assert.Fail(); + } + catch (ArgumentException) + { + // OK. + } + + try + { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptText = "print 'foo'"; + builder.ScriptLanguage = " "; + builder.Build(); + Assert.Fail(); + } + catch (ArgumentException) + { + // OK. + } + } + + [Test] + public void testTextNotNull() + { + ScriptBuilder builder = new ScriptBuilder(); + try + { + builder.ScriptLanguage = "Groovy"; + builder.Build(); + Assert.Fail(); + } + catch (ArgumentNullException) + { + // OK. + } + + // The text can be empty. + builder = new ScriptBuilder(); + builder.ScriptLanguage = "Groovy"; + builder.ScriptText = ""; + builder.Build(); + } + } +} diff --git a/dotnet/framework/FrameworkTests/StringUtilTests.cs b/dotnet/framework/FrameworkTests/StringUtilTests.cs new file mode 100755 index 00000000..94fc6007 --- /dev/null +++ b/dotnet/framework/FrameworkTests/StringUtilTests.cs @@ -0,0 +1,224 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System; +using System.Collections.Generic; +using System.Text; +using Org.IdentityConnectors.Common; +using NUnit.Framework; + +namespace FrameworkTests +{ + /// + /// Description of ScriptTests. + /// + [TestFixture] + public class StringUtilTests + { + [Test] + public virtual void TestIndexOfDigit() + { + int test = 0; + const string TEST0 = null; + const string TEST1 = "fsadlkjffj"; + const string TEST2 = "abac2dafj"; + const string TEST3 = "fa323jf4af"; + + test = StringUtil.IndexOfDigit(TEST0); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfDigit(TEST1); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfDigit(TEST2); + Assert.AreEqual(test, 4); + test = StringUtil.IndexOfDigit(TEST3); + Assert.AreEqual(test, 2); + } + + [Test] + public virtual void TestIndexOfNonDigit() + { + int test = 0; + const string TEST0 = null; + const string TEST1 = "2131398750976"; + const string TEST2 = "21312a9320484"; + const string TEST3 = "32323aa323435"; + + test = StringUtil.IndexOfNonDigit(TEST0); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfNonDigit(TEST1); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfNonDigit(TEST2); + Assert.AreEqual(test, 5); + test = StringUtil.IndexOfNonDigit(TEST3); + Assert.AreEqual(test, 5); + } + + [Test] + public virtual void TestSubDigitString() + { + } + + [Test] + public virtual void TestStripXmlAttribute() + { + string[][] DATA = new string[][] { new string[] { null, null, null }, new string[] { "attr='fads'", "attr", "" }, new string[] { "at1='fasd' at1=''", "at1", "" } }; + string tst = null; + for (int i = 0; i < DATA.Length; i++) + { + tst = StringUtil.StripXmlAttribute(DATA[i][0], DATA[i][1]); + Assert.AreEqual(tst, DATA[i][2]); + } + } + + /// + /// Make sure it removes '\n'. + /// + [Test] + public virtual void TestStripNewlines() + { + string[][] TESTS = new string[][] { new string[] { null, null }, new string[] { "afdslf\n", "afdslf" }, new string[] { "afds\nfadkfj", "afdsfadkfj" }, new string[] { "afds \nfadkfj", "afds fadkfj" }, new string[] { "afds\n fadkfj", "afds fadkfj" } }; + string tmp; + foreach (string[] data in TESTS) + { + tmp = StringUtil.StripNewlines(data[0]); + Assert.AreEqual(tmp, data[1]); + } + } + + [Test] + public virtual void TestStripXmlComments() + { + string[][] DATA = new string[][] { new string[] { null, null }, new string[] { "", "" }, new string[] { "test data", "test data" }, new string[] { "", "test data-->" }, new string[] { "test data ", "test data " }, new string[] { " test data", " test data" }, new string[] { " test data", " test data" } }; + + string tst = null; + for (int i = 0; i < DATA.Length; i++) + { + tst = StringUtil.StripXmlComments(DATA[i][0]); + Assert.AreEqual(tst, DATA[i][1]); + } + } + + /// + /// Tests the methods on the + /// arguments provided. + /// + internal static void ParseLineTest(char textQ, char fieldD, IList values) + { + string csv = CreateCSVLine(textQ, fieldD, values); + IList parsedValues = StringUtil.ParseLine(csv, fieldD, textQ); + Assert.AreEqual(parsedValues, ToStringList(values)); + } + + /// + /// Create a CSV line based on the values provided. + /// + internal static string CreateCSVLine(char textQ, char fieldD, IList values) + { + StringBuilder bld = new StringBuilder(); + bool first = true; + foreach (object o in values) + { + // apply field delimiter.. + if (first) + { + first = false; + } + else + { + bld.Append(fieldD); + } + // if its a string add the text qualifiers.. + // don't bother escape text qualifiers in the string yet.. + if (o is string) + { + bld.Append(textQ); + } + bld.Append(o); + if (o is string) + { + bld.Append(textQ); + } + } + return bld.ToString(); + } + + /// + /// Converts a of objects to a of s. + /// + internal static IList ToStringList(IList list) + { + IList ret = new List(); + foreach (object o in list) + { + ret.Add(o.ToString()); + } + return ret; + } + + internal static IList RandomList(Random r, int size, char[] invalid, char valid) + { + IList ret = new List(); + for (int i = 0; i < size; i++) + { + object add; + if (r.Next(100) % 2 == 0) + { + add = r.Next(); + } + else if (r.Next(100) % 2 == 0) + { + add = r.NextDouble(); + } + else + { + string str = StringUtil.RandomString(r, r.Next(30)); + foreach (char c in invalid) + { + // replace all w/ 'a'.. + str = str.Replace(c, valid); + } + add = str; + } + ret.Add(add); + } + return ret; + } + + [Test] + public virtual void TestRandomString() + { + // just execute it because it doesn't really matter.. + string s = StringUtil.RandomString(); + Assert.IsTrue(s.Length < 257); + } + + [Test] + public virtual void testEndsWith() + { + Assert.IsTrue(StringUtil.EndsWith("afdsf", 'f')); + Assert.IsFalse(StringUtil.EndsWith(null, 'f')); + Assert.IsFalse(StringUtil.EndsWith("fadsfkj", 'f')); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/TestHelperTests.cs b/dotnet/framework/FrameworkTests/TestHelperTests.cs new file mode 100644 index 00000000..4f00a155 --- /dev/null +++ b/dotnet/framework/FrameworkTests/TestHelperTests.cs @@ -0,0 +1,494 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; +using System.Collections.Generic; +using NUnit.Framework; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Test.Common; +using System.Text; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.TestConnector; + +namespace FrameworkTests +{ + /// + /// Description of TestHelperTests. + /// + [TestFixture] + public class TestHelperTests + { + [Test] + public void TestReadConfiguration() + { + using (var memoryStream = new MemoryStream()) + { + var properties = new Dictionary() + { + {"bob", "bobsValue"}, + {"bob2", "bob2sValue"} + }; + + CreateXmlConfiguration(memoryStream, properties); + + memoryStream.Seek(0, SeekOrigin.Begin); + // load the properties files + var dict = TestHelpers.ReadConfiguration(memoryStream); + foreach (var property in properties) + { + Assert.AreEqual(dict[property.Key], property.Value); + } + } + } + + /// + /// Creates an XML configuration in the specified stream based on the . + /// + /// The output stream. + /// The properties to be stored. + /// The caller is responsible for closing the stream. + private static void CreateXmlConfiguration(Stream stream, IDictionary properties) + { + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + CloseOutput = false + }; + using (var writer = XmlWriter.Create(stream, settings)) + { + writer.WriteStartDocument(); + writer.WriteStartElement("config"); + foreach (var property in properties) + { + writer.WriteStartElement("property"); + writer.WriteAttributeString("name", property.Key); + writer.WriteAttributeString("value", property.Value); + writer.WriteEndElement(); + } + writer.WriteEndDocument(); + writer.Flush(); + } + } + + [Test] + public void TestGetProperties() + { + const string testConfigName = "myconfig"; + Type connectorType = typeof(Org.IdentityConnectors.TestConnector.FakeConnector); + + //store environment variables, they must be restored at the end + var oldTestConfig = Environment.GetEnvironmentVariable(TestHelpers.TestConfigEVName); + var oldPrivateConfigRoot = Environment.GetEnvironmentVariable(TestHelpers.PrivateConfigRootEVName); + Environment.SetEnvironmentVariable(TestHelpers.TestConfigEVName, testConfigName); + + var privateConfigRoot = Path.GetTempPath(); + + try + { + //set the TestHelpers.PrivateConfigRootEVName environment variable used by this test + Environment.SetEnvironmentVariable(TestHelpers.PrivateConfigRootEVName, privateConfigRoot); + + //at the end the created dir structure must be deleted, hence we need to store this + privateConfigRoot = Path.Combine(privateConfigRoot, "config"); + + var privateConfigPath = Path.Combine(Path.Combine(privateConfigRoot, connectorType.FullName), + "config-private"); + //create the directory structure for the general and the specific "testConfigName" private config + Directory.CreateDirectory(Path.Combine(privateConfigPath, testConfigName)); + + //create general private config file + using (var configFile = File.Create(Path.Combine(privateConfigPath, "config.xml"))) + { + CreateXmlConfiguration(configFile, new Dictionary() { { "privatekey", "value" } }); + } + + //create specific private config file + using (var configFile = File.Create(Path.Combine(Path.Combine(privateConfigPath, testConfigName), "config.xml"))) + { + CreateXmlConfiguration(configFile, new Dictionary() { { "myconfig.privatekey", "value" } }); + } + + PropertyBag bag1 = TestHelpers.GetProperties(connectorType); + CheckProperties(bag1); + PropertyBag bag2 = TestHelpers.GetProperties(connectorType); + Assert.AreSame(bag1, bag2, "TestHepers must create the same PropertyBag for the same connector"); + } + finally + { + if (oldTestConfig != null) + { + Environment.SetEnvironmentVariable(TestHelpers.TestConfigEVName, oldTestConfig); + } + if (oldPrivateConfigRoot != null) + { + Environment.SetEnvironmentVariable(TestHelpers.PrivateConfigRootEVName, oldPrivateConfigRoot); + } + + try + { + if (Directory.Exists(privateConfigRoot)) + { + Directory.Delete(privateConfigRoot, true); + } + } + catch (Exception ex) + { + //although, something bad happened there is no need to fail the test since the next time it will overwrite + //the temporary files if any exists + Trace.TraceWarning(ex.ToString()); + } + } + } + + private static void CheckProperties(PropertyBag bag) + { + var properties = new Dictionary + { + {"Help", "Me"}, + {"publickey", "value"}, + {"myconfig.publickey", "value"}, + {"privatekey", "value"}, + {"myconfig.privatekey", "value"} + }; + + var bagElements = bag.ToDictionary(); + Assert.That( + CollectionUtil.DictionariesEqual(properties, bagElements) && CollectionUtil.DictionariesEqual(bagElements, properties), + "Expected test properties not equal"); + } + + [Test] + public void TestFillConfiguration() + { + TestConfiguration testConfig = new TestConfiguration(); + // There is no "Foo" property in the config bean. We want to ensure + // that TestHelpers.FillConfiguration() does not fail for unknown properties. + IDictionary configData = new Dictionary(); + configData["Host"] = "example.com"; + configData["Port"] = 1234; + configData["Foo"] = "bar"; + TestHelpers.FillConfiguration(testConfig, configData); + + Assert.AreEqual("example.com", testConfig.Host); + Assert.AreEqual(1234, testConfig.Port); + } + + [Test] + public void TestCreateTestConfiguration() + { + IDictionary expectedData = new Dictionary(); + expectedData["String"] = "retipipiter"; + expectedData["StringArray"] = new [] { "value1", "value2", "value3" }; + expectedData["Long"] = 11L; + expectedData["LongArray"] = new []{12L, 13L}; + expectedData["LongObject"] = 14L; + expectedData["LongObjectArray"] = new long?[]{15, null}; + expectedData["Char"] = 'a'; + expectedData["CharArray"] = new []{'b','c'}; + expectedData["Character"] = 'd'; + expectedData["CharacterArray"] = new char?[]{'e','f'}; + expectedData["Double"] = 0D; + expectedData["DoubleArray"] = new []{0D, 100D}; + expectedData["DoubleObject"] = 0d; + expectedData["DoubleObjectArray"] = new double?[] { 0D, 100D }; + expectedData["Float"] = 0F; + expectedData["FloatArray"] = new[] { 0F, 100F }; + expectedData["FloatObject"] = null; + expectedData["FloatObjectArray"] = new float?[] { 0F, 100F }; + expectedData["Int"] = 0; + expectedData["IntArray"] = new[] { 0, 100 }; + expectedData["Integer"] = 0; + expectedData["IntegerArray"] = new int?[] { 0, 100 }; + expectedData["Boolean"] = true; + expectedData["BooleanArray"] = new[]{true, false}; + expectedData["BooleanObject"] = false; + expectedData["BooleanObjectArray"] = new bool?[] { true, false }; + expectedData["URI"] = new Uri("http://localhost:8080"); expectedData["URIArray"] = ""; + expectedData["URIArray"] = new[] { new Uri("http://localhost:8080"), new Uri("http://localhost:8443") }; + expectedData["File"] = new FileName("c:\\Users\\Admin"); + expectedData["FileArray"] = new[] {new FileName("c:\\Users\\Admin\\Documents"), new FileName("c:\\Users\\Admin\\Settings")}; + var array = new GuardedByteArray(); + Encoding.UTF8.GetBytes("array").ToList().ForEach(array.AppendByte); + expectedData["GuardedByteArray"] = array; + + array = new GuardedByteArray(); + Encoding.UTF8.GetBytes("item1").ToList().ForEach(array.AppendByte); + var array2 = new GuardedByteArray(); + Encoding.UTF8.GetBytes("item2").ToList().ForEach(array2.AppendByte); + expectedData["GuardedByteArrayArray"] = new []{array, array2}; + + var secret = new GuardedString(); + "secret".ToCharArray().ToList().ForEach(secret.AppendChar); + expectedData["GuardedString"] = secret; + + secret = new GuardedString(); + "secret1".ToCharArray().ToList().ForEach(secret.AppendChar); + var secret2 = new GuardedString(); + "secret2".ToCharArray().ToList().ForEach(secret2.AppendChar); + + expectedData["GuardedStringArray"] = new[]{secret, secret2}; + expectedData["Script"] = new ScriptBuilder { ScriptLanguage = "PowerShell", ScriptText = "echo 'Hello OpenICF Developer'" }.Build(); + expectedData["ScriptArray"] = new[]{new ScriptBuilder { ScriptLanguage = "Groovy", ScriptText = "println 'Hello'" }.Build(),new ScriptBuilder { ScriptLanguage = "Groovy", ScriptText = "println 'OpenICF Developer'" }.Build()}; + + Environment.SetEnvironmentVariable(TestHelpers.TestConfigEVName, "converter"); + + FieldInfo info = typeof (TestHelpers).GetField("_propertyBags", BindingFlags.NonPublic | BindingFlags.Static); + (info.GetValue(null) as Dictionary).Clear(); + + PropertyBag propertyBag = + TestHelpers.GetProperties(typeof(Org.IdentityConnectors.TestConnector.FakeConnector)); + (info.GetValue(null) as Dictionary).Clear(); + + APIConfiguration testable = TestHelpers.CreateTestConfiguration(SafeType.Get(), propertyBag, null); + + foreach (KeyValuePair entry in expectedData) + { + Assert.AreEqual(entry.Value, testable.ConfigurationProperties.GetProperty(entry.Key).Value, "Configuration property: " + entry.Key + " has different value"); + } + } + + } +} + +namespace Org.IdentityConnectors.TestConnector +{ + + + public class TestConfiguration : Configuration + { + + public ConnectorMessages ConnectorMessages + { + get + { + return null; + } + set + { + Assert.Fail("Should not set ConnectorMessages"); + } + } + + public void Validate() + { + Assert.Fail("Should not call Validate()"); + } + + public String Host { get; set; } + + public int Port { get; set; } + + public String Unused { get; set; } + + + //string + + [ConfigurationProperty(Order = 1)] + public string String { get; set; } + + [ConfigurationProperty(Order = 2)] + public string[] StringArray { get; set; } + + + // long + + [ConfigurationProperty(Order = 3)] + public long Long { get; set; } + + [ConfigurationProperty(Order = 4)] + public virtual long[] LongArray { get; set; } + + + // Long + + [ConfigurationProperty(Order = 5)] + public long? LongObject { get; set; } + + [ConfigurationProperty(Order = 6)] + public long?[] LongObjectArray { get; set; } + + + // char + + [ConfigurationProperty(Order = 7)] + public char Char { get; set; } + + [ConfigurationProperty(Order = 8)] + public char[] CharArray { get; set; } + + + // Character + + [ConfigurationProperty(Order = 9)] + public char? Character { get; set; } + + [ConfigurationProperty(Order = 10)] + public char?[] CharacterArray { get; set; } + + + // double + + [ConfigurationProperty(Order = 11)] + public double Double { get; set; } + + [ConfigurationProperty(Order = 12)] + public double[] DoubleArray { get; set; } + + + // Double + + [ConfigurationProperty(Order = 13)] + public double? DoubleObject { get; set; } + + [ConfigurationProperty(Order = 14)] + public double?[] DoubleObjectArray { get; set; } + + + // float + + [ConfigurationProperty(Order = 15)] + public float Float { get; set; } + + [ConfigurationProperty(Order = 16)] + public float[] FloatArray { get; set; } + + + // Float + + [ConfigurationProperty(Order = 17)] + public float? FloatObject { get; set; } + + [ConfigurationProperty(Order = 18)] + public float?[] FloatObjectArray { get; set; } + + + // int + + [ConfigurationProperty(Order = 19)] + public int Int { get; set; } + + [ConfigurationProperty(Order = 20)] + public int[] IntArray { get; set; } + + + // Integer + + [ConfigurationProperty(Order = 21)] + public int? Integer { get; set; } + + [ConfigurationProperty(Order = 22)] + public int?[] IntegerArray { get; set; } + + + // boolean + + [ConfigurationProperty(Order = 23)] + public bool Boolean { get; set; } + + [ConfigurationProperty(Order = 24)] + public bool[] BooleanArray { get; set; } + + + // Boolean + + [ConfigurationProperty(Order = 25)] + public bool? BooleanObject { get; set; } + + [ConfigurationProperty(Order = 26)] + public bool?[] BooleanObjectArray { get; set; } + + + // URI + + [ConfigurationProperty(Order = 27)] + public Uri URI { get; set; } + + [ConfigurationProperty(Order = 28)] + public Uri[] URIArray { get; set; } + + + // File + + [ConfigurationProperty(Order = 29)] + public FileName File { get; set; } + + [ConfigurationProperty(Order = 30)] + public virtual FileName[] FileArray { get; set; } + + + // GuardedByteArray + + [ConfigurationProperty(Order = 31)] + public GuardedByteArray GuardedByteArray { get; set; } + + [ConfigurationProperty(Order = 32)] + public GuardedByteArray[] GuardedByteArrayArray { get; set; } + + + // GuardedString + + [ConfigurationProperty(Order = 33)] + public GuardedString GuardedString { get; set; } + + [ConfigurationProperty(Order = 34)] + public GuardedString[] GuardedStringArray { get; set; } + + + // Script + + [ConfigurationProperty(Order = 35)] + public Script Script { get; set; } + + [ConfigurationProperty(Order = 36)] + public Script[] ScriptArray { get; set; } + } + + [ConnectorClass("FakeConnector", + "FakeConnector.category", + typeof(TestConfiguration) + )] + public class FakeConnector : Connector + { + #region Connector Members + public void Init(Configuration configuration) + { + throw new NotImplementedException(); + } + #endregion + + #region IDisposable Members + public void Dispose() + { + throw new NotImplementedException(); + } + #endregion + } +} diff --git a/dotnet/framework/FrameworkTests/TestUtil.cs b/dotnet/framework/FrameworkTests/TestUtil.cs new file mode 100644 index 00000000..8b1fc9c4 --- /dev/null +++ b/dotnet/framework/FrameworkTests/TestUtil.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Diagnostics; + +namespace FrameworkTests +{ + /// + /// Description of TestUtil. + /// + public static class TestUtil + { + private static readonly object LOCK = new Object(); + private static bool _initialized; + + + public static void InitializeLogging() + { + lock (LOCK) + { + if (!_initialized) + { + ConsoleTraceListener listener = + new ConsoleTraceListener(); + listener.TraceOutputOptions = + TraceOptions.ThreadId | TraceOptions.Timestamp; + Trace.Listeners.Add(listener); + _initialized = true; + } + } + } + } +} diff --git a/dotnet/framework/FrameworkTests/UpdateImplTests.cs b/dotnet/framework/FrameworkTests/UpdateImplTests.cs new file mode 100644 index 00000000..013b7ece --- /dev/null +++ b/dotnet/framework/FrameworkTests/UpdateImplTests.cs @@ -0,0 +1,226 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Security; +using NUnit.Framework; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace FrameworkTests +{ + /// + /// Description of UpdateImplTests. + /// + [TestFixture] + public class UpdateImplTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ValidateUidArg() + { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, null, new HashSet(), true); + } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ValidateObjectClassArg() + { + UpdateImpl.ValidateInput(null, new Uid("foo"), new HashSet(), true); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ValidateAttrsArg() + { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), null, true); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateUidAttribute() + { + HashSet attrs = new HashSet(); + attrs.Add(new Uid("foo")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAddWithNullAttribute() + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("something")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAttemptToAddName() + { + ICollection attrs = new HashSet(); + attrs.Add(new Name("fadf")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); + } + + [Test] + public void ValidateAttemptToAddDeleteOperationalAttribute() + { + // list of all the operational attributes.. + ICollection list = new List(); + list.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + list.Add(ConnectorAttributeBuilder.BuildLockOut(true)); + list.Add(ConnectorAttributeBuilder.BuildCurrentPassword(newSecureString("fadsf"))); + list.Add(ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.Now)); + list.Add(ConnectorAttributeBuilder.BuildPassword(newSecureString("fadsf"))); + foreach (ConnectorAttribute attr in list) + { + ICollection attrs = new HashSet(); + attrs.Add(attr); + try + { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("1"), attrs, true); + Assert.Fail("Failed: " + attr.Name); + } + catch (ArgumentException) + { + // this is a good thing.. + } + } + } + + private static SecureString newSecureString(string password) + { + SecureString rv = new SecureString(); + foreach (char c in password.ToCharArray()) + { + rv.AppendChar(c); + } + return rv; + } + + /// + /// Validate two collections are equal. (Not fast but effective) + /// + public static bool AreEqual(ICollection arg1, + ICollection arg2) + { + if (arg1.Count != arg2.Count) + { + return false; + } + foreach (ConnectorAttribute attr in arg1) + { + if (!arg2.Contains(attr)) + { + return false; + } + } + return true; + } + [Test] + public void MergeAddAttribute() + { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); + actual = up.Merge(changeset, baseAttrs, true); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeAddToExistingAttribute() + { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); + actual = up.Merge(changeset, baseAttrs, true); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteNonExistentAttribute() + { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + actual = up.Merge(changeset, baseAttrs, false); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteToExistingAttribute() + { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1)); + actual = up.Merge(changeset, baseAttrs, false); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteToExistingAttributeCompletely() + { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1, 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc")); + actual = up.Merge(changeset, baseAttrs, false); + Assert.IsTrue(AreEqual(expected, actual)); + } + } +} diff --git a/dotnet/framework/FrameworkTests/VersionRangeTests.cs b/dotnet/framework/FrameworkTests/VersionRangeTests.cs new file mode 100755 index 00000000..96a032ba --- /dev/null +++ b/dotnet/framework/FrameworkTests/VersionRangeTests.cs @@ -0,0 +1,166 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2015 ForgeRock AS. All Rights Reserved + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common; + +namespace FrameworkTests +{ + [TestFixture] + public class VersionRangeTests + { + [Test] + public virtual void TestIsInRange() + { + Version reference0 = new Version(1, 1, 0, 0); + Version reference1 = new Version(1, 1, 0, 1); + Version reference2 = new Version(1, 1, 0, 2); + Version reference3 = new Version(1, 1, 0, 3); + Version reference4 = new Version(1, 1, 0, 4); + VersionRange range = VersionRange.Parse("[1.1.0.1,1.1.0.3)"); + + Assert.IsFalse(range.IsInRange(reference0)); + Assert.IsTrue(range.IsInRange(reference1)); + Assert.IsTrue(range.IsInRange(reference2)); + Assert.IsFalse(range.IsInRange(reference3)); + Assert.IsFalse(range.IsInRange(reference4)); + } + + [Test] + public virtual void TestIsExact() + { + Assert.IsTrue(VersionRange.Parse("1.1.0.0").Exact); + //Version string portion was too short or too long (major.minor[.build[.revision]]). + //Assert.IsTrue(VersionRange.Parse(" [ 1 , 1 ] ").Exact); + Assert.IsTrue(VersionRange.Parse("[ 1.1 , 1.1 ]").Exact); + Assert.IsTrue(VersionRange.Parse(" [1.1.1 , 1.1.1] ").Exact); + Assert.IsTrue(VersionRange.Parse("[1.1.0.0,1.1.0.0]").Exact); + Assert.IsTrue(VersionRange.Parse("(1.1.0.0,1.1.0.2)").Exact); + } + + [Test] + public virtual void TestIsEmpty() + { + Assert.IsTrue(VersionRange.Parse("(1.1.0.0,1.1.0.0)").Empty); + Assert.IsTrue(VersionRange.Parse("(1.2.0.0,1.1.0.0]").Empty); + } + + [Test] + public virtual void TestValidSyntax() + { + try + { + VersionRange.Parse("(1.1.0.0)"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.FormatException) + { + // ok + } + try + { + VersionRange.Parse("1.1.0.0,1.1)]"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException) + { + // ok + } + try + { + VersionRange.Parse("(1.1.0.0-1.1)"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException) + { + // ok + } + try + { + VersionRange.Parse("1.1.0.0,1.1"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException) + { + // ok + } + try + { + VersionRange.Parse("( , 1.1)"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException) + { + // ok + } + } + + [Test] + public virtual void TestIsEqual() + { + VersionRange range1 = VersionRange.Parse("[1.1.0.1,1.1.0.3)"); + VersionRange range2 = VersionRange.Parse(range1.ToString()); + Assert.IsTrue(range1.Equals(range2)); + } + + [Test] + public virtual void TestConnectorKeysInRange() + { + ConnectorKeyRange r1 = + ConnectorKeyRange.NewBuilder() + .SetBundleName("B") + .SetConnectorName("C") + .SetBundleVersion("1.1.0.0") + .Build(); + + ConnectorKeyRange r2 = + ConnectorKeyRange.NewBuilder() + .SetBundleName("B") + .SetConnectorName("C") + .SetBundleVersion("[1.1.0.0,1.2.0.0]") + .Build(); + + ConnectorKey k1 = new ConnectorKey("B", "1.1.0.0", "C"); + ConnectorKey k2 = new ConnectorKey("B", "1.2.0.0", "C"); + + Assert.IsTrue(r1.BundleVersionRange.Exact); + Assert.IsFalse(r2.BundleVersionRange.Exact); + + Assert.IsTrue(r1.IsInRange(k1)); + Assert.IsFalse(r1.IsInRange(k2)); + + Assert.IsTrue(r2.IsInRange(k1)); + Assert.IsTrue(r2.IsInRange(k2)); + + ConnectorKeyRange r45 = + ConnectorKeyRange.NewBuilder().SetBundleName("B").SetConnectorName("C") + .SetBundleVersion("[1.4.0.0,1.5.0.0)").Build(); + Assert.IsTrue(r45.IsInRange(new ConnectorKey("B", "1.4.0.0", "C"))); + + Assert.IsFalse(r45.IsInRange(new ConnectorKey("B", "1.5.0.0", "C"))); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/app.config b/dotnet/framework/FrameworkTests/app.config new file mode 100644 index 00000000..8a99629e --- /dev/null +++ b/dotnet/framework/FrameworkTests/app.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/framework/FrameworkTests/packages.config b/dotnet/framework/FrameworkTests/packages.config new file mode 100755 index 00000000..4b8fa985 --- /dev/null +++ b/dotnet/framework/FrameworkTests/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/FrameworkTests/version.template b/dotnet/framework/FrameworkTests/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/FrameworkTests/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs b/dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs new file mode 100644 index 00000000..c4810972 --- /dev/null +++ b/dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs @@ -0,0 +1,127 @@ +/** +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +* +* Copyright (c) 2012-2014 ForgeRock AS. All Rights Reserved +* +* The contents of this file are subject to the terms +* of the Common Development and Distribution License +* (the License). You may not use this file except in +* compliance with the License. +* +* You can obtain a copy of the License at +* http://forgerock.org/license/CDDLv1.0.html +* See the License for the specific language governing +* permission and limitations under the License. +* +* When distributing Covered Code, include this CDDL +* Header Notice in each file and include the License file +* at http://forgerock.org/license/CDDLv1.0.html +* If applicable, add the following below the CDDL Header, +* with the fields enclosed by brackets [] replaced by +* your own identifying information: +* "Portions Copyrighted [year] [name of copyright owner]" +* +* +* Author: Gael Allioux +*/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Script; + + +// namespace Org.IdentityConnectors.Common.Script.PowerShell +namespace Org.ForgeRock.OpenICF.Framework.Common.Script.PowerShell +{ + [ScriptExecutorFactoryClass("PowerShell")] + public class PowerShellScriptExecutorFactory : ScriptExecutorFactory + { + + /// + /// Creates a script executor given the PowerShell script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly[] referencedAssemblies, string script, bool compile) + { + return new PowerShellScriptExecutor(script); + } + + /// + /// Processes the script. + /// + class PowerShellScriptExecutor : ScriptExecutor + { + private readonly string _script; + + public PowerShellScriptExecutor() + { + } + + public PowerShellScriptExecutor(string script) + { + _script = script; + } + + public PowerShellScriptExecutor(Assembly[] referencedAssemblies, string script) + { + _script = script; + } + + public object Execute(IDictionary arguments) + { + Command myCommand = new Command(_script,true); + Runspace runspace = RunspaceFactory.CreateRunspace(); + Pipeline pipeline = null; + Collection results = null; + + //foreach (String argumentName in arguments.Keys) + //{ + // CommandParameter param = new CommandParameter(argumentName, arguments[argumentName]); + // myCommand.Parameters.Add(param); + //} + + try + { + runspace.Open(); + // create a pipeline and give it the command + pipeline = runspace.CreatePipeline(); + pipeline.Commands.Add(myCommand); + //pipeline.Commands.Add("Out-String"); + foreach (String argumentName in arguments.Keys) + { + runspace.SessionStateProxy.SetVariable(argumentName, arguments[argumentName]); + } + // execute the script + results = pipeline.Invoke(); + } + catch (Exception e) + { + TraceUtil.TraceException("Unable to run PowerShell script on resource.", e); + throw; + } + finally + { + pipeline.Dispose(); + // close & dispose the runspace + runspace.Close(); + runspace.Dispose(); + } + + // return the script result as a single string + IDictionary result = new Dictionary(); + int index = 0; + foreach (PSObject obj in results) + { + result.Add(index.ToString(),obj.ToString()); + index++; + } + return result; + } + } + } +} diff --git a/dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj b/dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj new file mode 100644 index 00000000..d3d30e0c --- /dev/null +++ b/dotnet/framework/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj @@ -0,0 +1,96 @@ + + + + + {57754FFA-BB1F-4722-A2FA-70C4F27C6784} + Debug + AnyCPU + Library + Properties + Org.ForgeRock.OpenICF.Framework.Common.Script.PowerShell + PowerShell.ScriptExecutorFactory + v4.5.2 + true + + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + false + + + pdbonly + bin\Release\ + prompt + true + 4 + AnyCPU + True + False + TRACE + false + + + + + + 4.0 + + + False + + + 4.0 + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/PowerShellScriptExecutorFactory/version.template b/dotnet/framework/PowerShellScriptExecutorFactory/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/PowerShellScriptExecutorFactory/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/Service/FR_ICF_sq_med.ico b/dotnet/framework/Service/FR_ICF_sq_med.ico new file mode 100755 index 0000000000000000000000000000000000000000..10240f4759865dd5741f62a4b15549b1e2e712c1 GIT binary patch literal 32038 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 I>*ay}189e!r~m)} literal 0 HcmV?d00001 diff --git a/dotnet/framework/Service/Program.cs b/dotnet/framework/Service/Program.cs new file mode 100644 index 00000000..3f8189ef --- /dev/null +++ b/dotnet/framework/Service/Program.cs @@ -0,0 +1,332 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Configuration; +using System.Configuration.Install; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Security.Cryptography; +using System.ServiceProcess; +using Org.IdentityConnectors.Common.Security; +using System.Security.Cryptography.X509Certificates; +using Org.IdentityConnectors.Framework.Service.Properties; + +namespace Org.IdentityConnectors.Framework.Service +{ + + static class Program + { + private const string OPT_SERVICE_NAME = "/serviceName"; + private const string OPT_CERTSTOR_NAME = "/storeName"; + private const string OPT_CERTFILE_NAME = "/certificateFile"; + + private static void Usage() + { + Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); + Console.WriteLine(" /install [/serviceName ] - Installs the service."); + Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); + Console.WriteLine(" /run - Runs the service from the console."); + Console.WriteLine(" /setKey [] - Sets the connector server key."); + Console.WriteLine(" /setDefaults - Sets default app.config"); + Console.WriteLine(" /storeCertificate [/storeName ] [/certificateFile ]- Stores the Certificate in the storage."); + } + + private static IDictionary ParseOptions(string[] args) + { + IDictionary rv = new Dictionary(); + + for (int i = 1; i < args.Length; i++) + { + String optionName = null; + String opt = args[i].ToLower(); + + if (OPT_SERVICE_NAME.Equals(opt, StringComparison.InvariantCultureIgnoreCase)) + { + optionName = OPT_SERVICE_NAME; + } + else if (OPT_CERTFILE_NAME.Equals(opt, StringComparison.InvariantCultureIgnoreCase)) + { + optionName = OPT_CERTFILE_NAME; + } + else if (OPT_CERTSTOR_NAME.Equals(opt, StringComparison.InvariantCultureIgnoreCase)) + { + optionName = OPT_CERTSTOR_NAME; + } + if (optionName != null) + { + i++; + if (i < args.Length) + { + rv[optionName] = args[i]; + } + else + { + Usage(); + return null; + } + } + else + { + Usage(); + return null; + } + } + return rv; + } + + /// + /// This method starts the service. + /// + static void Main(string[] args) + { + if (args.Length == 0) + { + Usage(); + } + else + { + String cmd = args[0].ToLower(); + if (cmd.Equals("/setkey", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 2) + { + Usage(); + return; + } + DoSetKey(args.Length > 1 ? args[1] : null); + return; + } + if (cmd.Equals("/setDefaults", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 1) + { + Usage(); + return; + } + using (var file = new StreamWriter(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false)) + { + file.WriteLine(Resources.ResourceManager.GetString("DefaultConfig")); + Console.WriteLine("Default configuration successfully restored."); + } + return; + } + IDictionary options = + ParseOptions(args); + if (options == null) + { + //there's a parse error in the options, return + return; + } + if ("/install".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoInstall(options); + } + else if ("/uninstall".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoUninstall(options); + } + else if ("/run".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoRun(options); + } + else if ("/service".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + ServiceBase.Run(new ServiceBase[] { new Service() }); + } + else if ("/storecertificate".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoStoreCertificate(options); + } + else + { + Usage(); + return; + } + } + } + + private static void DoInstall(IDictionary options) + { + if (options.ContainsKey(OPT_SERVICE_NAME)) + { + ProjectInstaller.ServiceName = options[OPT_SERVICE_NAME]; + } + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = + { + Assembly.GetExecutingAssembly ().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("install.log", + cmdline); + ti.Context = ctx; + ti.Install(new System.Collections.Hashtable()); + } + + private static void DoUninstall(IDictionary options) + { + if (options.ContainsKey(OPT_SERVICE_NAME)) + { + ProjectInstaller.ServiceName = options[OPT_SERVICE_NAME]; + } + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = + { + Assembly.GetExecutingAssembly ().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("uninstall.log", + cmdline); + ti.Context = ctx; + ti.Uninstall(null); + } + + private static void DoRun(IDictionary options) + { + Service svc = new Service(); + + svc.StartService(new String[0]); + + Console.WriteLine("Press q to shutdown."); + Console.WriteLine("Press t for a thread dump."); + + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(); + if (info.KeyChar == 'q') + { + break; + } + else if (info.KeyChar == 't') + { + svc.DumpRequests(); + } + } + + svc.StopService(); + } + + private static GuardedString ReadPassword() + { + GuardedString rv = new GuardedString(); + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(true); + if (info.Key == ConsoleKey.Enter) + { + Console.WriteLine(); + rv.MakeReadOnly(); + return rv; + } + else + { + Console.Write("*"); + rv.AppendChar(info.KeyChar); + } + } + } + + private static void DoSetKey(string key) + { + GuardedString str; + if (key == null) + { + Console.Write("Please enter the new key: "); + GuardedString v1 = ReadPassword(); + Console.Write("Please confirm the new key: "); + GuardedString v2 = ReadPassword(); + if (!v1.Equals(v2)) + { + Console.WriteLine("Error: Key mismatch."); + return; + } + str = v2; + } + else + { + str = new GuardedString(); + foreach (char c in key) + { + str.AppendChar(c); + } + } + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(Service.PROP_KEY); + config.AppSettings.Settings.Add(Service.PROP_KEY, str.GetBase64SHA1Hash()); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine("Key has been successfully updated."); + } + + private static void DoStoreCertificate(IDictionary options) + { + string storeName = options.ContainsKey(OPT_CERTSTOR_NAME) ? options[OPT_CERTSTOR_NAME] : "ConnectorServerSSLCertificate"; + + + if (!options.ContainsKey(OPT_CERTFILE_NAME) || String.IsNullOrEmpty(options[OPT_CERTFILE_NAME])) + { + Usage(); + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required argument: " + OPT_CERTFILE_NAME); + } + X509Certificate2 certificate = null; + try + { + certificate = new X509Certificate2(options[OPT_CERTFILE_NAME]); + } + catch (CryptographicException) + { + Console.Write("Please enter the keystore password: "); + GuardedString v1 = ReadPassword(); + certificate = new X509Certificate2(options[OPT_CERTFILE_NAME], SecurityUtil.Decrypt(v1), X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); + } + X509Store store = new X509Store(storeName, StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadWrite); + X509CertificateCollection certificates = store.Certificates; + if (certificates.Count != 0) + { + if (certificates.Count == 1) + { + store.Remove(store.Certificates[0]); + Console.WriteLine("Previous certificate has been removed."); + } + else + { + Console.WriteLine("There are multiple certificates were found. You may point to the wrong store."); + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("There is supported to be exactly one certificate in the store: " + storeName); + } + } + store.Add(certificate); + store.Close(); + Console.WriteLine("Certificate is stored in " + storeName); + } + } +} \ No newline at end of file diff --git a/dotnet/framework/Service/ProjectInstaller.cs b/dotnet/framework/Service/ProjectInstaller.cs new file mode 100644 index 00000000..ea050069 --- /dev/null +++ b/dotnet/framework/Service/ProjectInstaller.cs @@ -0,0 +1,86 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System.ComponentModel; +using System.Configuration.Install; +using System.ServiceProcess; + +namespace Org.IdentityConnectors.Framework.Service +{ + [RunInstaller(true)] + public class ProjectInstaller : Installer + { + public static string ServiceName { get; set; } + public static string DisplayName { get; set; } + public static string Description { get; set; } + + static ProjectInstaller() + { + ServiceName = "LegacyConnectorServerService"; + DisplayName = "OpenICF Legacy Connector Server"; + Description = "OpenICF Legacy Connector Server"; + } + + private ServiceProcessInstaller serviceProcessInstaller; + private ServiceInstaller serviceInstaller; + + public ProjectInstaller() + { + serviceProcessInstaller = new ServiceProcessInstaller(); + serviceInstaller = new ServiceInstaller(); + + // Here you can set properties on serviceProcessInstaller or register event handlers + serviceProcessInstaller.Account = ServiceAccount.LocalSystem; + + serviceInstaller.ServiceName = ServiceName; + serviceInstaller.Description = Description; + serviceInstaller.DisplayName = DisplayName; + serviceInstaller.StartType = ServiceStartMode.Automatic; + + this.Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller }); + } + + protected virtual string AppendPathParameter(string path, string parameter) + { + if (path.Length > 0 && path[0] != '"') + { + path = "\"" + path + "\""; + } + path += " " + parameter; + return path; + } + + protected override void OnBeforeInstall(System.Collections.IDictionary savedState) + { + Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); + base.OnBeforeInstall(savedState); + } + + protected override void OnBeforeUninstall(System.Collections.IDictionary savedState) + { + Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); + base.OnBeforeUninstall(savedState); + } + } +} diff --git a/dotnet/framework/Service/Properties/Resources.Designer.cs b/dotnet/framework/Service/Properties/Resources.Designer.cs new file mode 100755 index 00000000..4bc9e4f4 --- /dev/null +++ b/dotnet/framework/Service/Properties/Resources.Designer.cs @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34209 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Org.IdentityConnectors.Framework.Service.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("Org.IdentityConnectors.Framework.Service.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<configuration> + /// <runtime> + /// <loadFromRemoteSources enabled="true"/> + /// </runtime> + /// <connectionStrings> + /// <!-- Example connection to a SQL Server Database on localhost. --> + /// <!-- <add name="ExampleConnectionString" + /// connectionString="Data Source=.;Initial Catalog=DBName;Integrated Security=True" + /// providerName="System.Data.SqlClient" /> --> + /// </connectionStrings> + /// <appSettings> + /// <!-- access these values via the property: + /// [rest of string was truncated]";. + /// + internal static string DefaultConfig { + get { + return ResourceManager.GetString("DefaultConfig", resourceCulture); + } + } + } +} diff --git a/dotnet/framework/Service/Properties/Resources.resx b/dotnet/framework/Service/Properties/Resources.resx new file mode 100755 index 00000000..6d0123b9 --- /dev/null +++ b/dotnet/framework/Service/Properties/Resources.resx @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version="1.0" encoding="utf-8"?> +<configuration> + <runtime> + <loadFromRemoteSources enabled="true"/> + </runtime> + <connectionStrings> + <!-- Example connection to a SQL Server Database on localhost. --> + <!-- <add name="ExampleConnectionString" + connectionString="Data Source=.;Initial Catalog=DBName;Integrated Security=True" + providerName="System.Data.SqlClient" /> --> + </connectionStrings> + <appSettings> + <!-- access these values via the property: + System.Configuration.ConfigurationManager.AppSettings[key] + --> + <add key="connectorserver.port" value="8760"/> + <add key="connectorserver.usessl" value="false"/> + <add key="connectorserver.certificatestorename" value="ConnectorServerSSLCertificate"/> + <add key="connectorserver.ifaddress" value="0.0.0.0"/> + <add key="connectorserver.maxFacadeLifeTime" value="0"/> + <add key="connectorserver.key" value="lmA6bMfENJGlIDbfrVtklXFK32s="/> + <!-- + Enable/Disable the logging proxy for all operations. + --> + <add key="logging.proxy" value="false"/> + </appSettings> + <system.diagnostics> + <trace autoflush="true" indentsize="4"> + <listeners> + <remove name="Default"/> + <add name="console"/> + <add name="file"/> + </listeners> + </trace> + <sources> + <source name="ConnectorServer" switchName="switch1"> + <listeners> + <remove name="Default"/> + <add name="file"/> + </listeners> + </source> + <source name="MyClass2" switchName="switch2"> + <listeners> + <remove name="Default"/> + <add name="file"/> + </listeners> + </source> + </sources> + <switches> + <add name="switch1" value="Information"/> + <add name="switch2" value="Warning"/> + </switches> + <sharedListeners> + <add name="console" type="System.Diagnostics.ConsoleTraceListener"> + </add> + <add name="file" type="System.Diagnostics.TextWriterTraceListener" initializeData="logs\connectorserver.log" traceOutputOptions="DateTime"> + <filter type="System.Diagnostics.EventTypeFilter" initializeData="Information"/> + </add> + </sharedListeners> + </system.diagnostics> + +</configuration> + + \ No newline at end of file diff --git a/dotnet/framework/Service/Service.cs b/dotnet/framework/Service/Service.cs new file mode 100644 index 00000000..96a542b7 --- /dev/null +++ b/dotnet/framework/Service/Service.cs @@ -0,0 +1,185 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.ServiceProcess; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Server; + +namespace Org.IdentityConnectors.Framework.Service +{ + public class Service : ServiceBase + { + private const string PROP_PORT = "connectorserver.port"; + private const string PROP_SSL = "connectorserver.usessl"; + private const string PROP_CERTSTORE = "connectorserver.certificatestorename"; + private const string PROP_IFADDRESS = "connectorserver.ifaddress"; + public const string PROP_KEY = "connectorserver.key"; + public const string PROP_FACADE_LIFETIME = "connectorserver.maxFacadeLifeTime"; + + private ConnectorServer _server; + + public Service() + { + } + + public void DumpRequests() + { + _server.DumpRequests(); + } + + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + private void initializeCurrentDirectory() + { + Assembly assembly = + Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + thisAssemblyFile.Directory; + Environment.CurrentDirectory = + directory.FullName; + + } + + private NameValueCollection GetApplicationSettings() + { + return ConfigurationManager.AppSettings; + } + + private X509Certificate GetCertificate() + { + NameValueCollection settings = + GetApplicationSettings(); + String storeName = settings.Get(PROP_CERTSTORE); + if (storeName == null) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration setting: "+PROP_CERTSTORE); + } + + X509Store store = new X509Store(storeName, + StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); + X509CertificateCollection certificates = store.Certificates; + if ( certificates.Count != 1 ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("There is supported to be exactly one certificate in the store: "+storeName); + } + X509Certificate certificate = store.Certificates[0]; + store.Close(); + return certificate; + } + + public void StartService(string [] args) + { + OnStart(args); + } + + /// + /// Start this service. + /// + protected override void OnStart(string[] args) + { + try { + initializeCurrentDirectory(); + Trace.TraceInformation("Starting connector server: "+Environment.CurrentDirectory); + NameValueCollection settings = + GetApplicationSettings(); + String portStr = + settings.Get(PROP_PORT); + if ( portStr == null ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_PORT); + } + String keyHash = settings.Get(PROP_KEY); + if ( keyHash == null ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_KEY); + } + + int port = Int32.Parse(portStr); + bool useSSL = Boolean.Parse(settings.Get(PROP_SSL)??"false"); + _server = ConnectorServer.NewInstance(); + _server.Port = port; + _server.UseSSL = useSSL; + _server.KeyHash = keyHash; + if (useSSL) { + _server.ServerCertificate = + GetCertificate(); + } + String ifaddress = settings.Get(PROP_IFADDRESS); + if ( ifaddress != null ) { + _server.IfAddress = + IOUtil.GetIPAddress(ifaddress); + } + String facedeLifeTimeStr = settings.Get(PROP_FACADE_LIFETIME); + if (facedeLifeTimeStr != null) + { + _server.MaxFacadeLifeTime = Int32.Parse(facedeLifeTimeStr); + } + _server.Start(); + Trace.TraceInformation("Started connector server"); + } + catch (Exception e) { + TraceUtil.TraceException("Exception occured starting connector server", + e); + throw; + } + } + + public void StopService() + { + OnStop(); + } + + + /// + /// Stop this service. + /// + protected override void OnStop() + { + try { + Trace.TraceInformation("Stopping connector server"); + if (_server != null) { + _server.Stop(); + } + Trace.TraceInformation("Stopped connector server"); + } + catch (Exception e) { + TraceUtil.TraceException("Exception occured stopping connector server", + e); + } + } + } +} diff --git a/dotnet/framework/Service/Service.csproj b/dotnet/framework/Service/Service.csproj new file mode 100644 index 00000000..56504037 --- /dev/null +++ b/dotnet/framework/Service/Service.csproj @@ -0,0 +1,140 @@ + + + + + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E} + Debug + AnyCPU + Exe + Org.IdentityConnectors.Framework.Service + ConnectorServer + Open Connectors Framework - Connector Server Service + v4.5.2 + False + false + + + + prompt + ./bin/Debug/ + true + Full + False + True + DEBUG;TRACE + Exe + ConnectorServer + False + 4 + false + + + ./bin/Release/ + true + pdbonly + True + False + TRACE + prompt + Exe + ConnectorServer + False + 4 + false + + + False + Auto + 4194304 + AnyCPU + 4096 + + + FR_ICF_sq_med.ico + + + + + + + True + + + 4.0 + + + + True + + + 4.0 + + + + + + + + Component + + + True + True + Resources.resx + + + Component + + + Designer + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/Service/app.config b/dotnet/framework/Service/app.config new file mode 100644 index 00000000..d2273dae --- /dev/null +++ b/dotnet/framework/Service/app.config @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/framework/Service/version.template b/dotnet/framework/Service/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/Service/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/ServiceInstall/ExtBuild.proj b/dotnet/framework/ServiceInstall/ExtBuild.proj new file mode 100644 index 00000000..932d44f2 --- /dev/null +++ b/dotnet/framework/ServiceInstall/ExtBuild.proj @@ -0,0 +1,71 @@ + + + + Debug + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/framework/ServiceInstall/File.bottom b/dotnet/framework/ServiceInstall/File.bottom new file mode 100644 index 00000000..034b63c4 --- /dev/null +++ b/dotnet/framework/ServiceInstall/File.bottom @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dotnet/framework/ServiceInstall/File.top b/dotnet/framework/ServiceInstall/File.top new file mode 100644 index 00000000..5a817260 --- /dev/null +++ b/dotnet/framework/ServiceInstall/File.top @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/ServiceInstall/Localize/UILoc_en-us.wxl b/dotnet/framework/ServiceInstall/Localize/UILoc_en-us.wxl new file mode 100644 index 00000000..5feef838 --- /dev/null +++ b/dotnet/framework/ServiceInstall/Localize/UILoc_en-us.wxl @@ -0,0 +1,29 @@ + + + + Connector Server Settings + Shared Key + TCP Port + Failed to setup configuration. ([2] [3] [4] [5]) + \ No newline at end of file diff --git a/dotnet/framework/ServiceInstall/Resources/FR_ICF_sq_med.ico b/dotnet/framework/ServiceInstall/Resources/FR_ICF_sq_med.ico new file mode 100755 index 0000000000000000000000000000000000000000..10240f4759865dd5741f62a4b15549b1e2e712c1 GIT binary patch literal 32038 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 I>*ay}189e!r~m)} literal 0 HcmV?d00001 diff --git a/dotnet/framework/ServiceInstall/Resources/SetupBanner.bmp b/dotnet/framework/ServiceInstall/Resources/SetupBanner.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5ba1a37a8a28bfebe738c0ea934b0acbdb51e0ca GIT binary patch literal 85896 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 literal 0 HcmV?d00001 diff --git a/dotnet/framework/ServiceInstall/ServiceInstall.wixproj b/dotnet/framework/ServiceInstall/ServiceInstall.wixproj new file mode 100644 index 00000000..c83a85cb --- /dev/null +++ b/dotnet/framework/ServiceInstall/ServiceInstall.wixproj @@ -0,0 +1,146 @@ + + + + + {1CBA8F74-050C-432B-8437-08BD13BDC684} + Debug + AnyCPU + Package + ServiceInstall + ServiceInstall + $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets + $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets + en-US + ICE45 + 1.5.0.0-SNAPSHOT + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + true + True + False + 1.5.0.0 + + + + + + $(WixExtDir)\WixUtilExtension.dll + WixUtilExtension + + + $(WixExtDir)\WixUIExtension.dll + WixUIExtension + + + $(WixExtDir)\WixNetFxExtension.dll + WixNetFxExtension + + + + + + + + + + + + + + + + + + + + + + Common + {f140e8da-52b4-4159-992a-9da10ea8eefb} + True + True + + + ConnectorServerService + {e5dcc07f-7b42-4ae9-8d6c-a15525476e0a} + True + True + Binaries;Content;Satellites + INSTALLFOLDER + + + FrameworkInternal + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + True + True + + + Framework + {8b24461b-456a-4032-89a1-cd418f7b5b62} + True + True + + + PowerShellScriptExecutorFactory + {57754ffa-bb1f-4722-a2fa-70c4f27c6784} + True + True + + + Service + {a9d6374a-d51f-4fa3-8c02-5b1d23faa82e} + True + True + + + ShellScriptExecutorFactory + {4700690a-2d29-40a0-86ac-e5a9f71a479a} + True + True + + + TestCommon + {e6a207d2-e083-41bf-b522-d9d3ec09323e} + True + True + + + \ No newline at end of file diff --git a/dotnet/framework/ServiceInstall/SettingsDlg.wxs b/dotnet/framework/ServiceInstall/SettingsDlg.wxs new file mode 100755 index 00000000..141d0cb3 --- /dev/null +++ b/dotnet/framework/ServiceInstall/SettingsDlg.wxs @@ -0,0 +1,95 @@ + + + + + + + + Please enter your service configuration + + + {\WixUI_Font_Title}!(loc.SettingsDlg_Title) + + + + + + + + + + + + + Service Port: + + + + + + + + + 1 + + + + + 1 + + + 1 + CostingComplete = 1 + ProductID + + + 1 + + + + + + + + + [SourceDir]ConnectorServerService.exe + + + + NOT Installed + + + diff --git a/dotnet/framework/ServiceInstall/Setup.wxs b/dotnet/framework/ServiceInstall/Setup.wxs new file mode 100644 index 00000000..d2d532a3 --- /dev/null +++ b/dotnet/framework/ServiceInstall/Setup.wxs @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OpenICF Remote Connector Server + Visit us at https://www.forgerock.com/contact/ + https://www.forgerock.com/services/support-services/ + +44-1935-804797 + https://forgerock.org/openicf/ + https://forgerock.org/openicf/ + + + + + + + + + + + + + + + + + + + + 8759 + 8760 + + diff --git a/dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs new file mode 100644 index 00000000..19a66798 --- /dev/null +++ b/dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -0,0 +1,221 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS + */ +using System; +using System.IO; +using System.Security; +using System.Diagnostics; +using System.Reflection; +using System.Collections.Generic; +using Org.IdentityConnectors.Common.Security; +using System.Threading; + +namespace Org.IdentityConnectors.Common.Script.Shell +{ + + /// + /// Process shell scripts. Valid arguments are below, the rest will be + /// used as environment variables. The return is the exit code of the + /// process. + ///
    + ///
  • USERNAME - name of the user to run this script as..
  • + ///
  • PASSWORD - (GuardedString or SecureString) password for the user to run this script as..
  • + ///
  • WORKINGDIR - working directory run this script in..
  • + ///
  • TIMEOUT - timeout waiting for script to finish in ms (default: 30 secs)
  • + ///
+ ///
+ [ScriptExecutorFactoryClass("Shell")] + public class ShellScriptExecutorFactory : ScriptExecutorFactory + { + + /// + /// Creates a script executor give the Shell script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly[] refs, string script, bool compile) + { + return new ShellScriptExecutor(script); + } + + /// + /// Processes the script. + /// + class ShellScriptExecutor : ScriptExecutor + { + private readonly string _script; + + public ShellScriptExecutor(string script) + { + _script = script; + } + public object Execute(IDictionary arguments) + { + // + string fn = String.Empty; + // create the process info.. + Process process = new Process(); + // set the defaults.. + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + // set the default timeout.. + int timeout = 1000 * 30; // 30 secss + IDictionary result = new Dictionary(); + int exitCode = 1; + try + { + Trace.TraceInformation("About to execute script: {0}", _script); + // if there are any environment varibles set to false.. + process.StartInfo.UseShellExecute = false;//arguments.Count == 0; + // take out username and password if they're in the options. + foreach (KeyValuePair kv in arguments) + { + if (kv.Key.ToUpper().Equals("USERNAME")) + { + string domainUser = kv.Value.ToString(); + string[] split = domainUser.Split(new char[] { '\\' }); + if (split.Length == 1) + { + process.StartInfo.UserName = split[0]; + } + else + { + process.StartInfo.Domain = split[0]; + process.StartInfo.UserName = split[1]; + } + } + else if (kv.Key.ToUpper().Equals("PASSWORD")) + { + if (kv.Value is SecureString) + { + process.StartInfo.Password = (SecureString)kv.Value; + } + else if (kv.Value is GuardedString) + { + process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); + } + else + { + throw new ArgumentException("Invalid type for password."); + } + } + else if (kv.Key.ToUpper().Equals("WORKINGDIR")) + { + process.StartInfo.WorkingDirectory = kv.Value.ToString(); + } + else if (kv.Key.ToUpper().Equals("TIMEOUT")) + { + timeout = Int32.Parse(kv.Value.ToString()); + } + else + { + if (kv.Value == null) + { + Trace.TraceWarning("...parameter {0} has null value, skipping it", kv.Key); + } + else + { + Trace.TraceInformation("...with parameter {0} set to {1}", kv.Key, kv.Value); + process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); + } + } + } + // write out the script.. + fn = Path.GetTempFileName() + ".cmd"; + StreamWriter sw = null; + try + { + sw = new StreamWriter(fn); + sw.Write(_script); + } + finally + { + sw.Close(); + sw.Dispose(); + } + // set temp file.. + process.StartInfo.FileName = fn; + // execute script.. + process.Start(); + string stdout = process.StandardOutput.ReadToEnd(); + Trace.TraceInformation("execution started; stdout = {0}", stdout); // this is quite suspicious... + // http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx + // Use asynchronous read operations on at least one of the streams. + AsynchronousReader msr_stderr = new AsynchronousReader(process.StandardError); + // Create the thread objects to run the code asynchronously + Thread t_stderr = new Thread(msr_stderr.Go); + t_stderr.Start(); + t_stderr.Join(); + // wait for the process to exit.. + if (!process.WaitForExit(timeout)) + { + throw new TimeoutException("Script failed to exit in time!"); + } + exitCode = process.ExitCode; + result.Add("stdout", stdout); + result.Add("stderr", msr_stderr.Text); + Trace.TraceInformation("execution finished; stderr = {0}", msr_stderr.Text); + } + catch (Exception e) + { + Trace.TraceError("Failed to execute script with exception {0}", e.Message); + } + finally + { + // close up the process + process.Close(); + process.Dispose(); + } + // clean up temp file.. + try + { + File.Delete(fn); + } + catch (Exception) + { + + } + Trace.TraceInformation("exitCode = {0}", exitCode); + result.Add("exitCode", exitCode); + return result; + } + } + } + internal class AsynchronousReader + { + StreamReader _sr = null; + string _text = null; + public string Text { get { return _text; } } + + public AsynchronousReader(StreamReader sr) + { + _sr = sr; + } + + public void Go() + { + _text = _sr.ReadToEnd(); + } + } +} diff --git a/dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj new file mode 100644 index 00000000..47cd1591 --- /dev/null +++ b/dotnet/framework/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -0,0 +1,86 @@ + + + + + {4700690A-2D29-40A0-86AC-E5A9F71A479A} + Debug + AnyCPU + Library + Sun.OpenConnectors.Common.Script.Shell + Shell.ScriptExecutorFactory + ShellScriptExecutorFactory + v4.5.2 + true + + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + false + + + pdbonly + bin\Release\ + prompt + true + 4 + AnyCPU + True + False + TRACE + false + + + + + + 4.0 + + + + 4.0 + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/ShellScriptExecutorFactory/version.template b/dotnet/framework/ShellScriptExecutorFactory/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/ShellScriptExecutorFactory/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV1/AssemblyInfo.cs b/dotnet/framework/TestBundles/TestBundleV1/AssemblyInfo.cs new file mode 100644 index 00000000..968dd3be --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV1/AssemblyInfo.cs @@ -0,0 +1,56 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +#region Using directives + +using System.Reflection; +using System.Runtime.InteropServices; +using System.Resources; + +#endregion + +// 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("TestBundleV1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestBundleV1")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguage("en-US")] + diff --git a/dotnet/framework/TestBundles/TestBundleV1/Messages.es-ES.resx b/dotnet/framework/TestBundles/TestBundleV1/Messages.es-ES.resx new file mode 100644 index 00000000..912c5950 --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV1/Messages.es-ES.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tstField.display_es-ES + + + tstField.help_es-ES + + + tstField.group_es-ES + + \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV1/Messages.es.resx b/dotnet/framework/TestBundles/TestBundleV1/Messages.es.resx new file mode 100644 index 00000000..600a2b43 --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV1/Messages.es.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tstField.help_es + + + tstField.display_es + + + tstField.group_es + + \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV1/Messages.resx b/dotnet/framework/TestBundles/TestBundleV1/Messages.resx new file mode 100644 index 00000000..9742ebb9 --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV1/Messages.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Help for test field. + + + Display for test field. + + + Group for test field. + + \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV1/TestBundleV1.csproj b/dotnet/framework/TestBundles/TestBundleV1/TestBundleV1.csproj new file mode 100644 index 00000000..1a0e561d --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV1/TestBundleV1.csproj @@ -0,0 +1,94 @@ + + + + + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6} + Debug + AnyCPU + Library + TestBundleV1 + TestBundleV1.Connector + v4.5.2 + OnBuildSuccess + + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + false + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + false + + + + + + + + + + + + + + Designer + + + Designer + + + Designer + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV1/TestConnector.cs b/dotnet/framework/TestBundles/TestBundleV1/TestConnector.cs new file mode 100755 index 00000000..552cd3e9 --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV1/TestConnector.cs @@ -0,0 +1,1304 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2015 ForgeRock AS. + */ + +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using ICF = Org.IdentityConnectors.Framework.Common.Objects; + +namespace org.identityconnectors.testconnector +{ + + #region MyTstConnection + + public class MyTstConnection + { + private readonly int _connectionNumber; + private bool _isGood = true; + + public MyTstConnection(int connectionNumber) + { + _connectionNumber = connectionNumber; + } + + public void Test() + { + if (!_isGood) + { + throw new ConnectorException("Connection is bad"); + } + } + + public void Dispose() + { + _isGood = false; + } + + public bool IsGood() + { + return _isGood; + } + + public int GetConnectionNumber() + { + return _connectionNumber; + } + } + + #endregion + + #region TstAbstractConnector + + public abstract class TstAbstractConnector : AuthenticateOp, IConnectorEventSubscriptionOp, + CreateOp, DeleteOp, ResolveUsernameOp, SchemaOp, ScriptOnResourceOp, SearchOp, + ISyncEventSubscriptionOp, SyncOp, TestOp, UpdateOp + { + internal sealed class ResourceComparator : IComparer + { + private readonly IList sortKeys; + + public ResourceComparator(ICF.SortKey[] sortKeys) + { + this.sortKeys = sortKeys; + } + + + public int Compare(ICF.ConnectorObject r1, ICF.ConnectorObject r2) + { + foreach (ICF.SortKey sortKey in sortKeys) + { + int result = Compare(r1, r2, sortKey); + if (result != 0) + { + return result; + } + } + return 0; + } + + private int Compare(ICF.ConnectorObject r1, ICF.ConnectorObject r2, ICF.SortKey sortKey) + { + IList vs1 = ValuesSorted(r1, sortKey.Field); + IList vs2 = ValuesSorted(r2, sortKey.Field); + if (vs1.Count == 0 && vs2.Count == 0) + { + return 0; + } + else if (vs1.Count == 0) + { + // Sort resources with missing attributes last. + return 1; + } + else if (vs2.Count == 0) + { + // Sort resources with missing attributes last. + return -1; + } + else + { + object v1 = vs1[0]; + object v2 = vs2[0]; + return sortKey.IsAscendingOrder() ? CompareValues(v1, v2) : -CompareValues(v1, v2); + } + } + + private IList ValuesSorted(ICF.ConnectorObject resource, string field) + { + ICF.ConnectorAttribute value = resource.GetAttributeByName(field); + if (value == null || value.Value == null || value.Value.Count == 0) + { + return new List(); + } + else if (value.Value.Count > 1) + { + List results = new List(value.Value); + results.Sort(VALUE_COMPARATOR); + return results; + } + else + { + return value.Value; + } + } + } + + private static readonly IComparer VALUE_COMPARATOR = new ComparatorAnonymousInnerClassHelper(); + + private class ComparatorAnonymousInnerClassHelper : IComparer + { + public ComparatorAnonymousInnerClassHelper() + { + } + + public virtual int Compare(object o1, object o2) + { + return CompareValues(o1, o2); + } + } + + private static int CompareValues(object v1, object v2) + { + if (v1 is string && v2 is string) + { + string s1 = (string)v1; + string s2 = (string)v2; + return StringComparer.OrdinalIgnoreCase.Compare(s1, s2); + } + else if (v1 is double && v2 is double) + { + double n1 = (double)v1; + double n2 = (double)v2; + return n1.CompareTo(n2); + } + else if (v1 is int && v2 is int) + { + int n1 = (int)v1; + int n2 = (int)v2; + return n1.CompareTo(n2); + } + else if (v1 is bool && v2 is bool) + { + bool b1 = (bool)v1; + bool b2 = (bool)v2; + return b1.CompareTo(b2); + } + else + { + return v1.GetType().FullName.CompareTo(v2.GetType().FullName); + } + } + + protected TstStatefulConnectorConfig _config; + + public void Init(Configuration cfg) + { + _config = (TstStatefulConnectorConfig)cfg; + Guid g = _config.Guid; + } + + public void Update() + { + _config.UpdateTest(); + } + + public virtual ICF.Uid Authenticate(ICF.ObjectClass objectClass, string username, GuardedString password, + ICF.OperationOptions options) + { + if (_config.returnNullTest) + { + return null; + } + else + { + return _config.Authenticate(objectClass, username, password); + } + } + + public ICF.ISubscription Subscribe(ICF.ObjectClass objectClass, Filter eventFilter, + IObserver handler, ICF.OperationOptions operationOptions) + { + ICF.ConnectorObjectBuilder builder = new ICF.ConnectorObjectBuilder { ObjectClass = objectClass }; + + Object op = CollectionUtil.GetValue(operationOptions.Options, "eventCount", null); + int? eventCount = op as int? ?? 10; + + bool doComplete = operationOptions.Options.ContainsKey("doComplete"); + + ICF.CancellationSubscription subscription = new ICF.CancellationSubscription(); + + DoPeriodicWorkAsync(runCount => + { + if (_config == null) + { + handler.OnError(new InvalidOperationException("Connector has been disposed")); + return false; + } + builder.SetUid(Convert.ToString(runCount)); + builder.SetName(Convert.ToString(runCount)); + handler.OnNext(builder.Build()); + + if (runCount >= eventCount) + { + // Locally stop serving subscription + if (doComplete) + { + handler.OnCompleted(); + } + else + { + handler.OnError(new ConnectorException("Subscription channel is closed")); + } + // The loop should be stopped from here. + return false; + } + return true; + }, new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), subscription.Token).ConfigureAwait(false); + + + return subscription; + } + + private async Task DoPeriodicWorkAsync(Func action, + TimeSpan interval, TimeSpan dueTime, CancellationToken token) + { + // Initial wait time before we begin the periodic loop. + await Task.Delay(dueTime, token); + + Int32 i = 0; + // Repeat this loop until cancelled. + while (!token.IsCancellationRequested) + { + if (action(++i)) + { + // Wait to repeat again. + await Task.Delay(interval, token); + } + else + { + break; + } + } + } + + public ICF.ISubscription Subscribe(ICF.ObjectClass objectClass, ICF.SyncToken token, + IObserver handler, ICF.OperationOptions operationOptions) + { + var coBuilder = new ICF.ConnectorObjectBuilder() { ObjectClass = objectClass }; + coBuilder.SetUid("0"); + coBuilder.SetName("SYNC_EVENT"); + + ICF.SyncDeltaBuilder builder = new ICF.SyncDeltaBuilder() + { + DeltaType = ICF.SyncDeltaType.CREATE_OR_UPDATE, + Object = coBuilder.Build() + }; + + Object op = CollectionUtil.GetValue(operationOptions.Options, "eventCount", null); + int? eventCount = op as int? ?? 10; + + bool doComplete = operationOptions.Options.ContainsKey("doComplete"); + + ICF.CancellationSubscription subscription = new ICF.CancellationSubscription(); + + DoPeriodicWorkAsync(runCount => + { + if (_config == null) + { + handler.OnError(new InvalidOperationException("Connector has been disposed")); + return false; + } + builder.Token = new ICF.SyncToken(runCount); + handler.OnNext(builder.Build()); + + if (runCount >= eventCount) + { + // Locally stop serving subscription + if (doComplete) + { + handler.OnCompleted(); + } + else + { + handler.OnError(new ConnectorException("Subscription channel is closed")); + } + // ScheduledFuture should be stopped from here. + return false; + } + return true; + }, new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), subscription.Token).ConfigureAwait(false); + + + return subscription; + } + + + public ICF.Uid Create(ICF.ObjectClass objectClass, ICollection createAttributes, + ICF.OperationOptions options) + { + ICF.ConnectorAttributesAccessor accessor = new ICF.ConnectorAttributesAccessor(createAttributes); + if (_config.returnNullTest) + { + return null; + } + if (_config.IsTestObjectClass(objectClass)) + { + return _config.GeObjectCache(objectClass).Create(createAttributes); + } + else + { + if (accessor.HasAttribute("fail")) + { + throw new ConnectorException("Test Exception"); + } + else if (accessor.HasAttribute("exist") && accessor.FindBoolean("exist") == true) + { + throw new AlreadyExistsException(accessor.GetName().GetNameValue()); + } + else if (accessor.HasAttribute("emails")) + { + object value = ICF.ConnectorAttributeUtil.GetSingleValue(accessor.Find("emails")); + if (value is IDictionary) + { + return new ICF.Uid((string)((IDictionary)value)["email"]); + } + else + { + throw new InvalidAttributeValueException("Expecting Map"); + } + } + return new ICF.Uid(_config.Guid.ToString()); + } + } + + public void Delete(ICF.ObjectClass objectClass, ICF.Uid uid, ICF.OperationOptions options) + { + if (_config.returnNullTest) + { + return; + } + if (_config.IsTestObjectClass(objectClass)) + { + _config.GeObjectCache(objectClass).Delete(uid); + } + else + { + if (null == uid.Revision) + { + throw new PreconditionRequiredException("Version is required for MVCC"); + } + else if (_config.Guid.ToString().Equals(uid.Revision)) + { + // Delete + String a = _config.Guid.ToString(); + String b = _config.Guid.ToString(); + String c = _config.Guid.ToString(); + } + else + { + throw new PreconditionFailedException("Current version of resource is 0 and not match with: " + + uid.Revision); + } + } + } + + public virtual ICF.Uid ResolveUsername(ICF.ObjectClass objectClass, string username, + ICF.OperationOptions options) + { + if (_config.returnNullTest) + { + return null; + } + else + { + return _config.ResolveByUsername(objectClass, username); + } + } + + + public virtual ICF.Schema Schema() + { + if (_config.returnNullTest) + { + return null; + } + else + { + ICF.SchemaBuilder builder = new ICF.SchemaBuilder(SafeType.ForRawType(GetType())); + foreach (string type in _config.testObjectClass) + { + ICF.ObjectClassInfoBuilder classInfoBuilder = new ICF.ObjectClassInfoBuilder(); + classInfoBuilder.ObjectType = type; + classInfoBuilder.AddAttributeInfo(ICF.OperationalAttributeInfos.PASSWORD); + builder.DefineObjectClass(classInfoBuilder.Build()); + } + return builder.Build(); + } + } + + public virtual object RunScriptOnResource(ICF.ScriptContext request, ICF.OperationOptions options) + { + if (_config.returnNullTest) + { + return null; + } + else + { + try + { + Assembly assembly = GetType().Assembly; + List list = assembly.GetReferencedAssemblies().Select(Assembly.Load).ToList(); + + return + ScriptExecutorFactory.NewInstance(request.ScriptLanguage) + .NewScriptExecutor(list.ToArray(), request.ScriptText, true) + .Execute(request.ScriptArguments); + } + catch (Exception e) + { + throw new ConnectorException(e.Message, e); + } + } + } + + public FilterTranslator CreateFilterTranslator(ICF.ObjectClass objectClass, ICF.OperationOptions options) + { + return new FilterTranslatorAnonymousInnerClassHelper(); + } + + private class FilterTranslatorAnonymousInnerClassHelper : FilterTranslator + { + public FilterTranslatorAnonymousInnerClassHelper() + { + } + + public IList Translate(Filter filter) + { + List filters = new List(1); + filters.Add(filter); + return filters; + } + } + + public void ExecuteQuery(ICF.ObjectClass objectClass, Filter query, ICF.ResultsHandler handler, + ICF.OperationOptions options) + { + ICF.SortKey[] sortKeys = options.SortKeys; + if (null == sortKeys) + { + sortKeys = new ICF.SortKey[] { new ICF.SortKey(ICF.Name.NAME, true) }; + } + + // Rebuild the full result set. + SortedSet resultSet = + new SortedSet(new ResourceComparator(sortKeys)); + if (_config.returnNullTest) + { + return; + } + else if (_config.IsTestObjectClass(objectClass)) + { + Filter filter = FilteredResultsHandlerVisitor.WrapFilter(query, _config.caseIgnore); + foreach (var connectorObject in _config.GeObjectCache(objectClass).GetIterable(filter)) + { + resultSet.Add(connectorObject); + } + } + else + { + if (null != query) + { + foreach (ICF.ConnectorObject co in collection.Values) + { + if (query.Accept(co)) + { + resultSet.Add(co); + } + } + } + else + { + resultSet.UnionWith(collection.Values); + } + } + // Handle the results + if (null != options.PageSize) + { + // Paged Search + string pagedResultsCookie = options.PagedResultsCookie; + string currentPagedResultsCookie = options.PagedResultsCookie; + int? pagedResultsOffset = null != options.PagedResultsOffset + ? Math.Max(0, (int)options.PagedResultsOffset) + : 0; + int? pageSize = options.PageSize; + int index = 0; + int pageStartIndex = null == pagedResultsCookie ? 0 : -1; + int handled = 0; + foreach (ICF.ConnectorObject entry in resultSet) + { + if (pageStartIndex < 0 && pagedResultsCookie.Equals(entry.Name.GetNameValue())) + { + pageStartIndex = index + 1; + } + if (pageStartIndex < 0 || index < pageStartIndex) + { + index++; + continue; + } + if (handled >= pageSize) + { + break; + } + if (index >= pagedResultsOffset + pageStartIndex) + { + if (handler.Handle(entry)) + { + handled++; + currentPagedResultsCookie = entry.Name.GetNameValue(); + } + else + { + break; + } + } + index++; + } + + if (index == resultSet.Count) + { + currentPagedResultsCookie = null; + } + + if (handler is SearchResultsHandler) + { + ((SearchResultsHandler)handler).HandleResult(new ICF.SearchResult(currentPagedResultsCookie, ICF.SearchResult.CountPolicy.EXACT, + resultSet.Count, resultSet.Count - index)); + } + } + else + { + // Normal Search + foreach (ICF.ConnectorObject entry in resultSet) + { + if (!handler.Handle(entry)) + { + break; + } + } + if (handler is SearchResultsHandler) + { + ((SearchResultsHandler)handler).HandleResult(new ICF.SearchResult()); + } + } + } + + public void Sync(ICF.ObjectClass objectClass, ICF.SyncToken token, ICF.SyncResultsHandler handler, + ICF.OperationOptions options) + { + if (_config.returnNullTest) + { + return; + } + if (_config.IsTestObjectClass(objectClass)) + { + foreach (ICF.SyncDelta delta in _config.Sync(objectClass, (int?)token.Value)) + { + if (!handler.Handle(delta)) + { + break; + } + } + if (handler is SyncTokenResultsHandler) + { + ((SyncTokenResultsHandler)handler).HandleResult(new ICF.SyncToken(_config.LatestSyncToken)); + } + } + else + { + if (handler is SyncTokenResultsHandler) + { + ((SyncTokenResultsHandler)handler).HandleResult(GetLatestSyncToken(objectClass)); + } + } + } + + public ICF.SyncToken GetLatestSyncToken(ICF.ObjectClass objectClass) + { + if (_config.returnNullTest) + { + return null; + } + else if (_config.IsTestObjectClass(objectClass)) + { + return new ICF.SyncToken(_config.LatestSyncToken); + } + else + { + return new ICF.SyncToken(_config.Guid.ToString()); + } + } + + public void Test() + { + if (_config.failValidation) + { + throw new ConnectorException("test failed " + CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); + } + } + + public ICF.Uid Update(ICF.ObjectClass objectClass, ICF.Uid uid, + ICollection replaceAttributes, ICF.OperationOptions options) + { + if (_config.returnNullTest) + { + return null; + } + else if (_config.IsTestObjectClass(objectClass)) + { + return _config.GeObjectCache(objectClass).Update(uid, replaceAttributes); + } + else + { + throw new System.NotSupportedException("Object Update is not supported: " + + objectClass.GetObjectClassValue()); + } + } + + private static readonly SortedDictionary collection = + new SortedDictionary(StringComparer.InvariantCultureIgnoreCase); + + static TstAbstractConnector() + { + bool enabled = true; + for (int i = 0; i < 100; i++) + { + ICF.ConnectorObjectBuilder builder = new ICF.ConnectorObjectBuilder(); + builder.SetUid(Convert.ToString(i)); + builder.SetName(string.Format("user{0:D3}", i)); + builder.AddAttribute(ICF.ConnectorAttributeBuilder.BuildEnabled(enabled)); + IDictionary mapAttribute = new Dictionary(); + mapAttribute["email"] = "foo@example.com"; + mapAttribute["primary"] = true; + mapAttribute["usage"] = new List() { "home", "work" }; + builder.AddAttribute("emails", mapAttribute); + ICF.ConnectorObject co = builder.Build(); + collection[co.Name.GetNameValue()] = co; + enabled = !enabled; + } + } + } + + #endregion + + #region TstConnector + + [ConnectorClass("TestConnector", + "TestConnector.category", + typeof(TstConnectorConfig), + MessageCatalogPaths = new String[] { "TestBundleV1.Messages" } + )] + public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp + { + private static int _connectionCount = 0; + private MyTstConnection _myConnection; + private TstConnectorConfig _config; + + public ICF.Uid Create(ICF.ObjectClass oclass, ICollection attrs, + ICF.OperationOptions options) + { + int? delay = (int?)CollectionUtil.GetValue(options.Options, "delay", null); + if (delay != null) + { + Thread.Sleep((int)delay); + } + + if (options.Options.ContainsKey("testPooling")) + { + return new ICF.Uid(_myConnection.GetConnectionNumber().ToString()); + } + else + { + String version = GetVersion(); + return new ICF.Uid(version); + } + } + + public void Init(Configuration cfg) + { + _config = (TstConnectorConfig)cfg; + if (_config.resetConnectionCount) + { + _connectionCount = 0; + } + _myConnection = new MyTstConnection(_connectionCount++); + } + + public static String GetVersion() + { + return "1.0"; + } + + public void Dispose() + { + if (_myConnection != null) + { + _myConnection.Dispose(); + _myConnection = null; + } + } + + /// + /// Used by the script tests + /// + public String concat(String s1, String s2) + { + return s1 + s2; + } + + /// + /// Used by the script tests + /// + public void Update() + { + _config.UpdateTest(); + } + + public void CheckAlive() + { + _myConnection.Test(); + } + + private class MyTranslator : AbstractFilterTranslator + { + } + + public FilterTranslator CreateFilterTranslator(ICF.ObjectClass oclass, ICF.OperationOptions options) + { + return new MyTranslator(); + } + + public void ExecuteQuery(ICF.ObjectClass oclass, String query, ICF.ResultsHandler handler, + ICF.OperationOptions options) + { + int remaining = _config.numResults; + for (int i = 0; i < _config.numResults; i++) + { + int? delay = (int?)CollectionUtil.GetValue(options.Options, "delay", null); + if (delay != null) + { + Thread.Sleep((int)delay); + } + ICF.ConnectorObjectBuilder builder = + new ICF.ConnectorObjectBuilder(); + builder.SetUid("" + i); + builder.SetName(i.ToString()); + builder.ObjectClass = oclass; + for (int j = 0; j < 50; j++) + { + builder.AddAttribute("myattribute" + j, "myvaluevaluevalue" + j); + } + ICF.ConnectorObject rv = builder.Build(); + if (handler.Handle(rv)) + { + remaining--; + } + else + { + break; + } + } + + if (handler is SearchResultsHandler) + { + ((SearchResultsHandler)handler).HandleResult(new ICF.SearchResult("", remaining)); + } + } + + public void Sync(ICF.ObjectClass objClass, ICF.SyncToken token, + ICF.SyncResultsHandler handler, + ICF.OperationOptions options) + { + int remaining = _config.numResults; + for (int i = 0; i < _config.numResults; i++) + { + ICF.ConnectorObjectBuilder obuilder = + new ICF.ConnectorObjectBuilder(); + obuilder.SetUid(i.ToString()); + obuilder.SetName(i.ToString()); + obuilder.ObjectClass = (objClass); + + ICF.SyncDeltaBuilder builder = + new ICF.SyncDeltaBuilder(); + builder.Object = (obuilder.Build()); + builder.DeltaType = (ICF.SyncDeltaType.CREATE_OR_UPDATE); + builder.Token = (new ICF.SyncToken("mytoken")); + ICF.SyncDelta rv = builder.Build(); + if (handler.Handle(rv)) + { + remaining--; + } + else + { + break; + } + } + if (handler is SyncTokenResultsHandler) + { + ((SyncTokenResultsHandler)handler).HandleResult(new ICF.SyncToken(remaining)); + } + } + + public ICF.SyncToken GetLatestSyncToken(ICF.ObjectClass objectClass) + { + return new ICF.SyncToken("mylatest"); + } + + public ICF.Schema Schema() + { + ICF.SchemaBuilder builder = new ICF.SchemaBuilder(SafeType.Get()); + for (int i = 0; i < 2; i++) + { + ICF.ObjectClassInfoBuilder classBuilder = new ICF.ObjectClassInfoBuilder(); + classBuilder.ObjectType = ("class" + i); + for (int j = 0; j < 200; j++) + { + classBuilder.AddAttributeInfo(ICF.ConnectorAttributeInfoBuilder.Build("attributename" + j, + typeof(String))); + } + builder.DefineObjectClass(classBuilder.Build()); + } + return builder.Build(); + } + } + + #endregion + + #region TstConnectorConfig + + public class TstConnectorConfig : AbstractConfiguration + { + /// + /// keep lower case for consistent unit tests + /// + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) })] + public string tstField { get; set; } + + /// + /// keep lower case for consistent unit tests + /// + public int numResults { get; set; } + + /// + /// keep lower case for consistent unit tests + /// + public bool failValidation { get; set; } + + /// + /// keep lower case for consistent unit tests + /// + public bool resetConnectionCount { get; set; } + + public override void Validate() + { + if (failValidation) + { + throw new ConnectorException("validation failed " + + CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); + } + } + + public void UpdateTest() + { + tstField = "change"; + NotifyConfigurationUpdate(); + } + } + + #endregion + + #region TstStatefulConnector + + [ConnectorClass("TestStatefulConnector", + "TestStatefulConnector.category", + typeof(TstStatefulConnectorConfig), + MessageCatalogPaths = new String[] { "TestBundleV1.Messages" } + )] + public class TstStatefulConnector : TstAbstractConnector, Connector + { + //public void Init(Configuration cfg) + //{ + // base.Init(cfg); + //} + + public Configuration Configuration + { + get { return _config; } + } + + public void Dispose() + { + _config = null; + } + } + + #endregion + + #region TstStatefulConnectorConfig + + public class TstStatefulConnectorConfig : TstConnectorConfig, StatefulConfiguration + { + public Boolean caseIgnore { get; set; } + + public String[] testObjectClass { get; set; } + + public Boolean returnNullTest { get; set; } + + public String randomString { get; set; } + + private Guid? guid; + + public Guid Guid + { + get + { + lock (this) + { + if (null == guid) + { + guid = Guid.NewGuid(); + } + return (Guid)guid; + } + } + } + + public void Release() + { + guid = null; + } + + public bool IsTestObjectClass(ICF.ObjectClass objectClass) + { + return null != objectClass && null != testObjectClass && + testObjectClass.Contains(objectClass.GetObjectClassValue(), StringComparer.OrdinalIgnoreCase); + } + + public virtual ICF.Uid ResolveByUsername(ICF.ObjectClass objectClass, string username) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null != cache) + { + ConnectorObjectCacheEntry entry = cache.GetByName(username); + if (null != entry) + { + return entry.ConnectorObject.Uid; + } + } + return null; + } + + public virtual ICF.Uid Authenticate(ICF.ObjectClass objectClass, string username, GuardedString password) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null != cache) + { + ConnectorObjectCacheEntry entry = cache.GetByName(username); + if (null != entry) + { + if (entry.Authenticate(password)) + { + return entry.ConnectorObject.Uid; + } + throw new InvalidPasswordException("Invalid Password"); + } + throw new InvalidCredentialException("Unknown username: " + username); + } + throw new InvalidCredentialException("Empty ObjectClassCache: " + objectClass.GetObjectClassValue()); + } + + internal virtual IEnumerable Sync(ICF.ObjectClass objectClass, int? token) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null != cache) + { + return cache.ObjectCache.Values.Where(x => + { + int rev = Convert.ToInt32(x.ConnectorObject.Uid.Revision); + return null == token || rev > token; + }).OrderBy(x => x.ConnectorObject.Uid.Revision).Select(x => + { + var builder = new ICF.SyncDeltaBuilder(); + builder.DeltaType = x.DeltaType; + builder.Token = new ICF.SyncToken(Convert.ToInt32(x.ConnectorObject.Uid.Revision)); + builder.Object = x.ConnectorObject; + return builder.Build(); + }); + } + return Enumerable.Empty(); + } + + private Int32 _revision = 0; + + internal virtual Int32 LatestSyncToken + { + get { return _revision; } + } + + internal virtual ICF.Uid GetNextUid(string uid) + { + return new ICF.Uid(uid, Convert.ToString(Interlocked.Increment(ref _revision))); + } + + private Int32 _id = 0; + + private ICF.Uid NewUid() + { + return GetNextUid(Convert.ToString(Interlocked.Increment(ref _id))); + } + + private readonly ConcurrentDictionary objectCache = + new ConcurrentDictionary(); + + internal virtual ObjectClassCacheEntry GeObjectCache(ICF.ObjectClass objectClass) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null == cache) + { + cache = new ObjectClassCacheEntry(objectClass, NewUid, GetNextUid); + ObjectClassCacheEntry rv = objectCache.GetOrAdd(objectClass, cache); + if (null != rv) + { + cache = rv; + } + } + return cache; + } + + internal class ObjectClassCacheEntry + { + private readonly ICF.ObjectClass _objectClass; + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + + private readonly ConcurrentDictionary _uniqueNameIndex = + new ConcurrentDictionary(); + + internal readonly ConcurrentDictionary ObjectCache = + new ConcurrentDictionary(); + + private readonly Func _newUid; + private readonly Func _getNextUid; + + public ObjectClassCacheEntry(ICF.ObjectClass objectClass, Func newUid, + Func getNextUid) + { + _objectClass = objectClass; + _newUid = newUid; + _getNextUid = getNextUid; + } + + internal virtual ConnectorObjectCacheEntry GetByName(string username) + { + ConnectorObjectCacheEntry entry = null; + string uid; + _uniqueNameIndex.TryGetValue(username, out uid); + if (null != uid) + { + ObjectCache.TryGetValue(uid, out entry); + } + return entry; + } + + public virtual ICF.Uid Create(ICollection createAttributes) + { + ICF.Name name = ICF.ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); + if (name == null) + { + throw new InvalidAttributeValueException("__NAME__ Required"); + } + if (String.IsNullOrWhiteSpace(name.GetNameValue())) + { + throw new InvalidAttributeValueException("__NAME__ can not be blank"); + } + ICF.Uid uid = _newUid(); + + var d = _uniqueNameIndex.GetOrAdd(name.GetNameValue(), uid.GetUidValue()); + if (d == uid.GetUidValue()) + { + var builder = new ICF.ConnectorObjectBuilder { ObjectClass = _objectClass }; + builder.AddAttributes(createAttributes).SetUid(uid); + + ObjectCache.TryAdd(uid.GetUidValue(), + new ConnectorObjectCacheEntry(builder.Build(), _getNextUid)); + return uid; + } + + else + { + throw (new AlreadyExistsException()).InitUid(new ICF.Uid(name.GetNameValue())); + } + } + + public virtual ICF.Uid Update(ICF.Uid uid, ICollection updateAttributes) + { + ConnectorObjectCacheEntry entry = null; + ObjectCache.TryGetValue(uid.GetUidValue(), out entry); + if (null == entry) + { + throw new UnknownUidException(uid, _objectClass); + } + if (_lock.TryEnterWriteLock(new TimeSpan(0, 1, 0))) + { + try + { + IDictionary attributeMap = + CollectionUtil.NewCaseInsensitiveDictionary(); + foreach (ICF.ConnectorAttribute attr in entry.ConnectorObject.GetAttributes()) + { + attributeMap[attr.Name] = attr; + } + foreach (ICF.ConnectorAttribute attribute in updateAttributes) + { + if (attribute.Value == null) + { + attributeMap.Remove(attribute.Name); + } + else + { + attributeMap[attribute.Name] = attribute; + } + } + return entry.Update(attributeMap.Values); + } + finally + { + _lock.ExitWriteLock(); + } + } + else + { + throw new ConnectorException("Failed to acquire lock", + new TimeoutException("Failed to acquire lock")); + } + } + + public virtual void Delete(ICF.Uid uid) + { + ConnectorObjectCacheEntry entry = null; + ObjectCache.TryGetValue(uid.GetUidValue(), out entry); + if (null == entry) + { + throw new UnknownUidException(uid, _objectClass); + } + + + if (_lock.TryEnterWriteLock(new TimeSpan(0, 1, 0))) + { + try + { + entry.Update(entry.ConnectorObject.GetAttributes()); + entry.DeltaType = ICF.SyncDeltaType.DELETE; + } + finally + { + _lock.ExitWriteLock(); + } + } + else + { + throw new ConnectorException("Failed to acquire lock", + new TimeoutException("Failed to acquire lock")); + } + } + + public virtual IEnumerable GetIterable(Filter filter) + { + return + ObjectCache.Values.Where( + x => + !ICF.SyncDeltaType.DELETE.Equals(x.DeltaType) && + (null == filter || filter.Accept(x.ConnectorObject))).Select(x => x.ConnectorObject); + } + } + + + internal class ConnectorObjectCacheEntry + { + internal ICF.SyncDeltaType DeltaType = ICF.SyncDeltaType.CREATE; + + internal ICF.ConnectorObject ConnectorObject { get; set; } + private readonly Func _getNextUid; + + public ConnectorObjectCacheEntry(ICF.ConnectorObject connectorConnectorObject, + Func getNextUid) + { + ConnectorObject = connectorConnectorObject; + _getNextUid = getNextUid; + } + + public virtual bool Authenticate(GuardedString password) + { + ICF.ConnectorAttribute pw = ConnectorObject.GetAttributeByName(ICF.OperationalAttributes.PASSWORD_NAME); + return null != pw && null != password && ICF.ConnectorAttributeUtil.GetSingleValue(pw).Equals(password); + } + + public virtual ICF.Uid Update(ICollection updateAttributes) + { + var builder = new ICF.ConnectorObjectBuilder { ObjectClass = ConnectorObject.ObjectClass }; + builder.AddAttributes(updateAttributes).SetUid(_getNextUid(ConnectorObject.Uid.GetUidValue())); + ConnectorObject = builder.Build(); + DeltaType = ICF.SyncDeltaType.UPDATE; + return ConnectorObject.Uid; + } + } + } + + #endregion + + #region TstStatefulPoolableConnector + + [ConnectorClass("TestStatefulPoolableConnector", + "TestStatefulPoolableConnector.category", + typeof(TstStatefulConnectorConfig), + MessageCatalogPaths = new String[] { "TestBundleV1.Messages" } + )] + public class TstStatefulPoolableConnector : TstAbstractConnector, PoolableConnector + { + //public void Init(Configuration cfg) + //{ + // base.Init(cfg); + //} + + public Configuration Configuration + { + get { return _config; } + } + + public void Dispose() + { + _config = null; + } + + public void CheckAlive() + { + } + } + + #endregion +} \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV2/AssemblyInfo.cs b/dotnet/framework/TestBundles/TestBundleV2/AssemblyInfo.cs new file mode 100644 index 00000000..f35521a1 --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV2/AssemblyInfo.cs @@ -0,0 +1,54 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +#region Using directives + +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// 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("TestBundleV2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestBundleV2")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("2.0.0.0")] + diff --git a/dotnet/framework/TestBundles/TestBundleV2/TestBundleV2.csproj b/dotnet/framework/TestBundles/TestBundleV2/TestBundleV2.csproj new file mode 100644 index 00000000..a7ed629f --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV2/TestBundleV2.csproj @@ -0,0 +1,85 @@ + + + + + {3E737796-3A83-4924-9FF1-DC542F21F59E} + Debug + AnyCPU + Library + TestBundleV2 + TestBundleV2.Connector + v4.5.2 + OnBuildSuccess + + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + false + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + false + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + \ No newline at end of file diff --git a/dotnet/framework/TestBundles/TestBundleV2/TestConnector.cs b/dotnet/framework/TestBundles/TestBundleV2/TestConnector.cs new file mode 100644 index 00000000..13823a41 --- /dev/null +++ b/dotnet/framework/TestBundles/TestBundleV2/TestConnector.cs @@ -0,0 +1,63 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace org.identityconnectors.testconnector +{ + public class TstConnectorConfig : AbstractConfiguration + { + public override void Validate() + { + } + } + + [ConnectorClass("TestConnector", + typeof(TstConnectorConfig), + MessageCatalogPaths = new String[] { "TestBundleV2.Messages" } + )] + public class TstConnector : CreateOp, Connector, SchemaOp + { + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) + { + String version = "2.0"; + return new Uid(version); + } + public void Init(Configuration cfg) + { + } + + public void Dispose() + { + + } + + public Schema Schema() + { + return null; + } + } +} diff --git a/dotnet/framework/TestCommon/FrameworkInternalBridge.cs b/dotnet/framework/TestCommon/FrameworkInternalBridge.cs new file mode 100644 index 00000000..aad9f9ea --- /dev/null +++ b/dotnet/framework/TestCommon/FrameworkInternalBridge.cs @@ -0,0 +1,58 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Reflection; +using Org.IdentityConnectors.Common; + +namespace Org.IdentityConnectors.Test.Common +{ + internal static class FrameworkInternalBridge + { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + /// + /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static SafeType LoadType(String typeName) where T : class + { + + Assembly assembly; + lock (LOCK) + { + if (_assembly == null) + { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return SafeType.ForRawType(assembly.GetType(typeName, true)); + + } + } +} diff --git a/dotnet/framework/TestCommon/PropertyBag.cs b/dotnet/framework/TestCommon/PropertyBag.cs new file mode 100644 index 00000000..582ad3a6 --- /dev/null +++ b/dotnet/framework/TestCommon/PropertyBag.cs @@ -0,0 +1,367 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace Org.IdentityConnectors.Test.Common +{ + /// + /// Encapsulates a read-only bag of properties, which can be accessed in a type-safe manner. + /// + /// + /// The simplest way to obtain a required (i.e., the property must be in the bag, otherwise an exception is thrown) + /// property value is . + /// If the property is not a required one, the method can be used, + /// which also takes a default value which is returned when the property is not present in the bag. + /// + public sealed class PropertyBag + { + #region Member variables + private readonly Dictionary _bag; + #endregion + + #region Constructors + /// + /// Initializes a new instance of the class with the properties. + /// + /// The properties contained in the bag. + internal PropertyBag(IDictionary bag) + { + _bag = new Dictionary(bag); + } + #endregion + + #region Methods + /// + /// Gets the value of a required property defined by in a type-safe manner. + /// + /// The type of the property to get. + /// The name of the property. + /// The value of the property in bag; the value might be null. + /// Thrown when no property found defined by + /// Thrown when the property exists, but its value is not of type . + /// See for details on the types that can be defined in . + public T GetProperty(string name) + { + if (!_bag.ContainsKey(name)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, @"Property named ""{0}"" not found in bag.", name)); + } + + return CastValue(name); + } + + /// + /// Casts the value of a property defined by to the type . + /// + /// The type, the property must be cast to. + /// The name of the property. + /// The value of the property in bag cast to type ; the value might be null. + /// Thrown when the value of the property is not of type . + /// The property must be in the bag! + /// + /// The .Net BCL does not provide wrappers for primitive classes, hence to make sure that a property containing a null value + /// cannot break your code use a nullable primitive type. + /// For example: + /// + /// PropertyBag bag = TestHelpers.GetProperties(typeof(yourConnector)); + /// int? mightBeNull = bag.GetProperty("property name"); + /// + /// + /// + /// + private T CastValue(string name) + { + object value = _bag[name]; + + //if the value of the property is null then just return null, there is no need for type checking + if ((value != null) && + !(value is T)) //otherwise check if value is an instance of T + { + throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, + @"Property named ""{0}"" is of type ""{1}"" but expected type was ""{2}""", + name, value.GetType(), typeof(T))); + } + + return (T)value; + } + + /// + /// Gets a property value, returning a default value when no property with the specified name exists in the bag. + /// + /// The type of the property to get. + /// The name of the property. + /// The default value returned when no property with the specified name exists in the bag. + /// The value of the property in bag cast to type or the default value ; + /// the value might be null. + /// Thrown when the property exists, but its value is not of type . + /// See for details on the types that can be defined in . + public T GetProperty(string name, T def) + { + if (!_bag.ContainsKey(name)) + { + return def; + } + return CastValue(name); + } + + /// + /// Gets a property value, returning a default value when no property with the specified name exists in the bag. + /// + /// The name of the property. + /// The type of the property to get. + /// The default value returned when no property with the specified name exists in the bag. + /// The value of the property in bag cast to type or the default value ; + /// the value might be null. + /// Thrown when the property exists, but its value is not of type . + public dynamic GetProperty(string name, Type type, dynamic def) + { + if (!_bag.ContainsKey(name)) + { + return Convert.ChangeType(def, type); + } + object value = _bag[name]; + //if the value of the property is null then just return null, there is no need for type checking + if ((value != null) && + !(value.GetType() == type)) //otherwise check if value is an instance of T + { + if (type.IsArray) + { + var listType = typeof(List<>); + var constructedListType = listType.MakeGenericType(type.GetElementType()); + var instance = (IList)Activator.CreateInstance(constructedListType); + + var listValue = value as List; + if (listValue != null) + { + listValue.ForEach(o => instance.Add(ConvertFromString(o, type.GetElementType()))); + } + else + { + var singleSource = value as string; + if (singleSource != null) + { + instance.Add(ConvertFromString(singleSource, type.GetElementType())); + } + else + { + throw new NotSupportedException("The conversion cannot be performed."); + } + } + + object newValue = Activator.CreateInstance(type, new object[] { instance.Count }); + + instance.CopyTo((Array)newValue, 0); + return newValue; + + // return Convert.ChangeType(value, type); + // var newValue = Activator.CreateInstance(type, new object[] {1}); + // return Array.ConvertAll(new string[] {value}, null); + + } + else + { + if (value is ICollection) + { + throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, + @"Property named ""{0}"" is of type ""{1}"" but expected type was ""{2}""", + name, value.GetType(), type)); + } + else + { + // Convert simple value + return ConvertFromString((String)value, type); + } + } + } + return value; + } + + private object ConvertFromString(string sourceValue, Type target) + { + + if (typeof(string) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(long) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(long?) == target) + { + if (StringUtil.IsBlank(sourceValue)) + { + return null; + } + else + { + return Convert.ChangeType(sourceValue, typeof(long)); + } + } + else if (typeof(char) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(char?) == target) + { + if (StringUtil.IsBlank(sourceValue)) + { + return null; + } + else + { + return Convert.ChangeType(sourceValue, typeof(char)); + } + } + else if (typeof(double) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(double?) == target) + { + if (StringUtil.IsBlank(sourceValue)) + { + return null; + } + else + { + return Convert.ChangeType(sourceValue, typeof(double)); + } + } + else if (typeof(float) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(float?) == target) + { + if (StringUtil.IsBlank(sourceValue)) + { + return null; + } + else + { + return Convert.ChangeType(sourceValue, typeof(float)); + } + } + else if (typeof(int) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(int?) == target) + { + if (StringUtil.IsBlank(sourceValue)) + { + return null; + } + else + { + return Convert.ChangeType(sourceValue, typeof(int)); + } + } + else if (typeof(bool) == target) + { + return Convert.ChangeType(sourceValue, target); + } + else if (typeof(bool?) == target) + { + if (StringUtil.IsBlank(sourceValue)) + { + return null; + } + else + { + return Convert.ChangeType(sourceValue, typeof(bool)); + } + } + else if (typeof(Uri) == target) + { + return new Uri(sourceValue); + } + else if (typeof(FileName) == target) + { + return new FileName(sourceValue); + } + else if (typeof(GuardedByteArray) == target) + { + var result = new GuardedByteArray(); + System.Text.Encoding.UTF8.GetBytes(sourceValue).ToList().ForEach(result.AppendByte); + return result; + } + else if (typeof(GuardedString) == target) + { + var result = new GuardedString(); + sourceValue.ToCharArray().ToList().ForEach(result.AppendChar); + return result; + } + else if (typeof(Script) == target) + { + + int i = sourceValue.IndexOf('|'); + if (i > 0 && i < sourceValue.Length) + { + var scriptLanguage = sourceValue.Substring(0, i); + var scriptText = sourceValue.Substring(i + 1); + return new ScriptBuilder { ScriptLanguage = scriptLanguage, ScriptText = scriptText }.Build(); + } + else + { + throw new FormatException("Expected format is 'ScriptLanguage|ScriptText'"); + } + } + throw new NotSupportedException("The conversion cannot be performed."); + } + + /// + /// Gets a required property value known to be of type . + /// + /// The name of the property. + /// The value of the property in bag; the value might be null. + /// Thrown when no property found defined by + /// Thrown when the property exists, but its value is not of type . + /// The method expects that the value is an instance of . It does not attempt to + /// call on the value. + public string GetStringProperty(string name) + { + return GetProperty(name); + } + + /// + /// Returns a key-value pair collection that represents the current . + /// + /// A instance that represents the current . + internal IDictionary ToDictionary() + { + return CollectionUtil.NewDictionary(_bag); + } + #endregion + } +} diff --git a/dotnet/framework/TestCommon/Test.cs b/dotnet/framework/TestCommon/Test.cs new file mode 100644 index 00000000..88c43bc2 --- /dev/null +++ b/dotnet/framework/TestCommon/Test.cs @@ -0,0 +1,566 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Test.Common.Spi; +using System.Globalization; +using System.Xml.Schema; + +namespace Org.IdentityConnectors.Test.Common +{ + /// + /// which stores all connector objects into + /// list retrievable with . + /// + public sealed class ToListResultsHandler + { + private IList _objects + = new List(); + + public IList Objects + { + get + { + return _objects; + } + } + + public ResultsHandler ResultsHandler + { + get + { + return new ResultsHandler + { + Handle = obj => + { + _objects.Add(obj); + return true; + } + }; + } + } + } + + /// + /// Bag of utility methods useful to connector tests. + /// + public sealed class TestHelpers + { + + private TestHelpers() + { + } + + /// + /// Method for convenient testing of local connectors. + /// + public static APIConfiguration CreateTestConfiguration(SafeType clazz, + Configuration config) + { + return GetSpi().CreateTestConfiguration(clazz, config); + } + + /// + /// Method for convenient testing of local connectors. + /// + public static APIConfiguration CreateTestConfiguration(SafeType clazz, + PropertyBag configData, string prefix) + { + return GetSpi().CreateTestConfiguration(clazz, configData, prefix); + } + + /// + /// Fills a configuration bean with data from the given map. + /// + /// + /// The map + /// keys are configuration property names and the values are + /// configuration property values. + /// + /// the configuration bean. + /// the map with configuration data. + public static void FillConfiguration(Configuration config, + IDictionary configData) + { + GetSpi().FillConfiguration(config, configData); + } + + /// + /// Creates an dummy message catalog ideal for unit testing. + /// + /// + /// All messages are formatted as follows: + /// + /// message-key: arg0.toString(), ..., argn.toString + /// + /// + /// A dummy message catalog. + public static ConnectorMessages CreateDummyMessages() + { + return GetSpi().CreateDummyMessages(); + } + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter) + { + return SearchToList(search, oclass, filter, null); + } + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter, + OperationOptions options) + { + ToListResultsHandler handler = new + ToListResultsHandler(); + search.Search(oclass, filter, handler.ResultsHandler, options); + return handler.Objects; + } + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The options - may be null - will + /// be cast to an empty OperationOptions + /// The list of results. + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter) where T : class + { + return SearchToList(search, oclass, filter, null); + } + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The options - may be null - will + /// be cast to an empty OperationOptions + /// The list of results. + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter, + OperationOptions options) where T : class + { + ToListResultsHandler handler = new + ToListResultsHandler(); + Search(search, oclass, filter, handler.ResultsHandler, options); + return handler.Objects; + } + + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The result handler + /// The options - may be null - will + /// be cast to an empty OperationOptions + public static void Search(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class + { + GetSpi().Search(search, oclass, filter, handler, options); + } + + //At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Test.TestHelpersImpl"; + private static readonly object LOCK = new object(); + private static TestHelpersSpi _instance; + + /// + /// Returns the instance of this factory. + /// + /// The instance of this factory + private static TestHelpersSpi GetSpi() + { + lock (LOCK) + { + if (_instance == null) + { + SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = type.CreateInstance(); + } + return _instance; + } + } + + private static Dictionary _propertyBags = new Dictionary(); + private const string ConfigFileName = "config.xml"; + private static readonly object ConfigurationLock = new object(); + private const string LoadConfigErrorMessage = @"TestHelpers: Unable to load optional XML configuration file ""{0}"". Exception: {1}"; + + /// + /// The name of the environment variable that contains the name of a certain configuration from which the + /// test configuration properties to be loaded besides the general properties. + /// + internal const string TestConfigEVName = "TEST_CONFIG"; + + /// + /// The name of the environment variable that contains the path of the root directory to the private configurations. + /// + internal const string PrivateConfigRootEVName = "PRIVATE_CONFIG_ROOT"; + + /// + /// The name of the environment variable, the value of which is the location of the current user's profile directory. + /// + internal const string UserProfileEVName = "USERPROFILE"; + + /// + /// Gets the configuration properties for a specified type. + /// + /// + /// + /// Gets the configuration properties for the specified from the provided . + /// + /// The type, the fully qualified name (FQN) of which to be used to identify the configuration. + /// The assembly, that contains the public configuration properties. Recommended to be the test project. + /// Bag of properties for the specified and optionally set configuration. + /// The properties are loaded from public and private configuration files, the former as manifest resources, the + /// latter as file system entries by using the specified as root prefix. Optionally, a certain test setup + /// can be used by defining the "TEST_CONFIG" environment variable that can be used to override the general configuration properties. Both + /// public and private configurations can be overridden with a certain test setup. + /// + /// Public configuration properties are loaded as manifest resources from the specified according + /// to the following: + /// + /// + /// Load the general properties from a resource, the name of which is constructed as follows: + /// type.FullName + ".config.config.xml". To achieve this, you need to create a new folder + /// in your test project named as the FQN of the specified , then create a folder called + /// "config", at last add the configuration file called "config.xml" to this folder and set its "Build Action" + /// property to "Embedded Resource". + /// + /// + /// + /// Load the configuration specific properties from a resource, the name of which is constructed + /// as follows: + /// type.FullName + ".config."+ Environment.GetEnvironmentVariable("TEST_CONFIG") + ".config.xml". + /// To achieve this, you need to create a new folder underneath the previously created "config" folder, its name + /// is defined by the configuration name and add the "config.xml" to this particular folder with "Build Action" + /// property set to "Embedded Resource". + /// + /// + /// + /// + /// + /// The private configuration properties are loaded from the file system as follows: + /// + /// + /// Load the general properties from a file, the path of which is constructed as follows: + /// Environment.GetEnvironmentVariable("PRIVATE_CONFIG_ROOT") + "\config\" + type.FullName + "\config-private\config.xml" + /// + /// + /// + /// Load the configuration specific properties from a file, the path of which is constructed as follows: + /// Environment.GetEnvironmentVariable("PRIVATE_CONFIG_ROOT") + "\config\" + type.FullName + "\config-private\" + + /// Environment.GetEnvironmentVariable("TEST_CONFIG") + "\config.xml" + /// + /// + /// + /// NOTE that if the "PRIVATE_CONFIG_ROOT" environment variable is not defined, it will be replaced in the path with the default root + /// which points to a directory of the current user's profile container, the path of which is constructed as follows: + /// Environment.GetEnvironmentVariable("USERPROFILE") + "\.connectors\" + type.Assembly.GetName().Name + /// For example: c:\Users\Administrator\.connectors\CONNECTOR_CONTAINER_ASSEMBLY_NAME\ + /// + /// + /// Thrown when the root directory of the private configuration cannot be determined. + public static PropertyBag GetProperties(Type type, Assembly assembly) + { + lock (ConfigurationLock) + { + PropertyBag bag; + if (_propertyBags.ContainsKey(type.FullName)) + { + bag = _propertyBags[type.FullName]; + } + else + { + bag = LoadProperties(type, assembly); + _propertyBags.Add(type.FullName, bag); + } + return bag; + } + } + + /// + /// Gets the configuration properties for the specified from the calling assembly. + /// + /// The type, the fully qualified name (FQN) of which to be used to identify the configuration. + /// Bag of properties for the specified and optionally set configuration. + /// See for details of the property loading mechanism. + public static PropertyBag GetProperties(Type type) + { + return GetProperties(type, Assembly.GetCallingAssembly()); + } + + /// + /// Loads the properties from public and private configurations, optionally from a certain configuration defined by + /// "TEST_CONFIG" environment variable. + /// + /// The type, the fully qualified name (FQN) of which to be used to identify the configuration. + /// The assembly, that contains the configuration resources. + /// Bag of properties for the specified . + /// Thrown when the root directory of the private configuration cannot be determined. + static PropertyBag LoadProperties(Type type, Assembly assembly) + { + string bagName = type.FullName; + string configFilePath = string.Empty; + IDictionary properties = null; + var ret = new Dictionary(); + + //load the general public properties file + configFilePath = string.Format(CultureInfo.InvariantCulture, "{0}.config.{1}", bagName, ConfigFileName); + properties = LoadConfigurationFromResource(assembly, configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); + + //load the configuration specific public properties file + string configurationName = Environment.GetEnvironmentVariable(TestConfigEVName); + if (!StringUtil.IsBlank(configurationName)) + { + configFilePath = string.Format(CultureInfo.InvariantCulture, "{0}.config.{1}.{2}", bagName, + configurationName, ConfigFileName); + properties = LoadConfigurationFromResource(assembly, configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); + } + + //determine the root directory of the private properties files + string privateConfigRoot = string.Empty; + if (Environment.GetEnvironmentVariable(PrivateConfigRootEVName) != null) + { + privateConfigRoot = Environment.GetEnvironmentVariable(PrivateConfigRootEVName); + } + else + { + if (Environment.GetEnvironmentVariable(UserProfileEVName) != null) + { + privateConfigRoot = Path.Combine(Environment.GetEnvironmentVariable(UserProfileEVName), + Path.Combine(".connectors", type.Assembly.GetName().Name)); + } + else + throw new InvalidOperationException( + @"Neither the ""PRIVATE_CONFIG_ROOT"" nor the ""USERPROFILE"" environment variable is defined."); + } + + privateConfigRoot = Path.Combine(privateConfigRoot, "config"); + + //load the general private properties file + configFilePath = Path.Combine(Path.Combine(Path.Combine(privateConfigRoot, bagName), "config-private"), ConfigFileName); + properties = LoadConfigurationFromFile(configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); + + // load the configuration specific private properties file + if (!StringUtil.IsBlank(configurationName)) + { + configFilePath = Path.Combine(Path.Combine(Path.Combine(Path.Combine(privateConfigRoot, bagName), + "config-private"), configurationName), ConfigFileName); + properties = LoadConfigurationFromFile(configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); + } + return new PropertyBag(ret); + } + + /// + /// Loads the configuration properties from the specified . + /// + /// The config file path. + /// A property name-value pair collection representing the configuration. + private static IDictionary LoadConfigurationFromFile(string filePath) + { + IDictionary properties = null; + try + { + if (File.Exists(filePath)) + { + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + properties = ReadConfiguration(stream); + } + } + else + { + Trace.TraceWarning(@"The configuration file on path ""{0}"" is not found.", filePath); + } + } + catch (Exception e) + { + Trace.TraceInformation(LoadConfigErrorMessage, filePath, e); + } + return properties; + } + + /// + /// Loads the configuration properties from a resource defined by . + /// + /// The assembly, that contains the resource. + /// The name of the resource. + /// A property name-value pair collection representing the configuration. + private static IDictionary LoadConfigurationFromResource(Assembly assembly, string configResName) + { + IDictionary properties = null; + try + { + //the default namespace with which the resource name starts is not known, therefore + //it must be looked up in the manifest resource list + var resourceName = (from name in assembly.GetManifestResourceNames() + where name.EndsWith(configResName, StringComparison.InvariantCultureIgnoreCase) + select name).FirstOrDefault(); + if (!StringUtil.IsBlank(resourceName)) + { + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream == null) + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + @"Although, the configuration file called ""{0}"" exists, it cannot be accessed.", + configResName)); + } + properties = ReadConfiguration(stream); + } + } + else + { + Trace.TraceWarning(@"The configuration resource called ""{0}"" is not found.", configResName); + } + } + catch (Exception e) + { + Trace.TraceInformation(LoadConfigErrorMessage, configResName, e); + } + return properties; + } + + /// + /// Reads the configuration properties from the provided stream. + /// + /// The stream containing the configuration properties. + /// A property name-value pair collection representing the configuration. + /// The configuration properties are stored in XML format. The stream, that is opened for reading or it can be read, + /// has to contain the configuration properties in XML that adheres to the config.xsd schema embedded to + /// the assembly of this project. + /// + /// For example: + /// + /// + /// + /// + /// ]]> + /// + /// + /// Thrown when the XSD used for validating the configuration is not found in the manifest. + /// Thrown when the contains XML that does not adhere to the schema. + internal static IDictionary ReadConfiguration(Stream configStream) + { + var properties = new Dictionary(); + //validate the XML configuration against the XSD + var schemaSet = new XmlSchemaSet(); + var schemaStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Org.IdentityConnectors.Test.Common.config.xsd"); + if (schemaStream == null) + { + throw new InvalidOperationException(@"The schema used for validating of the configuration file is not found."); + } + var schemaReader = XmlReader.Create(schemaStream); + schemaSet.Add(null, schemaReader); + + //load the reader with the data stream and ignore all white space nodes + var readerSettings = new XmlReaderSettings + { + IgnoreWhitespace = true, + ValidationType = ValidationType.Schema, + Schemas = schemaSet + }; + + using (var reader = XmlReader.Create(configStream, readerSettings)) + { + //read all the nodes and extract the ones that contain properties + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && + reader.Name.Equals("property")) + { + string name = reader.GetAttribute("name"); + string xmlValue = reader.GetAttribute("value"); + if (!StringUtil.IsBlank(name) && xmlValue != null) + { + if (properties.ContainsKey(name)) + { + var value = properties[name]; + if (value is ICollection) + { + (value as ICollection).Add(xmlValue); + } + else + { + var collectionValue = new List(); + collectionValue.Add((string)properties[name]); + collectionValue.Add(xmlValue); + properties[name] = collectionValue; + } + } + else + { + properties[name] = xmlValue; + } + + } + } + } + } + return properties; + } + } +} diff --git a/dotnet/framework/TestCommon/TestCommon.csproj b/dotnet/framework/TestCommon/TestCommon.csproj new file mode 100644 index 00000000..54052cf8 --- /dev/null +++ b/dotnet/framework/TestCommon/TestCommon.csproj @@ -0,0 +1,78 @@ + + + + Debug + AnyCPU + 2.0 + {E6A207D2-E083-41BF-B522-D9D3EC09323E} + Library + Properties + Org.IdentityConnectors.Test.Common + TestCommon + v4.5.2 + 512 + FrameworkTests + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + true + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + 4.0 + + + 4.0 + + + 4.0 + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/framework/TestCommon/TestHelpersSpi.cs b/dotnet/framework/TestCommon/TestHelpersSpi.cs new file mode 100644 index 00000000..5bea0ccb --- /dev/null +++ b/dotnet/framework/TestCommon/TestHelpersSpi.cs @@ -0,0 +1,58 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ +using System.Collections.Generic; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Test.Common.Spi +{ + /// + /// Private use only, do not implement! Use the methods in + /// instead. + /// + public interface TestHelpersSpi + { + APIConfiguration CreateTestConfiguration(SafeType clazz, + Configuration config); + + APIConfiguration CreateTestConfiguration(SafeType clazz, + PropertyBag configData, string prefix); + + void FillConfiguration(Configuration config, + IDictionary configData); + + SearchResult Search(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class; + + ConnectorMessages CreateDummyMessages(); + } +} diff --git a/dotnet/framework/TestCommon/config.xsd b/dotnet/framework/TestCommon/config.xsd new file mode 100644 index 00000000..7063d0c5 --- /dev/null +++ b/dotnet/framework/TestCommon/config.xsd @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + diff --git a/dotnet/framework/TestCommon/version.template b/dotnet/framework/TestCommon/version.template new file mode 100644 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/TestCommon/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/WcfServiceLibrary/ConnectorServerService.cs b/dotnet/framework/WcfServiceLibrary/ConnectorServerService.cs new file mode 100755 index 00000000..fe3a4881 --- /dev/null +++ b/dotnet/framework/WcfServiceLibrary/ConnectorServerService.cs @@ -0,0 +1,514 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Diagnostics; +using System.IdentityModel.Claims; +using System.IdentityModel.Policy; +using System.IdentityModel.Selectors; +using System.IdentityModel.Tokens; +using System.Linq; +using System.Net.WebSockets; +using System.Security.Principal; +using System.ServiceModel; +using System.ServiceModel.Activation; +using System.ServiceModel.Channels; +using System.ServiceModel.Description; +using System.ServiceModel.Dispatcher; +using System.ServiceModel.Security; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Org.ForgeRock.OpenICF.Common.ProtoBuf; +using Org.ForgeRock.OpenICF.Common.RPC; +using Org.ForgeRock.OpenICF.Framework.Remote; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; + +namespace Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary +{ + [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] + public class WcfWebsocket : WebSocketConnectionHolder, IWebSocketService + { + private readonly IWebSocketCallback _callback; + private ConnectionPrincipal _principal; + + //WCF uses this method when it's public + private static void Configure(ServiceConfiguration configuration) + { + Trace.TraceInformation("Blank Configuration"); + } + + public WcfWebsocket() + { + _callback = OperationContext.Current.GetCallbackChannel(); + OperationContext.Current.InstanceContext.Closed += InstanceContext_Closed; + Task.Factory.StartNew(WriteMessageAsync); + } + + private void InstanceContext_Closed(object sender, EventArgs e) + { + Trace.TraceInformation("Session closed here"); + _principal.OperationMessageListener.OnClose(this, 1000, ""); + } + + public async Task OnMessage(Message message) + { + if (message == null) + { + throw new ArgumentNullException("message"); + } + + WebSocketMessageProperty property = + (WebSocketMessageProperty)message.Properties[WebSocketMessageProperty.Name]; + + + if (!message.IsEmpty) + { + byte[] body = message.GetBody(); + if (body == null || body.Length == 0) // Connection open message + { + // _principal.OperationMessageListener.OnConnect(this); + } + else if (property.MessageType == WebSocketMessageType.Text) + { + string content = Encoding.UTF8.GetString(body); + _principal.OperationMessageListener.OnMessage(this, content); + } + else if (property.MessageType == WebSocketMessageType.Binary) + { + _principal.OperationMessageListener.OnMessage(this, body); + } + else + { + //Close Message + //TODO Debug the close reason + _principal.OperationMessageListener.OnClose(this, 1000, ""); + } + } + await Task.Yield(); + } + + public void OnOpen() + { + Trace.TraceInformation("WCFWebsocket is Opened"); + _principal = Thread.CurrentPrincipal as ConnectionPrincipal; + if (_principal == null) + { + throw new Exception("Unauthenticated"); + } + _principal.OperationMessageListener.OnConnect(this); + } + + + protected override async Task WriteMessageAsync(byte[] entry, WebSocketMessageType messageType) + { + Message message = ByteStreamMessage.CreateMessage( + new ArraySegment(entry)); + message.Properties[WebSocketMessageProperty.Name] = + new WebSocketMessageProperty { MessageType = messageType }; + await _callback.OnMessage(message); + } + + private RemoteOperationContext _context; + + protected override void Handshake(HandshakeMessage message) + { + _context = _principal.Handshake(this, message); + + if (null != _context) + { + Trace.TraceInformation("Client Connection Handshake succeeded"); + } + else + { + Trace.TraceError("Client Connection Handshake failed - Close Connection"); + TryClose(); + } + } + + protected override void TryClose() + { + OperationContext.Current.Channel.Close(); + } + + public override bool Operational + { + get + { + var communicationObject = _callback as ICommunicationObject; //IContextChannel + return communicationObject != null && communicationObject.State == CommunicationState.Opened; + } + } + + public override RemoteOperationContext RemoteConnectionContext + { + get { return _context; } + } + } + + #region ConnectorServiceHostFactory + + public class ConnectorServiceHostFactory : ServiceHostFactory + { + private const string PropKey = "connectorserver.key"; + + private readonly ClientAuthenticationValidator _authenticator; + + public ConnectorServiceHostFactory(ClientAuthenticationValidator authenticator) + { + if (authenticator == null) + { + throw new ArgumentNullException("authenticator"); + } + _authenticator = authenticator; + } + + public ConnectorServiceHostFactory() + { + NameValueCollection settings = ConfigurationManager.AppSettings; + String authenticatorType = settings.Get(typeof(ClientAuthenticationValidator).FullName); + if (String.IsNullOrEmpty(authenticatorType)) + { + throw new Exception("Missing required app configuration: " + + typeof(ClientAuthenticationValidator).FullName); + } + + var type = AppDomain.CurrentDomain.GetAssemblies() + .Where(a => !a.IsDynamic) + .SelectMany(a => a.GetTypes()) + .FirstOrDefault(t => t.FullName.Equals(authenticatorType)); + if (type != null) + { + _authenticator = (ClientAuthenticationValidator)Activator.CreateInstance(type); + //Add Principal and SharedKey + String keyHash = settings.Get(PropKey); + if (null != keyHash) + { + _authenticator.Add(new SingleTenantPrincipal(new ConnectorFramework()), keyHash); + } + } + else + { + Trace.TraceWarning("Type not found: " + authenticatorType); + } + + if (null == _authenticator) + { + throw new ArgumentNullException(authenticatorType); + } + } + + public ClientAuthenticationValidator ClientAuthenticationValidator + { + get { return _authenticator; } + } + + protected override ServiceHost CreateServiceHost(Type serviceType, + Uri[] baseAddresses) + { + return new ConnectorServiceHost(_authenticator, baseAddresses); + } + } + + #endregion + + public class ConnectorServiceHost : ServiceHost + { + public ConnectorServiceHost(ClientAuthenticationValidator validator, params Uri[] baseAddresses) + : base(typeof(WcfWebsocket), baseAddresses) + { + if (validator == null) + { + throw new ArgumentNullException("validator"); + } + + // Add a custom authentication validator + Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom; + Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = validator; + + // Add a custom authorization policy + var policies = new List { validator }; + Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom; + Authorization.ExternalAuthorizationPolicies = policies.AsReadOnly(); + + /* + foreach (var cd in ImplementedContracts.Values) + { + cd.Behaviors.Add(new WebSocketServiceInstanceProvider()); + }*/ + } + + protected override void OnClosed() + { + base.OnClosed(); + IDisposable disposable = Credentials.UserNameAuthentication.CustomUserNamePasswordValidator as IDisposable; + if (null != disposable) + { + disposable.Dispose(); + } + } + } + + #region ClientAuthenticationValidator + + public class ClientAuthenticationValidator : UserNamePasswordValidator, IAuthorizationPolicy, IDisposable + { + private readonly ConcurrentDictionary> _tenantsDictionary = + new ConcurrentDictionary>(); + + public bool Add(ConnectionPrincipal tenant, String secret) + { + var pair = new Pair(tenant, secret); + if (_tenantsDictionary.TryAdd(tenant.Identity.Name, pair)) + { + tenant.Disposed += (sender, args) => + { + IPrincipal principal = sender as IPrincipal; + Pair ignore; + if (principal != null) _tenantsDictionary.TryRemove(principal.Identity.Name, out ignore); + }; + return true; + } + return false; + } + + public override void Validate(string userName, string password) + { + // validate arguments + if (String.IsNullOrEmpty(userName)) + throw new FaultException("Unknown null or empty userName"); + if (String.IsNullOrEmpty(password)) + throw new FaultException("Unknown null or empty password"); + + Pair principal = FindPrincipal(userName); + + if (principal == null) + { + throw new SecurityTokenException("Unknown username"); + } + + if (!Verify(principal.Second, password)) + { + throw new SecurityTokenException("Unknown password"); + } + } + + public Pair FindPrincipal(String userName) + { + Pair principal; + _tenantsDictionary.TryGetValue(userName, out principal); + return principal; + } + + public static bool Verify(String principalKey, String password) + { + GuardedString key = new GuardedString(); + password.ToCharArray().ToList().ForEach(p => key.AppendChar(p)); + try + { + return key.VerifyBase64SHA1Hash(principalKey); + } + finally + { + key.Dispose(); + } + } + + #region IAuthorizationPolicy Members + + private string _id; + + public string Id + { + get { return _id ?? (_id = Guid.NewGuid().ToString()); } + } + + public bool Evaluate(EvaluationContext evaluationContext, ref object state) + { + // get the authenticated client identity + IIdentity client = GetClientIdentity(evaluationContext); + + String evaluated = state as String; + if (null == evaluated || !client.Name.Equals(evaluated)) + { + // set the custom principal + Pair principal; + + _tenantsDictionary.TryGetValue(client.Name, out principal); + if (principal != null) + { + evaluationContext.Properties["Principal"] = principal.First; + state = principal.First.Identity.Name; + } + } + return true; + } + + private IIdentity GetClientIdentity(EvaluationContext evaluationContext) + { + object obj; + if (!evaluationContext.Properties.TryGetValue("Identities", out obj)) + throw new Exception("No Identity found"); + IList identities = obj as IList; + if (identities == null || identities.Count <= 0) + throw new Exception("No Identity found"); + return identities[0]; + } + + public ClaimSet Issuer + { + get { return ClaimSet.System; } + } + + #endregion + + public void Dispose() + { + foreach (var key in _tenantsDictionary.Keys) + { + Pair pair; + _tenantsDictionary.TryRemove(key, out pair); + if (null == pair) continue; + try + { + pair.First.Dispose(); + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to dispose ConnectionPrincipal:" + key, e); + } + } + } + } + + #endregion + + #region SingleTenant + + public class SingleTenantPrincipal : ConnectionPrincipal + { + private readonly ConnectorFramework _connectorFramework; + private readonly Timer _timer; + + public SingleTenantPrincipal(ConnectorFramework connectorFramework) + : this(new OpenICFServerAdapter(connectorFramework, connectorFramework.LocalManager, false)) + { + _connectorFramework = connectorFramework; + } + + public SingleTenantPrincipal( + IMessageListener listener) + : base(listener, new ConcurrentDictionary()) + { + _timer = new Timer(state => + { + if (isRunning == 1) + { + foreach (var connectionGroup in ConnectionGroups.Values) + { + Trace.TraceInformation("Check ConnectionGroup:{0} - operational={1}", connectionGroup.RemoteSessionId + , connectionGroup.Operational); + connectionGroup.CheckIsActive(); + } + } + }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(4)); + + } + + protected override void DoClose() + { + _timer.Dispose(); + _connectorFramework.Dispose(); + } + } + + #endregion + + public class WebSocketServiceInstanceProvider : IInstanceProvider, IContractBehavior + { + private readonly Func _authenticate; + + public WebSocketServiceInstanceProvider(Func authenticate) + { + if (authenticate == null) + { + throw new ArgumentNullException("authenticate"); + } + + _authenticate = authenticate; + } + + #region IInstanceProvider Members + + public object GetInstance(InstanceContext instanceContext, Message message) + { + return GetInstance(instanceContext); + } + + public object GetInstance(InstanceContext instanceContext) + { + return new WcfWebsocket(); + } + + public void ReleaseInstance(InstanceContext instanceContext, object instance) + { + var disposable = instance as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + } + + #endregion + + #region IContractBehavior Members + + public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, + BindingParameterCollection bindingParameters) + { + } + + public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, + ClientRuntime clientRuntime) + { + } + + public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, + DispatchRuntime dispatchRuntime) + { + dispatchRuntime.InstanceProvider = this; + } + + public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) + { + } + + #endregion + } +} \ No newline at end of file diff --git a/dotnet/framework/WcfServiceLibrary/IWebSocketService.cs b/dotnet/framework/WcfServiceLibrary/IWebSocketService.cs new file mode 100755 index 00000000..722d8d68 --- /dev/null +++ b/dotnet/framework/WcfServiceLibrary/IWebSocketService.cs @@ -0,0 +1,45 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading.Tasks; + +namespace Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary +{ + [ServiceContract(Namespace = "http://openicf.forgerock.org")] + public interface IWebSocketCallback + { + [OperationContract(Action = "*", IsOneWay = true)] + Task OnMessage(Message message); + } + + [ServiceContract(CallbackContract = typeof (IWebSocketCallback), Namespace = "http://openicf.forgerock.org")] + public interface IWebSocketService : IWebSocketCallback + { + [OperationContract(Action = WebSocketTransportSettings.ConnectionOpenedAction, IsInitiating = true, + IsOneWay = true)] + void OnOpen(); + } +} \ No newline at end of file diff --git a/dotnet/framework/WcfServiceLibrary/WcfServiceLibrary.csproj b/dotnet/framework/WcfServiceLibrary/WcfServiceLibrary.csproj new file mode 100755 index 00000000..7bacd0ab --- /dev/null +++ b/dotnet/framework/WcfServiceLibrary/WcfServiceLibrary.csproj @@ -0,0 +1,120 @@ + + + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {D1771E11-C7D3-43FD-9D87-46F1231846F1} + Library + Properties + Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary + WcfServiceLibrary + OpenICF Framework - WCF Service Library + {3D9AD99F-2412-4246-B90B-4EAA41C64699};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + v4.5.2 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\Google.ProtocolBuffers.3\lib\Google.Protobuf.dll + + + + + + + + + + + + + + + + + {f140e8da-52b4-4159-992a-9da10ea8eefb} + Common + + + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} + FrameworkProtoBuf + + + {B85C5A35-E3A2-4B04-9693-795E57D66DE2} + FrameworkRpc + + + {5B47BEFD-C60B-4E80-943E-A7151CEEA568} + FrameworkServer + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + + + + + True + + + + + + \ No newline at end of file diff --git a/dotnet/framework/WcfServiceLibrary/version.template b/dotnet/framework/WcfServiceLibrary/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/dotnet/framework/WcfServiceLibrary/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/dotnet/framework/legal/CDDLv1.txt b/dotnet/framework/legal/CDDLv1.txt new file mode 100644 index 00000000..1717cf28 --- /dev/null +++ b/dotnet/framework/legal/CDDLv1.txt @@ -0,0 +1,383 @@ + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + +1. Definitions. + +1.1. "Contributor" means each individual or entity that +creates or contributes to the creation of Modifications. + +1.2. "Contributor Version" means the combination of the +Original Software, prior Modifications used by a +Contributor (if any), and the Modifications made by that +particular Contributor. + +1.3. "Covered Software" means (a) the Original Software, or +(b) Modifications, or (c) the combination of files +containing Original Software with files containing +Modifications, in each case including portions thereof. + +1.4. "Executable" means the Covered Software in any form +other than Source Code. + +1.5. "Initial Developer" means the individual or entity +that first makes Original Software available under this +License. + +1.6. "Larger Work" means a work which combines Covered +Software or portions thereof with code not governed by the +terms of this License. + +1.7. "License" means this document. + +1.8. "Licensable" means having the right to grant, to the +maximum extent possible, whether at the time of the initial +grant or subsequently acquired, any and all of the rights +conveyed herein. + +1.9. "Modifications" means the Source Code and Executable +form of any of the following: + +A. Any file that results from an addition to, +deletion from or modification of the contents of a +file containing Original Software or previous +Modifications; + +B. Any new file that contains any part of the +Original Software or previous Modification; or + +C. Any new file that is contributed or otherwise made +available under the terms of this License. + +1.10. "Original Software" means the Source Code and +Executable form of computer software code that is +originally released under this License. + +1.11. "Patent Claims" means any patent claim(s), now owned +or hereafter acquired, including without limitation, +method, process, and apparatus claims, in any patent +Licensable by grantor. + +1.12. "Source Code" means (a) the common form of computer +software code in which modifications are made and (b) +associated documentation included in or with such code. + +1.13. "You" (or "Your") means an individual or a legal +entity exercising rights under, and complying with all of +the terms of, this License. For legal entities, "You" +includes any entity which controls, is controlled by, or is +under common control with You. For purposes of this +definition, "control" means (a) the power, direct or +indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (b) ownership +of more than fifty percent (50%) of the outstanding shares +or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. + +Conditioned upon Your compliance with Section 3.1 below and +subject to third party intellectual property claims, the +Initial Developer hereby grants You a world-wide, +royalty-free, non-exclusive license: + +(a) under intellectual property rights (other than +patent or trademark) Licensable by Initial Developer, +to use, reproduce, modify, display, perform, +sublicense and distribute the Original Software (or +portions thereof), with or without Modifications, +and/or as part of a Larger Work; and + +(b) under Patent Claims infringed by the making, +using or selling of Original Software, to make, have +made, use, practice, sell, and offer for sale, and/or +otherwise dispose of the Original Software (or +portions thereof). + +(c) The licenses granted in Sections 2.1(a) and (b) +are effective on the date Initial Developer first +distributes or otherwise makes the Original Software +available to a third party under the terms of this +License. + +(d) Notwithstanding Section 2.1(b) above, no patent +license is granted: (1) for code that You delete from +the Original Software, or (2) for infringements +caused by: (i) the modification of the Original +Software, or (ii) the combination of the Original +Software with other software or devices. + +2.2. Contributor Grant. + +Conditioned upon Your compliance with Section 3.1 below and +subject to third party intellectual property claims, each +Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than +patent or trademark) Licensable by Contributor to +use, reproduce, modify, display, perform, sublicense +and distribute the Modifications created by such +Contributor (or portions thereof), either on an +unmodified basis, with other Modifications, as +Covered Software and/or as part of a Larger Work; and + +(b) under Patent Claims infringed by the making, +using, or selling of Modifications made by that +Contributor either alone and/or in combination with +its Contributor Version (or portions of such +combination), to make, use, sell, offer for sale, +have made, and/or otherwise dispose of: (1) +Modifications made by that Contributor (or portions +thereof); and (2) the combination of Modifications +made by that Contributor with its Contributor Version +(or portions of such combination). + +(c) The licenses granted in Sections 2.2(a) and +2.2(b) are effective on the date Contributor first +distributes or otherwise makes the Modifications +available to a third party. + +(d) Notwithstanding Section 2.2(b) above, no patent +license is granted: (1) for any code that Contributor +has deleted from the Contributor Version; (2) for +infringements caused by: (i) third party +modifications of Contributor Version, or (ii) the +combination of Modifications made by that Contributor +with other software (except as part of the +Contributor Version) or other devices; or (3) under +Patent Claims infringed by Covered Software in the +absence of Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. + +Any Covered Software that You distribute or otherwise make +available in Executable form must also be made available in +Source Code form and that Source Code form must be +distributed only under the terms of this License. You must +include a copy of this License with every copy of the +Source Code form of the Covered Software You distribute or +otherwise make available. You must inform recipients of any +such Covered Software in Executable form as to how they can +obtain such Covered Software in Source Code form in a +reasonable manner on or through a medium customarily used +for software exchange. + +3.2. Modifications. + +The Modifications that You create or to which You +contribute are governed by the terms of this License. You +represent that You believe Your Modifications are Your +original creation(s) and/or You have sufficient rights to +grant the rights conveyed by this License. + +3.3. Required Notices. + +You must include a notice in each of Your Modifications +that identifies You as the Contributor of the Modification. +You may not remove or alter any copyright, patent or +trademark notices contained within the Covered Software, or +any notices of licensing or any descriptive text giving +attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. + +You may not offer or impose any terms on any Covered +Software in Source Code form that alters or restricts the +applicable version of this License or the recipients' +rights hereunder. You may choose to offer, and to charge a +fee for, warranty, support, indemnity or liability +obligations to one or more recipients of Covered Software. +However, you may do so only on Your own behalf, and not on +behalf of the Initial Developer or any Contributor. You +must make it absolutely clear that any such warranty, +support, indemnity or liability obligation is offered by +You alone, and You hereby agree to indemnify the Initial +Developer and every Contributor for any liability incurred +by the Initial Developer or such Contributor as a result of +warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. + +You may distribute the Executable form of the Covered +Software under the terms of this License or under the terms +of a license of Your choice, which may contain terms +different from this License, provided that You are in +compliance with the terms of this License and that the +license for the Executable form does not attempt to limit +or alter the recipient's rights in the Source Code form +from the rights set forth in this License. If You +distribute the Covered Software in Executable form under a +different license, You must make it absolutely clear that +any terms which differ from this License are offered by You +alone, not by the Initial Developer or Contributor. You +hereby agree to indemnify the Initial Developer and every +Contributor for any liability incurred by the Initial +Developer or such Contributor as a result of any such terms +You offer. + +3.6. Larger Works. + +You may create a Larger Work by combining Covered Software +with other code not governed by the terms of this License +and distribute the Larger Work as a single product. In such +a case, You must make sure the requirements of this License +are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. + +Sun Microsystems, Inc. is the initial license steward and +may publish revised and/or new versions of this License +from time to time. Each version will be given a +distinguishing version number. Except as provided in +Section 4.3, no one other than the license steward has the +right to modify this License. + +4.2. Effect of New Versions. + +You may always continue to use, distribute or otherwise +make the Covered Software available under the terms of the +version of the License under which You originally received +the Covered Software. If the Initial Developer includes a +notice in the Original Software prohibiting it from being +distributed or otherwise made available under any +subsequent version of the License, You must distribute and +make the Covered Software available under the terms of the +version of the License under which You originally received +the Covered Software. Otherwise, You may also choose to +use, distribute or otherwise make the Covered Software +available under the terms of any subsequent version of the +License published by the license steward. + +4.3. Modified Versions. + +When You are an Initial Developer and You want to create a +new license for Your Original Software, You may create and +use a modified version of this License if You: (a) rename +the license and remove any references to the name of the +license steward (except to note that the license differs +from this License); and (b) otherwise make it clear that +the license contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" +BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED +SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR +PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY +COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE +INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF +ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF +WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF +ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS +DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will +terminate automatically if You fail to comply with terms +herein and fail to cure such breach within 30 days of +becoming aware of the breach. Provisions which, by their +nature, must remain in effect beyond the termination of +this License shall survive. + +6.2. If You assert a patent infringement claim (excluding +declaratory judgment actions) against Initial Developer or +a Contributor (the Initial Developer or Contributor against +whom You assert such claim is referred to as "Participant") +alleging that the Participant Software (meaning the +Contributor Version where the Participant is a Contributor +or the Original Software where the Participant is the +Initial Developer) directly or indirectly infringes any +patent, then any and all rights granted directly or +indirectly to You by such Participant, the Initial +Developer (if the Initial Developer is not the Participant) +and all Contributors under Sections 2.1 and/or 2.2 of this +License shall, upon 60 days notice from Participant +terminate prospectively and automatically at the expiration +of such 60 day notice period, unless if within such 60 day +period You withdraw Your claim with respect to the +Participant Software against such Participant either +unilaterally or pursuant to a written agreement with +Participant. + +6.3. In the event of termination under Sections 6.1 or 6.2 +above, all end user licenses that have been validly granted +by You or any distributor hereunder prior to termination +(excluding licenses granted to You by any distributor) +shall survive termination. + +7. LIMITATION OF LIABILITY. + +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT +(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE +INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF +COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE +LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR +CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT +LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK +STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER +COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN +INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF +LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL +INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT +APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO +NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR +CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT +APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + +The Covered Software is a "commercial item," as that term is +defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial +computer software" (as that term is defined at 48 C.F.R. $ +252.227-7014(a)(1)) and "commercial computer software +documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. +1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 +through 227.7202-4 (June 1995), all U.S. Government End Users +acquire Covered Software with only those rights set forth herein. +This U.S. Government Rights clause is in lieu of, and supersedes, +any other FAR, DFAR, or other clause or provision that addresses +Government rights in computer software under this License. + +9. MISCELLANEOUS. + +This License represents the complete agreement concerning subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the +extent necessary to make it enforceable. This License shall be +governed by the law of the jurisdiction specified in a notice +contained within the Original Software (except to the extent +applicable law, if any, provides otherwise), excluding such +jurisdiction's conflict-of-law provisions. Any litigation +relating to this License shall be subject to the jurisdiction of +the courts located in the jurisdiction and venue specified in a +notice contained within the Original Software, with the losing +party responsible for costs, including, without limitation, court +costs and reasonable attorneys' fees and expenses. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. Any law or +regulation which provides that the language of a contract shall +be construed against the drafter shall not apply to this License. +You agree that You alone are responsible for compliance with the +United States export administration regulations (and the export +control laws and regulation of any other countries) when You use, +distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + +As between Initial Developer and the Contributors, each party is +responsible for claims and damages arising, directly or +indirectly, out of its utilization of rights under this License +and You agree to work with Initial Developer and Contributors to +distribute such responsibility on an equitable basis. Nothing +herein is intended or shall be deemed to constitute any admission +of liability. + diff --git a/dotnet/framework/legal/ForgeRock_License.txt b/dotnet/framework/legal/ForgeRock_License.txt new file mode 100644 index 00000000..748cd377 --- /dev/null +++ b/dotnet/framework/legal/ForgeRock_License.txt @@ -0,0 +1,144 @@ +READ THIS SOFTWARE LICENSE AGREEMENT CAREFULLY. BY DOWNLOADING OR INSTALLING +THE FORGEROCK SOFTWARE, YOU, ON BEHALF OF YOURSELF AND YOUR COMPANY, AGREE TO +BE BOUND BY THIS SOFTWARE LICENSE AGREEMENT. IF YOU DO NOT AGREE TO THESE +TERMS, DO NOT DOWNLOAD OR INSTALL THE FORGEROCK SOFTWARE. + +1. Software License. + +1.1. Development Right to Use. If Company intends to or does use the ForgeRock +Software only for the purpose(s) of developing, testing, prototyping and +demonstrating its application software, then ForgeRock hereby grants Company a +nonexclusive, nontransferable, limited license to use the ForgeRock Software +only for those purposes, solely at Company's facilities and only in a +non-production environment. ForgeRock may audit Company's use of the ForgeRock +Software to confirm that a production license is not required upon reasonable +written notice to Company. If Company intends to use the ForgeRock Software in +a live environment, Company must purchase a production license and may only use +the ForgeRock Software licensed thereunder in accordance with the terms and +conditions of that subscription agreement. + +1.2. Restrictions. Except as expressly set forth in this ForgeRock Software +License Agreement (the "Agreement"), Company shall not, directly or indirectly: +(a) sublicense, resell, rent, lease, distribute or otherwise transfer rights or +usage in the ForgeRock Software, including without limitation to Company +subsidiaries and affiliates; (b) remove or alter any copyright, trademark or +proprietary notices in the ForgeRock Software; or (c) use the ForgeRock +Software in any way that would subject the ForgeRock Software, in whole in or +in part, to a Copyleft License. As used herein, "Copyleft License" means a +software license that requires that information necessary for reproducing and +modifying such software must be made available publicly to recipients of +executable versions of such software (see, e.g., GNU General Public License and +http://www.gnu.org/copyleft/). + +2. Proprietary Rights. + +2.1. ForgeRock Intellectual Property. Title to and ownership of all copies of +the ForgeRock Software whether in machine-readable (source, object code or +other format) or printed form, and all related technical know-how and all +rights therein (including without limitation all intellectual property rights +applicable thereto), belong to ForgeRock and its licensors and shall remain the +exclusive property thereof. ForgeRock's name, logo, trade names and trademarks +are owned exclusively by ForgeRock and no right is granted to Company to use +any of the foregoing except as expressly permitted herein. All rights not +expressly granted to Company are reserved by ForgeRock and its licensors. + +2.2. Suggestions. Company hereby grants to ForgeRock a royalty-free, worldwide, +transferable, sublicensable and irrevocable right and license to use, copy, +modify and distribute, including by incorporating into any product or service +owned by ForgeRock, any suggestions, enhancements, recommendations or other +feedback provided by Company relating to any product or service owned or +offered by ForgeRock. + +2.3. Source Code. The source code underlying the ForgeRock Software is +available at www.forgerock.org. + +3. Term and Termination. The terms of this Agreement shall commence on the +Effective Date and shall continue in force unless earlier terminated in +accordance this Section. This Agreement shall terminate without notice to +Company in the event Company is in material breach of any of the terms and +conditions of this Agreement. As used herein, "Effective Date" means the date +on which Company first accepted this Agreement and downloads the ForgeRock +Software. + +4. Disclaimer of Warranties. THE FORGEROCK SOFTWARE LICENSED HEREUNDER IS +LICENSED "AS IS" AND WITHOUT WARRANTY OF ANY KIND. FORGEROCK AND IT'S LICENSORS +EXPRESSLY DISCLAIM ALL WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, +INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY OF NON-INFRINGEMENT. + +5. General Indemnification. Company shall defend, indemnify and hold ForgeRock +harmless from and against any and all liabilities, damages, losses, costs and +expenses (including but not limited to reasonable fees of attorneys and other +professionals) payable to third parties based upon any claim arising out of or +related to the use of Company's products, provided that ForgeRock: (a) promptly +notifies Company of the claim; (b) provides Company with all reasonable +information and assistance, at Company's expense, to defend or settle such a +claim; and (c) grants Company authority and control of the defense or +settlement of such claim. Company shall not settle any such claim, without +ForgeRock's prior written consent, if such settlement would in any manner +effect ForgeRock's rights in the ForgeRock Software or otherwise. ForgeRock +reserves the right to retain counsel, at ForgeRock's expense, to participate in +the defense and settlement of any such claim. + +6. Limitation of Liability. IN NO EVENT SHALL FORGEROCK BE LIABLE FOR THE COST +OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, ANY LOST PROFITS, REVENUE, OR +DATA, INTERRUPTION OF BUSINESS OR FOR ANY INCIDENTAL, SPECIAL, CONSEQUENTIAL OR +INDIRECT DAMAGES OF ANY KIND, AND WHETHER ARISING OUT OF BREACH OF WARRANTY, +BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE OR IF SUCH DAMAGE COULD HAVE +BEEN REASONABLY FORESEEN. IN NO EVENT SHALL FORGEROCK'S LIABILITY ARISING OUT +OF OR RELATED TO THIS AGREEMENT WHETHER IN CONTRACT, TORT OR UNDER ANY OTHER +THEORY OF LIABILITY, EXCEED IN THE AGGREGATE $1,000 USD. + +7. General. + +7.1. Governing Law. This Agreement shall be governed by and interpreted in +accordance with the laws of the State of California without reference to its +conflicts of law provisions. + +7.2. Assignment. Company may not assign any of its rights or obligations under +this Agreement without the prior written consent of ForgeRock, which consent +shall not be unreasonably withheld. Any assignment not in conformity with this +Section shall be null and void. + +7.3. Waiver. A waiver on one occasion shall not be construed as a waiver of any +right on any future occasion. No delay or omission by a party in exercising any +of its rights hereunder shall operate as a waiver of such rights. + +7.4. Compliance with Law. The ForgeRock Software is subject to U.S. export +control laws, including the U.S. Export Administration Act and its associated +regulations, and may be subject to export or import regulations in other +countries. Company agrees to comply with all laws and regulations of the United +States and other countries ("Export Laws") to assure that neither the ForgeRock +Software, nor any direct products thereof are; (a) exported, directly or +indirectly, in violation of Export Laws, either to any countries that are +subject to U.S. export restrictions or to any end user who has been prohibited +from participating in the U.S. export transactions by any federal agency of the +U.S. government or (b) intended to be used for any purpose prohibited by Export +Laws, including, without limitation, nuclear, chemical, or biological weapons +proliferation. + +7.5. US Government Restrictions. Company acknowledges that the ForgeRock +Software consists of "commercial computer software" and "commercial computer +software documentation" as such terms are defined in the Code of Federal +Regulations. No Government procurement regulations or contract clauses or +provisions shall be deemed a part of any transaction between the parties unless +its inclusion is required by law, or mutually agreed in writing by the parties +in connection with a specific transaction. Use, duplication, reproduction, +release, modification, disclosure or transfer of the ForgeRock Software is +restricted in accordance with the terms of this Agreement. + +7.6. Provision Severability. In the event that it is determined by a court of +competent jurisdiction that any provision of this Agreement is invalid, +illegal, or otherwise unenforceable, such provision shall be enforced as nearly +as possible in accordance with the stated intention of the parties, while the +remainder of this Agreement shall remain in full force and effect and bind the +parties according to its terms. To the extent any provision cannot be enforced +in accordance with the stated intentions of the parties, such terms and +conditions shall be deemed not to be a part of this Agreement. + +7.7. Entire Agreement. This Agreement constitutes the entire and exclusive +agreement between the parties with respect to the subject matter hereof and +supersede any prior agreements between the parties with respect to such subject +matter + diff --git a/dotnet/framework/packages/Google.ProtocolBuffers.3/lib/Google.Protobuf.dll b/dotnet/framework/packages/Google.ProtocolBuffers.3/lib/Google.Protobuf.dll new file mode 100755 index 0000000000000000000000000000000000000000..0eb3ec70e2d90f7e22819c8b24a0d0f2ff164530 GIT binary patch literal 252928 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

<2jevbKJc8X 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

SMbDw<(F^F0=tcArdKvu*y@Fmvuc1Gq*U>8U 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 mo<$^GmtumCVY1`;i5<|a~kvXH-msRa*P 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

+vrP3JA@ciI^fPrY7NE^P*Hz%=4#6mKQ1h7Yot8 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%0DVS#j49tVQ{C*UbHfyPjRb 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 X&F!?ff|a^VcD-DBO`7bJ{_np5)d_kl literal 0 HcmV?d00001 diff --git a/dotnet/framework/server.pfx b/dotnet/framework/server.pfx new file mode 100644 index 0000000000000000000000000000000000000000..63f632a287bcbab06114bdde513c6b95c616be6d GIT binary patch literal 1704 zcmY+Dc|6p47{`Aze*K1Q>@-RyjO5G?zsZ=HZN$hmMvf#`VMnfF4I$Tr#^#7D8sr|i z!V*e!IEzZDSSqvFvTg2;DNWL>{pr>2zV?sr^L?K8b9}#lJRg_`RiaT+Fc10xlG%~W zOcrlIVNe1dv=8J#b_lkCc~UL^OG#CLJgG7S7a=DflKtBC&3Y7Cz(ZewdFU2c8Cw4j zMuy9Q(yVc-fI8yT5E_kn1oF^Xbc#*1_s~A#@Ol4_{1b&gn@69;7rhl%cs$Nj91|Kl z&TeB+#~Ww+*jjI791dETOOBQ%BpL`0izlv+uW}8P0`+5(%UaPj6tfZ!?ScG&=PTJt z4ilYGH@7wP@sv}U^eg%ANA)I!qjVtOKy;3(`;vGab2$=t;J<@V?%Q9`ROFi_(#8t>&7 zoL3VNq~t7X0p}s>Y*Tg6`r|KBrdh+Fa@}bY{)0lp)s=Ow5(8HwGYok~CQoUfkF1DzRsa3I~Ea1Ek110bF;u_mz`&iFgr;+$nIZBc^HY!jcj5?7v@uP$E zjz^!n3+}a>-mSurz(Gsq$vUrm7K3s~?Q|BV*i4R_y4$<<$rNL>xfsAph$YLFg3Y5n z$@_E%bmSDaEo~g@Y3@@_YWAR>CYHJNvQgN##9`{Rk%f|Fr@Yk%*>F1`tfyY z!0~4o8lJ>{nVvC^{`6dxN*bg6YD|Z~Tf-@+YT3fPG2u?f;koDT{7v>@Cx*LC$t9U` zHkZ0ru*7OWqGd&7RqdZJxxrEZ|a{ zmsjQ$D~B3S+FG^lnay2Th%8qis4&?n+gtTdY9Ig5mD>Bw2+d|=g|7@=Y4LOZe!eE@ z__Yx z2>}9-$Q^;Vyp)})%qLo>_lTW-sup11OqYu+0e@2O3XPH)_YcN*zs%jTl> z&SmIaT!|Y#dfR%Yf=)5>n#b6_FRAC+Wd~^xo@IoncI?%0$=UsW^ATG{6mRk}&G)sy zygKvck-{ldNA=6t%T`hBqLQvMpIv)z4i`18f*)_y=iX-ztb{S%Q54|raojntLp6!f z2H@SzwIf$s-M94;tSTdadCoE5+!PwZLOvz%%#Ju|@}TI$L^R=YeG{Sy{P zx3_QMrT=z4HP}&iB&5=b0?XMgblcM&Hk}RBoQ{8bQJB<@7 zNRPa>_?}nf-Vc4X_uaB{et5==kTHuCL`+8Kk$}ig@8(@`62?G&@%0OpF@4rdQNEF_ zmXV`*-{kxFok=R+`c3^lm&CM`xMmomKI|6I$lN7cZQ8oNcu8O&`IF_43J! zrvv1`;DGt|ycVC7Snc|IH4Y%vHB{QHZ6?TWIjf_>9URCMZK)H@&HQyWY#yr>ACFeg zm~UJ-l!?_|6uC8I75=r