Skip to content

Implementation for secure password #3251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions config/ModuleMetadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
37 changes: 36 additions & 1 deletion src/readme.graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"))')
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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 <Your password> -AsPlainText -Force *****************");')
}

return $;
}
Expand Down Expand Up @@ -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:
Expand Down
27 changes: 26 additions & 1 deletion tools/Custom/HttpMessageLogFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ private static object SanitizeBody(string body)
{
body = matcher.Replace(body, "$1\"<redacted>\"");
}

// Remove password:* instances in body.
body = AbstractPasswords(body);
return body;
}

Expand Down Expand Up @@ -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<Dictionary<string, object>>(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;
}
}
}
3 changes: 2 additions & 1 deletion tools/Custom/PropertyTracker.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Security;
using System.Collections.Generic;


Expand Down Expand Up @@ -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());
}
Expand Down
20 changes: 20 additions & 0 deletions tools/Custom/SecureStringExtension.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
Loading