1.7.0: identity and compliance verification in all five clients (identityVerify / complianceCheck; Go: IdentityVerify / ComplianceCheck with the presentation as its JSON bytes)
POST /v1/identity/verify: an AERE Identity presentation judged by the published command line -> VALID / PARTIAL / INVALID with every row, the claims shown and the subject when no row failed, and the command that reproduces the verdict. POST /v1/compliance/check: your policy judged on a presentation -> COMPLIANT / NOT_COMPLIANT with the reasons and, with record, the decision as an AIP-23 envelope without personal data. In Go the presentation, keys, lists and policy travel as json.RawMessage: a map or float64 would change numbers above 2^53 and the canonical text the signatures cover. The .NET negative control now builds clean for each planted defect (an incremental build could run the tests against the previous planted library). A negative control for the JavaScript and Python routes is added. Tests: JavaScript 13, Python 14, Go ok, Java 10/10 (and the Java 8 API gate); negative controls Go 9/9, Java 10/10, .NET 12/12, JavaScript and Python 4/4.
This commit is contained in:
parent
88ed2ac92c
commit
0fe1828f5b
@ -70,6 +70,12 @@ const v = await aere.pqVerify({ scheme: 'ml-dsa-44', interface: 'external', publ
|
|||||||
// verifier, with the levels it checked and the command that reproduces the verdict. Served by cloud.aere.network since 2026-09-29.
|
// verifier, with the levels it checked and the command that reproduces the verdict. Served by cloud.aere.network since 2026-09-29.
|
||||||
const r = await aere.verify(envelope, { chain: 2800 }); // { verdict, levels: [{ level, state, detail }], verifier, reproduce }
|
const r = await aere.verify(envelope, { chain: 2800 }); // { verdict, levels: [{ level, state, detail }], verifier, reproduce }
|
||||||
|
|
||||||
|
// identity and compliance (1.7.0): an AERE Identity presentation (post-quantum credential, selective disclosure, bound to your audience
|
||||||
|
// and nonce) judged by the published command line; read `verdict` (PARTIAL: some rows could not be judged). No claims come back from
|
||||||
|
// complianceCheck; with record: true it returns the decision as an AIP-23 envelope without personal data, to notarize.
|
||||||
|
const id = await aere.identityVerify({ presentation, audience: 'https://your.app/verifier', nonce, trustedIssuers: [issuerId], statusLists: [list] });
|
||||||
|
const ok = await aere.complianceCheck({ presentation, policy, audience: 'https://your.app/verifier', nonce, statusLists: [list], record: true });
|
||||||
|
|
||||||
// transfer history for an address (token from genesis, native from launch)
|
// transfer history for an address (token from genesis, native from launch)
|
||||||
const tx = await aere.transfers('0xYourAddress', 50);
|
const tx = await aere.transfers('0xYourAddress', 50);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Probele clientului .NET (Aere.Cloud 1.6.0), fara framework de probe: fiecare proba tipareste "--- PASS: Nume" sau "--- FAIL: Nume: motiv",
|
// Probele clientului .NET (Aere.Cloud 1.7.0), fara framework de probe: fiecare proba tipareste "--- PASS: Nume" sau "--- FAIL: Nume: motiv",
|
||||||
// iar codul de iesire e numarul de probe picate (0 = toate verzi). Aceleasi garzi ca la clientul Go: cheia obligatorie, cheia trimisa NUMAI
|
// iar codul de iesire e numarul de probe picate (0 = toate verzi). Aceleasi garzi ca la clientul Go: cheia obligatorie, cheia trimisa NUMAI
|
||||||
// unde trebuie, redirectarile NEurmate (dovedit pe un server local real, fiindca numai handlerul implicit al SDK-ului o poate arata),
|
// unde trebuie, redirectarile NEurmate (dovedit pe un server local real, fiindca numai handlerul implicit al SDK-ului o poate arata),
|
||||||
// exportul jurnalului verificat pe octetii primiti, verificarea unei copii pastrate (tinuta / stearsa / eroare), webhook-ul in timp constant.
|
// exportul jurnalului verificat pe octetii primiti, verificarea unei copii pastrate (tinuta / stearsa / eroare), webhook-ul in timp constant.
|
||||||
@ -49,7 +49,7 @@ await Proba("ReadinessCheiaNumaiLaAttest", async () =>
|
|||||||
await c.PqFinalityAsync(); Cer(!ciot.Ultima.Antete.ContainsKey("x-api-key"), "finalitatea AIP-21 fara cheie");
|
await c.PqFinalityAsync(); Cer(!ciot.Ultima.Antete.ContainsKey("x-api-key"), "finalitatea AIP-21 fara cheie");
|
||||||
await c.HealthAsync(); Cer(!ciot.Ultima.Antete.ContainsKey("x-api-key"), "health fara cheie");
|
await c.HealthAsync(); Cer(!ciot.Ultima.Antete.ContainsKey("x-api-key"), "health fara cheie");
|
||||||
await c.AccountAsync(); Cer(ciot.Ultima.Antete.GetValueOrDefault("x-api-key") == Cheie, "account cu cheie");
|
await c.AccountAsync(); Cer(ciot.Ultima.Antete.GetValueOrDefault("x-api-key") == Cheie, "account cu cheie");
|
||||||
Cer(ciot.Ultima.Antete.GetValueOrDefault("user-agent", "").StartsWith("aere-cloud-csharp/1.6.0"), "user-agent-ul clientului");
|
Cer(ciot.Ultima.Antete.GetValueOrDefault("user-agent", "").StartsWith("aere-cloud-csharp/1.7.0"), "user-agent-ul clientului");
|
||||||
});
|
});
|
||||||
|
|
||||||
await Proba("PerimetruFinalitateDovada", async () =>
|
await Proba("PerimetruFinalitateDovada", async () =>
|
||||||
@ -77,6 +77,17 @@ await Proba("TrustApiVerify", async () =>
|
|||||||
Cer(ciot.Ultima.Url.EndsWith("/verify"), "fara lant: /verify");
|
Cer(ciot.Ultima.Url.EndsWith("/verify"), "fara lant: /verify");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Proba("IdentityCompliance", async () =>
|
||||||
|
{
|
||||||
|
var (c, ciot) = Client(_ => Json(200, "{\"verdict\":\"PARTIAL\"}"));
|
||||||
|
var r = await c.IdentityVerifyAsync(new Dictionary<string, object> { ["audience"] = "a", ["nonce"] = "n" });
|
||||||
|
Cer(ciot.Ultima.Metoda == "POST" && ciot.Ultima.Url.EndsWith("/v1/identity/verify") && ciot.Ultima.Antete.ContainsKey("x-api-key"), "POST /identity/verify cu cheie: " + ciot.Ultima.Url);
|
||||||
|
Cer(ciot.Ultima.Corp == "{\"audience\":\"a\",\"nonce\":\"n\"}", "cererea trimisa ca atare: " + ciot.Ultima.Corp);
|
||||||
|
Cer(r.GetProperty("verdict").GetString() == "PARTIAL", "verdictul intors");
|
||||||
|
await c.ComplianceCheckAsync(new Dictionary<string, object> { ["record"] = true });
|
||||||
|
Cer(ciot.Ultima.Url.EndsWith("/v1/compliance/check") && ciot.Ultima.Antete.ContainsKey("x-api-key") && ciot.Ultima.Corp == "{\"record\":true}", "POST /compliance/check: " + ciot.Ultima.Url + " " + ciot.Ultima.Corp);
|
||||||
|
});
|
||||||
|
|
||||||
await Proba("ExportulZilei", async () =>
|
await Proba("ExportulZilei", async () =>
|
||||||
{
|
{
|
||||||
var corp = Encoding.UTF8.GetBytes("{\"a\":1}\n{\"b\":2}\n");
|
var corp = Encoding.UTF8.GetBytes("{\"a\":1}\n{\"b\":2}\n");
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<RootNamespace>Aere.Cloud</RootNamespace>
|
<RootNamespace>Aere.Cloud</RootNamespace>
|
||||||
<AssemblyName>Aere.Cloud</AssemblyName>
|
<AssemblyName>Aere.Cloud</AssemblyName>
|
||||||
<PackageId>Aere.Cloud</PackageId>
|
<PackageId>Aere.Cloud</PackageId>
|
||||||
<Version>1.6.0</Version>
|
<Version>1.7.0</Version>
|
||||||
<Authors>Aere Network</Authors>
|
<Authors>Aere Network</Authors>
|
||||||
<Description>Official .NET client for the Aere Cloud API (Aere Network, chain 2800): RPC, post-quantum verification, the Trust API (AERE Proof Protocol envelopes), chain data, notarization and proofs, quantum readiness, audit log, webhooks and gas sponsorship.</Description>
|
<Description>Official .NET client for the Aere Cloud API (Aere Network, chain 2800): RPC, post-quantum verification, the Trust API (AERE Proof Protocol envelopes), chain data, notarization and proofs, quantum readiness, audit log, webhooks and gas sponsorship.</Description>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Aere.Cloud - the official .NET client for the Aere Cloud API (Aere Network, chain 2800).
|
// Aere.Cloud - the official .NET client for the Aere Cloud API (Aere Network, chain 2800).
|
||||||
//
|
//
|
||||||
// Same routes and semantics as @aere/cloud (JS), aere-cloud (Python), aere-cloud (Java) and the Go client, 1.6.0. No dependency beyond
|
// Same routes and semantics as @aere/cloud (JS), aere-cloud (Python), aere-cloud (Java) and the Go client, 1.7.0. No dependency beyond
|
||||||
// the .NET base library. JSON routes return the server's JSON as a JsonElement, so the integrator binds it to whatever types they
|
// the .NET base library. JSON routes return the server's JSON as a JsonElement, so the integrator binds it to whatever types they
|
||||||
// already use; the two routes that return FILES (the audit export and the check of a kept export) return typed results with the
|
// already use; the two routes that return FILES (the audit export and the check of a kept export) return typed results with the
|
||||||
// digest computed locally from the bytes received. Non-2xx answers are thrown as AereCloudException (HTTP status, server body, path).
|
// digest computed locally from the bytes received. Non-2xx answers are thrown as AereCloudException (HTTP status, server body, path).
|
||||||
@ -52,7 +52,7 @@ public sealed record KeptCheck(string Day, string State, string Digest, string L
|
|||||||
public sealed class AereCloudClient : IDisposable
|
public sealed class AereCloudClient : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>Version of this client, sent in the User-Agent (the edge refuses generic library agents on every route).</summary>
|
/// <summary>Version of this client, sent in the User-Agent (the edge refuses generic library agents on every route).</summary>
|
||||||
public const string Version = "1.6.0";
|
public const string Version = "1.7.0";
|
||||||
/// <summary>The production gateway.</summary>
|
/// <summary>The production gateway.</summary>
|
||||||
public const string DefaultBaseUrl = "https://cloud.aere.network/v1";
|
public const string DefaultBaseUrl = "https://cloud.aere.network/v1";
|
||||||
|
|
||||||
@ -137,6 +137,14 @@ public sealed class AereCloudClient : IDisposable
|
|||||||
public Task<JsonElement> VerifyAsync(object envelope, ulong chain = 0, CancellationToken ct = default) =>
|
public Task<JsonElement> VerifyAsync(object envelope, ulong chain = 0, CancellationToken ct = default) =>
|
||||||
JsonAsync(HttpMethod.Post, "/verify" + (chain > 0 ? "?chain=" + chain : ""), envelope, true, ct);
|
JsonAsync(HttpMethod.Post, "/verify" + (chain > 0 ? "?chain=" + chain : ""), envelope, true, ct);
|
||||||
|
|
||||||
|
/// <summary>Identity verification (1.7.0): {presentation, audience, nonce, trustedIssuers?, statusLists?, revocations?, maxAgeSeconds?} ->
|
||||||
|
/// VALID / PARTIAL / INVALID with every row, judged by the published AERE Identity command line. Read "verdict" (PARTIAL: some rows not judged).</summary>
|
||||||
|
public Task<JsonElement> IdentityVerifyAsync(object request, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/identity/verify", request, true, ct);
|
||||||
|
|
||||||
|
/// <summary>Compliance check (1.7.0): {presentation, policy, audience, nonce, statusLists?, revocations?, record?} -> COMPLIANT / NOT_COMPLIANT
|
||||||
|
/// with the reasons and, with record, an AIP-23 record without personal data.</summary>
|
||||||
|
public Task<JsonElement> ComplianceCheckAsync(object request, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/compliance/check", request, true, ct);
|
||||||
|
|
||||||
/// <summary>GET /data/head.</summary>
|
/// <summary>GET /data/head.</summary>
|
||||||
public Task<JsonElement> DataHeadAsync(CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/data/head", null, true, ct);
|
public Task<JsonElement> DataHeadAsync(CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/data/head", null, true, ct);
|
||||||
/// <summary>GET /data/validators.</summary>
|
/// <summary>GET /data/validators.</summary>
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
The official .NET client for the [Aere Cloud API](https://aere.network/cloud.html) on Aere Network (chain 2800).
|
The official .NET client for the [Aere Cloud API](https://aere.network/cloud.html) on Aere Network (chain 2800).
|
||||||
**No dependencies** beyond the .NET base library (`System.Net.Http`, `System.Text.Json`, `System.Security.Cryptography`).
|
**No dependencies** beyond the .NET base library (`System.Net.Http`, `System.Text.Json`, `System.Security.Cryptography`).
|
||||||
Targets .NET 8 (LTS) and runs on later versions. Same routes and semantics as the JS, Python, Go and Java clients 1.6.0,
|
Targets .NET 8 (LTS) and runs on later versions. Same routes and semantics as the JS, Python, Go and Java clients 1.7.0,
|
||||||
including `VerifyAsync`, the Trust API.
|
including `VerifyAsync`, the Trust API, and `IdentityVerifyAsync` / `ComplianceCheckAsync`.
|
||||||
|
|
||||||
JSON routes return the server's JSON as a `JsonElement`, so you bind it to the types you already use. The two routes
|
JSON routes return the server's JSON as a `JsonElement`, so you bind it to the types you already use. The two routes
|
||||||
that return files (the audit export and the check of a kept export) return typed results, with the SHA-256 computed
|
that return files (the audit export and the check of a kept export) return typed results, with the SHA-256 computed
|
||||||
|
|||||||
@ -24,6 +24,8 @@ P = [
|
|||||||
("webhook acceptat fara semnatura", 'return CryptographicOperations.FixedTimeEquals(want, got);', 'return got.Length >= 0 || CryptographicOperations.FixedTimeEquals(want, got);', "VerifyWebhook"),
|
("webhook acceptat fara semnatura", 'return CryptographicOperations.FixedTimeEquals(want, got);', 'return got.Length >= 0 || CryptographicOperations.FixedTimeEquals(want, got);', "VerifyWebhook"),
|
||||||
("cheia goala acceptata", 'if (string.IsNullOrEmpty(apiKey)) throw', 'if (apiKey == "niciodata-" + "asa") throw', "CheiaEObligatorie"),
|
("cheia goala acceptata", 'if (string.IsNullOrEmpty(apiKey)) throw', 'if (apiKey == "niciodata-" + "asa") throw', "CheiaEObligatorie"),
|
||||||
("lantul cerut scapat din Trust API", '(chain > 0 ? "?chain=" + chain : "")', '(chain > 0 ? "" : "")', "TrustApiVerify"),
|
("lantul cerut scapat din Trust API", '(chain > 0 ? "?chain=" + chain : "")', '(chain > 0 ? "" : "")', "TrustApiVerify"),
|
||||||
|
("conformitatea pe ruta identitatii (1.7.0)", 'JsonAsync(HttpMethod.Post, "/compliance/check", request, true, ct);', 'JsonAsync(HttpMethod.Post, "/identity/verify", request, true, ct);', "IdentityCompliance"),
|
||||||
|
("identitatea fara cheie (1.7.0)", 'JsonAsync(HttpMethod.Post, "/identity/verify", request, true, ct);', 'JsonAsync(HttpMethod.Post, "/identity/verify", request, false, ct);', "IdentityCompliance"),
|
||||||
("un rezultat RPC fara forma citit ca zero", ' throw new AereCloudException(200, e.GetRawText(), "/rpc");', ' return 0;', "EroriSiBlockNumber"),
|
("un rezultat RPC fara forma citit ca zero", ' throw new AereCloudException(200, e.GetRawText(), "/rpc");', ' return 0;', "EroriSiBlockNumber"),
|
||||||
]
|
]
|
||||||
if sys.argv[1] == "n": print(len(P)); sys.exit(0)
|
if sys.argv[1] == "n": print(len(P)); sys.exit(0)
|
||||||
@ -33,6 +35,10 @@ if t.count(vechi) != 1: print("ANCORA %d" % t.count(vechi)); sys.exit(3)
|
|||||||
open(sys.argv[3], "w").write(t.replace(vechi, nou)); print(nume + "|" + tinta)
|
open(sys.argv[3], "w").write(t.replace(vechi, nou)); print(nume + "|" + tinta)
|
||||||
PY
|
PY
|
||||||
ruleaza() { # -> iesirea probelor in $W/iesire.txt; cod: 0 rulat, 2 nu compileaza
|
ruleaza() { # -> iesirea probelor in $W/iesire.txt; cod: 0 rulat, 2 nu compileaza
|
||||||
|
# 2026-09-30: constructie CURATA la fiecare plantare. Cu constructia incrementala, "cheia goala acceptata" a iesit de doua ori din trei
|
||||||
|
# CONTROL CAZUT: probele rulau cu biblioteca plantarii de DINAINTE (copiata sau nu in bin-ul probelor dupa marcile de timp), deci
|
||||||
|
# masurau alta plantare decat cea numita. Fara bin/ si obj/ nu ramane nimic de la rularea anterioara.
|
||||||
|
rm -rf "$W"/Aere.Cloud/bin "$W"/Aere.Cloud/obj "$W"/Aere.Cloud.Tests/bin "$W"/Aere.Cloud.Tests/obj
|
||||||
( cd "$W" && dotnet build Aere.Cloud.Tests -c Release -p:NuGetAudit=false -nodeReuse:false -p:UseSharedCompilation=false > build.log 2>&1 < /dev/null ) || return 2
|
( cd "$W" && dotnet build Aere.Cloud.Tests -c Release -p:NuGetAudit=false -nodeReuse:false -p:UseSharedCompilation=false > build.log 2>&1 < /dev/null ) || return 2
|
||||||
# exact UN dll de probe in iesirea acestei constructii (un `| head -1` peste mai multe ar alege la intamplare)
|
# exact UN dll de probe in iesirea acestei constructii (un `| head -1` peste mai multe ar alege la intamplare)
|
||||||
local dll; dll=$(ls "$W"/Aere.Cloud.Tests/bin/Release/net*/Aere.Cloud.Tests.dll 2>/dev/null); [ "$(printf '%s\n' "$dll" | grep -c .)" = 1 ] || return 2
|
local dll; dll=$(ls "$W"/Aere.Cloud.Tests/bin/Release/net*/Aere.Cloud.Tests.dll 2>/dev/null); [ "$(printf '%s\n' "$dll" | grep -c .)" = 1 ] || return 2
|
||||||
|
|||||||
@ -30,6 +30,11 @@ k, err := c.AuditVerifyKept(ctx, "2026-05-01", keptBytes)
|
|||||||
// Pass the envelope's bytes as received (os.ReadFile): a Go map would sort the keys and change the statement's hash.
|
// Pass the envelope's bytes as received (os.ReadFile): a Go map would sort the keys and change the statement's hash.
|
||||||
verdict, err := c.Verify(ctx, json.RawMessage(envelopeBytes), 2800)
|
verdict, err := c.Verify(ctx, json.RawMessage(envelopeBytes), 2800)
|
||||||
|
|
||||||
|
// identity and compliance (1.7.0): the presentation, keys, lists and policy travel as their JSON bytes (numbers above 2^53 stay exact)
|
||||||
|
id, err := c.IdentityVerify(ctx, aerecloud.IdentityVerifyRequest{Presentation: presBytes, Audience: "https://your.app/verifier", Nonce: nonce,
|
||||||
|
TrustedIssuers: []json.RawMessage{json.RawMessage(`"aere-id:..."`)}, StatusLists: []json.RawMessage{listBytes}})
|
||||||
|
ok, err := c.ComplianceCheck(ctx, aerecloud.ComplianceCheckRequest{Presentation: presBytes, Policy: policyBytes, Audience: "https://your.app/verifier", Nonce: nonce, Record: true})
|
||||||
|
|
||||||
// verify a webhook delivery (constant-time HMAC-SHA256 over the exact bytes received)
|
// verify a webhook delivery (constant-time HMAC-SHA256 over the exact bytes received)
|
||||||
ok := aerecloud.VerifyWebhook(rawBody, r.Header.Get("x-aere-signature"), secret)
|
ok := aerecloud.VerifyWebhook(rawBody, r.Header.Get("x-aere-signature"), secret)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Version of this client, sent in the User-Agent (the edge refuses generic library agents on every route).
|
// Version of this client, sent in the User-Agent (the edge refuses generic library agents on every route).
|
||||||
const Version = "1.6.0"
|
const Version = "1.7.0"
|
||||||
|
|
||||||
// DefaultBaseURL is the production gateway.
|
// DefaultBaseURL is the production gateway.
|
||||||
const DefaultBaseURL = "https://cloud.aere.network/v1"
|
const DefaultBaseURL = "https://cloud.aere.network/v1"
|
||||||
@ -174,6 +174,42 @@ func (c *Client) Verify(ctx context.Context, envelope json.RawMessage, chain uin
|
|||||||
}
|
}
|
||||||
return c.jsonReq(ctx, "POST", p, envelope, true)
|
return c.jsonReq(ctx, "POST", p, envelope, true)
|
||||||
}
|
}
|
||||||
|
// IdentityVerifyRequest is the body of POST /v1/identity/verify (1.7.0). The presentation, keys, status lists and revocations are kept as
|
||||||
|
// their JSON bytes: a Go map or float64 would change numbers above 2^53 and the canonical text the signatures cover.
|
||||||
|
type IdentityVerifyRequest struct {
|
||||||
|
Presentation json.RawMessage `json:"presentation"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
TrustedIssuers []json.RawMessage `json:"trustedIssuers,omitempty"`
|
||||||
|
StatusLists []json.RawMessage `json:"statusLists,omitempty"`
|
||||||
|
Revocations []json.RawMessage `json:"revocations,omitempty"`
|
||||||
|
MaxAgeSeconds int `json:"maxAgeSeconds,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComplianceCheckRequest is the body of POST /v1/compliance/check (1.7.0).
|
||||||
|
type ComplianceCheckRequest struct {
|
||||||
|
Presentation json.RawMessage `json:"presentation"`
|
||||||
|
Policy json.RawMessage `json:"policy"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
StatusLists []json.RawMessage `json:"statusLists,omitempty"`
|
||||||
|
Revocations []json.RawMessage `json:"revocations,omitempty"`
|
||||||
|
Record bool `json:"record,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentityVerify (1.7.0): an AERE Identity presentation judged by the published command line -> VALID / PARTIAL / INVALID with every row,
|
||||||
|
// the claims shown and the subject when no row failed, and the command (with the moment) that reproduces the verdict. Read "verdict":
|
||||||
|
// PARTIAL means some rows could not be judged. Needs a gateway that serves POST /v1/identity/verify.
|
||||||
|
func (c *Client) IdentityVerify(ctx context.Context, req IdentityVerifyRequest) (json.RawMessage, error) {
|
||||||
|
return c.jsonReq(ctx, "POST", "/identity/verify", req, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComplianceCheck (1.7.0): your policy judged on an AERE Identity presentation -> COMPLIANT / NOT_COMPLIANT with the reasons, and with
|
||||||
|
// Record the record as an AIP-23 envelope without personal data. No claims come back.
|
||||||
|
func (c *Client) ComplianceCheck(ctx context.Context, req ComplianceCheckRequest) (json.RawMessage, error) {
|
||||||
|
return c.jsonReq(ctx, "POST", "/compliance/check", req, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) DataHead(ctx context.Context) (json.RawMessage, error) {
|
func (c *Client) DataHead(ctx context.Context) (json.RawMessage, error) {
|
||||||
return c.jsonReq(ctx, "GET", "/data/head", nil, true)
|
return c.jsonReq(ctx, "GET", "/data/head", nil, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -217,6 +217,31 @@ func TestVerifyPastreazaOrdinea(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1.7.0: cererea de identitate pleaca pe ruta ei, cu cheie, cu prezentarea NESCHIMBATA (un numar peste 2^53 ramane exact)
|
||||||
|
func TestIdentityVerifyPastreazaOctetii(t *testing.T) {
|
||||||
|
s, c := porneste(t)
|
||||||
|
prez := `{"v":1,"kind":"aere-presentation","credential":{"statement":{"claims":{"id":12345678901234567891}}}}`
|
||||||
|
if _, err := c.IdentityVerify(context.Background(), IdentityVerifyRequest{Presentation: json.RawMessage(prez), Audience: "https://v.example", Nonce: "n-1", TrustedIssuers: []json.RawMessage{json.RawMessage(`"aere-id:00"`)}}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
v := s.ultima()
|
||||||
|
if v.Method != "POST" || v.Path != "/v1/identity/verify" || v.Key == "" || !strings.Contains(v.Body, `"presentation":`+prez) || !strings.Contains(v.Body, `"trustedIssuers":["aere-id:00"]`) || strings.Contains(v.Body, "statusLists") {
|
||||||
|
t.Fatalf("cererea de identitate: %+v", v)
|
||||||
|
}
|
||||||
|
if _, err := c.ComplianceCheck(context.Background(), ComplianceCheckRequest{Presentation: json.RawMessage(prez), Policy: json.RawMessage(`{"id":"p"}`), Audience: "a", Nonce: "n", Record: true}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if v = s.ultima(); v.Path != "/v1/compliance/check" || v.Key == "" || !strings.Contains(v.Body, `"presentation":`+prez) || !strings.Contains(v.Body, `"record":true`) {
|
||||||
|
t.Fatalf("cererea de conformitate: %+v", v)
|
||||||
|
}
|
||||||
|
var m map[string]any
|
||||||
|
_ = json.Unmarshal([]byte(prez), &m)
|
||||||
|
prin, _ := json.Marshal(m)
|
||||||
|
if strings.Contains(string(prin), "12345678901234567891") {
|
||||||
|
t.Fatal("controlul probei: un map ar fi trebuit sa strice numarul mare")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVerifyWebhook(t *testing.T) {
|
func TestVerifyWebhook(t *testing.T) {
|
||||||
secret := "AERE-SINTETIC-secret"
|
secret := "AERE-SINTETIC-secret"
|
||||||
corp := []byte(`{"event":"pq-anchor","data":{"height":42}}`)
|
corp := []byte(`{"event":"pq-anchor","data":{"height":42}}`)
|
||||||
|
|||||||
@ -24,6 +24,10 @@ const PLANTARI = [
|
|||||||
// 2026-09-29: plicul trecut printr-un map de Go (cheile sortate = alt text canonic, un plic valid ar iesi INVALID la Trust API)
|
// 2026-09-29: plicul trecut printr-un map de Go (cheile sortate = alt text canonic, un plic valid ar iesi INVALID la Trust API)
|
||||||
['Verify trece plicul printr-un map', 'return c.jsonReq(ctx, "POST", p, envelope, true)',
|
['Verify trece plicul printr-un map', 'return c.jsonReq(ctx, "POST", p, envelope, true)',
|
||||||
'var m map[string]any\n\t_ = json.Unmarshal(envelope, &m)\n\treturn c.jsonReq(ctx, "POST", p, m, true)', 'TestVerifyPastreazaOrdinea'],
|
'var m map[string]any\n\t_ = json.Unmarshal(envelope, &m)\n\treturn c.jsonReq(ctx, "POST", p, m, true)', 'TestVerifyPastreazaOrdinea'],
|
||||||
|
// 1.7.0: prezentarea trecuta printr-un map (numerele mari devin float64) si ruta de conformitate trimisa la identitate
|
||||||
|
['IdentityVerify trece prezentarea printr-un map', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)',
|
||||||
|
'var m map[string]any\n\t_ = json.Unmarshal(req.Presentation, &m)\n\tb, _ := json.Marshal(m)\n\treq.Presentation = b\n\treturn c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'],
|
||||||
|
['ComplianceCheck pe ruta de identitate', 'return c.jsonReq(ctx, "POST", "/compliance/check", req, true)', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'],
|
||||||
];
|
];
|
||||||
|
|
||||||
function ruleaza(src) {
|
function ruleaza(src) {
|
||||||
|
|||||||
17
index.mjs
17
index.mjs
@ -75,6 +75,23 @@ export class AereCloud {
|
|||||||
return this._req('POST', '/verify' + (chain ? `?chain=${encodeURIComponent(chain)}` : ''), { body: envelope });
|
return this._req('POST', '/verify' + (chain ? `?chain=${encodeURIComponent(chain)}` : ''), { body: envelope });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* identity verification (1.7.0): an AERE Identity presentation judged by the published command line -> VALID / PARTIAL / INVALID with every
|
||||||
|
* row, the claims shown and the subject when no row failed, and the command (with the moment) that reproduces the verdict. Read `verdict`:
|
||||||
|
* PARTIAL means some rows could not be judged (no trusted issuers, no status list). Needs a gateway that serves POST /v1/identity/verify.
|
||||||
|
* @param {{presentation:object, audience:string, nonce:string, trustedIssuers?:(string|object)[], statusLists?:object[], revocations?:object[], maxAgeSeconds?:number}} request
|
||||||
|
* @returns {Promise<{verdict:'VALID'|'PARTIAL'|'INVALID', notJudged:number, subject:object|null, claims:object|null, rows:object[], judgedAt:string, verifier:object, reproduce:string}>}
|
||||||
|
*/
|
||||||
|
identityVerify(request) { return this._req('POST', '/identity/verify', { body: request }); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compliance check (1.7.0): your policy judged on an AERE Identity presentation -> COMPLIANT / NOT_COMPLIANT with the reasons, and with
|
||||||
|
* `record: true` the record of the decision as an AIP-23 envelope without personal data (notarize it with notarize()). No claims come back.
|
||||||
|
* @param {{presentation:object, policy:object, audience:string, nonce:string, statusLists?:object[], revocations?:object[], record?:boolean}} request
|
||||||
|
* @returns {Promise<{verdict:'COMPLIANT'|'NOT_COMPLIANT', compliant:boolean, reasons:string[], policyId:string, policyHash:string, presentationHash:string, record?:object, judgedAt:string, verifier:object, reproduce:string}>}
|
||||||
|
*/
|
||||||
|
complianceCheck(request) { return this._req('POST', '/compliance/check', { body: request }); }
|
||||||
|
|
||||||
// ---- chain data ----
|
// ---- chain data ----
|
||||||
dataHead() { return this._req('GET', '/data/head'); }
|
dataHead() { return this._req('GET', '/data/head'); }
|
||||||
validators() { return this._req('GET', '/data/validators'); }
|
validators() { return this._req('GET', '/data/validators'); }
|
||||||
|
|||||||
@ -28,6 +28,10 @@ String acct = aere.sponsorCreatePqAccount("0x09" + falconPkHex, 0);
|
|||||||
// Trust API (1.6.0): any AERE Proof Protocol (AIP-23) envelope (JSON text) -> VALID / INVALID / PARTIAL with the levels checked
|
// Trust API (1.6.0): any AERE Proof Protocol (AIP-23) envelope (JSON text) -> VALID / INVALID / PARTIAL with the levels checked
|
||||||
String verdict = aere.verify(envelopeJson, 2800);
|
String verdict = aere.verify(envelopeJson, 2800);
|
||||||
|
|
||||||
|
// identity and compliance (1.7.0): the request as JSON text, sent as given; read "verdict" (PARTIAL: some rows not judged)
|
||||||
|
String id = aere.identityVerify("{\"presentation\":" + presentationJson + ",\"audience\":\"https://your.app/verifier\",\"nonce\":\"" + nonce + "\"}");
|
||||||
|
String ok = aere.complianceCheck(requestWithPolicyJson);
|
||||||
|
|
||||||
// verify a webhook delivery (constant-time HMAC) — never trust one without this:
|
// verify a webhook delivery (constant-time HMAC) — never trust one without this:
|
||||||
boolean ok = AereCloud.verifyWebhook(rawBodyBytes, request.header("x-aere-signature"), secret);
|
boolean ok = AereCloud.verifyWebhook(rawBodyBytes, request.header("x-aere-signature"), secret);
|
||||||
```
|
```
|
||||||
|
|||||||
@ -27,6 +27,9 @@ const PLANTARI = [
|
|||||||
'if (r.status < 200 || r.status >= 300) return new KeptCheck(day, "held", null, local, false, null);\n String dig = sha256Hex(r.octeti);', 'auditVerifyKept'],
|
'if (r.status < 200 || r.status >= 300) return new KeptCheck(day, "held", null, local, false, null);\n String dig = sha256Hex(r.octeti);', 'auditVerifyKept'],
|
||||||
// 1.6.1: HttpURLConnection urmeaza redirectarile implicit; fara oprire, cheia pleaca spre gazda din Location
|
// 1.6.1: HttpURLConnection urmeaza redirectarile implicit; fara oprire, cheia pleaca spre gazda din Location
|
||||||
['redirectarea urmata (cheia pleaca spre alta gazda)', 'c.setInstanceFollowRedirects(false);', 'c.setInstanceFollowRedirects(true);', '302'],
|
['redirectarea urmata (cheia pleaca spre alta gazda)', 'c.setInstanceFollowRedirects(false);', 'c.setInstanceFollowRedirects(true);', '302'],
|
||||||
|
// 1.7.0: conformitatea trimisa pe ruta identitatii; identitatea fara cheie
|
||||||
|
['complianceCheck pe ruta identitatii', 'return req("POST", "/compliance/check", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, true);', '1.7.0'],
|
||||||
|
['identityVerify fara cheie', 'return req("POST", "/identity/verify", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, false);', '1.7.0'],
|
||||||
];
|
];
|
||||||
// 1.6.1: poarta Android = clasa compileaza cu `javac --release 8`. Un API de dupa Java 8 trece probele pe JDK 21 si trebuie prins
|
// 1.6.1: poarta Android = clasa compileaza cu `javac --release 8`. Un API de dupa Java 8 trece probele pe JDK 21 si trebuie prins
|
||||||
// numai de poarta; plantarea NU e o proba, e o compilare care TREBUIE sa cada.
|
// numai de poarta; plantarea NU e o proba, e o compilare care TREBUIE sa cada.
|
||||||
|
|||||||
@ -32,7 +32,8 @@ public final class AereCloud {
|
|||||||
// 1.6.1 (2026-09-28): transportul pe HttpURLConnection in loc de java.net.http (care NU exista pe Android), fara redirectari
|
// 1.6.1 (2026-09-28): transportul pe HttpURLConnection in loc de java.net.http (care NU exista pe Android), fara redirectari
|
||||||
// urmate (ca inainte: HttpClient nu le urma implicit); URLEncoder cu numele setului de caractere (varianta cu Charset e
|
// urmate (ca inainte: HttpClient nu le urma implicit); URLEncoder cu numele setului de caractere (varianta cu Charset e
|
||||||
// Java 10+). Aceeasi suprafata publica.
|
// Java 10+). Aceeasi suprafata publica.
|
||||||
public static final String VERSIUNE = "1.6.1";
|
// 1.7.0 (2026-09-30): identityVerify() si complianceCheck() (POST /v1/identity/verify, /v1/compliance/check)
|
||||||
|
public static final String VERSIUNE = "1.7.0";
|
||||||
private static final String BAZA_IMPLICITA = "https://cloud.aere.network/v1";
|
private static final String BAZA_IMPLICITA = "https://cloud.aere.network/v1";
|
||||||
private static final int TIMP_MS = 30000;
|
private static final int TIMP_MS = 30000;
|
||||||
|
|
||||||
@ -163,6 +164,10 @@ public final class AereCloud {
|
|||||||
/** the Trust API (1.6.0): one AERE Proof Protocol (AIP-23) envelope of any kind -> VALID / INVALID / PARTIAL with the levels checked and the command that reproduces the verdict; the judgement is the published reference verifier. Needs a gateway that serves POST /v1/verify. chain: 2800 or 28001; 0 = the chain the envelope declares. */
|
/** the Trust API (1.6.0): one AERE Proof Protocol (AIP-23) envelope of any kind -> VALID / INVALID / PARTIAL with the levels checked and the command that reproduces the verdict; the judgement is the published reference verifier. Needs a gateway that serves POST /v1/verify. chain: 2800 or 28001; 0 = the chain the envelope declares. */
|
||||||
public String verify(String envelopeJson, long chain) { return req("POST", "/verify" + (chain > 0 ? "?chain=" + chain : ""), envelopeJson, true); }
|
public String verify(String envelopeJson, long chain) { return req("POST", "/verify" + (chain > 0 ? "?chain=" + chain : ""), envelopeJson, true); }
|
||||||
public String verify(String envelopeJson) { return verify(envelopeJson, 0); }
|
public String verify(String envelopeJson) { return verify(envelopeJson, 0); }
|
||||||
|
/** identity verification (1.7.0): {presentation, audience, nonce, trustedIssuers?, statusLists?, revocations?, maxAgeSeconds?} -> VALID / PARTIAL / INVALID with every row, judged by the published AERE Identity command line; read "verdict" (PARTIAL: some rows not judged). The request is sent as the JSON you give. */
|
||||||
|
public String identityVerify(String requestJson) { return req("POST", "/identity/verify", requestJson, true); }
|
||||||
|
/** compliance check (1.7.0): {presentation, policy, audience, nonce, statusLists?, revocations?, record?} -> COMPLIANT / NOT_COMPLIANT with the reasons and, with record, an AIP-23 record without personal data. */
|
||||||
|
public String complianceCheck(String requestJson) { return req("POST", "/compliance/check", requestJson, true); }
|
||||||
|
|
||||||
// ---- chain data ----
|
// ---- chain data ----
|
||||||
public String dataHead() { return req("GET", "/data/head", null, true); }
|
public String dataHead() { return req("GET", "/data/head", null, true); }
|
||||||
|
|||||||
@ -120,6 +120,13 @@ public final class TestAereCloud {
|
|||||||
lc.verify("{}");
|
lc.verify("{}");
|
||||||
cere(ultima.get()[1].equals("/v1/verify"), ultima.get()[1]);
|
cere(ultima.get()[1].equals("/v1/verify"), ultima.get()[1]);
|
||||||
});
|
});
|
||||||
|
proba("1.7.0: identityVerify si complianceCheck trimit cererea neschimbata pe rutele lor, cu cheie", () -> {
|
||||||
|
lc.identityVerify("{\"presentation\":{\"v\":1},\"audience\":\"a\",\"nonce\":\"n\"}");
|
||||||
|
String[] v = ultima.get();
|
||||||
|
cere(v[0].equals("POST") && v[1].equals("/v1/identity/verify") && v[2] != null && v[3].equals("{\"presentation\":{\"v\":1},\"audience\":\"a\",\"nonce\":\"n\"}"), String.join(" | ", v));
|
||||||
|
lc.complianceCheck("{\"policy\":{}}");
|
||||||
|
cere(ultima.get()[1].equals("/v1/compliance/check") && ultima.get()[2] != null && ultima.get()[3].equals("{\"policy\":{}}"), String.join(" | ", ultima.get()));
|
||||||
|
});
|
||||||
proba("1.5.0: auditExport: jsonl verified=true pe octetii care dau digestul, FALSE pe alti octeti, null la cef; un 400 se ridica", () -> {
|
proba("1.5.0: auditExport: jsonl verified=true pe octetii care dau digestul, FALSE pe alti octeti, null la cef; un 400 se ridica", () -> {
|
||||||
raspunsuri.put("/v1/account/audit", new Object[]{200, zi, new String[]{"x-aere-digest", dZi, "x-aere-day", "2026-05-01", "x-aere-entries", "1"}});
|
raspunsuri.put("/v1/account/audit", new Object[]{200, zi, new String[]{"x-aere-digest", dZi, "x-aere-day", "2026-05-01", "x-aere-entries", "1"}});
|
||||||
AereCloud.AuditExport e = lc.auditExport("2026-05-01", "jsonl");
|
AereCloud.AuditExport e = lc.auditExport("2026-05-01", "jsonl");
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@aere/cloud",
|
"name": "@aere/cloud",
|
||||||
"version": "1.6.0",
|
"version": "1.7.0",
|
||||||
"description": "Official client for the Aere Cloud API: keyed RPC, post-quantum verification, chain data, notarization, webhooks and gas sponsorship on Aere Network (chain 2800).",
|
"description": "Official client for the Aere Cloud API: keyed RPC, post-quantum verification, chain data, notarization, webhooks and gas sponsorship on Aere Network (chain 2800).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
|
|||||||
@ -41,6 +41,9 @@ receipt = aere.notarize("0x" + sha256_hex)
|
|||||||
|
|
||||||
# Trust API (1.6.0): any AERE Proof Protocol (AIP-23) envelope -> VALID / INVALID / PARTIAL, with the levels checked
|
# Trust API (1.6.0): any AERE Proof Protocol (AIP-23) envelope -> VALID / INVALID / PARTIAL, with the levels checked
|
||||||
r = aere.verify(envelope, chain=2800) # {"verdict", "levels", "verifier", "reproduce"}
|
r = aere.verify(envelope, chain=2800) # {"verdict", "levels", "verifier", "reproduce"}
|
||||||
|
# identity and compliance (1.7.0): an AERE Identity presentation judged by the published command line; read "verdict"
|
||||||
|
idv = aere.identity_verify({"presentation": presentation, "audience": "https://your.app/verifier", "nonce": nonce, "trustedIssuers": [issuer_id]})
|
||||||
|
cc = aere.compliance_check({"presentation": presentation, "policy": policy, "audience": "https://your.app/verifier", "nonce": nonce, "record": True})
|
||||||
proof = aere.proof_of("0x" + sha256_hex)
|
proof = aere.proof_of("0x" + sha256_hex)
|
||||||
|
|
||||||
# a smart account with a POST-QUANTUM owner (Falcon-512), gas sponsored:
|
# a smart account with a POST-QUANTUM owner (Falcon-512), gas sponsored:
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import urllib.request
|
|||||||
import urllib.error
|
import urllib.error
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
__version__ = "1.6.0"
|
__version__ = "1.7.0"
|
||||||
__all__ = ["AereCloud", "AereCloudError", "verify_webhook"]
|
__all__ = ["AereCloud", "AereCloudError", "verify_webhook"]
|
||||||
|
|
||||||
_BAZA = "https://cloud.aere.network/v1"
|
_BAZA = "https://cloud.aere.network/v1"
|
||||||
@ -111,6 +111,19 @@ class AereCloud:
|
|||||||
chain: 2800 or 28001; None = the chain the envelope declares."""
|
chain: 2800 or 28001; None = the chain the envelope declares."""
|
||||||
return self._req("POST", "/verify" + (f"?chain={int(chain)}" if chain else ""), envelope)
|
return self._req("POST", "/verify" + (f"?chain={int(chain)}" if chain else ""), envelope)
|
||||||
|
|
||||||
|
def identity_verify(self, request: dict) -> dict:
|
||||||
|
"""identity verification (1.7.0): an AERE Identity presentation judged by the published command line -> VALID / PARTIAL / INVALID
|
||||||
|
with every row, the claims shown and the subject when no row failed, and the command (with the moment) that reproduces the verdict.
|
||||||
|
request: {presentation, audience, nonce, trustedIssuers?, statusLists?, revocations?, maxAgeSeconds?}. Read 'verdict': PARTIAL
|
||||||
|
means some rows could not be judged. Needs a gateway that serves POST /v1/identity/verify."""
|
||||||
|
return self._req("POST", "/identity/verify", request)
|
||||||
|
|
||||||
|
def compliance_check(self, request: dict) -> dict:
|
||||||
|
"""compliance check (1.7.0): your policy judged on an AERE Identity presentation -> COMPLIANT / NOT_COMPLIANT with the reasons,
|
||||||
|
and with record=True the record as an AIP-23 envelope without personal data. No claims come back.
|
||||||
|
request: {presentation, policy, audience, nonce, statusLists?, revocations?, record?}."""
|
||||||
|
return self._req("POST", "/compliance/check", request)
|
||||||
|
|
||||||
# ---- chain data ----
|
# ---- chain data ----
|
||||||
def data_head(self) -> dict:
|
def data_head(self) -> dict:
|
||||||
return self._req("GET", "/data/head")
|
return self._req("GET", "/data/head")
|
||||||
|
|||||||
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "aere-cloud"
|
name = "aere-cloud"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
description = "Official Python client for the Aere Cloud API (Aere Network, chain 2800): RPC, post-quantum verification, chain data, notarization, webhooks, gas sponsorship."
|
description = "Official Python client for the Aere Cloud API (Aere Network, chain 2800): RPC, post-quantum verification, chain data, notarization, webhooks, gas sponsorship."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
|
|||||||
@ -91,6 +91,18 @@ def t_verify():
|
|||||||
assert tr.cereri[0].get_header("X-api-key") == "k", "verify cere cheia"
|
assert tr.cereri[0].get_header("X-api-key") == "k", "verify cere cheia"
|
||||||
assert json.loads(tr.cereri[0].data.decode()) == {"statement": {}, "statementHash": "0x00"}
|
assert json.loads(tr.cereri[0].data.decode()) == {"statement": {}, "statementHash": "0x00"}
|
||||||
|
|
||||||
|
def t_identitate():
|
||||||
|
tr = Transport([(200, {"verdict": "PARTIAL", "notJudged": 1}), (200, {"verdict": "COMPLIANT", "compliant": True})])
|
||||||
|
c = AereCloud("k", opener=tr)
|
||||||
|
cerere = {"presentation": {"kind": "aere-presentation", "v": 1}, "audience": "https://v.example", "nonce": "n-1"}
|
||||||
|
assert c.identity_verify(cerere)["verdict"] == "PARTIAL"
|
||||||
|
assert tr.cereri[0].full_url.endswith("/identity/verify") and tr.cereri[0].get_method() == "POST"
|
||||||
|
assert tr.cereri[0].get_header("X-api-key") == "k", "identity_verify cere cheia"
|
||||||
|
assert json.loads(tr.cereri[0].data.decode()) == cerere
|
||||||
|
cc = {**cerere, "policy": {"id": "p"}, "record": True}
|
||||||
|
assert c.compliance_check(cc)["verdict"] == "COMPLIANT"
|
||||||
|
assert tr.cereri[1].full_url.endswith("/compliance/check") and json.loads(tr.cereri[1].data.decode()) == cc
|
||||||
|
|
||||||
def t_pq_sponsor():
|
def t_pq_sponsor():
|
||||||
tr = Transport([(200, {"txHash": "0xab", "account": "0xcd", "alreadyDeployed": False})])
|
tr = Transport([(200, {"txHash": "0xab", "account": "0xcd", "alreadyDeployed": False})])
|
||||||
c = AereCloud("k", opener=tr)
|
c = AereCloud("k", opener=tr)
|
||||||
@ -114,6 +126,7 @@ proba("rpc impacheteaza corect si ridica eroarea RPC", t_rpc)
|
|||||||
proba("un status de eroare devine AereCloudError cu corpul serverului", t_eroare_http)
|
proba("un status de eroare devine AereCloudError cu corpul serverului", t_eroare_http)
|
||||||
proba("rutele de date construiesc corect query-ul", t_query)
|
proba("rutele de date construiesc corect query-ul", t_query)
|
||||||
proba("verify (Trust API, 1.6.0): ruta, ?chain, cheia si plicul neschimbat", t_verify)
|
proba("verify (Trust API, 1.6.0): ruta, ?chain, cheia si plicul neschimbat", t_verify)
|
||||||
|
proba("identity_verify si compliance_check (1.7.0): rutele, cheia si cererea neschimbata", t_identitate)
|
||||||
proba("sponsor_create_pq_account: corp si cale corecte", t_pq_sponsor)
|
proba("sponsor_create_pq_account: corp si cale corecte", t_pq_sponsor)
|
||||||
proba("verify_webhook: buna trece; stricata, prelungita, corp atins pica", t_webhook)
|
proba("verify_webhook: buna trece; stricata, prelungita, corp atins pica", t_webhook)
|
||||||
|
|
||||||
|
|||||||
53
test/control-negativ-js-python.mjs
Normal file
53
test/control-negativ-js-python.mjs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Controlul negativ al probelor offline ale clientilor JavaScript si Python pentru rutele 1.7.0 (identityVerify / identity_verify,
|
||||||
|
// complianceCheck / compliance_check): fiecare paznic se strica intr-o COPIE a SDK-ului (depozitul nu se atinge), proba ruleaza pe copie
|
||||||
|
// si proba numita TREBUIE sa pice. Trei stari: ROSU cum trebuia, CONTROL CAZUT (proba a ramas verde), STRICAT (ancora nu apare exact o
|
||||||
|
// data, sau probele nu au ajuns la rezumat). La urma, copia neatinsa trebuie sa fie verde (martorul).
|
||||||
|
// node sdk/test/control-negativ-js-python.mjs -> 0 toate rosii si martorul verde, 1 altfel
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import os from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { spawnSync } from 'node:child_process';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const SDK = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
const PLANTARI = [
|
||||||
|
// [nume, fisier, vechi, nou, limbaj, proba (fragment din numele ei)]
|
||||||
|
['JS: complianceCheck pe ruta identitatii', 'index.mjs', "complianceCheck(request) { return this._req('POST', '/compliance/check', { body: request }); }", "complianceCheck(request) { return this._req('POST', '/identity/verify', { body: request }); }", 'js', '1.7.0: identityVerify si complianceCheck'],
|
||||||
|
['JS: identityVerify trimite o cerere schimbata', 'index.mjs', "identityVerify(request) { return this._req('POST', '/identity/verify', { body: request }); }", "identityVerify(request) { return this._req('POST', '/identity/verify', { body: { ...request, maxAgeSeconds: 3600 } }); }", 'js', '1.7.0: identityVerify si complianceCheck'],
|
||||||
|
['Python: compliance_check pe ruta identitatii', 'python/aere_cloud/__init__.py', 'return self._req("POST", "/compliance/check", request)', 'return self._req("POST", "/identity/verify", request)', 'py', 'identity_verify si compliance_check'],
|
||||||
|
['Python: identity_verify trimite o cerere schimbata', 'python/aere_cloud/__init__.py', 'return self._req("POST", "/identity/verify", request)', 'return self._req("POST", "/identity/verify", {**request, "maxAgeSeconds": 3600})', 'py', 'identity_verify si compliance_check'],
|
||||||
|
];
|
||||||
|
function copie() {
|
||||||
|
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-sdk-cn-'));
|
||||||
|
for (const f of ['index.mjs', 'package.json', 'test/sdk.test.mjs', 'python/aere_cloud/__init__.py', 'python/test_aere_cloud.py', 'python/pyproject.toml']) {
|
||||||
|
fs.mkdirSync(path.dirname(path.join(d, f)), { recursive: true }); fs.copyFileSync(path.join(SDK, f), path.join(d, f));
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
function ruleaza(d, limbaj) {
|
||||||
|
const r = limbaj === 'js'
|
||||||
|
? spawnSync(process.execPath, ['test/sdk.test.mjs'], { cwd: d, encoding: 'utf8', env: { ...process.env, AERE_CHEIE: '' } })
|
||||||
|
: spawnSync(process.platform === 'win32' ? 'python' : 'python3', ['test_aere_cloud.py'], { cwd: path.join(d, 'python'), encoding: 'utf8', env: { ...process.env, AERE_KEY: '' } });
|
||||||
|
const out = (r.stdout || '') + (r.stderr || '');
|
||||||
|
// JS: '<n> teste trecute' si randuri ' ESEC <nume>'; Python: '<n> treceri, <m> esecuri' si randuri ' PICA <nume>'
|
||||||
|
return { out, rulat: /\d+ (teste trecute|treceri)/.test(out), rosii: out.split('\n').filter((l) => /^\s*(ESEC|PICA)\b/.test(l)) };
|
||||||
|
}
|
||||||
|
let rele = 0;
|
||||||
|
for (const limbaj of ['js', 'py']) {
|
||||||
|
const d = copie(); const m = ruleaza(d, limbaj); fs.rmSync(d, { recursive: true, force: true });
|
||||||
|
if (m.rulat && !m.rosii.length) console.log(` martor ${limbaj}: copia neatinsa verde`); else { rele++; console.log(` STRICAT martorul ${limbaj} nu e verde (${m.rosii.length} rosii, rulat ${m.rulat})`); }
|
||||||
|
}
|
||||||
|
for (const [nume, fisier, vechi, nou, limbaj, tinta] of PLANTARI) {
|
||||||
|
const d = copie();
|
||||||
|
try {
|
||||||
|
const f = path.join(d, fisier); const t = fs.readFileSync(f, 'utf8');
|
||||||
|
if (t.split(vechi).length !== 2) { rele++; console.log(` STRICAT ${nume}: ancora apare de ${t.split(vechi).length - 1} ori`); continue; }
|
||||||
|
fs.writeFileSync(f, t.replace(vechi, nou));
|
||||||
|
const r = ruleaza(d, limbaj);
|
||||||
|
if (!r.rulat) { rele++; console.log(` STRICAT ${nume}: probele nu au ajuns la rezumat`); continue; }
|
||||||
|
if (r.rosii.some((l) => l.includes(tinta))) console.log(` ROSU cum trebuia ${nume}`);
|
||||||
|
else { rele++; console.log(` CONTROL CAZUT ${nume}: '${tinta}' a ramas verde`); }
|
||||||
|
} finally { fs.rmSync(d, { recursive: true, force: true }); }
|
||||||
|
}
|
||||||
|
console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `CONTROL NEGATIV TRECUT (${PLANTARI.length} din ${PLANTARI.length} rosii, martorii verzi)`);
|
||||||
|
process.exitCode = rele ? 1 : 0;
|
||||||
@ -61,6 +61,18 @@ await test('un status HTTP de eroare devine AereCloudError cu corpul serverului'
|
|||||||
(e) => e instanceof AereCloudError && e.status === 400 && e.body.error === 'bad_publicKey');
|
(e) => e instanceof AereCloudError && e.status === 400 && e.body.error === 'bad_publicKey');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await test('1.7.0: identityVerify si complianceCheck trimit cererea neschimbata pe rutele lor, cu cheie', async () => {
|
||||||
|
const rute = { 'POST /identity/verify': { status: 200, corp: { verdict: 'PARTIAL', notJudged: 1 } }, 'POST /compliance/check': { status: 200, corp: { verdict: 'COMPLIANT', compliant: true } } };
|
||||||
|
const c = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: fetchFals(rute) });
|
||||||
|
const cerere = { presentation: { kind: 'aere-presentation', v: 1 }, audience: 'https://v.example', nonce: 'n-1', trustedIssuers: ['aere-id:' + 'ab'.repeat(20)] };
|
||||||
|
assert.equal((await c.identityVerify(cerere)).verdict, 'PARTIAL');
|
||||||
|
assert.deepEqual(rute['POST /identity/verify']._vazut.body, cerere);
|
||||||
|
assert.ok(rute['POST /identity/verify']._vazut.headers['x-api-key'], 'identityVerify cere cheia');
|
||||||
|
const cc = { presentation: { kind: 'aere-presentation', v: 1 }, policy: { id: 'p', trustedIssuers: ['aere-id:' + 'ab'.repeat(20)], require: [{ claim: 'x', present: true }] }, audience: 'a', nonce: 'n', record: true };
|
||||||
|
assert.equal((await c.complianceCheck(cc)).verdict, 'COMPLIANT');
|
||||||
|
assert.deepEqual(rute['POST /compliance/check']._vazut.body, cc);
|
||||||
|
});
|
||||||
|
|
||||||
await test('rutele de date construiesc corect query-ul', async () => {
|
await test('rutele de date construiesc corect query-ul', async () => {
|
||||||
const rute = {
|
const rute = {
|
||||||
'GET /data/anchors?limit=3': { status: 200, corp: { anchors: [] } },
|
'GET /data/anchors?limit=3': { status: 200, corp: { anchors: [] } },
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user