-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Improve missing/malformed file handling in user-jwts #42309
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| using System.IdentityModel.Tokens.Jwt; | ||
| using System.Linq; | ||
| using System.Text.Json; | ||
| using System.Text.Json.Nodes; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.Configuration.UserSecrets; | ||
| using Microsoft.Extensions.Tools.Internal; | ||
|
|
@@ -84,35 +85,36 @@ public static byte[] CreateSigningKeyMaterial(string userSecretsId, bool reset = | |
| var secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId); | ||
| Directory.CreateDirectory(Path.GetDirectoryName(secretsFilePath)); | ||
|
|
||
| IDictionary<string, string> secrets = null; | ||
| JsonObject secrets = null; | ||
| if (File.Exists(secretsFilePath)) | ||
| { | ||
| using var secretsFileStream = new FileStream(secretsFilePath, FileMode.Open, FileAccess.Read); | ||
| if (secretsFileStream.Length > 0) | ||
| { | ||
| secrets = JsonSerializer.Deserialize<IDictionary<string, string>>(secretsFileStream) ?? new Dictionary<string, string>(); | ||
| secrets = JsonSerializer.Deserialize<JsonObject>(secretsFileStream); | ||
| } | ||
| } | ||
|
|
||
| secrets ??= new Dictionary<string, string>(); | ||
| secrets ??= new JsonObject(); | ||
|
|
||
| if (reset && secrets.ContainsKey(DevJwtsDefaults.SigningKeyConfigurationKey)) | ||
| { | ||
| secrets.Remove(DevJwtsDefaults.SigningKeyConfigurationKey); | ||
| } | ||
| secrets.Add(DevJwtsDefaults.SigningKeyConfigurationKey, Convert.ToBase64String(newKeyMaterial)); | ||
| secrets.Add(DevJwtsDefaults.SigningKeyConfigurationKey, JsonValue.Create(Convert.ToBase64String(newKeyMaterial))); | ||
|
|
||
| using var secretsWriteStream = new FileStream(secretsFilePath, FileMode.Create, FileAccess.Write); | ||
| JsonSerializer.Serialize(secretsWriteStream, secrets); | ||
|
|
||
| return newKeyMaterial; | ||
| } | ||
|
|
||
| public static string[] GetAudienceCandidatesFromLaunchSettings(string project) | ||
| public static List<string> GetAudienceCandidatesFromLaunchSettings(string project) | ||
| { | ||
| ArgumentException.ThrowIfNullOrEmpty(nameof(project)); | ||
|
|
||
| var launchSettingsFilePath = Path.Combine(Path.GetDirectoryName(project)!, "Properties", "launchSettings.json"); | ||
| var applicationUrls = new List<string>(); | ||
| if (File.Exists(launchSettingsFilePath)) | ||
| { | ||
| using var launchSettingsFileStream = new FileStream(launchSettingsFilePath, FileMode.Open, FileAccess.Read); | ||
|
|
@@ -124,26 +126,65 @@ public static string[] GetAudienceCandidatesFromLaunchSettings(string project) | |
| var profilesEnumerator = profiles.EnumerateObject(); | ||
| foreach (var profile in profilesEnumerator) | ||
| { | ||
| if (profile.Value.TryGetProperty("commandName", out var commandName)) | ||
| if (ExtractKestrelUrlsFromProfile(profile) is { } kestrelUrls) | ||
| { | ||
| if (commandName.ValueEquals("Project")) | ||
| { | ||
| if (profile.Value.TryGetProperty("applicationUrl", out var applicationUrl)) | ||
| { | ||
| var value = applicationUrl.GetString(); | ||
| if (value is { } applicationUrls) | ||
| { | ||
| return applicationUrls.Split(';'); | ||
| } | ||
| } | ||
| } | ||
| applicationUrls.AddRange(kestrelUrls); | ||
| } | ||
|
|
||
| if (ExtractIISExpressUrlFromProfile(profile) is { } iisUrls) | ||
| { | ||
| applicationUrls.AddRange(iisUrls); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| return applicationUrls; | ||
|
|
||
| static List<string> ExtractIISExpressUrlFromProfile(JsonProperty profile) | ||
| { | ||
| if (profile.NameEquals("iisSettings")) | ||
| { | ||
| if (profile.Value.TryGetProperty("iisExpress", out var iisExpress)) | ||
| { | ||
| List<string> iisUrls = new(); | ||
| if (iisExpress.TryGetProperty("applicationUrl", out var iisUrl)) | ||
| { | ||
| iisUrls.Add(iisUrl.GetString()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can these urls also be
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't think so, but we'd need to check with VS
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested it out. VS will throw if you have something like this: |
||
| } | ||
|
|
||
| if (iisExpress.TryGetProperty("sslPort", out var sslPort)) | ||
| { | ||
| iisUrls.Add($"https://localhost:{sslPort.GetInt32()}"); | ||
| } | ||
|
|
||
| return iisUrls; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| static string[] ExtractKestrelUrlsFromProfile(JsonProperty profile) | ||
| { | ||
| if (profile.Value.TryGetProperty("commandName", out var commandName)) | ||
| { | ||
| if (commandName.ValueEquals("Project")) | ||
| { | ||
| if (profile.Value.TryGetProperty("applicationUrl", out var applicationUrl)) | ||
| { | ||
| var value = applicationUrl.GetString(); | ||
| if (value is { } urls) | ||
| { | ||
| return urls.Split(';'); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
|
|
||
| public static void PrintJwt(IReporter reporter, Jwt jwt, bool showAll, JwtSecurityToken fullToken = null) | ||
|
|
@@ -163,12 +204,12 @@ public static void PrintJwt(IReporter reporter, Jwt jwt, bool showAll, JwtSecuri | |
| : string.Join(", ", jwt.Scopes); | ||
| reporter.Output($"{Resources.JwtPrint_Scopes}: {scopesValue}"); | ||
| } | ||
|
|
||
| if (!jwt.Roles.IsNullOrEmpty() || showAll) | ||
| { | ||
| var rolesValue = jwt.Roles.IsNullOrEmpty() | ||
| ? "none" | ||
| : String.Join(", ", jwt.Roles); | ||
| : string.Join(", ", jwt.Roles); | ||
| reporter.Output($"{Resources.JwtPrint_Roles}: [{rolesValue}]"); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: else if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's the right call here. We want to populate audience from both IIS and Kestrel-based URLs here so we get a complete set of audiences by the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I think it should be else if is because if the Kestrel check of
profile.Value.TryGetProperty("commandName", out var commandName)passes then the IIS check won't pass for this specificprofile. Again, it's a nit, not code we need to worry about performance for.