aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-util/src/http-client/exchange.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-util/src/http-client/exchange.ts')
-rw-r--r--packages/taler-util/src/http-client/exchange.ts221
1 files changed, 166 insertions, 55 deletions
diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts
index f55be0043..68d68267f 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -1,82 +1,179 @@
-import { HttpRequestLibrary } from "../http-common.js";
+import { HttpRequestLibrary, readTalerErrorResponse } from "../http-common.js";
import { HttpStatusCode } from "../http-status-codes.js";
import { createPlatformHttpLib } from "../http.js";
import { LibtoolVersion } from "../libtool-version.js";
import { hash } from "../nacl-fast.js";
-import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js";
-import { TalerSignaturePurpose, amountToBuffer, bufferForUint32, buildSigPS, decodeCrock, eddsaSign, encodeCrock, stringToBytes, timestampRoundedToBuffer } from "../taler-crypto.js";
-import { OfficerAccount, PaginationParams, SigningKey, TalerExchangeApi, codecForAmlDecisionDetails, codecForAmlRecords, codecForExchangeConfig } from "./types.js";
-import { addPaginationParams } from "./utils.js";
+import {
+ FailCasesByMethod,
+ ResultByMethod,
+ opEmptySuccess,
+ opFixedSuccess,
+ opKnownHttpFailure,
+ opSuccessFromHttp,
+ opUnknownFailure,
+} from "../operation.js";
+import {
+ TalerSignaturePurpose,
+ amountToBuffer,
+ bufferForUint32,
+ buildSigPS,
+ decodeCrock,
+ eddsaSign,
+ encodeCrock,
+ stringToBytes,
+ timestampRoundedToBuffer,
+} from "../taler-crypto.js";
+import {
+ OfficerAccount,
+ PaginationParams,
+ SigningKey,
+ TalerExchangeApi,
+ codecForAmlDecisionDetails,
+ codecForAmlRecords,
+ codecForExchangeConfig,
+ codecForExchangeKeys,
+} from "./types.js";
+import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js";
+
+export type TalerExchangeResultByMethod<
+ prop extends keyof TalerExchangeHttpClient,
+> = ResultByMethod<TalerExchangeHttpClient, prop>;
+export type TalerExchangeErrorsByMethod<
+ prop extends keyof TalerExchangeHttpClient,
+> = FailCasesByMethod<TalerExchangeHttpClient, prop>;
+
+export enum TalerExchangeCacheEviction {
+ CREATE_DESCISION,
+}
-export type TalerExchangeResultByMethod<prop extends keyof TalerExchangeHttpClient> = ResultByMethod<TalerExchangeHttpClient, prop>
-export type TalerExchangeErrorsByMethod<prop extends keyof TalerExchangeHttpClient> = FailCasesByMethod<TalerExchangeHttpClient, prop>
/**
*/
export class TalerExchangeHttpClient {
httpLib: HttpRequestLibrary;
- public readonly PROTOCOL_VERSION = "17:0:0";
+ public readonly PROTOCOL_VERSION = "18:0:1";
+ cacheEvictor: CacheEvictor<TalerExchangeCacheEviction>;
constructor(
readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
+ cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction>,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
+ this.cacheEvictor = cacheEvictor ?? nullEvictor;
}
isCompatible(version: string): boolean {
- const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version)
- return compare?.compatible ?? false
+ const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
+ return compare?.compatible ?? false;
}
/**
- * https://docs.taler.net/core/api-merchant.html#get--config
- *
+ * https://docs.taler.net/core/api-exchange.html#get--seed
+ *
+ */
+ async getSeed() {
+ const url = new URL(`seed`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ const buffer = await resp.bytes();
+ const uintar = new Uint8Array(buffer);
+
+ return opFixedSuccess(uintar);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+ /**
+ * https://docs.taler.net/core/api-exchange.html#get--config
+ *
*/
async getConfig() {
const url = new URL(`config`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
- method: "GET"
+ method: "GET",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForExchangeConfig());
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+ /**
+ * https://docs.taler.net/core/api-merchant.html#get--config
+ *
+ * PARTIALLY IMPLEMENTED!!
+ */
+ async getKeys() {
+ const url = new URL(`keys`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
});
switch (resp.status) {
- case HttpStatusCode.Ok: return opSuccess(resp, codecForExchangeConfig())
- default: return opUnknownFailure(resp, await resp.text())
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForExchangeKeys());
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
+ // TERMS
+
//
// AML operations
//
/**
* https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions-$STATE
- *
+ *
*/
- async getDecisionsByState(auth: OfficerAccount, state: TalerExchangeApi.AmlState, pagination?: PaginationParams) {
- const url = new URL(`aml/${auth.id}/decisions/${TalerExchangeApi.AmlState[state]}`, this.baseUrl);
- addPaginationParams(url, pagination)
+ async getDecisionsByState(
+ auth: OfficerAccount,
+ state: TalerExchangeApi.AmlState,
+ pagination?: PaginationParams,
+ ) {
+ const url = new URL(
+ `aml/${auth.id}/decisions/${TalerExchangeApi.AmlState[state]}`,
+ this.baseUrl,
+ );
+ addPaginationParams(url, pagination);
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers: {
- "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey)
- }
+ "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey),
+ },
});
switch (resp.status) {
- case HttpStatusCode.Ok: return opSuccess(resp, codecForAmlRecords())
- case HttpStatusCode.NoContent: return opFixedSuccess({ records: [] })
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForAmlRecords());
+ case HttpStatusCode.NoContent:
+ return opFixedSuccess({ records: [] });
//this should be unauthorized
- case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
- case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp);
- case HttpStatusCode.NotFound: return opKnownFailure("officer-not-found", resp);
- case HttpStatusCode.Conflict: return opKnownFailure("officer-disabled", resp);
- default: return opUnknownFailure(resp, await resp.text())
+ case HttpStatusCode.Forbidden:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.Unauthorized:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.Conflict:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decision-$H_PAYTO
- *
+ *
*/
async getDecisionDetails(auth: OfficerAccount, account: string) {
const url = new URL(`aml/${auth.id}/decision/${account}`, this.baseUrl);
@@ -84,48 +181,62 @@ export class TalerExchangeHttpClient {
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers: {
- "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey)
- }
+ "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey),
+ },
});
switch (resp.status) {
- case HttpStatusCode.Ok: return opSuccess(resp, codecForAmlDecisionDetails())
- case HttpStatusCode.NoContent: return opFixedSuccess({ aml_history: [], kyc_attributes: [] })
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForAmlDecisionDetails());
+ case HttpStatusCode.NoContent:
+ return opFixedSuccess({ aml_history: [], kyc_attributes: [] });
//this should be unauthorized
- case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
- case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp);
- case HttpStatusCode.NotFound: return opKnownFailure("officer-not-found", resp);
- case HttpStatusCode.Conflict: return opKnownFailure("officer-disabled", resp);
- default: return opUnknownFailure(resp, await resp.text())
+ case HttpStatusCode.Forbidden:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.Unauthorized:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.Conflict:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-exchange.html#post--aml-$OFFICER_PUB-decision
- *
+ *
*/
- async addDecisionDetails(auth: OfficerAccount, decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">) {
+ async addDecisionDetails(
+ auth: OfficerAccount,
+ decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">,
+ ) {
const url = new URL(`aml/${auth.id}/decision`, this.baseUrl);
- const body = buildDecisionSignature(auth.signingKey, decision)
+ const body = buildDecisionSignature(auth.signingKey, decision);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.NoContent:
+ return opEmptySuccess(resp);
//FIXME: this should be unauthorized
- case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
- case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp);
- //FIXME: this two need to be splitted by error code
- case HttpStatusCode.NotFound: return opKnownFailure("officer-or-account-not-found", resp);
- case HttpStatusCode.Conflict: return opKnownFailure("officer-disabled-or-recent-decision", resp);
- default: return opUnknownFailure(resp, await resp.text())
+ case HttpStatusCode.Forbidden:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.Unauthorized:
+ return opKnownHttpFailure(resp.status, resp);
+ //FIXME: this two need to be split by error code
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.Conflict:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
-
-
}
function buildQuerySignature(key: SigningKey): string {
@@ -140,11 +251,11 @@ function buildDecisionSignature(
key: SigningKey,
decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">,
): TalerExchangeApi.AmlDecision {
- const zero = new Uint8Array(new ArrayBuffer(64))
+ const zero = new Uint8Array(new ArrayBuffer(64));
const sigBlob = buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION)
//TODO: new need the null terminator, also in the exchange
- .put(hash(stringToBytes(decision.justification)))//check null
+ .put(hash(stringToBytes(decision.justification))) //check null
.put(timestampRoundedToBuffer(decision.decision_time))
.put(amountToBuffer(decision.new_threshold))
.put(decodeCrock(decision.h_payto))
@@ -155,6 +266,6 @@ function buildDecisionSignature(
const officer_sig = encodeCrock(eddsaSign(sigBlob, key));
return {
...decision,
- officer_sig
- }
-} \ No newline at end of file
+ officer_sig,
+ };
+}