diff --git a/config/ModuleMetadata.json b/config/ModuleMetadata.json index 35c19776f9..8512ab5f61 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" } } } diff --git a/src/readme.graph.md b/src/readme.graph.md index aaa35410a6..29365aa246 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,9 @@ directive: } }); } + 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 $; } @@ -963,6 +977,27 @@ directive: set: alias: ^(.*)(OnPremises)(.*)$ +# Secure password implementation. + - 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"; # Setting the alias below as per the request on issue [#3241](https://github.com/microsoftgraph/msgraph-sdk-powershell/issues/3241) - where: diff --git a/tools/Custom/HttpMessageLogFormatter.cs b/tools/Custom/HttpMessageLogFormatter.cs index 76c33700c3..258ee2cae2 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 1d8dde56db..6028486fe4 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 0000000000..a821eb7e1c --- /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