M7: Auth providers - JWT/OIDC/LDAP/Azure/GCP IAM via Connect-Infisical parameter sets
This commit is contained in:
@@ -0,0 +1,157 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using PSInfisicalAPI.Authentication;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Tests
|
||||||
|
{
|
||||||
|
public class AuthProviderTests
|
||||||
|
{
|
||||||
|
private sealed class CapturingHttpClient : IInfisicalHttpClient
|
||||||
|
{
|
||||||
|
public InfisicalHttpRequest CapturedRequest { get; private set; }
|
||||||
|
public string ResponseBody { get; set; } = "{\"accessToken\":\"abc.def.ghi\",\"expiresIn\":3600,\"tokenType\":\"Bearer\"}";
|
||||||
|
|
||||||
|
public InfisicalHttpResponse Send(InfisicalHttpRequest request)
|
||||||
|
{
|
||||||
|
CapturedRequest = request;
|
||||||
|
return new InfisicalHttpResponse
|
||||||
|
{
|
||||||
|
StatusCode = 200,
|
||||||
|
Body = ResponseBody,
|
||||||
|
Headers = new Dictionary<string, string>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InfisicalAuthenticationRequest BaseRequest()
|
||||||
|
{
|
||||||
|
return new InfisicalAuthenticationRequest
|
||||||
|
{
|
||||||
|
BaseUri = new Uri("https://example.invalid"),
|
||||||
|
ApiVersion = "v1"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JwtAuthProvider_Posts_IdentityId_And_Jwt()
|
||||||
|
{
|
||||||
|
CapturingHttpClient http = new CapturingHttpClient();
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.IdentityId = "identity-1";
|
||||||
|
request.Jwt = SecureStringUtility.ToReadOnlySecureString("token.value");
|
||||||
|
|
||||||
|
InfisicalAuthenticationResult result = new JwtAuthProvider().Authenticate(request, http, null);
|
||||||
|
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.NotNull(http.CapturedRequest);
|
||||||
|
Assert.Equal("POST", http.CapturedRequest.Method);
|
||||||
|
Assert.EndsWith("/api/v1/auth/jwt-auth/login", http.CapturedRequest.Uri.AbsolutePath);
|
||||||
|
JObject body = JObject.Parse(http.CapturedRequest.Body);
|
||||||
|
Assert.Equal("identity-1", (string)body["identityId"]);
|
||||||
|
Assert.Equal("token.value", (string)body["jwt"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OidcAuthProvider_Posts_IdentityId_And_Jwt_To_Oidc_Endpoint()
|
||||||
|
{
|
||||||
|
CapturingHttpClient http = new CapturingHttpClient();
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.IdentityId = "identity-2";
|
||||||
|
request.Jwt = SecureStringUtility.ToReadOnlySecureString("oidc.token");
|
||||||
|
|
||||||
|
new OidcAuthProvider().Authenticate(request, http, null);
|
||||||
|
|
||||||
|
Assert.EndsWith("/api/v1/auth/oidc-auth/login", http.CapturedRequest.Uri.AbsolutePath);
|
||||||
|
JObject body = JObject.Parse(http.CapturedRequest.Body);
|
||||||
|
Assert.Equal("identity-2", (string)body["identityId"]);
|
||||||
|
Assert.Equal("oidc.token", (string)body["jwt"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LdapAuthProvider_Posts_Username_And_Password_To_Ldap_Endpoint()
|
||||||
|
{
|
||||||
|
CapturingHttpClient http = new CapturingHttpClient();
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.Username = "svc.account";
|
||||||
|
request.Password = SecureStringUtility.ToReadOnlySecureString("P@ssw0rd!");
|
||||||
|
|
||||||
|
new LdapAuthProvider().Authenticate(request, http, null);
|
||||||
|
|
||||||
|
Assert.EndsWith("/api/v1/auth/ldap-auth/login", http.CapturedRequest.Uri.AbsolutePath);
|
||||||
|
JObject body = JObject.Parse(http.CapturedRequest.Body);
|
||||||
|
Assert.Equal("svc.account", (string)body["username"]);
|
||||||
|
Assert.Equal("P@ssw0rd!", (string)body["password"]);
|
||||||
|
Assert.False(body.ContainsKey("identityId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LdapAuthProvider_Includes_IdentityId_When_Supplied()
|
||||||
|
{
|
||||||
|
CapturingHttpClient http = new CapturingHttpClient();
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.Username = "u";
|
||||||
|
request.Password = SecureStringUtility.ToReadOnlySecureString("p");
|
||||||
|
request.IdentityId = "id-ldap";
|
||||||
|
|
||||||
|
new LdapAuthProvider().Authenticate(request, http, null);
|
||||||
|
|
||||||
|
JObject body = JObject.Parse(http.CapturedRequest.Body);
|
||||||
|
Assert.Equal("id-ldap", (string)body["identityId"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AzureAuthProvider_Posts_IdentityId_And_Jwt_To_Azure_Endpoint()
|
||||||
|
{
|
||||||
|
CapturingHttpClient http = new CapturingHttpClient();
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.IdentityId = "identity-az";
|
||||||
|
request.Jwt = SecureStringUtility.ToReadOnlySecureString("az.token");
|
||||||
|
|
||||||
|
new AzureAuthProvider().Authenticate(request, http, null);
|
||||||
|
|
||||||
|
Assert.EndsWith("/api/v1/auth/azure-auth/login", http.CapturedRequest.Uri.AbsolutePath);
|
||||||
|
JObject body = JObject.Parse(http.CapturedRequest.Body);
|
||||||
|
Assert.Equal("identity-az", (string)body["identityId"]);
|
||||||
|
Assert.Equal("az.token", (string)body["jwt"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GcpIamAuthProvider_Posts_IdentityId_And_Jwt_To_Gcp_Endpoint()
|
||||||
|
{
|
||||||
|
CapturingHttpClient http = new CapturingHttpClient();
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.IdentityId = "identity-gcp";
|
||||||
|
request.Jwt = SecureStringUtility.ToReadOnlySecureString("gcp.token");
|
||||||
|
|
||||||
|
new GcpIamAuthProvider().Authenticate(request, http, null);
|
||||||
|
|
||||||
|
Assert.EndsWith("/api/v1/auth/gcp-auth/login", http.CapturedRequest.Uri.AbsolutePath);
|
||||||
|
JObject body = JObject.Parse(http.CapturedRequest.Body);
|
||||||
|
Assert.Equal("identity-gcp", (string)body["identityId"]);
|
||||||
|
Assert.Equal("gcp.token", (string)body["jwt"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JwtAuthProvider_Throws_When_IdentityId_Missing()
|
||||||
|
{
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.Jwt = SecureStringUtility.ToReadOnlySecureString("x");
|
||||||
|
Assert.Throws<InfisicalAuthenticationException>(() =>
|
||||||
|
new JwtAuthProvider().Authenticate(request, new CapturingHttpClient(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LdapAuthProvider_Throws_When_Password_Missing()
|
||||||
|
{
|
||||||
|
InfisicalAuthenticationRequest request = BaseRequest();
|
||||||
|
request.Username = "u";
|
||||||
|
Assert.Throws<InfisicalAuthenticationException>(() =>
|
||||||
|
new LdapAuthProvider().Authenticate(request, new CapturingHttpClient(), null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,8 +69,6 @@ namespace PSInfisicalAPI.Tests
|
|||||||
[InlineData(InfisicalEndpointNames.JwtAuthLogin, "POST", "/api/v1/auth/jwt-auth/login")]
|
[InlineData(InfisicalEndpointNames.JwtAuthLogin, "POST", "/api/v1/auth/jwt-auth/login")]
|
||||||
[InlineData(InfisicalEndpointNames.OidcAuthLogin, "POST", "/api/v1/auth/oidc-auth/login")]
|
[InlineData(InfisicalEndpointNames.OidcAuthLogin, "POST", "/api/v1/auth/oidc-auth/login")]
|
||||||
[InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")]
|
[InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")]
|
||||||
[InlineData(InfisicalEndpointNames.KubernetesAuthLogin, "POST", "/api/v1/auth/kubernetes-auth/login")]
|
|
||||||
[InlineData(InfisicalEndpointNames.AwsAuthLogin, "POST", "/api/v1/auth/aws-auth/login")]
|
|
||||||
[InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")]
|
[InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")]
|
||||||
[InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-auth/login")]
|
[InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-auth/login")]
|
||||||
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
|
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Authentication
|
||||||
|
{
|
||||||
|
public sealed class AzureAuthProvider : IInfisicalAuthProvider
|
||||||
|
{
|
||||||
|
private const string Component = "AzureAuthProvider";
|
||||||
|
|
||||||
|
public string Name { get { return "AzureAuth"; } }
|
||||||
|
|
||||||
|
public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (request == null || string.IsNullOrEmpty(request.IdentityId))
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("IdentityId is required for Azure Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Jwt == null || request.Jwt.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("Jwt is required for Azure Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IdentityLoginExecutor.Execute(InfisicalEndpointNames.AzureAuthLogin, Component, request, httpClient, logger, serializer =>
|
||||||
|
{
|
||||||
|
return SecureStringUtility.UsePlainText(request.Jwt, plainJwt =>
|
||||||
|
{
|
||||||
|
Dictionary<string, string> bodyObject = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "identityId", request.IdentityId },
|
||||||
|
{ "jwt", plainJwt ?? string.Empty }
|
||||||
|
};
|
||||||
|
|
||||||
|
return serializer.Serialize(bodyObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Authentication
|
||||||
|
{
|
||||||
|
public sealed class GcpIamAuthProvider : IInfisicalAuthProvider
|
||||||
|
{
|
||||||
|
private const string Component = "GcpIamAuthProvider";
|
||||||
|
|
||||||
|
public string Name { get { return "GcpIamAuth"; } }
|
||||||
|
|
||||||
|
public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (request == null || string.IsNullOrEmpty(request.IdentityId))
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("IdentityId is required for GCP IAM Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Jwt == null || request.Jwt.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("Jwt is required for GCP IAM Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IdentityLoginExecutor.Execute(InfisicalEndpointNames.GcpIamAuthLogin, Component, request, httpClient, logger, serializer =>
|
||||||
|
{
|
||||||
|
return SecureStringUtility.UsePlainText(request.Jwt, plainJwt =>
|
||||||
|
{
|
||||||
|
Dictionary<string, string> bodyObject = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "identityId", request.IdentityId },
|
||||||
|
{ "jwt", plainJwt ?? string.Empty }
|
||||||
|
};
|
||||||
|
|
||||||
|
return serializer.Serialize(bodyObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Security;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
using PSInfisicalAPI.Serialization;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Authentication
|
||||||
|
{
|
||||||
|
internal static class IdentityLoginExecutor
|
||||||
|
{
|
||||||
|
internal static InfisicalAuthenticationResult Execute(
|
||||||
|
string endpointName,
|
||||||
|
string component,
|
||||||
|
InfisicalAuthenticationRequest request,
|
||||||
|
IInfisicalHttpClient httpClient,
|
||||||
|
IInfisicalLogger logger,
|
||||||
|
Func<JsonInfisicalSerializer, string> bodyFactory)
|
||||||
|
{
|
||||||
|
if (request == null) { throw new ArgumentNullException(nameof(request)); }
|
||||||
|
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
|
||||||
|
if (bodyFactory == null) { throw new ArgumentNullException(nameof(bodyFactory)); }
|
||||||
|
|
||||||
|
IInfisicalLogger log = logger ?? NullInfisicalLogger.Instance;
|
||||||
|
log.Information(component, "Attempting to authenticate to Infisical. Please Wait...");
|
||||||
|
|
||||||
|
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(endpointName);
|
||||||
|
Uri uri = InfisicalUriBuilder.Build(request.BaseUri, definition, null, null);
|
||||||
|
JsonInfisicalSerializer serializer = new JsonInfisicalSerializer();
|
||||||
|
string body = bodyFactory(serializer);
|
||||||
|
|
||||||
|
InfisicalHttpRequest httpRequest = new InfisicalHttpRequest
|
||||||
|
{
|
||||||
|
OperationName = "Authenticate",
|
||||||
|
EndpointName = definition.Name,
|
||||||
|
Method = definition.Method,
|
||||||
|
Uri = uri,
|
||||||
|
Body = body,
|
||||||
|
ContentType = "application/json",
|
||||||
|
ContainsSecretMaterialInRequest = definition.ContainsSecretMaterialInRequest,
|
||||||
|
ContainsSecretMaterialInResponse = definition.ContainsSecretMaterialInResponse,
|
||||||
|
Headers = new Dictionary<string, string> { { "Accept", "application/json" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
InfisicalHttpResponse response = httpClient.Send(httpRequest);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (response.StatusCode < 200 || response.StatusCode >= 300)
|
||||||
|
{
|
||||||
|
log.Error(component, "Infisical authentication failed.");
|
||||||
|
throw new InfisicalAuthenticationException(string.Concat(component, " login returned status ", response.StatusCode.ToString(CultureInfo.InvariantCulture), "."));
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityLoginResponse parsed = serializer.Deserialize<IdentityLoginResponse>(response.Body);
|
||||||
|
if (parsed == null || string.IsNullOrEmpty(parsed.AccessToken))
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException(string.Concat(component, " login response did not contain an access token."));
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureString accessToken = SecureStringUtility.ToReadOnlySecureString(parsed.AccessToken);
|
||||||
|
|
||||||
|
DateTimeOffset? expiresAt = null;
|
||||||
|
if (parsed.ExpiresIn > 0)
|
||||||
|
{
|
||||||
|
expiresAt = DateTimeOffset.UtcNow.AddSeconds(parsed.ExpiresIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed.AccessToken = null;
|
||||||
|
|
||||||
|
log.Information(component, "Infisical authentication was successful.");
|
||||||
|
return new InfisicalAuthenticationResult
|
||||||
|
{
|
||||||
|
AccessToken = accessToken,
|
||||||
|
TokenType = string.IsNullOrEmpty(parsed.TokenType) ? "Bearer" : parsed.TokenType,
|
||||||
|
ExpiresAtUtc = expiresAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
response.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class IdentityLoginResponse
|
||||||
|
{
|
||||||
|
[Newtonsoft.Json.JsonProperty("accessToken")]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("expiresIn")]
|
||||||
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("tokenType")]
|
||||||
|
public string TokenType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,5 +10,10 @@ namespace PSInfisicalAPI.Authentication
|
|||||||
public string ClientId { get; set; }
|
public string ClientId { get; set; }
|
||||||
public SecureString ClientSecret { get; set; }
|
public SecureString ClientSecret { get; set; }
|
||||||
public SecureString PreSuppliedAccessToken { get; set; }
|
public SecureString PreSuppliedAccessToken { get; set; }
|
||||||
|
|
||||||
|
public string IdentityId { get; set; }
|
||||||
|
public SecureString Jwt { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public SecureString Password { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Authentication
|
||||||
|
{
|
||||||
|
public sealed class JwtAuthProvider : IInfisicalAuthProvider
|
||||||
|
{
|
||||||
|
private const string Component = "JwtAuthProvider";
|
||||||
|
|
||||||
|
public string Name { get { return "JwtAuth"; } }
|
||||||
|
|
||||||
|
public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (request == null || string.IsNullOrEmpty(request.IdentityId))
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("IdentityId is required for JWT Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Jwt == null || request.Jwt.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("Jwt is required for JWT Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IdentityLoginExecutor.Execute(InfisicalEndpointNames.JwtAuthLogin, Component, request, httpClient, logger, serializer =>
|
||||||
|
{
|
||||||
|
return SecureStringUtility.UsePlainText(request.Jwt, plainJwt =>
|
||||||
|
{
|
||||||
|
Dictionary<string, string> bodyObject = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "identityId", request.IdentityId },
|
||||||
|
{ "jwt", plainJwt ?? string.Empty }
|
||||||
|
};
|
||||||
|
|
||||||
|
return serializer.Serialize(bodyObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Authentication
|
||||||
|
{
|
||||||
|
public sealed class LdapAuthProvider : IInfisicalAuthProvider
|
||||||
|
{
|
||||||
|
private const string Component = "LdapAuthProvider";
|
||||||
|
|
||||||
|
public string Name { get { return "LdapAuth"; } }
|
||||||
|
|
||||||
|
public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (request == null || string.IsNullOrEmpty(request.Username))
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("Username is required for LDAP Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Password == null || request.Password.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("Password is required for LDAP Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IdentityLoginExecutor.Execute(InfisicalEndpointNames.LdapAuthLogin, Component, request, httpClient, logger, serializer =>
|
||||||
|
{
|
||||||
|
return SecureStringUtility.UsePlainText(request.Password, plainPassword =>
|
||||||
|
{
|
||||||
|
Dictionary<string, string> bodyObject = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "username", request.Username },
|
||||||
|
{ "password", plainPassword ?? string.Empty }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.IdentityId))
|
||||||
|
{
|
||||||
|
bodyObject["identityId"] = request.IdentityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializer.Serialize(bodyObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PSInfisicalAPI.Endpoints;
|
||||||
|
using PSInfisicalAPI.Errors;
|
||||||
|
using PSInfisicalAPI.Http;
|
||||||
|
using PSInfisicalAPI.Logging;
|
||||||
|
using PSInfisicalAPI.Security;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Authentication
|
||||||
|
{
|
||||||
|
public sealed class OidcAuthProvider : IInfisicalAuthProvider
|
||||||
|
{
|
||||||
|
private const string Component = "OidcAuthProvider";
|
||||||
|
|
||||||
|
public string Name { get { return "OidcAuth"; } }
|
||||||
|
|
||||||
|
public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (request == null || string.IsNullOrEmpty(request.IdentityId))
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("IdentityId is required for OIDC Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Jwt == null || request.Jwt.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InfisicalAuthenticationException("Jwt is required for OIDC Auth.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IdentityLoginExecutor.Execute(InfisicalEndpointNames.OidcAuthLogin, Component, request, httpClient, logger, serializer =>
|
||||||
|
{
|
||||||
|
return SecureStringUtility.UsePlainText(request.Jwt, plainJwt =>
|
||||||
|
{
|
||||||
|
Dictionary<string, string> bodyObject = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "identityId", request.IdentityId },
|
||||||
|
{ "jwt", plainJwt ?? string.Empty }
|
||||||
|
};
|
||||||
|
|
||||||
|
return serializer.Serialize(bodyObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
private const string ParameterSetUniversalAuth = "UniversalAuth";
|
private const string ParameterSetUniversalAuth = "UniversalAuth";
|
||||||
private const string ParameterSetToken = "Token";
|
private const string ParameterSetToken = "Token";
|
||||||
|
private const string ParameterSetJwt = "JwtAuth";
|
||||||
|
private const string ParameterSetOidc = "OidcAuth";
|
||||||
|
private const string ParameterSetLdap = "LdapAuth";
|
||||||
|
private const string ParameterSetAzure = "AzureAuth";
|
||||||
|
private const string ParameterSetGcpIam = "GcpIamAuth";
|
||||||
private const string Component = "ConnectInfisicalCmdlet";
|
private const string Component = "ConnectInfisicalCmdlet";
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@@ -38,6 +43,25 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[Parameter(ParameterSetName = ParameterSetToken)]
|
[Parameter(ParameterSetName = ParameterSetToken)]
|
||||||
public SecureString AccessToken { get; set; }
|
public SecureString AccessToken { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetJwt)]
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetOidc)]
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetAzure)]
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetGcpIam)]
|
||||||
|
[Parameter(ParameterSetName = ParameterSetLdap)]
|
||||||
|
public string IdentityId { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetJwt)]
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetOidc)]
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetAzure)]
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetGcpIam)]
|
||||||
|
public SecureString Jwt { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetLdap)]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = ParameterSetLdap)]
|
||||||
|
public SecureString Password { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string SecretPath { get; set; } = "/";
|
public string SecretPath { get; set; } = "/";
|
||||||
|
|
||||||
@@ -58,28 +82,91 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
InfisicalAuthenticationRequest request;
|
InfisicalAuthenticationRequest request;
|
||||||
InfisicalAuthType authType;
|
InfisicalAuthType authType;
|
||||||
|
|
||||||
if (string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal))
|
switch (ParameterSetName)
|
||||||
{
|
{
|
||||||
provider = new TokenAuthProvider();
|
case ParameterSetToken:
|
||||||
authType = InfisicalAuthType.Token;
|
provider = new TokenAuthProvider();
|
||||||
request = new InfisicalAuthenticationRequest
|
authType = InfisicalAuthType.Token;
|
||||||
{
|
request = new InfisicalAuthenticationRequest
|
||||||
BaseUri = BaseUri,
|
{
|
||||||
ApiVersion = ApiVersion,
|
BaseUri = BaseUri,
|
||||||
PreSuppliedAccessToken = AccessToken
|
ApiVersion = ApiVersion,
|
||||||
};
|
PreSuppliedAccessToken = AccessToken
|
||||||
}
|
};
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
provider = new UniversalAuthProvider();
|
case ParameterSetJwt:
|
||||||
authType = InfisicalAuthType.UniversalAuth;
|
provider = new JwtAuthProvider();
|
||||||
request = new InfisicalAuthenticationRequest
|
authType = InfisicalAuthType.Jwt;
|
||||||
{
|
request = new InfisicalAuthenticationRequest
|
||||||
BaseUri = BaseUri,
|
{
|
||||||
ApiVersion = ApiVersion,
|
BaseUri = BaseUri,
|
||||||
ClientId = ClientId,
|
ApiVersion = ApiVersion,
|
||||||
ClientSecret = ClientSecret
|
IdentityId = IdentityId,
|
||||||
};
|
Jwt = Jwt
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParameterSetOidc:
|
||||||
|
provider = new OidcAuthProvider();
|
||||||
|
authType = InfisicalAuthType.Oidc;
|
||||||
|
request = new InfisicalAuthenticationRequest
|
||||||
|
{
|
||||||
|
BaseUri = BaseUri,
|
||||||
|
ApiVersion = ApiVersion,
|
||||||
|
IdentityId = IdentityId,
|
||||||
|
Jwt = Jwt
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParameterSetLdap:
|
||||||
|
provider = new LdapAuthProvider();
|
||||||
|
authType = InfisicalAuthType.Ldap;
|
||||||
|
request = new InfisicalAuthenticationRequest
|
||||||
|
{
|
||||||
|
BaseUri = BaseUri,
|
||||||
|
ApiVersion = ApiVersion,
|
||||||
|
IdentityId = IdentityId,
|
||||||
|
Username = Username,
|
||||||
|
Password = Password
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParameterSetAzure:
|
||||||
|
provider = new AzureAuthProvider();
|
||||||
|
authType = InfisicalAuthType.Azure;
|
||||||
|
request = new InfisicalAuthenticationRequest
|
||||||
|
{
|
||||||
|
BaseUri = BaseUri,
|
||||||
|
ApiVersion = ApiVersion,
|
||||||
|
IdentityId = IdentityId,
|
||||||
|
Jwt = Jwt
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParameterSetGcpIam:
|
||||||
|
provider = new GcpIamAuthProvider();
|
||||||
|
authType = InfisicalAuthType.GcpIam;
|
||||||
|
request = new InfisicalAuthenticationRequest
|
||||||
|
{
|
||||||
|
BaseUri = BaseUri,
|
||||||
|
ApiVersion = ApiVersion,
|
||||||
|
IdentityId = IdentityId,
|
||||||
|
Jwt = Jwt
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
provider = new UniversalAuthProvider();
|
||||||
|
authType = InfisicalAuthType.UniversalAuth;
|
||||||
|
request = new InfisicalAuthenticationRequest
|
||||||
|
{
|
||||||
|
BaseUri = BaseUri,
|
||||||
|
ApiVersion = ApiVersion,
|
||||||
|
ClientId = ClientId,
|
||||||
|
ClientSecret = ClientSecret
|
||||||
|
};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
InfisicalAuthenticationResult authResult = provider.Authenticate(request, HttpClient, Logger);
|
InfisicalAuthenticationResult authResult = provider.Authenticate(request, HttpClient, Logger);
|
||||||
@@ -123,6 +210,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
private void ResolveMissingParametersFromEnvironment()
|
private void ResolveMissingParametersFromEnvironment()
|
||||||
{
|
{
|
||||||
bool tokenSet = string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal);
|
bool tokenSet = string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal);
|
||||||
|
bool universalSet = string.Equals(ParameterSetName, ParameterSetUniversalAuth, StringComparison.Ordinal);
|
||||||
|
|
||||||
bool needsScan =
|
bool needsScan =
|
||||||
BaseUri == null ||
|
BaseUri == null ||
|
||||||
@@ -130,8 +218,8 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
string.IsNullOrWhiteSpace(ProjectId) ||
|
string.IsNullOrWhiteSpace(ProjectId) ||
|
||||||
string.IsNullOrWhiteSpace(Environment) ||
|
string.IsNullOrWhiteSpace(Environment) ||
|
||||||
(tokenSet && (AccessToken == null || AccessToken.Length == 0)) ||
|
(tokenSet && (AccessToken == null || AccessToken.Length == 0)) ||
|
||||||
(!tokenSet && string.IsNullOrWhiteSpace(ClientId)) ||
|
(universalSet && string.IsNullOrWhiteSpace(ClientId)) ||
|
||||||
(!tokenSet && (ClientSecret == null || ClientSecret.Length == 0));
|
(universalSet && (ClientSecret == null || ClientSecret.Length == 0));
|
||||||
|
|
||||||
if (!needsScan)
|
if (!needsScan)
|
||||||
{
|
{
|
||||||
@@ -161,7 +249,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
AccessToken = InfisicalEnvironmentResolver.ResolveSecureString("AccessToken", InfisicalEnvironmentResolver.AccessTokenPatterns, AccessToken, Logger);
|
AccessToken = InfisicalEnvironmentResolver.ResolveSecureString("AccessToken", InfisicalEnvironmentResolver.AccessTokenPatterns, AccessToken, Logger);
|
||||||
}
|
}
|
||||||
else
|
else if (universalSet)
|
||||||
{
|
{
|
||||||
ClientId = InfisicalEnvironmentResolver.ResolveString("ClientId", InfisicalEnvironmentResolver.ClientIdPatterns, ClientId, Logger);
|
ClientId = InfisicalEnvironmentResolver.ResolveString("ClientId", InfisicalEnvironmentResolver.ClientIdPatterns, ClientId, Logger);
|
||||||
ClientSecret = InfisicalEnvironmentResolver.ResolveSecureString("ClientSecret", InfisicalEnvironmentResolver.ClientSecretPatterns, ClientSecret, Logger);
|
ClientSecret = InfisicalEnvironmentResolver.ResolveSecureString("ClientSecret", InfisicalEnvironmentResolver.ClientSecretPatterns, ClientSecret, Logger);
|
||||||
@@ -199,7 +287,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
if (AccessToken == null || AccessToken.Length == 0) { missing.Add("AccessToken"); }
|
if (AccessToken == null || AccessToken.Length == 0) { missing.Add("AccessToken"); }
|
||||||
}
|
}
|
||||||
else
|
else if (string.Equals(ParameterSetName, ParameterSetUniversalAuth, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(ClientId)) { missing.Add("ClientId"); }
|
if (string.IsNullOrWhiteSpace(ClientId)) { missing.Add("ClientId"); }
|
||||||
if (ClientSecret == null || ClientSecret.Length == 0) { missing.Add("ClientSecret"); }
|
if (ClientSecret == null || ClientSecret.Length == 0) { missing.Add("ClientSecret"); }
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
public const string JwtAuthLogin = "JwtAuthLogin";
|
public const string JwtAuthLogin = "JwtAuthLogin";
|
||||||
public const string OidcAuthLogin = "OidcAuthLogin";
|
public const string OidcAuthLogin = "OidcAuthLogin";
|
||||||
public const string LdapAuthLogin = "LdapAuthLogin";
|
public const string LdapAuthLogin = "LdapAuthLogin";
|
||||||
public const string KubernetesAuthLogin = "KubernetesAuthLogin";
|
|
||||||
public const string AwsAuthLogin = "AwsAuthLogin";
|
|
||||||
public const string AzureAuthLogin = "AzureAuthLogin";
|
public const string AzureAuthLogin = "AzureAuthLogin";
|
||||||
public const string GcpIamAuthLogin = "GcpIamAuthLogin";
|
public const string GcpIamAuthLogin = "GcpIamAuthLogin";
|
||||||
|
|
||||||
|
|||||||
@@ -92,30 +92,6 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
ContainsSecretMaterialInResponse = true
|
ContainsSecretMaterialInResponse = true
|
||||||
});
|
});
|
||||||
|
|
||||||
Add(map, new InfisicalEndpointDefinition
|
|
||||||
{
|
|
||||||
Name = InfisicalEndpointNames.KubernetesAuthLogin,
|
|
||||||
Resource = "Authentication",
|
|
||||||
Version = "v1",
|
|
||||||
Method = "POST",
|
|
||||||
Template = "/api/v1/auth/kubernetes-auth/login",
|
|
||||||
RequiresAuthorization = false,
|
|
||||||
ContainsSecretMaterialInRequest = true,
|
|
||||||
ContainsSecretMaterialInResponse = true
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(map, new InfisicalEndpointDefinition
|
|
||||||
{
|
|
||||||
Name = InfisicalEndpointNames.AwsAuthLogin,
|
|
||||||
Resource = "Authentication",
|
|
||||||
Version = "v1",
|
|
||||||
Method = "POST",
|
|
||||||
Template = "/api/v1/auth/aws-auth/login",
|
|
||||||
RequiresAuthorization = false,
|
|
||||||
ContainsSecretMaterialInRequest = true,
|
|
||||||
ContainsSecretMaterialInResponse = true
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(map, new InfisicalEndpointDefinition
|
Add(map, new InfisicalEndpointDefinition
|
||||||
{
|
{
|
||||||
Name = InfisicalEndpointNames.AzureAuthLogin,
|
Name = InfisicalEndpointNames.AzureAuthLogin,
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ namespace PSInfisicalAPI.Models
|
|||||||
public enum InfisicalAuthType
|
public enum InfisicalAuthType
|
||||||
{
|
{
|
||||||
UniversalAuth,
|
UniversalAuth,
|
||||||
Token
|
Token,
|
||||||
|
Jwt,
|
||||||
|
Oidc,
|
||||||
|
Ldap,
|
||||||
|
Azure,
|
||||||
|
GcpIam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user