Skip to content

Commit

Permalink
Client credentials now work (#75)
Browse files Browse the repository at this point in the history
* Client credentials now work  #74
* Crazy fast winget package search
  • Loading branch information
svrooij authored May 28, 2024
1 parent 83819fc commit e9d1aa8
Show file tree
Hide file tree
Showing 14 changed files with 785 additions and 33 deletions.
8 changes: 8 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
"script": "${file}",
"cwd": "${fileDirname}"
},
{
"name": "Build and Load PowerShell Module",
"type": "PowerShell",
"request": "launch",
"preLaunchTask": "dotnet: build-module", // replace with your actual build task name
"script": "Import-Module .\\bin\\Debug\\net6.0\\WinTuner.psd1 -Verbose", // replace with your actual script path
"cwd": "${workspaceFolder}\\src\\Svrooij.WinTuner.CmdLets"
},
{
"name": ".NET Core Package Microsoft SQL Server Management Studio",
"type": "coreclr",
Expand Down
12 changes: 11 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@
"isDefault": true
},
"problemMatcher": "$msCompile"
}
},
{
"type": "shell",
"label": "dotnet: build-module",
"command": "dotnet build ${workspaceFolder}\\src\\Svrooij.WinTuner.CmdLets\\Svrooij.WinTuner.CmdLets.csproj",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
}
]
}
45 changes: 34 additions & 11 deletions src/Svrooij.WinTuner.CmdLets/Commands/BaseIntuneCmdlet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Svrooij.WinTuner.CmdLets.Commands;
public abstract class BaseIntuneCmdlet : DependencyCmdlet<Startup>
{
private const string DefaultClientId = "d5a8a406-3b1d-4069-91cc-d76acdd812fe";
private const string DefaultClientCredentialScope = "https://graph.microsoft.com/.default";

/// <summary>
///
Expand All @@ -25,7 +26,7 @@ public abstract class BaseIntuneCmdlet : DependencyCmdlet<Startup>
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = false,
HelpMessage = "Use a managed identity to connect to Intune")]
public bool UseManagedIdentity { get; set; }
public bool UseManagedIdentity { get; set; } = Environment.GetEnvironmentVariable("AZURE_USE_MANAGED_IDENTITY")?.Equals("true", StringComparison.OrdinalIgnoreCase) == true;

/// <summary>
///
Expand All @@ -36,7 +37,7 @@ public abstract class BaseIntuneCmdlet : DependencyCmdlet<Startup>
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = false,
HelpMessage = "Use default Azure Credentials from Azure.Identity to connect to Intune")]
public bool UseDefaultAzureCredential { get; set; }
public bool UseDefaultAzureCredential { get; set; } = Environment.GetEnvironmentVariable("AZURE_USE_DEFAULT_CREDENTIALS")?.Equals("true", StringComparison.OrdinalIgnoreCase) == true;

/// <summary>
///
Expand All @@ -47,7 +48,7 @@ public abstract class BaseIntuneCmdlet : DependencyCmdlet<Startup>
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = false,
HelpMessage = "Use a token from another source to connect to Intune")]
public string? Token { get; set; }
public string? Token { get; set; } = Environment.GetEnvironmentVariable("AZURE_TOKEN");

/// <summary>
///
Expand Down Expand Up @@ -105,6 +106,17 @@ public abstract class BaseIntuneCmdlet : DependencyCmdlet<Startup>
HelpMessage = "Specify the client secret, mandatory for Client Credentials flow. Loaded from `AZURE_CLIENT_SECRET`")]
public string? ClientSecret { get; set; } = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");

/// <summary>
///
/// </summary>
[Parameter(
Mandatory = false,
Position = 40,
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = false,
HelpMessage = "Specify the scopes to request, default is `DeviceManagementConfiguration.ReadWrite.All`, `DeviceManagementApps.ReadWrite.All`")]
public string[]? Scopes { get; set; } = Environment.GetEnvironmentVariable("AZURE_SCOPES")?.Split(' ');

/// <summary>
///
/// </summary>
Expand All @@ -119,6 +131,7 @@ internal void ValidateAuthenticationParameters()

if (UseManagedIdentity || UseDefaultAzureCredential)
{
Scopes ??= new[] { DefaultClientCredentialScope };
return;
}

Expand All @@ -127,6 +140,12 @@ internal void ValidateAuthenticationParameters()
return;
}

if (!string.IsNullOrEmpty(ClientId) && !string.IsNullOrEmpty(ClientSecret) && !string.IsNullOrEmpty(TenantId))
{
Scopes ??= new[] { DefaultClientCredentialScope };
return;
}

throw new ArgumentException($"Use `{nameof(Token)}`, `{nameof(UseManagedIdentity)}`, `{nameof(UseDefaultAzureCredential)}` or `{nameof(Username)}` to select the graph connection type", nameof(ParameterSetName));
}

Expand All @@ -137,7 +156,7 @@ internal IAuthenticationProvider CreateAuthenticationProvider(string[]? scopes =
return new WingetIntune.Internal.Msal.StaticAuthenticationProvider(Token);
}

var scope = (scopes ?? DefaultScopes)[0];
var scope = (Scopes ?? scopes ?? DefaultScopes)[0];

if (UseManagedIdentity || UseDefaultAzureCredential)
{
Expand All @@ -148,16 +167,20 @@ internal IAuthenticationProvider CreateAuthenticationProvider(string[]? scopes =
return new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(credentials, null, null, scope);
}

if (!string.IsNullOrEmpty(ClientId) && !string.IsNullOrEmpty(ClientSecret) && !string.IsNullOrEmpty(TenantId))
if (!string.IsNullOrEmpty(ClientId) && !string.IsNullOrEmpty(TenantId))
{
return new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(new Azure.Identity.ClientSecretCredential(TenantId, ClientId, ClientSecret, new Azure.Identity.ClientSecretCredentialOptions
if (!string.IsNullOrEmpty(ClientSecret))
{
TokenCachePersistenceOptions = new Azure.Identity.TokenCachePersistenceOptions
return new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(new Azure.Identity.ClientSecretCredential(TenantId, ClientId, ClientSecret, new Azure.Identity.ClientSecretCredentialOptions
{
Name = "WinTuner-PowerShell-CC",
UnsafeAllowUnencryptedStorage = true,
}
}), scopes: new[] { scope });
TokenCachePersistenceOptions = new Azure.Identity.TokenCachePersistenceOptions
{
Name = "WinTuner-PowerShell-CC",
UnsafeAllowUnencryptedStorage = true,
}
}), scopes: scope);
}

}

// Alternative interactive authentication in case the broker is not working as expected.
Expand Down
46 changes: 46 additions & 0 deletions src/Svrooij.WinTuner.CmdLets/Commands/SearchWtWinGetPackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Svrooij.PowerShell.DependencyInjection;
using System;
using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;

namespace Svrooij.WinTuner.CmdLets.Commands;

/// <summary>
/// <para type="synopsis">Search for packages in winget</para>
/// <para type="description">Search for WinGet packages, but faster</para>
/// </summary>
/// <example>
/// <para type="description">Search for 'fire', did I tell you it's fast?</para>
/// <code>Search-WtWinGetPackage fire</code>
/// </example>
[Cmdlet(VerbsCommon.Search, "WtWinGetPackage", HelpUri = "https://wintuner.app/docs/wintuner-powershell/Search-WtWingetPackage")]
[OutputType(typeof(Winget.CommunityRepository.Models.WingetEntry[]))]
public class SearchWtWinGetPackage : DependencyCmdlet<Startup>
{
/// <summary>
///
/// </summary>
[Parameter(
Mandatory = true,
Position = 0,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
HelpMessage = "Part of the package ID, 2 characters minimum")]
public string? PackageId { get; set; }

[ServiceDependency]
private Winget.CommunityRepository.WingetRepository wingetRepository;

/// <inheritdoc />
public override async Task ProcessRecordAsync(CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(PackageId) || PackageId.Length <= 1)
{
throw new ArgumentException("PackageId is required");
}
var packages = await wingetRepository.SearchPackage(PackageId ?? throw new ArgumentNullException(nameof(PackageId)), cancellationToken);

WriteObject(packages);
}
}
Loading

0 comments on commit e9d1aa8

Please sign in to comment.