Skip to content

Commit ca4b518

Browse files
Add authentication status and new request params (#17)
* Create PIAuthenticationStatus.cs * add client and client user agent parameters * Update PrivacyIDEA.cs * Update PrivacyIDEA_Client/PrivacyIDEA.cs Co-authored-by: Copilot <[email protected]> * Update PrivacyIDEA_Client/PrivacyIDEA.cs Co-authored-by: Copilot <[email protected]> * Update PrivacyIDEA.cs * change new params to customAttributes map * Update PrivacyIDEA.cs --------- Co-authored-by: Copilot <[email protected]>
1 parent 8733bac commit ca4b518

File tree

2 files changed

+78
-16
lines changed

2 files changed

+78
-16
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+

2+
namespace PrivacyIDEA_Client
3+
{
4+
public enum PIAuthenticationStatus
5+
{
6+
UNDEFINED,
7+
ACCEPT,
8+
REJECT,
9+
CHALLENGE
10+
}
11+
}

PrivacyIDEA_Client/PrivacyIDEA.cs

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ public PrivacyIDEA(string url, string userAgent, bool sslVerify = true)
7474
/// <param name="username">username to trigger challenges for</param>
7575
/// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
7676
/// <param name="headers">optional headers which can be forwarded to the privacyIDEA server</param>
77+
/// <param name="customParameters">optional custom parameters to include</param>
7778
/// <param name="cancellationToken">optional</param>
7879
/// <returns>PIResponse object or null on error</returns>
79-
public async Task<PIResponse?> TriggerChallenges(string username, string? domain = null, List<KeyValuePair<string, string>>? headers = null, CancellationToken cancellationToken = default)
80+
public async Task<PIResponse?> TriggerChallenges(string username, string? domain = null, List<KeyValuePair<string, string>>? headers = null, Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
8081
{
81-
if (await GetAuthToken(cancellationToken) is false)
82+
if (await GetAuthToken(customParameters, cancellationToken) is false)
8283
{
8384
Error("Unable to trigger challenges without an auth token!");
8485
return null;
@@ -89,6 +90,7 @@ public PrivacyIDEA(string url, string userAgent, bool sslVerify = true)
8990
};
9091

9192
AddRealmForDomain(domain, parameters);
93+
AddCustomParameters(customParameters, parameters);
9294

9395
string response = await SendRequest("/validate/triggerchallenge", parameters, "POST", headers, cancellationToken);
9496
PIResponse? ret = PIResponse.FromJSON(response, this);
@@ -100,16 +102,18 @@ public PrivacyIDEA(string url, string userAgent, bool sslVerify = true)
100102
/// Check if the challenge for the given transaction id has been answered yet. This is done using the /validate/polltransaction endpoint.
101103
/// </summary>
102104
/// <param name="transactionid"></param>
105+
/// <param name="customParameters">optional custom parameters to include</param>
103106
/// <param name="cancellationToken">optional</param>
104107
/// <returns>true if challenge was answered. false if not or error</returns>
105-
public async Task<bool> PollTransaction(string transactionid, CancellationToken cancellationToken = default)
108+
public async Task<bool> PollTransaction(string transactionid, Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
106109
{
107110
if (string.IsNullOrEmpty(transactionid) is false)
108111
{
109112
var parameters = new Dictionary<string, string>
110113
{
111114
{ "transaction_id", transactionid }
112115
};
116+
AddCustomParameters(customParameters, parameters);
113117

114118
string response = await SendRequest("/validate/polltransaction", parameters, "GET", new List<KeyValuePair<string, string>>(), cancellationToken);
115119

@@ -137,11 +141,12 @@ public async Task<bool> PollTransaction(string transactionid, CancellationToken
137141
/// </summary>
138142
/// <param name="user">username</param>
139143
/// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
144+
/// <param name="customParameters">optional custom parameters to include</param>
140145
/// <param name="cancellationToken">optional</param>
141146
/// <returns>true if token exists. false if not or error</returns>
142-
public async Task<bool> UserHasToken(string user, string? domain = null, CancellationToken cancellationToken = default)
147+
public async Task<bool> UserHasToken(string user, string? domain = null, Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
143148
{
144-
if (await GetAuthToken(cancellationToken) is false)
149+
if (await GetAuthToken(customParameters, cancellationToken) is false)
145150
{
146151
Error("Unable to lookup tokens without an auth token!");
147152
return false;
@@ -151,6 +156,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
151156
{ "user", user }
152157
};
153158
AddRealmForDomain(domain, parameters);
159+
AddCustomParameters(customParameters, parameters);
154160

155161
string response = await SendRequest("/token/", parameters, "GET", new List<KeyValuePair<string, string>>(), cancellationToken);
156162
if (string.IsNullOrEmpty(response))
@@ -174,9 +180,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
174180
/// </summary>
175181
/// <param name="user">username</param>
176182
/// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
183+
/// <param name="customParameters">optional custom parameters to include</param>
177184
/// <param name="cancellationToken">optional</param>
178185
/// <returns>PIEnrollResponse object or null on error</returns>
179-
public async Task<PIEnrollResponse?> TokenInit(string user, string? domain = null, CancellationToken cancellationToken = default)
186+
public async Task<PIEnrollResponse?> TokenInit(string user, string? domain = null, Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
180187
{
181188
var parameters = new Dictionary<string, string>
182189
{
@@ -185,6 +192,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
185192
{ "genkey", "1" }
186193
};
187194
AddRealmForDomain(domain, parameters);
195+
AddCustomParameters(customParameters, parameters);
188196

189197
string response = await SendRequest("/token/init", parameters, "POST", new List<KeyValuePair<string, string>>(), cancellationToken);
190198
return PIEnrollResponse.FromJSON(response, this);
@@ -200,9 +208,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
200208
/// <param name="transactionid">optional transaction id to refer to a challenge</param>
201209
/// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
202210
/// <param name="headers">optional headers which can be forwarded to the privacyIDEA server</param>
211+
/// <param name="customParameters">optional custom parameters to include</param>
203212
/// <param name="cancellationToken">optional</param>
204213
/// <returns>PIResponse object or null on error</returns>
205-
public async Task<PIResponse?> ValidateCheck(string user, string otp, string? transactionid = null, string? domain = null, List<KeyValuePair<string, string>>? headers = null, CancellationToken cancellationToken = default)
214+
public async Task<PIResponse?> ValidateCheck(string user, string otp, string? transactionid = null, string? domain = null, List<KeyValuePair<string, string>>? headers = null, Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
206215
{
207216
var parameters = new Dictionary<string, string>
208217
{
@@ -216,6 +225,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
216225
}
217226

218227
AddRealmForDomain(domain, parameters);
228+
AddCustomParameters(customParameters, parameters);
219229

220230
string response = await SendRequest("/validate/check", parameters, "POST", headers, cancellationToken);
221231
return PIResponse.FromJSON(response, this);
@@ -231,9 +241,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
231241
/// <param name="origin">origin also returned by the browser</param>
232242
/// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
233243
/// <param name="headers">optional headers which can be forwarded to the privacyIDEA server</param>
244+
/// <param name="customParameters">optional custom parameters to include</param>
234245
/// <param name="cancellationToken">optional</param>
235246
/// <returns>PIResponse object or null on error</returns>
236-
public async Task<PIResponse?> ValidateCheckWebAuthn(string user, string transactionID, string webAuthnSignResponse, string origin, string? domain = null, List<KeyValuePair<string, string>>? headers = null, CancellationToken cancellationToken = default)
247+
public async Task<PIResponse?> ValidateCheckWebAuthn(string user, string transactionID, string webAuthnSignResponse, string origin, string? domain = null, List<KeyValuePair<string, string>>? headers = null, Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
237248
{
238249
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(transactionID) || string.IsNullOrEmpty(webAuthnSignResponse) || string.IsNullOrEmpty(origin))
239250
{
@@ -279,6 +290,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
279290
}
280291

281292
AddRealmForDomain(domain, parameters);
293+
AddCustomParameters(customParameters, parameters);
282294

283295
// The origin has to be set in the header for WebAuthn authentication
284296
headers ??= new List<KeyValuePair<string, string>>();
@@ -298,9 +310,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
298310
/// Gets an auth token from the privacyIDEA server using the service account.
299311
/// Afterward, the token is set as the default authentication header for the HttpClient.
300312
/// </summary>
313+
/// <param name="customParameters">optional custom parameters to include</param>
301314
/// <param name="cancellationToken">optional</param>
302315
/// <returns>true if success, false otherwise</returns>
303-
private async Task<bool> GetAuthToken(CancellationToken cancellationToken = default)
316+
private async Task<bool> GetAuthToken(Dictionary<string, string>? customParameters = null, CancellationToken cancellationToken = default)
304317
{
305318
if (string.IsNullOrEmpty(_serviceUser) || string.IsNullOrEmpty(_servicePass))
306319
{
@@ -320,6 +333,8 @@ private async Task<bool> GetAuthToken(CancellationToken cancellationToken = defa
320333
parameters.Add("realm", _serviceRealm);
321334
}
322335

336+
AddCustomParameters(customParameters, parameters);
337+
323338
string response = await SendRequest("/auth", parameters, "POST", null, cancellationToken);
324339

325340
if (string.IsNullOrEmpty(response))
@@ -351,13 +366,15 @@ private async Task<bool> GetAuthToken(CancellationToken cancellationToken = defa
351366
}
352367
}
353368

354-
public void SetServiceAccount(string user, string pass, string realm = "")
355-
{
356-
_serviceUser = user;
357-
_servicePass = pass;
358-
_serviceRealm = realm ?? "";
359-
}
360-
369+
/// <summary>
370+
/// Sends a request to the privacyIDEA server.
371+
/// </summary>
372+
/// <param name="endpoint">API endpoint</param>
373+
/// <param name="parameters">POST/GET parameters</param>
374+
/// <param name="method">HTTP method: POST or GET</param>
375+
/// <param name="headers">optional headers to add to the request</param>
376+
/// <param name="cancellationToken">optional</param>
377+
/// <returns>response string</returns>
361378
private Task<string> SendRequest(string endpoint, Dictionary<string, string> parameters, string method,
362379
List<KeyValuePair<string, string>>? headers = null, CancellationToken cancellationToken = default)
363380
{
@@ -415,6 +432,19 @@ private Task<string> SendRequest(string endpoint, Dictionary<string, string> par
415432
return Task.FromResult(ret);
416433
}
417434

435+
/// <summary>
436+
/// Sets the service account credentials.
437+
/// </summary>
438+
/// <param name="user">username</param>
439+
/// <param name="pass">password</param>
440+
/// <param name="realm">optional realm</param>
441+
public void SetServiceAccount(string user, string pass, string realm = "")
442+
{
443+
_serviceUser = user;
444+
_servicePass = pass;
445+
_serviceRealm = realm ?? "";
446+
}
447+
418448
/// <summary>
419449
/// Evaluates which realm to use for a given domain and adds it to the parameter dictionary.
420450
/// The realm mapping takes precedence over the general realm that can be set. If no realm is found, the parameter is omitted.
@@ -458,6 +488,27 @@ private void AddRealmForDomain(string? domain, Dictionary<string, string> parame
458488
}
459489
}
460490

491+
/// <summary>
492+
/// Adds custom parameters to request.
493+
/// </summary>
494+
/// <param name="customParameters">Dictionary of custom parameters to add.</param>
495+
/// <param name="parameters">The dictionary to add the parameters to.</param>
496+
private static void AddCustomParameters(Dictionary<string, string>? customParameters, Dictionary<string, string> parameters)
497+
{
498+
if (customParameters != null)
499+
{
500+
foreach (var attribute in customParameters)
501+
{
502+
parameters.Add(attribute.Key, attribute.Value);
503+
}
504+
}
505+
}
506+
507+
/// <summary>
508+
/// Converts a dictionary to a StringContent with application/x-www-form-urlencoded encoding.
509+
/// </summary>
510+
/// <param name="dict">The dictionary to convert.</param>
511+
/// <returns>A StringContent object with the encoded dictionary.</returns>
461512
internal static StringContent DictToEncodedStringContent(Dictionary<string, string> dict)
462513
{
463514
StringBuilder sb = new();

0 commit comments

Comments
 (0)