Conversation
Initial Release
There was a problem hiding this comment.
Pull request overview
Merges the 1.0.0 release branch into main by introducing the Akeyless PAM Provider implementation, adding unit/integration test projects, and publishing supporting documentation and CI/test automation.
Changes:
- Add the Akeyless PAM provider implementation (client adapter, configuration model, constants, manifest).
- Add unit + integration test suites (including CI workflow) and test-running tooling (Makefile).
- Add/refresh end-user documentation and integration metadata for release packaging/catalog publication.
Reviewed changes
Copilot reviewed 29 out of 30 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
tests/AkeylessPam.Unit.Tests/README.md |
Adds unit test project documentation and test case index. |
tests/AkeylessPam.Unit.Tests/AkeylessPamTests.cs |
Adds unit tests for configuration validation, auth behavior, and secret parsing. |
tests/AkeylessPam.Unit.Tests/AkeylessPam.Unit.Tests.csproj |
Introduces unit test project dependencies and reference to provider project. |
tests/AkeylessPam.Unit.Tests/AkeylessConfigurationTests.cs |
Adds tests for configuration model validation and constants. |
tests/AkeylessPam.Integration.Tests/README.md |
Documents integration test behavior, env vars, and test inventory. |
tests/AkeylessPam.Integration.Tests/DotEnvLoader.cs |
Adds helper to load .env values for local integration testing. |
tests/AkeylessPam.Integration.Tests/AkeylessPamIntegrationTests.cs |
Adds end-to-end integration tests for AkeylessPam.GetPassword(). |
tests/AkeylessPam.Integration.Tests/AkeylessPam.Integration.Tests.csproj |
Introduces integration test project dependencies and reference to provider project. |
tests/AkeylessPam.Integration.Tests/AkeylessApiClientTests.cs |
Adds integration tests for the low-level Akeyless API client wrapper. |
integration-manifest.json |
Updates integration manifest schema URL and enables GitHub link/catalog updates; adjusts parameter metadata. |
global.json |
Adjusts SDK rollForward behavior. |
docsource/testing.md |
Adds documentation for running unit/integration tests and required env vars. |
docsource/overview.md |
Adds brief doc overview for the integration. |
docsource/akeyless.md |
Adds detailed provider documentation (auth, secret types, mechanics, examples). |
docs/akeyless.md |
Adds/updates published documentation content for Akeyless provider. |
akeyless-pam/manifest.json |
Adds extension manifest for Keyfactor platform registration. |
akeyless-pam/akeyless-pam.csproj |
Updates target frameworks and build settings; adds InternalsVisibleTo for tests and Moq. |
akeyless-pam/Models/AkeylessConfiguration.cs |
Adds configuration model + validation logic for auth/secret types. |
akeyless-pam/IAkeylessApiClient.cs |
Adds API client abstraction for mocking and integration tests. |
akeyless-pam/Constants.cs |
Adds provider constants (default auth method/API URL). |
akeyless-pam/AkeylessPam.cs |
Adds the PAM provider implementation (auth, secret retrieval, parsing, validation, logging). |
akeyless-pam/AkeylessApiClient.cs |
Adds Akeyless SDK adapter (auth + secret retrieval). |
akeyless-pam.sln |
Updates solution to include new test projects and configurations; removes TestConsole from solution. |
TestConsole/TestConsole.csproj |
Removes the TestConsole project file. |
README.md |
Adds comprehensive repo README (install/use/support docs, payload examples). |
Makefile |
Adds build/test convenience targets. |
CHANGELOG.md |
Expands v1.0.0 entry with features/targets. |
.gitignore |
Updates ignore patterns (IDE files, manifests, Claude artifacts). |
.github/workflows/tests.yml |
Adds CI workflow to run unit + integration tests with coverage artifact upload. |
.gitattributes |
Forces Linguist language classification to C#. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // ── Debug ───────────────────────────────────────────────────────────────── | ||
|
|
||
| [SkippableFact] | ||
| public async Task Debug_K8sOrchestratorSecret_PrintsRawValue() | ||
| { | ||
| SkipIfMissingCredentials(); | ||
| const string secretName = "/pam/test/k8s-orchestrator"; | ||
|
|
||
| var client = Client; | ||
| var token = client.Authenticate(AccessId, AccessKey); | ||
| var result = await client.GetSecretValuesAsync([secretName], token); | ||
|
|
||
| // NOTE: secret value is intentionally not written to Console/output to prevent secret exposure in CI logs. | ||
|
|
||
| Assert.True(result.ContainsKey(secretName), $"Response did not contain key '{secretName}'"); | ||
| } |
There was a problem hiding this comment.
The test name says it "PrintsRawValue", but the body explicitly avoids writing the secret to output. Rename the test to reflect what it actually verifies (e.g., that the key exists / retrieval succeeds) to keep intent clear.
|
|
||
|
|
||
| ##### Define a PAM provider in Command | ||
| 1. In the Keyfactor Command Portal, hover over the ⚙️ (settings) icon in the top right corner of the screen and select **Priviledged Access Management**. |
There was a problem hiding this comment.
Spelling: "Priviledged" -> "Privileged".
| 1. In the Keyfactor Command Portal, hover over the ⚙️ (settings) icon in the top right corner of the screen and select **Priviledged Access Management**. | |
| 1. In the Keyfactor Command Portal, hover over the ⚙️ (settings) icon in the top right corner of the screen and select **Privileged Access Management**. |
| public class AkeylessPamIntegrationTests | ||
| { | ||
| private static Dictionary<string, string> BuildServerParams() | ||
| { | ||
| return new Dictionary<string, string> | ||
| { | ||
| ["Url"] = Env("AKEYLESS_API_URL", "https://api.akeyless.io"), | ||
| ["AuthType"] = Env("AKEYLESS_AUTH_TYPE", "access_key"), | ||
| ["AccessId"] = Env("AKEYLESS_ACCESS_ID"), | ||
| ["AccessKey"] = Env("AKEYLESS_ACCESS_KEY") | ||
| }; | ||
| } | ||
|
|
||
| private static string Env(string key, string? fallback = null) | ||
| => Environment.GetEnvironmentVariable(key) ?? fallback ?? string.Empty; | ||
|
|
There was a problem hiding this comment.
This integration test class relies on environment variables, but it never calls DotEnvLoader.Load(). As written, a repo-root .env file will not be loaded for these tests unless AkeylessApiClientTests happens to run first, which can cause unexpected skips locally. Consider invoking DotEnvLoader.Load() in a shared test initializer (e.g., module initializer / assembly fixture) or at least in this class' static constructor.
| @@ -39,6 +42,10 @@ | |||
| <PackageReference Include="Keyfactor.Logging" Version="1.3.0"/> | |||
| </ItemGroup> | |||
|
|
|||
| <ItemGroup Condition="'$(TargetFramework)' == 'net10.0'"> | |||
| <PackageReference Include="Keyfactor.Logging" Version="1.3.0"/> | |||
| </ItemGroup> | |||
There was a problem hiding this comment.
This project now targets only net8.0 and net10.0, but the csproj still contains conditional ItemGroups for netstandard2.0/netstandard2.1/net6.0/net9.0 that will never be used. Removing the unused TFMs (or re-adding them to TargetFrameworks) will avoid confusion and keep package reference logic maintainable.
| private ILogger Logger { get; } = LogHandler.GetClassLogger<AkeylessPam>(); | ||
|
|
||
| private string AuthToken { get; set; } = string.Empty; | ||
|
|
||
| public AkeylessPam() : this(basePath => new AkeylessApiClient(basePath)) | ||
| { | ||
| } | ||
|
|
There was a problem hiding this comment.
AuthToken is stored as mutable instance state and then reused for the subsequent secret call. If AkeylessPam is reused across requests (or invoked concurrently), concurrent calls can overwrite AuthToken and send the wrong token when fetching secrets. Prefer keeping the token as a local variable (e.g., have InitClient return (client, token) or pass the token into GetStaticSecret) so GetPassword is thread-safe/re-entrant.
| dotnet test tests/AkeylessPam.Integration.Tests/ | ||
| ``` | ||
|
|
||
| Integration tests load credentials from environment variables. As a convenience for local development, they also read a `.env` file in the repository root if present. Environment variables always take precedence over `.env` values. |
There was a problem hiding this comment.
This doc says integration tests read a repo-root .env automatically, but in code DotEnvLoader.Load() is only invoked from AkeylessApiClientTests' static constructor. If AkeylessPamIntegrationTests run without that class being initialized first, .env won't be loaded and tests may skip unexpectedly. Either move .env loading to a project-wide initializer or adjust this documentation to match actual behavior.
| Integration tests load credentials from environment variables. As a convenience for local development, they also read a `.env` file in the repository root if present. Environment variables always take precedence over `.env` values. | |
| Integration tests load credentials from environment variables. Set the required values in your shell or test runner environment before running them. If the same settings are provided from multiple sources, environment variables take precedence. |
| try | ||
| { | ||
| Logger.MethodEntry(); | ||
| Logger.LogDebug("Validating required parameter '{ParamName}'", paramName); | ||
|
|
||
| if (config.TryGetValue(paramName, out var value) && !string.IsNullOrEmpty(value)) return; | ||
| Logger.LogError("{ErrorPrefix} '{ParamName}' is required but was not provided", errorPrefix, paramName); | ||
| throw new MissingFieldException($"{errorPrefix} '{paramName}' not provided"); | ||
| } |
There was a problem hiding this comment.
ValidateRequiredParameter treats a value consisting only of whitespace as valid (it checks only string.IsNullOrEmpty). For parameters like SecretName/AccessId/AccessKey, whitespace should be rejected to fail fast and produce clearer configuration errors. Consider using string.IsNullOrWhiteSpace(value) instead.
| Logger.LogTrace("instanceParameters keys: [{Keys}]", string.Join(", ", instanceParameters.Keys)); | ||
|
|
||
| var config = BuildAkeylessConfiguration(instanceParameters, serverConfigurationParameters); | ||
| return GetAkeylessSecretAsync(config).Result; |
There was a problem hiding this comment.
GetPassword blocks on an async method via .Result, which wraps failures in AggregateException and can deadlock under certain synchronization contexts. If the interface must stay synchronous, prefer GetAkeylessSecretAsync(config).GetAwaiter().GetResult() and ensure awaited calls use ConfigureAwait(false) to avoid sync-context deadlocks.
| return GetAkeylessSecretAsync(config).Result; | |
| return GetAkeylessSecretAsync(config).GetAwaiter().GetResult(); |
| UNIT := tests/AkeylessPam.Unit.Tests/AkeylessPam.Unit.Tests.csproj | ||
| INT := tests/AkeylessPam.Integration.Tests/AkeylessPam.Integration.Tests.csproj | ||
| CONSOLE := TestConsole/TestConsole.csproj | ||
| MANIFEST := akeyless-pam/manifest.json | ||
| LIB_BIN := akeyless-pam/bin | ||
|
|
||
| .PHONY: all build build-release clean test test-unit test-integration console restore | ||
|
|
||
| all: build | ||
|
|
||
| ## Build (debug) | ||
| build: | ||
| dotnet build $(LIB) | ||
| @for d in $(LIB_BIN)/Debug/net*/; do cp $(MANIFEST) $$d; done | ||
|
|
||
| ## Build (release) | ||
| build-release: | ||
| dotnet build $(LIB) -c Release | ||
| @for d in $(LIB_BIN)/Release/net*/; do cp $(MANIFEST) $$d; done | ||
|
|
||
| ## Restore NuGet packages | ||
| restore: | ||
| dotnet restore $(SLN) | ||
|
|
||
| ## Clean all projects | ||
| clean: | ||
| dotnet clean $(SLN) | ||
|
|
||
| ## Run all tests | ||
| test: | ||
| dotnet test $(SLN) | ||
|
|
||
| ## Run unit tests only | ||
| test-unit: | ||
| dotnet test $(UNIT) | ||
|
|
||
| ## Run integration tests only | ||
| test-integration: | ||
| dotnet test $(INT) | ||
|
|
||
| ## Run the test console | ||
| console: | ||
| dotnet run --project $(CONSOLE) | ||
|
|
There was a problem hiding this comment.
Makefile's console target references TestConsole/TestConsole.csproj, but that project file is removed in this PR. Either remove the console target/variable or update it to the new location so make console doesn't fail.
| dotnet test tests/AkeylessPam.Integration.Tests/ | ||
| ``` | ||
|
|
||
| Credentials can be provided via environment variables or a `.env` file in the repo root. The `.env` file is loaded automatically by the test setup and does not override variables already set in the environment (safe for CI use). |
There was a problem hiding this comment.
This README says the integration test setup loads a repo-root .env automatically. In the current code, DotEnvLoader.Load() is only called in AkeylessApiClientTests' static constructor, so AkeylessPamIntegrationTests may run without .env being loaded (and skip). Either adjust the docs or move the .env loading into a project-wide initializer.
| Credentials can be provided via environment variables or a `.env` file in the repo root. The `.env` file is loaded automatically by the test setup and does not override variables already set in the environment (safe for CI use). | |
| Credentials should be provided via environment variables when running this test project. A repo-root `.env` file may be used as a local convenience, but automatic loading is not guaranteed for all integration tests, so do not rely on it as the only source of configuration. |
Merge release-1.0 to main - Automated PR