From 5c020f763efe1df3636b495c89b2e8650189ded8 Mon Sep 17 00:00:00 2001 From: Nilesh Ghodekar Date: Sun, 10 Jul 2016 20:31:19 +0100 Subject: [PATCH 1/2] Changed - RunPowerShellScript User for can now be specified in the UPN format. Domain\UserName or UPN format is also only required if Impersonate PowerShell User option is selected. Changed - Minor improvements to messaging in Register.ps1, UpdateWorkflowXoml.ps1, EncryptData.ps1 and Sign.cmd. Added - FormatMultivaluedString function now supports more than one list of strings as input to format. Added - ConvertToUniqueIdentifier now supports a Guid in byte[] format as input. Changed - Interation in CreateResource, DeleteResource and UpdateResources activities not re-evaluates not just //Value expression but also //WorkflowData expressions. Fixed - Unnecessay FormatException in Logger. --- src/Scripts/EncryptData.ps1 | 20 ++-- src/Scripts/Register.ps1 | 6 +- src/Scripts/Sign.cmd | 2 +- src/Scripts/UpdateWorkflowXoml.ps1 | Bin 8384 -> 8956 bytes src/VersionInfo.cs | 4 +- .../ActivitySettings.Designer.cs | 13 ++- .../ActivitySettings.resx | 7 +- .../Forms/RunPowerShellScriptForm.cs | 11 ++- .../Activities/CreateResource.cs | 4 +- .../Activities/DeleteResources.cs | 4 +- .../Activities/RunPowerShellScript.cs | 9 +- .../Activities/UpdateResources.cs | 4 +- .../Common/ExpressionFunction.cs | 88 +++++++++++++----- src/WorkflowActivityLibrary/Common/Logger.cs | 9 +- .../Messages.Designer.cs | 20 +++- src/WorkflowActivityLibrary/Messages.resx | 8 +- 16 files changed, 155 insertions(+), 54 deletions(-) diff --git a/src/Scripts/EncryptData.ps1 b/src/Scripts/EncryptData.ps1 index 57fc782..e766968 100644 --- a/src/Scripts/EncryptData.ps1 +++ b/src/Scripts/EncryptData.ps1 @@ -1,16 +1,23 @@ <# This script demonstrates how the credentials can be encrypted for the RunAs user in the RunPowerShellScript Activity. - If the password is encrypted using certificate based encryption, please make sure that you deploy that certificate on the the FIMService nodes. It can be an auto-genenrated cert. + If the password is encrypted using certificate based encryption (recommended due to ease of deployment), please make sure that you deploy that certificate on the the FIMService nodes. It can be an auto-genenrated cert. If the password is encrypted using DPAPI, ensure that you export and import the RSA machine keys. Follow the steps documented for web farm scenario mentioned in the article at http://msdn.microsoft.com/en-us/library/ms998283.aspx (How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA), but for the FIM Service account. + + NOTE: Edit the Version and PublicKeyToken of the WAL AssemblyName to match the one that you have deployed in GAC. + Also edit the $encryptionCertThumbprint of cert to be used for certificate based encryption. #> $Error.Clear() +$walAssemblyVersion = "2.16.0710.0" +$walAssemblyPublicKeyToken = "31bf3856ad364e35" +$encryptionCertThumbprint = "9C697919FB2FB2D6324ADE42D5F8CB49E8778C08" # cert to be used for encryption (from the cert:\localmachine\my\ store). + Add-Type -AssemblyName "System.Security" # use the full name for WAL assembly to eliminate need to assembly redirects for dependent assemblies. -Add-Type -AssemblyName "MicrosoftServices.IdentityManagement.WorkflowActivityLibrary, Version=2.16.305.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" +Add-Type -AssemblyName "MicrosoftServices.IdentityManagement.WorkflowActivityLibrary, Version=$walAssemblyVersion, Culture=neutral, PublicKeyToken=$walAssemblyPublicKeyToken" if ($Error) { @@ -20,10 +27,9 @@ if ($Error) function TestCertificateBasedEncryptionDecryption { - $thumbprint = "9C697919FB2FB2D6324ADE42D5F8CB49E8778C08" # cert to be used for encryption. $outFile = Join-Path -Path $PWD -ChildPath "cert-p.txt" - $base64EncodedPublicKeyXml = [MicrosoftServices.IdentityManagement.WorkflowActivityLibrary.Common.ProtectedData]::GetCertificatePublicKeyXml($thumbprint, "My", "LocalMachine") + $base64EncodedPublicKeyXml = [MicrosoftServices.IdentityManagement.WorkflowActivityLibrary.Common.ProtectedData]::GetCertificatePublicKeyXml($encryptionCertThumbprint, "My", "LocalMachine") $secretToEncrypt = "Pass@word1" @@ -31,7 +37,7 @@ function TestCertificateBasedEncryptionDecryption $encryptedData = [MicrosoftServices.IdentityManagement.WorkflowActivityLibrary.Common.ProtectedData]::EncryptData($secret, $base64EncodedPublicKeyXml) - $encryptedDataConfig = "cert:\localmachine\my\$thumbprint,$encryptedData" + $encryptedDataConfig = "cert:\localmachine\my\$encryptionCertThumbprint,$encryptedData" $encryptedDataConfig | Out-File $outFile @@ -41,12 +47,12 @@ function TestCertificateBasedEncryptionDecryption if ($plainText -eq $secretToEncrypt) { - Write-Host "`nEncryption and Decryption test using certificate '$thumbprint' succeeded!`n" + Write-Host "`nEncryption and Decryption test using certificate '$encryptionCertThumbprint' succeeded!`n" Write-Host "`nThe password config is saved in '$outFile'`n" } else { - Write-Error "`nEncryption and Decryption test using certificate '$thumbprint' failed!`n" + Write-Error "`nEncryption and Decryption test using certificate '$encryptionCertThumbprint' failed!`n" } } diff --git a/src/Scripts/Register.ps1 b/src/Scripts/Register.ps1 index 03751db..f1d9c2d 100644 --- a/src/Scripts/Register.ps1 +++ b/src/Scripts/Register.ps1 @@ -85,6 +85,10 @@ function RegisterAssembly throw ("Error Registering assembly to skip strong name verification: '$assemblyName' " ) } } + elseif ($executionStatus -eq "") + { + Write-Warning "Execution status of gacutil.exe was not confirmed. Make sure that you have correct version of the gacutil tool." + } } function RegisterActivity @@ -428,4 +432,4 @@ net start FIMService iisreset -Write-Host -ForegroundColor green "Review script console output for any errors. Once the deployment is successful on all the servers, update the assembly version in MIMWAL XOMLs by executing UpdateWorkflowXoml.ps1 script." +Write-Host -ForegroundColor green "Review script console output for any errors. Once the deployment is successful on *ALL* the servers, update the assembly version in MIMWAL XOMLs by executing UpdateWorkflowXoml.ps1 script." diff --git a/src/Scripts/Sign.cmd b/src/Scripts/Sign.cmd index 471908a..86c20c2 100644 --- a/src/Scripts/Sign.cmd +++ b/src/Scripts/Sign.cmd @@ -7,7 +7,7 @@ set walUIAssembly="%~dp0\MicrosoftServices.IdentityManagement.WorkflowActivityLi set snkFile="%~dp0..\WAL.snk" set snExe="%~dp0sn.exe" -if not exist %walAssembly% echo ERROR: File '%walAssembly%' Not Found. You need to compile WAL solution first! & (goto exit_error) +if not exist %walAssembly% echo ERROR: File '%walAssembly%' Not Found. You need to compile WAL solution first! Make sure you use REBUILD Solution menu. & (goto exit_error) if not exist %walUIAssembly% echo ERROR: File '%walUIAssembly%' Not Found. You need to compile WAL solution first! & (goto exit_error) if not exist %snkFile% echo ERROR: File '%snkFile%' Not Found. You need to specify correct path to your strong name file! & (goto exit_error) diff --git a/src/Scripts/UpdateWorkflowXoml.ps1 b/src/Scripts/UpdateWorkflowXoml.ps1 index 52532ea360eac345f657e149951463c41184b57b..41870b3d45066e1de207044433a9b56d4482e4e2 100644 GIT binary patch delta 656 zcmchVu}Z^G6o!8)(niri5TsGL)FD%S0Jjbi3vERaikpcE8VE_qtpyS3D_HL86F4cZ z4!(t(zCghjaPU6~DG1^yA-RA4oO93l&dqD*%e{M>SUAQN5-5yti3plX;~Y74Mo-}o z1N3l8)xkba;4(uYhR3fVVI!>|a?HbqJ#ny0Jzx<jY7;r%r=4|@8tyeO%Td9T|?(oAcPaywTc3(1@+$p6Mw1Wb!T$WT(km9TPt d9vvb0`9pXd+=#;%7Y7j)xnyfp20z)`MK0^^h8bc0{E(h`>fOIa9 zXEnJ%N_O&P8L`Q?Oq2D6gcNZoU - internal const string Version = "2.16.0320.0"; + internal const string Version = "2.16.0710.0"; /// /// File Version information for the assembly consists of the following four values: @@ -31,6 +31,6 @@ internal static class VersionInfo /// Build Number (MMDD) /// Revision (if any on the same day) /// - internal const string FileVersion = "2.16.0320.0"; + internal const string FileVersion = "2.16.0710.0"; } } \ No newline at end of file diff --git a/src/WorkflowActivityLibrary.UI/ActivitySettings.Designer.cs b/src/WorkflowActivityLibrary.UI/ActivitySettings.Designer.cs index 7e4772b..317a678 100644 --- a/src/WorkflowActivityLibrary.UI/ActivitySettings.Designer.cs +++ b/src/WorkflowActivityLibrary.UI/ActivitySettings.Designer.cs @@ -970,7 +970,7 @@ internal static string PowerShellUser { } /// - /// Looks up a localized string similar to Specify the PowerShell user in the format domain\userName.. + /// Looks up a localized string similar to The PowerShell user must be specified in the domain\userName or UPN format when Impersonate PowerShell User option is selected.. /// internal static string PowerShellUserFormatValidationError { get { @@ -979,7 +979,7 @@ internal static string PowerShellUserFormatValidationError { } /// - /// Looks up a localized string similar to Specify the user in the format domain\userName to be used to construct PowerShell Credential object.. + /// Looks up a localized string similar to Specify the user to be used to construct PowerShell Credential object. When impersonating, the user name must be in the domain\userName or UPN format.. /// internal static string PowerShellUserHelpText { get { @@ -1005,6 +1005,15 @@ internal static string PowerShellUserPasswordHelpText { } } + /// + /// Looks up a localized string similar to Specify the PowerShell user password.. + /// + internal static string PowerShellUserPasswordValidationError { + get { + return ResourceManager.GetString("PowerShellUserPasswordValidationError", resourceCulture); + } + } + /// /// Looks up a localized string similar to Queries. /// diff --git a/src/WorkflowActivityLibrary.UI/ActivitySettings.resx b/src/WorkflowActivityLibrary.UI/ActivitySettings.resx index 27c6cab..35633f7 100644 --- a/src/WorkflowActivityLibrary.UI/ActivitySettings.resx +++ b/src/WorkflowActivityLibrary.UI/ActivitySettings.resx @@ -658,7 +658,7 @@ PowerShell Script User - Specify the user in the format domain\userName to be used to construct PowerShell Credential object. + Specify the user to be used to construct PowerShell Credential object. When impersonating, the user name must be in the domain\userName or UPN format. PowerShell Script User Password @@ -670,7 +670,7 @@ Specify UserName and Password of the impersonated user. - Specify the PowerShell user in the format domain\userName. + The PowerShell user must be specified in the domain\userName or UPN format when Impersonate PowerShell User option is selected. Authorization Policy cannot be applied when Request Actor is Service Account @@ -807,4 +807,7 @@ The activity can be configured to resolve / evaluate lookups and expressions in the values returned by the expressions in update definitions. Example: The string returned by [//Queries/EmailTemplate/EmailBody] will be parsed and all lookups will be resolved. + + Specify the PowerShell user password. + \ No newline at end of file diff --git a/src/WorkflowActivityLibrary.UI/Forms/RunPowerShellScriptForm.cs b/src/WorkflowActivityLibrary.UI/Forms/RunPowerShellScriptForm.cs index 36284b1..cc2dd02 100644 --- a/src/WorkflowActivityLibrary.UI/Forms/RunPowerShellScriptForm.cs +++ b/src/WorkflowActivityLibrary.UI/Forms/RunPowerShellScriptForm.cs @@ -554,15 +554,18 @@ public override bool ValidateInputs() if (!string.IsNullOrEmpty(this.powerShellUser.Value)) { - if (!this.powerShellUser.Value.Contains(@"\")) + if (this.impersonatePowerShellUser.Value) { - this.controller.ValidationError = ActivitySettings.PowerShellUserFormatValidationError; - return false; + if (!this.powerShellUser.Value.Contains(@"\") && !this.powerShellUser.Value.Contains("@")) + { + this.controller.ValidationError = ActivitySettings.PowerShellUserFormatValidationError; + return false; + } } if (string.IsNullOrEmpty(this.powerShellUserPassword.Value)) { - this.controller.ValidationError = ActivitySettings.PowerShellImpersonationSettingsValidationError; + this.controller.ValidationError = ActivitySettings.PowerShellUserPasswordValidationError; return false; } diff --git a/src/WorkflowActivityLibrary/Activities/CreateResource.cs b/src/WorkflowActivityLibrary/Activities/CreateResource.cs index 032cd2b..4e69c23 100644 --- a/src/WorkflowActivityLibrary/Activities/CreateResource.cs +++ b/src/WorkflowActivityLibrary/Activities/CreateResource.cs @@ -703,9 +703,9 @@ private void PrepareIteration_ExecuteCode(object sender, EventArgs e) } } - // Pull any [//Value] expressions from the expression evaluator's lookup cache for + // Pull any [//Value] or [//WorkflowData] expressions from the expression evaluator's lookup cache for // resolution during iteration - foreach (string key in from key in this.ActivityExpressionEvaluator.LookupCache.Keys let lookup = new LookupEvaluator(key) where lookup.Parameter == LookupParameter.Value select key) + foreach (string key in from key in this.ActivityExpressionEvaluator.LookupCache.Keys let lookup = new LookupEvaluator(key) where lookup.Parameter == LookupParameter.Value || lookup.Parameter == LookupParameter.WorkflowData select key) { this.ValueExpressions.Add(key, null); } diff --git a/src/WorkflowActivityLibrary/Activities/DeleteResources.cs b/src/WorkflowActivityLibrary/Activities/DeleteResources.cs index 0e327e2..743583d 100644 --- a/src/WorkflowActivityLibrary/Activities/DeleteResources.cs +++ b/src/WorkflowActivityLibrary/Activities/DeleteResources.cs @@ -456,9 +456,9 @@ private void PrepareIteration_ExecuteCode(object sender, EventArgs e) } } - // Pull any [//Value] expressions from the expression evaluator's lookup cache for + // Pull any [//Value] or [//WorkflowData] expressions from the expression evaluator's lookup cache for // resolution during iteration - foreach (string key in from key in this.ActivityExpressionEvaluator.LookupCache.Keys let lookup = new LookupEvaluator(key) where lookup.Parameter == LookupParameter.Value select key) + foreach (string key in from key in this.ActivityExpressionEvaluator.LookupCache.Keys let lookup = new LookupEvaluator(key) where lookup.Parameter == LookupParameter.Value || lookup.Parameter == LookupParameter.WorkflowData select key) { this.ValueExpressions.Add(key, null); } diff --git a/src/WorkflowActivityLibrary/Activities/RunPowerShellScript.cs b/src/WorkflowActivityLibrary/Activities/RunPowerShellScript.cs index a8177ac..54d919f 100644 --- a/src/WorkflowActivityLibrary/Activities/RunPowerShellScript.cs +++ b/src/WorkflowActivityLibrary/Activities/RunPowerShellScript.cs @@ -988,10 +988,13 @@ private PSCredential GetPowerShellCredential() return null; } - string[] userParts = this.PowerShellUser.Split(new string[] { @"\" }, StringSplitOptions.RemoveEmptyEntries); - if (userParts.Length != 2) + if (this.ImpersonatePowerShellUser) { - throw Logger.Instance.ReportError(EventIdentifier.RunPowerShellScriptRunScriptExecutionFailedError, new WorkflowActivityLibraryException(Messages.RunPowerShellActivity_InvalidUserFormat, this.PowerShellUser)); + string[] userParts = this.PowerShellUser.Split(new string[] { @"\" }, StringSplitOptions.RemoveEmptyEntries); + if (userParts.Length != 2 && !this.PowerShellUser.Contains("@")) + { + throw Logger.Instance.ReportError(EventIdentifier.RunPowerShellScriptRunScriptExecutionFailedError, new WorkflowActivityLibraryException(Messages.RunPowerShellActivity_InvalidUserFormat, this.PowerShellUser)); + } } SecureString password = ProtectedData.DecryptData(this.PowerShellUserPassword); diff --git a/src/WorkflowActivityLibrary/Activities/UpdateResources.cs b/src/WorkflowActivityLibrary/Activities/UpdateResources.cs index 2ef65de..5f1cb96 100644 --- a/src/WorkflowActivityLibrary/Activities/UpdateResources.cs +++ b/src/WorkflowActivityLibrary/Activities/UpdateResources.cs @@ -552,9 +552,9 @@ private void PrepareIteration_ExecuteCode(object sender, EventArgs e) } } - // Pull any [//Value] expressions from the expression evaluator's lookup cache for + // Pull any [//Value] or [//WorkflowData] expressions from the expression evaluator's lookup cache for // resolution during iteration - foreach (string key in from key in this.ActivityExpressionEvaluator.LookupCache.Keys let lookup = new LookupEvaluator(key) where lookup.Parameter == LookupParameter.Value select key) + foreach (string key in from key in this.ActivityExpressionEvaluator.LookupCache.Keys let lookup = new LookupEvaluator(key) where lookup.Parameter == LookupParameter.Value || lookup.Parameter == LookupParameter.WorkflowData select key) { this.ValueExpressions.Add(key, null); } diff --git a/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs b/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs index a5fa1ea..2884a79 100644 --- a/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs +++ b/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs @@ -1756,7 +1756,7 @@ private string ConcatenateMultivaluedString() /// /// Formats the multivalued list as per the specified format. - /// Function Syntax: FormatMultivaluedString(format:string, list1:List of strings) + /// Function Syntax: FormatMultivaluedString(format:string, list1:List of strings[, list2:List of strings, ...] ) /// /// The formatted list. private object FormatMultivaluedList() @@ -1765,7 +1765,7 @@ private object FormatMultivaluedList() try { - if (this.parameters.Count != 2) + if (this.parameters.Count < 2) { throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterCountError, this.function, 2, this.parameters.Count)); } @@ -1776,53 +1776,94 @@ private object FormatMultivaluedList() throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, 1)); } - parameter = this.parameters[1]; - if (parameter == null) + for (int i = 1; i < this.parameters.Count; ++i) { - throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, 2)); + parameter = this.parameters[i]; + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } } Type parameterType = typeof(List); Type parameterType2 = typeof(string); - parameter = this.parameters[1]; - if (!this.VerifyType(parameter, parameterType) && !this.VerifyType(parameter, parameterType2)) + for (int i = 1; i < this.parameters.Count; ++i) { - throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListInvalidSecondFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidSecondFunctionParameterTypeError3, this.function, parameterType.Name, parameterType2.Name, parameter == null ? "null" : parameter.GetType().Name)); + parameter = this.parameters[i]; + if (!this.VerifyType(parameter, parameterType) && !this.VerifyType(parameter, parameterType2)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListInvalidSecondFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError2, this.function, i + 1, parameterType.Name, parameterType2.Name, parameter == null ? "null" : parameter.GetType().Name)); + } } - object result; + object result = null; if (this.mode != EvaluationMode.Parse) { if (this.parameters[1] is string) { - if (string.IsNullOrEmpty(this.parameters[1] as string)) + for (int i = 1; i < this.parameters.Count; ++i) + { + parameter = this.parameters[i]; + if (parameter is string == false) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListInvalidSecondFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, "string", parameter == null ? "null" : parameter.GetType().Name)); + } + } + } + else + { + var listCount = ((List)this.parameters[1]).Count; + if (listCount == 0) { throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, 2)); } - result = string.Format(CultureInfo.InvariantCulture, this.parameters[0].ToString(), this.parameters[1].ToString()); + for (int i = 2; i < this.parameters.Count; ++i) + { + parameter = this.parameters[i]; + if (parameter is List == false) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + var listCount2 = ((List)parameter).Count; + if (listCount2 != listCount) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterError2, this.function, i + 1, listCount, listCount2)); + } + } } - else if (((List)this.parameters[1]).Count == 0) + + if (this.parameters[1] is string) { - throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, 2)); + string[] args = new string[this.parameters.Count - 1]; + for (int i = 1; i < this.parameters.Count; ++i) + { + args[i - 1] = this.parameters[i] as string; + } + + result = string.Format(CultureInfo.InvariantCulture, this.parameters[0].ToString(), args); } else { - result = new List(((List)this.parameters[1]).Count); + var listCount = ((List)this.parameters[1]).Count; + result = new List(listCount); - foreach (string s in (List)this.parameters[1]) + string[] args = new string[this.parameters.Count - 1]; + for (int n = 0; n < listCount; ++n) { - ((List)result).Add(string.Format(CultureInfo.InvariantCulture, this.parameters[0].ToString(), s)); + for (int i = 1; i < this.parameters.Count; ++i) + { + args[i - 1] = ((List)this.parameters[i])[n]; + } + + ((List)result).Add(string.Format(CultureInfo.InvariantCulture, this.parameters[0].ToString(), args)); } } Logger.Instance.WriteVerbose(EventIdentifier.ExpressionFunctionConcatenateMultivaluedString, "FormatMultivaluedList() returned '{0}'.", result); } - else - { - result = null; - } return result; } @@ -1850,10 +1891,11 @@ private object ConvertToUniqueIdentifier() Type parameterType = typeof(List); Type parameterType2 = typeof(Guid); + Type parameterType3 = typeof(byte[]); object parameter = this.parameters[0]; - if (!this.VerifyType(parameter, parameterType) && !this.VerifyType(parameter, parameterType2)) + if (!this.VerifyType(parameter, parameterType) && !this.VerifyType(parameter, parameterType2) && !this.VerifyType(parameter, parameterType3)) { - throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionConvertToUniqueIdentifierInvalidFirstFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFirstFunctionParameterTypeError2, this.function, parameterType.Name, parameterType2.Name, parameter == null ? "null" : parameter.GetType().Name)); + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionConvertToUniqueIdentifierInvalidFirstFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFirstFunctionParameterTypeError, this.function, parameterType.Name, parameterType2.Name, parameter == null ? "null" : parameter.GetType().Name)); } object result; @@ -1873,7 +1915,7 @@ private object ConvertToUniqueIdentifier() } else { - result = new UniqueIdentifier((Guid)this.parameters[0]); + result = this.parameters[0] is byte[] ? new UniqueIdentifier(new Guid((byte[])this.parameters[0])) : new UniqueIdentifier((Guid)this.parameters[0]); } } diff --git a/src/WorkflowActivityLibrary/Common/Logger.cs b/src/WorkflowActivityLibrary/Common/Logger.cs index 92926db..4bd6b2a 100644 --- a/src/WorkflowActivityLibrary/Common/Logger.cs +++ b/src/WorkflowActivityLibrary/Common/Logger.cs @@ -562,7 +562,14 @@ private void WriteEvent(TraceEventType eventType, int eventId, [Localizable(fals { try { - traceSource.TraceEvent(eventType, eventId, message, args); + if (args != null && args.Length > 0) + { + traceSource.TraceEvent(eventType, eventId, message, args); + } + else + { + traceSource.TraceEvent(eventType, eventId, message); + } } catch (FormatException) { diff --git a/src/WorkflowActivityLibrary/Messages.Designer.cs b/src/WorkflowActivityLibrary/Messages.Designer.cs index 2305404..089a81b 100644 --- a/src/WorkflowActivityLibrary/Messages.Designer.cs +++ b/src/WorkflowActivityLibrary/Messages.Designer.cs @@ -339,6 +339,15 @@ internal static string ExpressionFunction_InvalidFunctionParameterError { } } + /// + /// Looks up a localized string similar to The function '{0}' expects list at position {1} to be of length '{2}'. It has a length of '{3}'.. + /// + internal static string ExpressionFunction_InvalidFunctionParameterError2 { + get { + return ResourceManager.GetString("ExpressionFunction_InvalidFunctionParameterError2", resourceCulture); + } + } + /// /// Looks up a localized string similar to The function '{0}' expects '{1} or more' parameters. It was supplied '{2}' parameters.. /// @@ -366,6 +375,15 @@ internal static string ExpressionFunction_InvalidFunctionParameterTypeError { } } + /// + /// Looks up a localized string similar to The function '{0}' expects parameter at position {1} to be of type '{2}' or '{3}'. It was supplied a '{4}' parameter.. + /// + internal static string ExpressionFunction_InvalidFunctionParameterTypeError2 { + get { + return ResourceManager.GetString("ExpressionFunction_InvalidFunctionParameterTypeError2", resourceCulture); + } + } + /// /// Looks up a localized string similar to The request is for an update of the singled-valued attribute '{0}'. Functions ParameterValue must be used instead to read the updated value.. /// @@ -664,7 +682,7 @@ internal static string ReuqestApproval_MissingEmailTemplateError { } /// - /// Looks up a localized string similar to Invalid UserName format {0}. Expected format "Domain\UserName".. + /// Looks up a localized string similar to Invalid UserName format {0}. Expected format is "Domain\UserName" or UPN.. /// internal static string RunPowerShellActivity_InvalidUserFormat { get { diff --git a/src/WorkflowActivityLibrary/Messages.resx b/src/WorkflowActivityLibrary/Messages.resx index 4758b9d..8a4bba1 100644 --- a/src/WorkflowActivityLibrary/Messages.resx +++ b/src/WorkflowActivityLibrary/Messages.resx @@ -247,7 +247,7 @@ The variable cache does not contain an entry for {0} - Invalid UserName format {0}. Expected format "Domain\UserName". + Invalid UserName format {0}. Expected format is "Domain\UserName" or UPN. The X.509 encryption certificate with thumbprint '{0}' could not be found. @@ -366,4 +366,10 @@ Unable to determine request actor. The supplied string is not a valid GUID format. + + The function '{0}' expects parameter at position {1} to be of type '{2}' or '{3}'. It was supplied a '{4}' parameter. + + + The function '{0}' expects list at position {1} to be of length '{2}'. It has a length of '{3}'. + \ No newline at end of file From 2eb68999b0fc1b6bfca876c9ba1fc730e5d8048c Mon Sep 17 00:00:00 2001 From: Nilesh Ghodekar Date: Sun, 10 Jul 2016 20:52:36 +0100 Subject: [PATCH 2/2] Updated ChangeLog.md for v2.16.0710.0 --- ChangeLog.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 4a443f1..18dc656 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,6 +8,20 @@ All notable changes to MIMWAL project will be documented in this file. The "Unre ------------ +### Version 2.16.0710.0 + +#### Changed + +* RunPowerShellScript User for can now be specified in the UPN format. The Domain\UserName or UPN format is also only required if "Impersonate PowerShell User" option is selected. +* Iteration in CreateResource, DeleteResource and UpdateResources activities now re-evaluates not only `[//Value]` expressions but also `[//WorkflowData]` expressions. + +#### Added + +* `FormatMultivaluedString` function now supports more than one list of strings as input to format. +* `ConvertToUniqueIdentifier` now supports a Guid in byte[] format as input. + +------------ + ### Version 2.16.0320.0 #### Changed