aboutsummaryrefslogtreecommitdiff
path: root/packages/anastasis-core
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-11-04 20:02:04 +0100
committerFlorian Dold <florian@dold.me>2021-11-04 20:16:17 +0100
commit11e8060ab156e5033c7dd5cd607648d584540883 (patch)
tree79c671ac6f12c3b4ef4a1a00e8694f69ac736cc8 /packages/anastasis-core
parent9ba0e8597dc0cb902bec3cd8f1090fc5746f790c (diff)
downloadwallet-core-11e8060ab156e5033c7dd5cd607648d584540883.tar.xz
anastasis-core: fix upload fee computation, prepare for payments
Diffstat (limited to 'packages/anastasis-core')
-rw-r--r--packages/anastasis-core/src/index.ts127
-rw-r--r--packages/anastasis-core/src/reducer-types.ts36
2 files changed, 98 insertions, 65 deletions
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts
index 2ed69b55a..0bab510de 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -51,21 +51,17 @@ import {
codecForActionArgsUpdateExpiration,
ContinentInfo,
CountryInfo,
- MethodSpec,
- Policy,
- PolicyProvider,
RecoveryInformation,
RecoveryInternalData,
RecoveryStates,
ReducerState,
ReducerStateBackup,
- ReducerStateBackupUserAttributesCollecting,
ReducerStateError,
ReducerStateRecovery,
SuccessDetails,
- UserAttributeSpec,
codecForActionArgsChangeVersion,
ActionArgsChangeVersion,
+ TruthMetaData,
} from "./reducer-types.js";
import fetchPonyfill from "fetch-ponyfill";
import {
@@ -302,35 +298,6 @@ async function backupEnterUserAttributes(
return newState;
}
-/**
- * Truth data as stored in the reducer.
- */
-interface TruthMetaData {
- uuid: string;
-
- key_share: string;
-
- policy_index: number;
-
- pol_method_index: number;
-
- /**
- * Nonce used for encrypting the truth.
- */
- nonce: string;
-
- /**
- * Key that the truth (i.e. secret question answer, email address, mobile number, ...)
- * is encrypted with when stored at the provider.
- */
- truth_key: string;
-
- /**
- * Truth-specific salt.
- */
- truth_salt: string;
-}
-
async function getTruthValue(
authMethod: AuthMethod,
truthUuid: string,
@@ -512,6 +479,8 @@ async function uploadSecret(
const successDetails: SuccessDetails = {};
+ const policyPayUris: string[] = [];
+
for (const prov of state.policy_providers!) {
const uid = uidMap[prov.provider_url];
const acctKeypair = accountKeypairDerive(uid);
@@ -536,26 +505,46 @@ async function uploadSecret(
body: decodeCrock(encRecoveryDoc),
},
);
- if (resp.status !== 204) {
- return {
- code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED,
- hint: `could not upload policy (http status ${resp.status})`,
+ if (resp.status === HttpStatusCode.Accepted) {
+ let policyVersion = 0;
+ let policyExpiration: Timestamp = { t_ms: 0 };
+ try {
+ policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
+ } catch (e) {}
+ try {
+ policyExpiration = {
+ t_ms:
+ 1000 *
+ Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
+ };
+ } catch (e) {}
+ successDetails[prov.provider_url] = {
+ policy_version: policyVersion,
+ policy_expiration: policyExpiration,
};
}
- let policyVersion = 0;
- let policyExpiration: Timestamp = { t_ms: 0 };
- try {
- policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
- } catch (e) {}
- try {
- policyExpiration = {
- t_ms:
- 1000 * Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
- };
- } catch (e) {}
- successDetails[prov.provider_url] = {
- policy_version: policyVersion,
- policy_expiration: policyExpiration,
+ if (resp.status === HttpStatusCode.PaymentRequired) {
+ const talerPayUri = resp.headers.get("Taler");
+ if (!talerPayUri) {
+ return {
+ code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE,
+ hint: `payment requested, but no taler://pay URI given`,
+ };
+ }
+ policyPayUris.push(talerPayUri);
+ continue;
+ }
+ return {
+ code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED,
+ hint: `could not upload policy (http status ${resp.status})`,
+ };
+ }
+
+ if (policyPayUris.length > 0) {
+ return {
+ ...state,
+ backup_state: BackupStates.PoliciesPaying,
+ payments: policyPayUris,
};
}
@@ -1048,7 +1037,6 @@ async function updateUploadFees(
}
logger.info("updating upload fees");
const feePerCurrency: Record<string, AmountJson> = {};
- const coveredProviders = new Set<string>();
const addFee = (x: AmountLike) => {
x = Amounts.jsonifyAmount(x);
feePerCurrency[x.currency] = Amounts.add(
@@ -1058,24 +1046,31 @@ async function updateUploadFees(
};
const years = Duration.toIntegerYears(Duration.getRemaining(expiration));
logger.info(`computing fees for ${years} years`);
+ // For now, we compute fees for *all* available providers.
+ for (const provUrl in state.authentication_providers ?? {}) {
+ const prov = state.authentication_providers![provUrl];
+ if ("annual_fee" in prov) {
+ const annualFee = Amounts.mult(prov.annual_fee, years).amount;
+ logger.info(`adding annual fee ${Amounts.stringify(annualFee)}`);
+ addFee(annualFee);
+ }
+ }
+ const coveredProvTruth = new Set<string>();
for (const x of state.policies ?? []) {
for (const m of x.methods) {
const prov = state.authentication_providers![
m.provider
] as AuthenticationProviderStatusOk;
const authMethod = state.authentication_methods![m.authentication_method];
- if (!coveredProviders.has(m.provider)) {
- const annualFee = Amounts.mult(prov.annual_fee, years).amount;
- logger.info(`adding annual fee ${Amounts.stringify(annualFee)}`);
- addFee(annualFee);
- coveredProviders.add(m.provider);
- }
- for (const pm of prov.methods) {
- if (pm.type === authMethod.type) {
- addFee(pm.usage_fee);
- break;
- }
+ const key = `${m.authentication_method}@${m.provider}`;
+ if (coveredProvTruth.has(key)) {
+ continue;
}
+ logger.info(
+ `adding cost for auth method ${authMethod.challenge} / "${authMethod.instructions}" at ${m.provider}`,
+ );
+ coveredProvTruth.add(key);
+ addFee(prov.truth_upload_fee);
}
}
return {
@@ -1252,7 +1247,9 @@ const recoveryTransitions: Record<
...transition("solve_challenge", codecForAny(), solveChallenge),
},
[RecoveryStates.ChallengePaying]: {},
- [RecoveryStates.RecoveryFinished]: {},
+ [RecoveryStates.RecoveryFinished]: {
+ ...transitionRecoveryJump("back", RecoveryStates.ChallengeSelecting),
+ },
};
export async function reduceAction(
diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts
index 08e61cefe..318e00f89 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -68,6 +68,13 @@ export interface ReducerStateBackup {
policies?: Policy[];
/**
+ * Map from truth key (`${methodIndex}/${providerUrl}`) to
+ * the truth metadata.
+ */
+ truth_metadata?: Record<string, TruthMetaData>;
+ recovery_document?: RecoveryDocument;
+
+ /**
* Policy providers are providers that we checked to be functional
* and that are actually used in policies.
*/
@@ -198,6 +205,35 @@ export interface ReducerStateRecovery {
authentication_providers?: { [url: string]: AuthenticationProviderStatus };
}
+/**
+ * Truth data as stored in the reducer.
+ */
+export interface TruthMetaData {
+ uuid: string;
+
+ key_share: string;
+
+ policy_index: number;
+
+ pol_method_index: number;
+
+ /**
+ * Nonce used for encrypting the truth.
+ */
+ nonce: string;
+
+ /**
+ * Key that the truth (i.e. secret question answer, email address, mobile number, ...)
+ * is encrypted with when stored at the provider.
+ */
+ truth_key: string;
+
+ /**
+ * Truth-specific salt.
+ */
+ truth_salt: string;
+}
+
export interface ReducerStateError {
backup_state?: undefined;
recovery_state?: undefined;