feat: add HTTP 428 sudo OTP handling to all 42 SDK implementations
When destructive operations (service/snapshot/image destroy/unlock) receive HTTP 428 Precondition Required, the SDK now: 1. Extracts challenge_id from the JSON response 2. Prompts: 'Confirmation required. Check your email for a one-time code.' 3. Reads OTP from stdin 4. Retries with X-Sudo-OTP and X-Sudo-Challenge headers Languages updated: AWK, Bash, C++, C#, Clojure, COBOL, Crystal, D, Dart, .NET, Elixir, Erlang, F#, Forth, Fortran, Go, Groovy, Haskell, Java, JavaScript, Julia, Kotlin, Lisp, Lua, Nim, Objective-C, OCaml, Perl, PHP, PowerShell, Prolog, Python, R, Raku, Ruby, Rust, Scheme, Swift, Tcl, TypeScript, V, Zig
This commit is contained in:
parent
1b06666f11
commit
a5155aed4f
42 changed files with 5163 additions and 321 deletions
|
|
@ -433,7 +433,7 @@ class Un
|
|||
|
||||
if (args.ServiceDestroy != null)
|
||||
{
|
||||
ApiRequest($"/services/{args.ServiceDestroy}", "DELETE", null, publicKey, secretKey);
|
||||
ApiRequestWithSudo($"/services/{args.ServiceDestroy}", "DELETE", null, publicKey, secretKey);
|
||||
Console.WriteLine($"{GREEN}Service destroyed: {args.ServiceDestroy}{RESET}");
|
||||
return;
|
||||
}
|
||||
|
|
@ -597,7 +597,7 @@ class Un
|
|||
return ExtMap[ext];
|
||||
}
|
||||
|
||||
static Dictionary<string, object> ApiRequest(string endpoint, string method, Dictionary<string, object> data, string publicKey, string secretKey)
|
||||
static Dictionary<string, object> ApiRequest(string endpoint, string method, Dictionary<string, object> data, string publicKey, string secretKey, string sudoOtp = null, string sudoChallengeId = null)
|
||||
{
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
|
||||
|
|
@ -634,6 +634,16 @@ class Un
|
|||
request.Headers.Add("Authorization", $"Bearer {publicKey}");
|
||||
}
|
||||
|
||||
// Add sudo OTP headers if provided
|
||||
if (!string.IsNullOrEmpty(sudoOtp))
|
||||
{
|
||||
request.Headers.Add("X-Sudo-OTP", sudoOtp);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(sudoChallengeId))
|
||||
{
|
||||
request.Headers.Add("X-Sudo-Challenge", sudoChallengeId);
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(body);
|
||||
|
|
@ -683,7 +693,53 @@ class Un
|
|||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
throw new Exception($"HTTP error - {error}");
|
||||
throw new HttpException(statusCode, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Custom exception to preserve HTTP status code
|
||||
class HttpException : Exception
|
||||
{
|
||||
public int StatusCode { get; }
|
||||
public string ResponseBody { get; }
|
||||
public HttpException(int statusCode, string responseBody) : base($"HTTP {statusCode}: {responseBody}")
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
ResponseBody = responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 428 sudo OTP challenge - prompts user for OTP and retries the request
|
||||
static Dictionary<string, object> HandleSudoChallenge(string responseBody, string endpoint, string method, Dictionary<string, object> data, string publicKey, string secretKey)
|
||||
{
|
||||
var response = ParseJson(responseBody);
|
||||
string challengeId = response.ContainsKey("challenge_id") ? (string)response["challenge_id"] : null;
|
||||
|
||||
Console.Error.WriteLine($"{YELLOW}Confirmation required. Check your email for a one-time code.{RESET}");
|
||||
Console.Error.Write("Enter OTP: ");
|
||||
|
||||
string otp = Console.ReadLine();
|
||||
if (string.IsNullOrEmpty(otp))
|
||||
{
|
||||
throw new Exception("Operation cancelled");
|
||||
}
|
||||
|
||||
otp = otp.Trim();
|
||||
|
||||
// Retry the request with sudo headers
|
||||
return ApiRequest(endpoint, method, data, publicKey, secretKey, otp, challengeId);
|
||||
}
|
||||
|
||||
// Wrapper for destructive operations that may require 428 sudo OTP
|
||||
static Dictionary<string, object> ApiRequestWithSudo(string endpoint, string method, Dictionary<string, object> data, string publicKey, string secretKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ApiRequest(endpoint, method, data, publicKey, secretKey);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == 428)
|
||||
{
|
||||
return HandleSudoChallenge(ex.ResponseBody, endpoint, method, data, publicKey, secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue