feat(push): add service-owned firebase channels and delivery
Add push contracts, provider delivery and channel diagnostics. Link email channels to services and document migration and validation. Ref: IT-1074
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
using System.Net.Mail;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HrynCo.NotificationService.DAL.Abstract.PushChannels;
|
||||
|
||||
// Parse only service-account fields; never honor caller-supplied OAuth endpoints.
|
||||
public sealed class FirebaseServiceAccountCredentials
|
||||
{
|
||||
public required string ProjectId { get; init; }
|
||||
public required string ClientEmail { get; init; }
|
||||
public required string PrivateKey { get; init; }
|
||||
|
||||
public static bool IsValidProjectId(string projectId) => Regex.IsMatch(projectId,
|
||||
"^[a-z][a-z0-9-]{4,28}[a-z0-9]$", RegexOptions.CultureInvariant, TimeSpan.FromSeconds(1));
|
||||
|
||||
public static FirebaseServiceAccountCredentials Parse(string json, string projectId)
|
||||
{
|
||||
if (!IsValidProjectId(projectId) || json.Length > 32768)
|
||||
throw new FormatException("Invalid Firebase configuration.");
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
if (Read(root, "type") != "service_account" || Read(root, "project_id") != projectId)
|
||||
throw new FormatException("The service account must belong to the selected Firebase project.");
|
||||
var email = Read(root, "client_email");
|
||||
if (!MailAddress.TryCreate(email, out var address) || address.Address != email)
|
||||
throw new FormatException("Invalid service account email.");
|
||||
var key = Read(root, "private_key");
|
||||
using var rsa = RSA.Create();
|
||||
rsa.ImportFromPem(key);
|
||||
// Exporting private parameters also rejects a public-key-only PEM.
|
||||
rsa.ExportParameters(true);
|
||||
return new FirebaseServiceAccountCredentials { ProjectId = projectId, ClientEmail = email, PrivateKey = key };
|
||||
}
|
||||
|
||||
private static string Read(JsonElement root, string name)
|
||||
{
|
||||
if (root.ValueKind != JsonValueKind.Object || !root.TryGetProperty(name, out var value)
|
||||
|| value.ValueKind != JsonValueKind.String)
|
||||
throw new FormatException("Missing service account fields.");
|
||||
return value.GetString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user