Skip to content

Commit 081fd1f

Browse files
committed
chore: refactor unit tests and update licensing
Signed-off-by: Matthew H. Irby <matt.irby@keyfactor.com>
1 parent 0c4baaa commit 081fd1f

File tree

3 files changed

+125
-164
lines changed

3 files changed

+125
-164
lines changed

cyberark-credentialprovider-pam-tests/Providers/CentralCredentialProviderPAMTests.cs

Lines changed: 109 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ public class CentralCredentialProviderPAMTests
2727
private readonly CentralCredentialProviderPAM _sut;
2828
private readonly Mock<IConjurHttpClient> _mockConjurHttpClient;
2929

30+
// Test data constants
31+
private const string ExpectedSecret = "foobar";
32+
private const string TestAppId = "TestAppId";
33+
private const string TestHost = "TestHost";
34+
private const string TestSite = "TestSite";
35+
private const string TestSafe = "TestSafe";
36+
private const string TestFolder = "TestFolder";
37+
private const string TestObject = "TestObject";
38+
3039
public CentralCredentialProviderPAMTests(ITestOutputHelper output)
3140
{
3241
var loggerFactory = LoggerFactory.Create(builder =>
@@ -39,46 +48,61 @@ public CentralCredentialProviderPAMTests(ITestOutputHelper output)
3948
_sut = new CentralCredentialProviderPAM(logger, _mockConjurHttpClient.Object);
4049
}
4150

51+
private static Dictionary<string, string> CreateInitializationInfo() => new()
52+
{
53+
{ "AppId", TestAppId },
54+
{ "Host", TestHost },
55+
{ "Site", TestSite }
56+
};
57+
58+
private static Dictionary<string, string> CreateInstanceParams() => new()
59+
{
60+
{ "Safe", TestSafe },
61+
{ "Folder", TestFolder },
62+
{ "Object", TestObject }
63+
};
64+
65+
private void SetupSuccessfulPasswordRetrieval(string secret = ExpectedSecret)
66+
{
67+
var httpResponse = new HttpResponseMessage
68+
{
69+
StatusCode = HttpStatusCode.OK,
70+
Content = new StringContent($"{{\"Content\":\"{secret}\"}}")
71+
};
72+
73+
_mockConjurHttpClient
74+
.Setup(p => p.GetPassword(
75+
It.IsAny<string>(),
76+
It.IsAny<string>(),
77+
It.IsAny<string>(),
78+
It.IsAny<string>(),
79+
It.IsAny<string>(),
80+
It.IsAny<string>()))
81+
.Returns(httpResponse);
82+
}
83+
4284
[Theory]
4385
[InlineData("AppId")]
4486
[InlineData("Host")]
4587
[InlineData("Site")]
4688
public void GetPassword_MissingRequiredInitializationParameter_ThrowsException(string keyToRemove)
4789
{
4890
// Arrange
49-
var initializationInfo = new Dictionary<string, string>()
50-
{
51-
{ "AppId", "TestAppId" },
52-
{ "Host", "TestHost" },
53-
{ "Site", "TestSite" }
54-
};
55-
56-
var instanceParams = new Dictionary<string, string>()
57-
{
58-
{"Safe", "TestSafe"},
59-
{"Folder", "TestFolder"},
60-
{"Object", "TestObject"},
61-
};
91+
var initializationInfo = CreateInitializationInfo();
92+
var instanceParams = CreateInstanceParams();
93+
var expectedMessage = $"Required field {keyToRemove} was missing a value or was not defined as expected in dictionary.";
6294

63-
// Scenario 1: Key is missing from dictionary
64-
95+
// Act & Assert - Scenario 1: Key is missing from dictionary
6596
initializationInfo.Remove(keyToRemove);
97+
var exception1 = Assert.Throws<ArgumentException>(() =>
98+
_sut.GetPassword(instanceParams, initializationInfo));
99+
Assert.Equal(expectedMessage, exception1.Message);
66100

67-
// Act
68-
var exception1 = Assert.Throws<ArgumentException>(() => _sut.GetPassword(instanceParams, initializationInfo));
69-
70-
// Assert
71-
Assert.Equal($"Required field {keyToRemove} was missing a value or was not defined as expected in dictionary.", exception1.Message);
72-
73-
74-
// Scenario 2: Key is present but value is null or whitespace
101+
// Act & Assert - Scenario 2: Key is present but value is empty
75102
initializationInfo[keyToRemove] = "";
76-
77-
// Act
78-
var exception2 = Assert.Throws<ArgumentException>(() => _sut.GetPassword(instanceParams, initializationInfo));
79-
80-
// Assert
81-
Assert.Equal($"Required field {keyToRemove} was missing a value or was not defined as expected in dictionary.", exception2.Message);
103+
var exception2 = Assert.Throws<ArgumentException>(() =>
104+
_sut.GetPassword(instanceParams, initializationInfo));
105+
Assert.Equal(expectedMessage, exception2.Message);
82106
}
83107

84108
[Theory]
@@ -88,118 +112,61 @@ public void GetPassword_MissingRequiredInitializationParameter_ThrowsException(s
88112
public void GetPassword_MissingRequiredInstanceParameter_ThrowsException(string keyToRemove)
89113
{
90114
// Arrange
91-
var initializationInfo = new Dictionary<string, string>()
92-
{
93-
{ "AppId", "TestAppId" },
94-
{ "Host", "TestHost" },
95-
{ "Site", "TestSite" }
96-
};
97-
98-
var instanceParams = new Dictionary<string, string>()
99-
{
100-
{"Safe", "TestSafe"},
101-
{"Folder", "TestFolder"},
102-
{"Object", "TestObject"},
103-
};
115+
var initializationInfo = CreateInitializationInfo();
116+
var instanceParams = CreateInstanceParams();
117+
var expectedMessage = $"Required field {keyToRemove} was missing a value or was not defined as expected in dictionary.";
104118

105-
// Scenario 1: Key is missing from dictionary
106-
119+
// Act & Assert - Scenario 1: Key is missing from dictionary
107120
instanceParams.Remove(keyToRemove);
121+
var exception1 = Assert.Throws<ArgumentException>(() =>
122+
_sut.GetPassword(instanceParams, initializationInfo));
123+
Assert.Equal(expectedMessage, exception1.Message);
108124

109-
// Act
110-
var exception1 = Assert.Throws<ArgumentException>(() => _sut.GetPassword(instanceParams, initializationInfo));
111-
112-
// Assert
113-
Assert.Equal($"Required field {keyToRemove} was missing a value or was not defined as expected in dictionary.", exception1.Message);
114-
115-
116-
// Scenario 2: Key is present but value is null or whitespace
125+
// Act & Assert - Scenario 2: Key is present but value is empty
117126
instanceParams[keyToRemove] = "";
118-
119-
// Act
120-
var exception2 = Assert.Throws<ArgumentException>(() => _sut.GetPassword(instanceParams, initializationInfo));
121-
122-
// Assert
123-
Assert.Equal($"Required field {keyToRemove} was missing a value or was not defined as expected in dictionary.", exception2.Message);
127+
var exception2 = Assert.Throws<ArgumentException>(() =>
128+
_sut.GetPassword(instanceParams, initializationInfo));
129+
Assert.Equal(expectedMessage, exception2.Message);
124130
}
125131

126132
[Fact]
127133
public void GetPassword_ValidConfiguration_ReturnsSecret()
128134
{
129135
// Arrange
130-
var initializationInfo = new Dictionary<string, string>()
131-
{
132-
{ "AppId", "TestAppId" },
133-
{ "Host", "TestHost" },
134-
{ "Site", "TestSite" }
135-
};
136-
137-
var instanceParams = new Dictionary<string, string>()
138-
{
139-
{"Safe", "TestSafe" },
140-
{"Folder", "TestFolder" },
141-
{"Object", "TestObject"},
142-
};
143-
144-
var expectedSecret = "foobar";
145-
146-
var httpResponse = new HttpResponseMessage()
147-
{
148-
StatusCode = HttpStatusCode.OK,
149-
Content = new StringContent($"{{\"Content\":\"{expectedSecret}\"}}")
150-
};
151-
152-
_mockConjurHttpClient
153-
.Setup(p => p.GetPassword(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
154-
It.IsAny<string>(), It.IsAny<string>()))
155-
.Returns(httpResponse);
136+
var initializationInfo = CreateInitializationInfo();
137+
var instanceParams = CreateInstanceParams();
138+
SetupSuccessfulPasswordRetrieval();
156139

157140
// Act
158141
var password = _sut.GetPassword(instanceParams, initializationInfo);
159142

160143
// Assert
161-
Assert.Equal(expectedSecret, password);
144+
Assert.Equal(ExpectedSecret, password);
162145
}
163146

164147
[Fact]
165148
public void GetPassword_HostDoesNotIncludeScheme_AddsHttpsScheme()
166149
{
167150
// Arrange
168-
var initializationInfo = new Dictionary<string, string>()
169-
{
170-
{ "AppId", "TestAppId" },
171-
{ "Host", "test.example.com:1234" },
172-
{ "Site", "TestSite" }
173-
};
174-
175-
var instanceParams = new Dictionary<string, string>()
176-
{
177-
{"Safe", "TestSafe" },
178-
{"Folder", "TestFolder" },
179-
{"Object", "TestObject"},
180-
};
151+
var initializationInfo = CreateInitializationInfo();
152+
initializationInfo["Host"] = "test.example.com:1234";
181153

154+
var instanceParams = CreateInstanceParams();
182155
var expectedHostname = "https://test.example.com:1234/";
183156

184-
var expectedSecret = "foobar";
185-
186-
var httpResponse = new HttpResponseMessage()
187-
{
188-
StatusCode = HttpStatusCode.OK,
189-
Content = new StringContent($"{{\"Content\":\"{expectedSecret}\"}}")
190-
};
191-
192-
_mockConjurHttpClient
193-
.Setup(p => p.GetPassword(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
194-
It.IsAny<string>(), It.IsAny<string>()))
195-
.Returns(httpResponse);
157+
SetupSuccessfulPasswordRetrieval();
196158

197159
// Act
198-
var password = _sut.GetPassword(instanceParams, initializationInfo);
160+
_sut.GetPassword(instanceParams, initializationInfo);
199161

200162
// Assert
201-
_mockConjurHttpClient.Verify(p => p.GetPassword(expectedHostname, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
202-
It.IsAny<string>(), It.IsAny<string>()), Times.Once);
163+
_mockConjurHttpClient.Verify(p => p.GetPassword(
164+
expectedHostname,
165+
It.IsAny<string>(),
166+
It.IsAny<string>(),
167+
It.IsAny<string>(),
168+
It.IsAny<string>(),
169+
It.IsAny<string>()), Times.Once);
203170
}
204171

205172
[Theory]
@@ -208,76 +175,56 @@ public void GetPassword_HostDoesNotIncludeScheme_AddsHttpsScheme()
208175
public void GetPassword_HostIncludesScheme_KeepsProvidedScheme(string host)
209176
{
210177
// Arrange
211-
var initializationInfo = new Dictionary<string, string>()
212-
{
213-
{ "AppId", "TestAppId" },
214-
{ "Host", host },
215-
{ "Site", "TestSite" }
216-
};
217-
218-
var instanceParams = new Dictionary<string, string>()
219-
{
220-
{"Safe", "TestSafe" },
221-
{"Folder", "TestFolder" },
222-
{"Object", "TestObject"},
223-
};
178+
var initializationInfo = CreateInitializationInfo();
179+
initializationInfo["Host"] = host;
224180

225-
var expectedHostname = host;
226-
227-
var expectedSecret = "foobar";
228-
229-
var httpResponse = new HttpResponseMessage()
230-
{
231-
StatusCode = HttpStatusCode.OK,
232-
Content = new StringContent($"{{\"Content\":\"{expectedSecret}\"}}")
233-
};
234-
235-
_mockConjurHttpClient
236-
.Setup(p => p.GetPassword(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
237-
It.IsAny<string>(), It.IsAny<string>()))
238-
.Returns(httpResponse);
181+
var instanceParams = CreateInstanceParams();
182+
SetupSuccessfulPasswordRetrieval();
239183

240184
// Act
241-
var password = _sut.GetPassword(instanceParams, initializationInfo);
185+
_sut.GetPassword(instanceParams, initializationInfo);
242186

243187
// Assert
244-
_mockConjurHttpClient.Verify(p => p.GetPassword(expectedHostname, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
245-
It.IsAny<string>(), It.IsAny<string>()), Times.Once);
188+
_mockConjurHttpClient.Verify(p => p.GetPassword(
189+
host,
190+
It.IsAny<string>(),
191+
It.IsAny<string>(),
192+
It.IsAny<string>(),
193+
It.IsAny<string>(),
194+
It.IsAny<string>()), Times.Once);
246195
}
247196

248197
[Fact]
249198
public void GetPassword_ObjectDoesNotExist_ThrowsException()
250199
{
251200
// Arrange
252-
var initializationInfo = new Dictionary<string, string>()
253-
{
254-
{ "AppId", "TestAppId" },
255-
{ "Host", "TestHost" },
256-
{ "Site", "TestSite" }
257-
};
258-
259-
var instanceParams = new Dictionary<string, string>()
260-
{
261-
{"Safe", "TestSafe" },
262-
{"Folder", "TestFolder" },
263-
{"Object", "objectdoesnotexist"},
264-
};
201+
var initializationInfo = CreateInitializationInfo();
202+
var instanceParams = CreateInstanceParams();
203+
instanceParams["Object"] = "objectdoesnotexist";
204+
205+
var errorResponse = "{\"ErrorCode\":\"APPAP004E\",\"ErrorMsg\":\"Password object matching query [Safe=partner;Folder=Root\\\\Secrets;Object=objectdoesnotexist] was not found (Diagnostic Info: 5). Please check that there is a password object that answers your query in the Vault and that both the Provider and the application user have the appropriate permissions needed in order to use the password.\"}";
265206

266-
var httpResponse = new HttpResponseMessage()
207+
var httpResponse = new HttpResponseMessage
267208
{
268209
StatusCode = HttpStatusCode.NotFound,
269-
Content = new StringContent($"{{\"ErrorCode\":\"APPAP004E\",\"ErrorMsg\":\"Password object matching query [Safe=partner;Folder=Root\\\\Secrets;Object=objectdoesnotexist] was not found (Diagnostic Info: 5). Please check that there is a password object that answers your query in the Vault and that both the Provider and the application user have the appropriate permissions needed in order to use the password.\"}}")
210+
Content = new StringContent(errorResponse)
270211
};
271212

272213
_mockConjurHttpClient
273-
.Setup(p => p.GetPassword(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
274-
It.IsAny<string>(), It.IsAny<string>()))
214+
.Setup(p => p.GetPassword(
215+
It.IsAny<string>(),
216+
It.IsAny<string>(),
217+
It.IsAny<string>(),
218+
It.IsAny<string>(),
219+
It.IsAny<string>(),
220+
It.IsAny<string>()))
275221
.Returns(httpResponse);
276222

277223
// Act
278-
var exception = Assert.Throws<HttpClientException>(() => _sut.GetPassword(instanceParams, initializationInfo));
224+
var exception = Assert.Throws<HttpClientException>(() =>
225+
_sut.GetPassword(instanceParams, initializationInfo));
279226

280227
// Assert
281-
Assert.Equal("Failed to retrieve secret from CyberArk Central Credential Provider. Status Code: 404 (NotFound). Response message: {\"ErrorCode\":\"APPAP004E\",\"ErrorMsg\":\"Password object matching query [Safe=partner;Folder=Root\\\\Secrets;Object=objectdoesnotexist] was not found (Diagnostic Info: 5). Please check that there is a password object that answers your query in the Vault and that both the Provider and the application user have the appropriate permissions needed in order to use the password.\"}", exception.Message);
228+
Assert.Equal($"Failed to retrieve secret from CyberArk Central Credential Provider. Status Code: 404 (NotFound). Response message: {errorResponse}", exception.Message);
282229
}
283230
}

cyberark-credentialprovider-pam/CentralCredentialProviderPAM.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 Keyfactor
1+
// Copyright 2026 Keyfactor
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -69,7 +69,7 @@ public string GetPassword(Dictionary<string, string> instanceParameters, Diction
6969
if (!host.StartsWith("http"))
7070
{
7171
_logger.LogTrace($"Host '{host}' does not include scheme. Prepending 'https://'.");
72-
baseAddress = $"https://{host}";
72+
baseAddress = $"https://{host}/";
7373
}
7474

7575
var response = _httpClient.GetPassword(baseAddress, site, appId, safe, folder, obj);

cyberark-credentialprovider-pam/Clients/ConjurHttpClient.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
// Copyright 2026 Keyfactor
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
115
using System.Net.Http;
216
using Keyfactor.Logging;
317
using Microsoft.Extensions.Logging;

0 commit comments

Comments
 (0)