feat(auth): implement ticket and API token authentication with session model

Add PveSession, PveAuthenticator, PveVersion, and PveAuthMode. Support
ticket-based auth (username/password with 2hr expiry) and API token auth
(USER@REALM!TOKENID=UUID format). Version detection on connect via
GET /api2/json/version. Custom exception types for API errors, expired
sessions, missing connections, version mismatches, and task failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-17 15:44:00 -05:00
parent 14d823af28
commit d802b117ba
11 changed files with 391 additions and 0 deletions
@@ -0,0 +1,29 @@
using System;
using System.Net;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when the Proxmox VE API returns an error response</summary>
public class PveApiException : Exception
{
public HttpStatusCode StatusCode { get; }
public string Resource { get; }
public string HttpMethod { get; }
public PveApiException(HttpStatusCode statusCode, string message, string resource, string httpMethod)
: base($"PVE API error ({(int)statusCode} {statusCode}) on {httpMethod} {resource}: {message}")
{
StatusCode = statusCode;
Resource = resource;
HttpMethod = httpMethod;
}
public PveApiException(HttpStatusCode statusCode, string message, string resource, string httpMethod, Exception innerException)
: base($"PVE API error ({(int)statusCode} {statusCode}) on {httpMethod} {resource}: {message}", innerException)
{
StatusCode = statusCode;
Resource = resource;
HttpMethod = httpMethod;
}
}
}
@@ -0,0 +1,18 @@
using System;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when authentication to a Proxmox VE server fails</summary>
public class PveAuthenticationException : Exception
{
public PveAuthenticationException(string message)
: base(message)
{
}
public PveAuthenticationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
@@ -0,0 +1,18 @@
using System;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when an operation is attempted without an active Proxmox VE session</summary>
public class PveNotConnectedException : Exception
{
public PveNotConnectedException()
: base("No active Proxmox VE session. Run Connect-PveServer first.")
{
}
public PveNotConnectedException(Exception innerException)
: base("No active Proxmox VE session. Run Connect-PveServer first.", innerException)
{
}
}
}
@@ -0,0 +1,18 @@
using System;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when the Proxmox VE session ticket has expired</summary>
public class PveSessionExpiredException : Exception
{
public PveSessionExpiredException()
: base("Your Proxmox VE session has expired. Please run Connect-PveServer to establish a new session.")
{
}
public PveSessionExpiredException(Exception innerException)
: base("Your Proxmox VE session has expired. Please run Connect-PveServer to establish a new session.", innerException)
{
}
}
}
@@ -0,0 +1,25 @@
using System;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when a Proxmox VE task completes with a failed exit status</summary>
public class PveTaskFailedException : Exception
{
public string Upid { get; }
public string ExitStatus { get; }
public PveTaskFailedException(string upid, string exitStatus)
: base($"Task {upid} failed with exit status: {exitStatus}")
{
Upid = upid;
ExitStatus = exitStatus;
}
public PveTaskFailedException(string upid, string exitStatus, Exception innerException)
: base($"Task {upid} failed with exit status: {exitStatus}", innerException)
{
Upid = upid;
ExitStatus = exitStatus;
}
}
}
@@ -0,0 +1,25 @@
using System;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when a Proxmox VE task does not complete within the allowed timeout period</summary>
public class PveTaskTimeoutException : Exception
{
public string Upid { get; }
public TimeSpan Timeout { get; }
public PveTaskTimeoutException(string upid, TimeSpan timeout)
: base($"Task {upid} did not complete within {timeout.TotalSeconds} seconds.")
{
Upid = upid;
Timeout = timeout;
}
public PveTaskTimeoutException(string upid, TimeSpan timeout, Exception innerException)
: base($"Task {upid} did not complete within {timeout.TotalSeconds} seconds.", innerException)
{
Upid = upid;
Timeout = timeout;
}
}
}
@@ -0,0 +1,29 @@
using System;
using PSProxmoxVE.Core.Authentication;
namespace PSProxmoxVE.Core.Exceptions
{
/// <summary>Exception thrown when the connected Proxmox VE server does not meet the minimum version requirement for an operation</summary>
public class PveVersionException : Exception
{
public int RequiredMajor { get; }
public int RequiredMinor { get; }
public PveVersion ActualVersion { get; }
public PveVersionException(int requiredMajor, int requiredMinor, PveVersion actualVersion)
: base($"This operation requires Proxmox VE {requiredMajor}.{requiredMinor} or later. Connected server is version {actualVersion}.")
{
RequiredMajor = requiredMajor;
RequiredMinor = requiredMinor;
ActualVersion = actualVersion;
}
public PveVersionException(int requiredMajor, int requiredMinor, PveVersion actualVersion, Exception innerException)
: base($"This operation requires Proxmox VE {requiredMajor}.{requiredMinor} or later. Connected server is version {actualVersion}.", innerException)
{
RequiredMajor = requiredMajor;
RequiredMinor = requiredMinor;
ActualVersion = actualVersion;
}
}
}