diff options
author | Florian Dold <florian@dold.me> | 2021-11-04 20:02:04 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2021-11-04 20:16:17 +0100 |
commit | 11e8060ab156e5033c7dd5cd607648d584540883 (patch) | |
tree | 79c671ac6f12c3b4ef4a1a00e8694f69ac736cc8 | |
parent | 9ba0e8597dc0cb902bec3cd8f1090fc5746f790c (diff) | |
download | wallet-core-11e8060ab156e5033c7dd5cd607648d584540883.tar.xz |
anastasis-core: fix upload fee computation, prepare for payments
-rw-r--r-- | packages/anastasis-core/src/index.ts | 127 | ||||
-rw-r--r-- | packages/anastasis-core/src/reducer-types.ts | 36 |
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; |