From c9ece7cb1e4009df041ac5952e62e02dec92bb66 Mon Sep 17 00:00:00 2001 From: Microsoft Graph DevX Tooling Date: Wed, 2 Apr 2025 00:15:15 +0300 Subject: [PATCH 1/3] Implementation for secure password --- src/readme.graph.md | 35 ++++++++++++++++++++++++- tools/Custom/HttpMessageLogFormatter.cs | 27 ++++++++++++++++++- tools/Custom/PropertyTracker.cs | 3 ++- tools/Custom/SecureStringExtension.cs | 20 ++++++++++++++ 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 tools/Custom/SecureStringExtension.cs diff --git a/src/readme.graph.md b/src/readme.graph.md index 798ccff4a12..e00950c520f 100644 --- a/src/readme.graph.md +++ b/src/readme.graph.md @@ -339,7 +339,15 @@ directive: const regexP = /AddIf\(\s*null\s*!=\s*\(\(\(object\)this\._(\w+).*?(\(Microsoft.*.PowerShell\.Runtime\.Json\.JsonNode\)).*?"(\w+)".*?container\.Add\s*\);/gm $ = $.replace(regexP, (match, p1, p2, p3) => { let capitalizedP1 = p1.charAt(0).toUpperCase() + p1.slice(1); // Capitalize first letter - return `if(this.IsPropertySet("${p1}"))\n\t\t{\n\t\t\tvar propertyInfo = this.GetType().GetProperty("${capitalizedP1}");\n\t\t\tif (propertyInfo != null)\n\t\t\t{\n\t\t\tSystem.Type propertyType = propertyInfo.PropertyType;\n\t\t\t\t\tAddIf(${p2}PropertyTracker.ConvertToJsonNode(propertyType, this._${p1}),"${p1}",container.Add);\n\t\t\t}\n\t\t}`; + // Check if `capitalizedP1` contains "PasswordSecure" and remove "Secure" + let adjustedP1 = capitalizedP1.includes("PasswordSecure") + ? capitalizedP1.replace("Secure", "") + : capitalizedP1; + let lowerCasedP1 = adjustedP1.charAt(0).toLowerCase() + adjustedP1.slice(1); + let jsonNodeFunction = capitalizedP1.includes("PasswordSecure") + ? `${p2}PropertyTracker.ConvertToJsonNode(propertyType, SecureStringExtension.ConvertToSecureStringToPlainText(this._${p1}))` + : `${p2}PropertyTracker.ConvertToJsonNode(propertyType, this._${p1})`; + return `if(this.IsPropertySet("${p1}"))\n\t\t{\n\t\t\tvar propertyInfo = this.GetType().GetProperty("${capitalizedP1}");\n\t\t\tif (propertyInfo != null)\n\t\t\t{\n\t\t\tSystem.Type propertyType = propertyInfo.PropertyType;\n\t\t\t\t\tAddIf(${jsonNodeFunction}, "${lowerCasedP1}",container.Add);\n\t\t\t}\n\t\t}`; }); $ = $.replace(/if\s*\(\s*null\s*!=\s*this\._(\w+)\s*\)/gm, 'if(this.IsPropertySet("$1"))') @@ -426,6 +434,9 @@ directive: $ = $.replace(/\bpublic\s+(Microsoft\.Graph\.[\w.]+\[\])\s+(\w+)\s*{\s*get\s*=>\s*this\.(\w+);\s*set\s*=>\s*this\.\3\s*=\s*value;\s*}/gm,'public $1 $2\n\t{\n\t\tget=>this.$3;\n\t\tset\n\t\t{\n\t\t\tthis.$3=value;\n\t\t\tTrackProperty(nameof($2));\n\t\t}\n\t}') + //Tracker for SecureString properties + $ = $.replace(/\bpublic\s+(System\.Security\.SecureString+)\s+(\w+)\s*{\s*get\s*=>\s*this\.(\w+);\s*set\s*=>\s*this\.\3\s*=\s*value;\s*}/gm,'public $1 $2\n\t{\n\t\tget=>this.$3;\n\t\tset\n\t\t{\n\t\t\tthis.$3=value;\n\t\t\tTrackProperty(nameof($2));\n\t\t}\n\t}') + const match = $documentPath.match(/generated%2Fapi%2FModels%2F([\w]*[\w\d]*)\.cs/gm); if (match) { let fileName = match[0]; @@ -495,6 +506,7 @@ directive: } }); } + $ = $.replace('// work', '// work\n\t\t\t\tConsole.WriteLine("*****************Warning: If you are passing any password related parameters please note that plain text passwords will be disabled soon.*****************");\n\t\t\t\tConsole.WriteLine("*****************Please convert your password to secure string *****************");\n\t\t\t\tConsole.WriteLine("*****************Example: $securePassword = ConvertTo-SecureString -String -AsPlainText -Force *****************");') return $; } @@ -962,5 +974,26 @@ directive: subject: ^(.*)(OnPremise)(.*)$ set: alias: ^(.*)(OnPremises)(.*)$ + + - from: openapi-document + where: $.components..properties.currentPassword + transform: > + $["x-ms-client-name"] = "currentPasswordSecure"; + $["format"] = "password"; + - from: openapi-document + where: $.components..properties.newPassword + transform: > + $["x-ms-client-name"] = "newPasswordSecure"; + $["format"] = "password"; + - from: openapi-document + where: $.components..properties.password + transform: > + $["x-ms-client-name"] = "passwordSecure"; + $["format"] = "password"; + - from: openapi-document + where: $.paths..requestBody..properties.password + transform: > + $["x-ms-client-name"] = "passwordSecure"; + $["format"] = "password"; ``` diff --git a/tools/Custom/HttpMessageLogFormatter.cs b/tools/Custom/HttpMessageLogFormatter.cs index 76c33700c36..258ee2cae2a 100644 --- a/tools/Custom/HttpMessageLogFormatter.cs +++ b/tools/Custom/HttpMessageLogFormatter.cs @@ -141,7 +141,8 @@ private static object SanitizeBody(string body) { body = matcher.Replace(body, "$1\"\""); } - + // Remove password:* instances in body. + body = AbstractPasswords(body); return body; } @@ -189,5 +190,29 @@ private static string FormatString(string content) return content; } + private static string AbstractPasswords(string content) + { + + // Check if the JSON string contains "password" (case-insensitive) + if (content.IndexOf("password", StringComparison.OrdinalIgnoreCase) >= 0) + { + // Deserialize JSON into Dictionary + var obj = JsonConvert.DeserializeObject>(content); + + // Replace values for properties containing "password" (case-insensitive) + foreach (var key in obj.Keys) + { + if (key.IndexOf("password", StringComparison.OrdinalIgnoreCase) >= 0) + { + obj[key] = "***"; + } + } + + // Serialize the updated dictionary back to JSON + content = JsonConvert.SerializeObject(obj, Formatting.Indented); + + } + return content; + } } } diff --git a/tools/Custom/PropertyTracker.cs b/tools/Custom/PropertyTracker.cs index 1d8dde56db3..6028486fe46 100644 --- a/tools/Custom/PropertyTracker.cs +++ b/tools/Custom/PropertyTracker.cs @@ -1,4 +1,5 @@ using System; +using System.Security; using System.Collections.Generic; @@ -41,7 +42,7 @@ public static NamespacePrefixPlaceholder.PowerShell.Runtime.Json.JsonNode Conver // Handle different types based on the declared type - if (propertyType == typeof(string)) + if (propertyType == typeof(string) || (propertyType == typeof(SecureString))) { return new NamespacePrefixPlaceholder.PowerShell.Runtime.Json.JsonString(value.ToString()); } diff --git a/tools/Custom/SecureStringExtension.cs b/tools/Custom/SecureStringExtension.cs new file mode 100644 index 00000000000..a821eb7e1ca --- /dev/null +++ b/tools/Custom/SecureStringExtension.cs @@ -0,0 +1,20 @@ +using System; +using System.Security; +namespace NamespacePrefixPlaceholder.PowerShell.Models +{ + public class SecureStringExtension + { + public static string ConvertToSecureStringToPlainText(SecureString secureString) + { + IntPtr unmanagedString = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(secureString); + try + { + return System.Runtime.InteropServices.Marshal.PtrToStringUni(unmanagedString); + } + finally + { + System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); + } + } + } +} \ No newline at end of file From b6ebae50e78d123486974b4d13ba637b16cfde94 Mon Sep 17 00:00:00 2001 From: Microsoft Graph DevX Tooling Date: Wed, 2 Apr 2025 11:17:27 +0300 Subject: [PATCH 2/3] Updated readme file --- src/readme.graph.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/readme.graph.md b/src/readme.graph.md index e91431192f5..29365aa246f 100644 --- a/src/readme.graph.md +++ b/src/readme.graph.md @@ -506,7 +506,9 @@ directive: } }); } - $ = $.replace('// work', '// work\n\t\t\t\tConsole.WriteLine("*****************Warning: If you are passing any password related parameters please note that plain text passwords will be disabled soon.*****************");\n\t\t\t\tConsole.WriteLine("*****************Please convert your password to secure string *****************");\n\t\t\t\tConsole.WriteLine("*****************Example: $securePassword = ConvertTo-SecureString -String -AsPlainText -Force *****************");') + if($.includes('password') || $.includes('Password')){ + $ = $.replace('// work', '// work\n\t\t\t\tConsole.WriteLine("*****************Warning: Plain text passwords will soon be disabled.*****************");\n\t\t\t\tConsole.WriteLine("*****************Please convert your password to a secure string *****************");\n\t\t\t\tConsole.WriteLine("*****************Example: $securePassword = ConvertTo-SecureString -String -AsPlainText -Force *****************");') + } return $; } From 3e34968a4a4bcbb1a5fb16f286bf6e52fd82b34d Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 4 Apr 2025 10:30:27 +0300 Subject: [PATCH 3/3] Update ModuleMetadata.json with SDK version --- config/ModuleMetadata.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/ModuleMetadata.json b/config/ModuleMetadata.json index 35c19776f9a..8512ab5f614 100644 --- a/config/ModuleMetadata.json +++ b/config/ModuleMetadata.json @@ -26,16 +26,16 @@ ], "versions": { "authentication": { - "prerelease": "", - "version": "2.26.1" + "prerelease": "preview1", + "version": "2.28.0" }, "beta": { - "prerelease": "", - "version": "2.26.1" + "prerelease": "preview1", + "version": "2.28.0" }, "v1.0": { "prerelease": "", - "version": "2.26.1" + "version": "2.28.0" } } }