Skip to content
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

Add Audit Logging To SecreteManager.exe #4183

Open
wants to merge 51 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
05b8a68
Initial change to add secuirty audit logging capabilites and to updat…
davfost Sep 26, 2024
cd9b948
Resolving merge conflicts with main
davfost Sep 26, 2024
7134128
Resolving merge conflict with main
davfost Sep 26, 2024
e661780
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Sep 27, 2024
87c81eb
Adding logging for Key creation in Azure Key vaults
davfost Sep 27, 2024
7f74cda
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Sep 27, 2024
80629f8
Removing temporary test project from solution.
davfost Sep 27, 2024
f71bee0
Resorting using statements per PR comments
davfost Sep 27, 2024
cc6d25e
Update to address unit test failure and add capabilities to set globa…
davfost Sep 30, 2024
db2f714
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Sep 30, 2024
6676fbe
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Oct 7, 2024
45d061c
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Oct 21, 2024
8c4a7d3
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Oct 24, 2024
665ec4c
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Info…
davfost Oct 24, 2024
e4e3ee6
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Proj…
davfost Oct 24, 2024
8cb0a82
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Proj…
davfost Oct 24, 2024
e7c01fa
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Proj…
davfost Oct 24, 2024
f92854a
Update for PR comment. Thorw on bad Guid ID in arguments.
davfost Oct 24, 2024
8764fe8
Merge branch 'dev/davfost/20240710' of https://github.com/dotnet/dnce…
davfost Oct 24, 2024
4e52cfe
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Vali…
davfost Oct 24, 2024
10e3150
Update src/SecretManager/Microsoft.DncEng.SecretManager/ITokenCredent…
davfost Oct 24, 2024
c964d13
Update src/SecretManager/Microsoft.DncEng.SecretManager/SecurityAudit…
davfost Oct 24, 2024
9204ef1
Update src/SecretManager/Microsoft.DncEng.SecretManager/SecurityAudit…
davfost Oct 24, 2024
09eb38a
Update src/SecretManager/Microsoft.DncEng.SecretManager/SecurityAudit…
davfost Oct 24, 2024
d6a9dfc
Updates for PR comments. IpV6 support for GetLocalIpAddress
davfost Oct 24, 2024
683509f
Merge branch 'dev/davfost/20240710' of https://github.com/dotnet/dnce…
davfost Oct 24, 2024
00b4f16
Update src/SecretManager/Microsoft.DncEng.SecretManager/Program.cs
davfost Oct 24, 2024
21c7bfe
Update src/SecretManager/Microsoft.DncEng.SecretManager/Program.cs
davfost Oct 24, 2024
f752944
Update src/SecretManager/Microsoft.DncEng.SecretManager/Program.cs
davfost Oct 24, 2024
c7e56da
Update src/SecretManager/Microsoft.DncEng.SecretManager/StorageTypes/…
davfost Oct 24, 2024
507ce96
Update src/SecretManager/Microsoft.DncEng.SecretManager/StorageTypes/…
davfost Oct 24, 2024
394d13c
Update to resolve collisions from direct PR submitted changes.
davfost Oct 24, 2024
b53ab8e
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Proj…
davfost Oct 28, 2024
5ad6fe2
Update src/SecretManager/Microsoft.DncEng.SecretManager/Commands/Proj…
davfost Oct 28, 2024
b2ff903
Updates to product altering ServiceTreeId from operation outside of c…
davfost Oct 28, 2024
846b088
Merge branch 'dev/davfost/20240710' of https://github.com/dotnet/dnce…
davfost Oct 28, 2024
0b204be
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Oct 28, 2024
0a93297
Removing unneeded comments in dependency injection logic.
davfost Oct 28, 2024
c341bf7
Updating SetCredentialIdentityValues to ensure it will not cause the …
davfost Oct 28, 2024
b5995a7
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Oct 28, 2024
40c421f
Merge remote-tracking branch 'origin/main' into dev/davfost/20240710
davfost Oct 28, 2024
5bd1293
Update to use private 'set' for property definition instead of using …
davfost Oct 28, 2024
b5c497e
Updating comment for servicetreeid based on PR comments.
davfost Oct 28, 2024
b208ff4
Renaming ValidateServiceTreeIdOption for PR comments.
davfost Oct 28, 2024
2fa7882
Removing comments per PR comment
davfost Oct 28, 2024
939cbef
Remobing local testing project that was checked in by mistake.
davfost Oct 28, 2024
0bbfd80
Changing access level of _globalCommand and ServiceTreeId class varia…
davfost Oct 28, 2024
032f375
Update src/SecretManager/Microsoft.DncEng.SecretManager/Program.cs
davfost Oct 28, 2024
b70116d
Update src/SecretManager/Microsoft.DncEng.SecretManager/SecretManager…
davfost Oct 28, 2024
f2253b6
Making SetCredentialIdentityValues non volitile based on PR comments.
davfost Oct 28, 2024
13199b5
Fixing a typo in the last change to make SetCredentialIdentityValues …
davfost Oct 28, 2024
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
15 changes: 8 additions & 7 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@
<PackageVersion Include="Microsoft.DotNet.SwaggerGenerator.MSBuild" Version="$(MicrosoftDotNetSwaggerGeneratorMSBuildVersion)" />
<PackageVersion Include="Microsoft.DotNet.VersionTools" Version="$(MicrosoftDotNetVersionToolsVersion)" />
<PackageVersion Include="Microsoft.DotNet.Web.Authentication" Version="$(MicrosoftDotNetWebAuthenticationVersion)" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
davfost marked this conversation as resolved.
Show resolved Hide resolved
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.21.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.0" />
<PackageVersion Include="Microsoft.Identity.Client" Version="4.62.0-preview" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Sdk.Functions" Version="4.1.1" />
Expand All @@ -81,6 +81,7 @@
<PackageVersion Include="NUnit" Version="4.2.2" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageVersion Include="Octokit" Version="13.0.1" />
<PackageVersion Include="OpenTelemetry.Audit.Geneva" Version="2.2.3" />
<PackageVersion Include="ServiceFabricMocks" Version="$(ServiceFabricMocksVersion)" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta1.21308.1" />
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
Expand Down
3 changes: 2 additions & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="AzureGenevaMonitoring" value="https://msblox.pkgs.visualstudio.com/_packaging/AzureGenevaMonitoring/nuget/v3/index.json" />
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What permissions are required to access this feed? Will there be issues with CI and individual users getting access?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is currently an issue with this feed and it blocked the PR build from completing. I have been discussing it with Matt Mitchell to see how it can be resolved. As you observed this feed requires external permissions that are not granted so the package restore is failing.

The basic issue is that the Audit team says we have to use the version of the package that comes from 'this' feed and not the public version of the package.

  • I need to investigate if the 'public' feed version of the package has the same class and methods defined. If so, we can do something where the 'internal feed' is different than the feed used in the 'public' processes.
  • I also have open questions to the Audit team to verify if the package from 'this' feed CAN be exposed publicly. If so we may be able to just manually move target versions into one of the feeds we manage directly.

Matt said these two options have been used in the past to deal with other packages that have similar issues.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good good, Matt should have good ideas here.

Just to say it explicitly, do not merge this PR until there's a good solution for public CI and for local dev.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I don't think we can merge (validly) until this is address as it build breaks at the NuGet restore step. So, it has to be fixed in some way in order to pass the PR validation build.

<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public ScenarioTestsBase()
protected async Task ExecuteSynchronizeCommand(string manifest)
{
ServiceCollection services = new ServiceCollection();
// Dependency injection instruction needed to support properties used for Geneval Logging operations
services.AddSingleton(new GlobalCommand());
services.AddSingleton(new SecurityAuditLogger(Guid.Empty));

// Original dependency injection instructions
services.AddSingleton<SynchronizeCommand>();

Program program = new Program();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;

Expand All @@ -7,9 +10,29 @@ public sealed class WrappedTokenProvider : ITokenCredentialProvider
{
private readonly TokenCredential _tokenCredential;

/// <inheritdoc/>
public string ApplicationId { get; internal set; }
/// <inheritdoc/>
public string TenantId { get; internal set; }

public WrappedTokenProvider(TokenCredential tokenCredential)
{
_tokenCredential = tokenCredential;
SetCredentialIdentityValues();
}

/// <inheritdoc/>
internal void SetCredentialIdentityValues()
{
// Get a token from the crendential provider
var tokenRequestContext = new TokenRequestContext(new[] { "https://management.azure.com/.default" });
var token = _tokenCredential.GetToken(tokenRequestContext, CancellationToken.None);

// Decode the JWT to get user identity information
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(token.Token) as JwtSecurityToken;
ApplicationId = jsonToken?.Claims?.FirstOrDefault(claim => claim.Type == "oid")?.Value ?? "Claim Oid Not Found";
TenantId = jsonToken?.Claims?.FirstOrDefault(claim => claim.Type == "tenant_id")?.Value ?? "Claim tenant_id Not Found";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these expressions throw instead of setting the properties to invalid values❓

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of reasons not to throw.

  • We want this logging operation to be non-volatile no matter what. It is better not to log anything than have logging or logging setup cause our service to fail.
  • The correctness of these value is not enforced by Geneva
    • They force us to pass a value but do not verify it is a correct or valid Id just that a value exists
  • Logs are still valuable even if we cannot get ever bit identifying data that would want so we would prefer to log something rather than nothing

Looking at this code again throw I think it should also be wrapped in a try catch block to ensure that even if the process of extracting clams value throws an exception the process does not cause the service to fail.

}

public Task<TokenCredential> GetCredentialAsync() => Task.FromResult(_tokenCredential);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -26,6 +24,11 @@ private async Task TestCommand(DateTimeOffset now, string manifestText, string l
var cancellationToken = cts.Token;

var services = new ServiceCollection();
// Dependency injection instruction needed to support properties used for Geneval Logging operations
services.AddSingleton(new GlobalCommand());
services.AddSingleton(new SecurityAuditLogger(Guid.Empty));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are test classes, would it be better to make a do-nothing version of SecurityAuditLogger? (I'm not sure what SecurityAuditLogger does.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly I think you would be describing a Stub class which I think would be fine. However, the SecurityAutidLogger class is supposed to be non-volatile meaning it should not throw even if logging fails. So, if this does throw in any test it would mean that there is a problem with the class.

NOTES:

SecurityAutidLogger is a wrapper class used to simplify calls to the OpenTelemetry.Audit.Geneva methods. This is because the OpenTelemetry.Audit.Geneva operation have heavy setup costs and are volatile. So, this wrapper reduces the setup costs of audit logging and ensure the logging operations are non-volatile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please confirm if you want me to make a stub for this class. My take is that creating a stub is an extra thing we would have to manage, and this class is supposed to be non-volatile so if it is throwing unexpectedly in test cases that is a real problem not a false positive. But I am fine making a stub if you prefer it that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to use the Moq lib to resolve this based on our offline conversation.


// Original dependency injection instructions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't thin kthis comment (or the previous comment) is necessary. The code shouldn't have a sense of "human time". That's what version control handles. The code should always just be "this is what it is right now".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to have an offline conversation with you about commenting in general. I was thinking about trying to start a team wide discussion but I would like to get some individual views first.

In my view the first set of comments should exist because it is not reasonable to assume a new Dev will intuitively know 'why' injection instruction were added. Meaning what are the objects used for and why are they needed. Yes you can 'figure it out' if you go back though history looking for when the lines were added and then examine the other code changes (hoping that it was a small well scoped change) to determine what caused the injection to be added or do global search fining where the objects are used and figuring out what they do in those classes. However, that is an investigation delay that can be avoided if you have a comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer we leave historical comments out of the code though they sometimes make sense in commit descriptions. investigations that go back in history tend to be about blame or providing more descriptive information for a fix e.g., "this partially reverts {some commit} to avoid {some problem}". but the history doesn't usually help figuring the fix out in the first place. put another way git log and similar for history, not comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the historical comment.

services.AddSingleton(Mock.Of<IConsole>());

var storageLocationTypeRegistry = new Mock<StorageLocationTypeRegistry>(MockBehavior.Strict);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DncEng.CommandLineLib;
using Microsoft.DncEng.SecretManager.Commands;

namespace Microsoft.DncEng.SecretManager;

[Command("info")]
class InfoCommand : Command
class InfoCommand : ProjectBaseCommand
{
private readonly IConsole _console;

public InfoCommand(IConsole console)
public InfoCommand(GlobalCommand globalCommand, IConsole console): base(globalCommand)
{
_console = console;
}

public override Task RunAsync(CancellationToken cancellationToken)
{
// Provides a courtesy warning message if the ServiceTreeId option is set to a empty guid
ValidateServiceTreeIdOption();

var exeName = Process.GetCurrentProcess().ProcessName;
var version = Assembly.GetEntryAssembly()
?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@

using System;
using Microsoft.DncEng.CommandLineLib;
using Mono.Options;

namespace Microsoft.DncEng.SecretManager.Commands
{
/// <summary>
/// This class is used to extend the CommandLineLib.GlobalCommand class to add a global options spacific to this project
/// </summary>
public class ProjectBaseCommand : GlobalCommand
{
/// <summary>
/// Indicates if the global option for 'quiet' is set
/// </summary>
public bool Quiet { get { return Verbosity == VerbosityLevel.Quiet; } }

/// <summary>
/// Check for local environment values to indicate you are running for Azure DevOps
/// SYSTEM_COLLECTIONURI is a default environment variable in Azure DevOps
/// </summary>
private bool RunningInAzureDevOps = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SYSTEM_COLLECTIONURI"));

/// <summary>
/// Provides the ServiceTreeId set with global options
/// The ID is a guid and is set to Guid.Empty if not set
/// </summary>
private Guid _ServiceTreeId = Guid.Empty;

/// <summary>
/// Provides read only access to the _ServiceTreeId
/// </summary>
public Guid ServiceTreeId { get { return _ServiceTreeId; } }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest removing _ServiceTreeId and

Suggested change
public Guid ServiceTreeId { get { return _ServiceTreeId; } }
public Guid ServiceTreeId { get; private set }


/// <summary>
/// Base constructor for the ProjectBaseCommand class
/// </summary>
public ProjectBaseCommand(GlobalCommand globalCommand)
{
Verbosity = globalCommand.Verbosity;
Help = globalCommand.Help;
}

/// <summary>
/// Overrides the GetOptions method from the base class to add a custom option for the ServiceTreeId
/// </summary>
public override OptionSet GetOptions()
{
return new OptionSet()
{
{"servicetreeid=", "The service tree ID (must be a valid GUID id from aka.ms/servicetree)", id =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what enforces validity in Service Tree❓

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing other than it being a 'Guid'. The audit logging process from does not verify that a ID is valid just that it is passed and the value meets the data type criteria.

If you pass an empty Guid or a valid Guid for a non-existent service tree ID it would still 'log' to Geneva successfully.

I don't think we have an additional obligation to make this process have to communicate with an external Service Tree API to ensure IDs are valid.

If the 'service' does not use a correct ID then it will still report to Geneva but Geneva will not close any SFI KPI work items associated with the service so in that sense you are incentivized to use a valid and correct ID.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put another way, very few non-empty Guids are not valid service tree IDs. looks like those are ignored until the option is used. that is, this checks for Guid validity and not service tree validity

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think there is any reasonable way for this library to ensure it is a valid service tree ID. I think about this instead as being sort of "tagging" information or metadata that gets passed to Geneva, then Geneva happens to do something interesting with it.

Copy link
Member

@dougbu dougbu Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine w/ the level of validation implemented here. my main concern is the option's description implies something more. how about the following❓

Suggested change
{"servicetreeid=", "The service tree ID (must be a valid GUID id from aka.ms/servicetree)", id =>
{"servicetreeid=", "The service tree ID (must be a non-empty GUID and should be a valid id from https://aka.ms/servicetree)", id =>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what you mean about the description for input but 'should be' implies that something else is OK to use here.

It is not. The value is only meaningful if it is a valid service tree ID and specifically the service tree ID for your service.

Any other Guid even one for a valid service tree ID for a service other than the one running the process would be invalid.

The value 'would' be accepted by Geneva and recorded but it is not correct and does not benefit the actual service that needs to report so it can close it's KPIs.

However, the way this option is being provided allows for service we do not own to also use this tool and generate a log that can go to Geneva.

We cannot predict what those possible IDs would be or how they will change in the future.

What we do know is that the ID is meant to be a Guid structure that comes from https://aka.ms/servicetree and relates to the specific service owned by the 'caller'.

I will change the comment to be something less directive so it does state must or should and instead just provides the link back to the location where valid IDs can be found.

{
if (Guid.TryParse(id, out var guid))
davfost marked this conversation as resolved.
Show resolved Hide resolved
{
_ServiceTreeId = guid;
}
else
{
throw new ArgumentException($"Failed to parse a valid Guid value from ServiceTreeId value '{id}'!");
}
}
}
};
}

/// <summary>
/// Provides a non-volitie warning message if the ServiceTreeId option is set to a empty guid value and argments have been parsed
internal void ValidateServiceTreeIdOption()
Copy link
Member

@dougbu dougbu Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, this doesn't actually validate the service tree ID. it only validates the value is a non-empty Guid. suggest using a different name or actually confirming the ID is valid

{
if (!Quiet && ServiceTreeId == Guid.Empty)
{
// If running in Azure DevOps use VSO tagging in the console output to the warning message will be handled by the Azure DevOps build system
if (RunningInAzureDevOps)
{
WriteWarningMessage("##vso[task.logissue type=warning]ServiceTreeId is set to an Empty Guid! Security Audit logging will be suppressed!");
}
// Else write a general warning messgae to console
else
{
WriteWarningMessage("ServiceTreeId is set to an Empty Guid! Security Audit logging will be suppressed!");
}
}
}

internal void WriteWarningMessage(string message)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(message);
Console.ResetColor();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
using ConsoleTables;
using Microsoft.DncEng.CommandLineLib;
using Microsoft.DncEng.SecretManager.StorageTypes;
using Microsoft.VisualStudio.Services.Common;

using Mono.Options;

using Command = Microsoft.DncEng.CommandLineLib.Command;

namespace Microsoft.DncEng.SecretManager.Commands;

[Command("synchronize")]
public class SynchronizeCommand : Command
public class SynchronizeCommand : ProjectBaseCommand
{
private readonly StorageLocationTypeRegistry _storageLocationTypeRegistry;
private readonly SecretTypeRegistry _secretTypeRegistry;
Expand All @@ -24,7 +27,7 @@ public class SynchronizeCommand : Command
private bool _verifyOnly = false;
private readonly List<string> _forcedSecrets = new();

public SynchronizeCommand(StorageLocationTypeRegistry storageLocationTypeRegistry, SecretTypeRegistry secretTypeRegistry, ISystemClock clock, IConsole console)
public SynchronizeCommand(GlobalCommand globalCommand, StorageLocationTypeRegistry storageLocationTypeRegistry, SecretTypeRegistry secretTypeRegistry, ISystemClock clock, IConsole console) : base(globalCommand)
{
_storageLocationTypeRegistry = storageLocationTypeRegistry;
_secretTypeRegistry = secretTypeRegistry;
Expand All @@ -42,19 +45,22 @@ public override List<string> HandlePositionalArguments(List<string> args)

public override OptionSet GetOptions()
{
return new OptionSet
return base.GetOptions().AddRange(new OptionSet()
{
{"f|force", "Force rotate all secrets", f => _force = !string.IsNullOrEmpty(f)},
{"force-secret=", "Force rotate the specified secret", _forcedSecrets.Add},
{"skip-untracked", "Skip untracked secrets", f => _skipUntracked = !string.IsNullOrEmpty(f)},
{"verify-only", "Does not rotate any secrets, instead, produces an error for every secret that needs to be rotated.", v => _verifyOnly = !string.IsNullOrEmpty(v)},
};
});
}

public override async Task RunAsync(CancellationToken cancellationToken)
{
try
{
// Provides a curtisy warning message if the ServiceTreeId option is set to a empty guid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment would not be necessary if the ValidateServiceTreeIdOption name were updated

ValidateServiceTreeIdOption();

Console.OutputEncoding = System.Text.Encoding.UTF8;
_console.WriteLine($"🔁 Synchronizing secrets contained in {_manifestFile}");
if (_force || _forcedSecrets.Any())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,25 @@
using Azure.Core;
using Microsoft.DncEng.CommandLineLib;

namespace Microsoft.DncEng.SecretManager;
namespace Microsoft.DncEng.SecretManager.Commands;

[Command("test")]
class TestCommand : Command
class TestCommand : ProjectBaseCommand
{
private readonly IConsole _console;
private readonly ITokenCredentialProvider _tokenProvider;

public TestCommand(IConsole console, ITokenCredentialProvider tokenProvider)
public TestCommand(GlobalCommand globalCommand, IConsole console, ITokenCredentialProvider tokenProvider) : base(globalCommand)
{
_console = console;
_tokenProvider = tokenProvider;
}

public override async Task RunAsync(CancellationToken cancellationToken)
{
// Provides a curtisy warning message if the ServiceTreeId option is set to a empty guid
ValidateServiceTreeIdOption();

var creds = await _tokenProvider.GetCredentialAsync();

var token = await creds.GetTokenAsync(new TokenRequestContext(new[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.DncEng.CommandLineLib;
using Microsoft.DncEng.SecretManager.StorageTypes;
using Microsoft.VisualStudio.Services.Common;
davfost marked this conversation as resolved.
Show resolved Hide resolved
using Mono.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand All @@ -14,15 +16,15 @@
namespace Microsoft.DncEng.SecretManager.Commands;

[Command("validate-all")]
public class ValidateAllCommand : Command
public class ValidateAllCommand : ProjectBaseCommand
{
private readonly IConsole _console;
private readonly SettingsFileValidator _settingsFileValidator;
private readonly StorageLocationTypeRegistry _storageLocationTypeRegistry;
private readonly List<string> _manifestFiles = new List<string>();
private string _basePath;

public ValidateAllCommand(IConsole console, SettingsFileValidator settingsFileValidator, StorageLocationTypeRegistry storageLocationTypeRegistry)
public ValidateAllCommand(GlobalCommand globalCommand, IConsole console, SettingsFileValidator settingsFileValidator, StorageLocationTypeRegistry storageLocationTypeRegistry) : base(globalCommand)
{
_console = console;
_settingsFileValidator = settingsFileValidator;
Expand All @@ -31,11 +33,11 @@ public ValidateAllCommand(IConsole console, SettingsFileValidator settingsFileVa

public override OptionSet GetOptions()
{
return new OptionSet
return base.GetOptions().AddRange(new OptionSet()
{
{"m|manifest-file=", "A secret manifest file. Can be specified more than once.", m => _manifestFiles.Add(m)},
{"b|base-path=", "The base path to search for settings files.", b => _basePath = b},
};
});
}

public override bool AreRequiredOptionsSet()
Expand All @@ -45,6 +47,9 @@ public override bool AreRequiredOptionsSet()

public override async Task RunAsync(CancellationToken cancellationToken)
{
// Provides a courtesy warning message if the ServiceTreeId option is set to an empty guid
ValidateServiceTreeIdOption();

bool haveErrors = false;
var manifestFiles = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (string manifestFile in _manifestFiles)
Expand Down
Loading
Loading