aboutsummaryrefslogtreecommitdiff
path: root/packages/anastasis-core
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-04-13 08:44:37 +0200
committerFlorian Dold <florian@dold.me>2022-04-13 08:44:37 +0200
commitb28583ba7e3be7e4f69aaf543d087b6446f50cf0 (patch)
tree3266e5187384370a403cb339bf34ee378a06e757 /packages/anastasis-core
parentf3d8b4474302a00fd923581046b0f5828a4c5976 (diff)
downloadwallet-core-b28583ba7e3be7e4f69aaf543d087b6446f50cf0.tar.xz
anastasis: tag state properly
Diffstat (limited to 'packages/anastasis-core')
-rw-r--r--packages/anastasis-core/src/index.ts44
-rw-r--r--packages/anastasis-core/src/reducer-types.ts46
2 files changed, 60 insertions, 30 deletions
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts
index 542e7eafb..5896dc655 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -196,6 +196,7 @@ function getCountries(
export async function getBackupStartState(): Promise<ReducerStateBackup> {
return {
+ reducer_type: "backup",
backup_state: BackupStates.ContinentSelecting,
continents: getContinents({
requireProvider: true,
@@ -205,6 +206,7 @@ export async function getBackupStartState(): Promise<ReducerStateBackup> {
export async function getRecoveryStartState(): Promise<ReducerStateRecovery> {
return {
+ reducer_type: "recovery",
recovery_state: RecoveryStates.ContinentSelecting,
continents: getContinents({
requireProvider: true,
@@ -571,6 +573,7 @@ async function uploadSecret(
const talerPayUri = resp.headers.get("Taler");
if (!talerPayUri) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE,
hint: `payment requested, but no taler://pay URI given`,
};
@@ -579,6 +582,7 @@ async function uploadSecret(
const parsedUri = parsePayUri(talerPayUri);
if (!parsedUri) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE,
hint: `payment requested, but no taler://pay URI given`,
};
@@ -587,6 +591,7 @@ async function uploadSecret(
continue;
}
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED,
hint: `could not upload truth (HTTP status ${resp.status})`,
};
@@ -674,6 +679,7 @@ async function uploadSecret(
const talerPayUri = resp.headers.get("Taler");
if (!talerPayUri) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE,
hint: `payment requested, but no taler://pay URI given`,
};
@@ -682,6 +688,7 @@ async function uploadSecret(
const parsedUri = parsePayUri(talerPayUri);
if (!parsedUri) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE,
hint: `payment requested, but no taler://pay URI given`,
};
@@ -690,6 +697,7 @@ async function uploadSecret(
continue;
}
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED,
hint: `could not upload policy (http status ${resp.status})`,
};
@@ -734,13 +742,13 @@ async function downloadPolicy(
throw Error("invalid state");
}
for (const prov of state.selected_version.providers) {
- const pi = state.authentication_providers?.[prov.provider_url];
+ const pi = state.authentication_providers?.[prov.url];
if (!pi || pi.status !== "ok") {
continue;
}
const userId = await userIdentifierDerive(userAttributes, pi.salt);
const acctKeypair = accountKeypairDerive(userId);
- const reqUrl = new URL(`policy/${acctKeypair.pub}`, prov.provider_url);
+ const reqUrl = new URL(`policy/${acctKeypair.pub}`, prov.url);
reqUrl.searchParams.set("version", `${prov.version}`);
const resp = await fetch(reqUrl.href);
if (resp.status !== 200) {
@@ -759,7 +767,7 @@ async function downloadPolicy(
policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
} catch (e) {}
foundRecoveryInfo = {
- provider_url: prov.provider_url,
+ provider_url: prov.url,
secret_name: rd.secret_name ?? "<unknown>",
version: policyVersion,
};
@@ -768,6 +776,7 @@ async function downloadPolicy(
}
if (!foundRecoveryInfo || !recoveryDoc) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED,
hint: "No backups found at any provider for your identity information.",
};
@@ -874,7 +883,7 @@ async function pollChallenges(
const s2 = await requestTruth(state, truth, {
pin: feedback.answer_code,
});
- if (s2.recovery_state) {
+ if (s2.reducer_type === "recovery") {
state = s2;
}
}
@@ -1001,6 +1010,7 @@ async function requestTruth(
}
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED,
hint: "got unexpected /truth/ response status",
http_status: resp.status,
@@ -1110,6 +1120,7 @@ async function selectChallenge(
// FIXME: look at response, include in challenge_feedback!
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED,
hint: "got unexpected /truth/.../challenge response status",
http_status: resp.status,
@@ -1125,6 +1136,7 @@ async function backupSelectContinent(
});
if (countries.length <= 0) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_INPUT_INVALID,
hint: "continent not found",
};
@@ -1423,10 +1435,14 @@ async function nextFromChallengeSelecting(
args: void,
): Promise<ReducerStateRecovery | ReducerStateError> {
const s2 = await tryRecoverSecret(state);
- if (s2.recovery_state === RecoveryStates.RecoveryFinished) {
+ if (
+ s2.reducer_type === "recovery" &&
+ s2.recovery_state === RecoveryStates.RecoveryFinished
+ ) {
return s2;
}
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID,
hint: "Not enough challenges solved",
};
@@ -1462,7 +1478,7 @@ export function mergeDiscoveryAggregate(
const oldIndex = polHashToIndex[pol.policy_hash];
if (oldIndex != null) {
aggregatedPolicies[oldIndex].providers.push({
- provider_url: pol.provider_url,
+ url: pol.provider_url,
version: pol.version,
});
} else {
@@ -1471,7 +1487,7 @@ export function mergeDiscoveryAggregate(
policy_hash: pol.policy_hash,
providers: [
{
- provider_url: pol.provider_url,
+ url: pol.provider_url,
version: pol.version,
},
],
@@ -1592,7 +1608,7 @@ const recoveryTransitions: Record<
...transition("add_provider", codecForAny(), addProviderRecovery),
...transition("delete_provider", codecForAny(), deleteProviderRecovery),
...transition(
- "change_version",
+ "select_version",
codecForActionArgsChangeVersion(),
changeVersion,
),
@@ -1621,7 +1637,7 @@ export async function discoverPolicies(
state: ReducerState,
cursor?: DiscoveryCursor,
): Promise<DiscoveryResult> {
- if (!state.recovery_state) {
+ if (state.reducer_type !== "recovery") {
throw Error("can only discover providers in recovery state");
}
@@ -1685,12 +1701,14 @@ export async function reduceAction(
h = recoveryTransitions[state.recovery_state][action];
} else {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID,
hint: `Invalid state (needs backup_state or recovery_state)`,
};
}
if (!h) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID,
hint: `Unsupported action '${action}' in state '${stateName}'`,
};
@@ -1700,9 +1718,10 @@ export async function reduceAction(
parsedArgs = h.argCodec.decode(args);
} catch (e: any) {
return {
+ reducer_type: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_INPUT_INVALID,
hint: "argument validation failed",
- message: e.toString(),
+ detail: e.toString(),
};
}
try {
@@ -1710,7 +1729,10 @@ export async function reduceAction(
} catch (e) {
logger.error("action handler failed");
if (e instanceof ReducerError) {
- return e.errorJson;
+ return {
+ reducer_type: "error",
+ ...e.errorJson,
+ }
}
throw e;
}
diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts
index 7e8d4da25..03b40e4bd 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -73,19 +73,30 @@ export interface CoreSecret {
}
export interface ReducerStateBackup {
- recovery_state?: undefined;
+ reducer_type: "backup";
+
backup_state: BackupStates;
- code?: undefined;
+
currencies?: string[];
+
continents?: ContinentInfo[];
+
countries?: CountryInfo[];
+
identity_attributes?: { [n: string]: string };
+
authentication_providers?: { [url: string]: AuthenticationProviderStatus };
+
authentication_methods?: AuthMethod[];
+
required_attributes?: UserAttributeSpec[];
+
selected_continent?: string;
+
selected_country?: string;
+
secret_name?: string;
+
policies?: Policy[];
recovery_data?: {
@@ -179,17 +190,9 @@ export interface RecoveryInformation {
}
export interface ReducerStateRecovery {
- recovery_state: RecoveryStates;
+ reducer_type: "recovery";
- /**
- * Unused in the recovery states.
- */
- backup_state?: undefined;
-
- /**
- * Unused in the recovery states.
- */
- code?: undefined;
+ recovery_state: RecoveryStates;
identity_attributes?: { [n: string]: string };
@@ -267,11 +270,10 @@ export interface TruthMetaData {
}
export interface ReducerStateError {
- backup_state?: undefined;
- recovery_state?: undefined;
+ reducer_type: "error";
code: number;
hint?: string;
- message?: string;
+ detail?: string;
}
export enum BackupStates {
@@ -302,12 +304,13 @@ export interface MethodSpec {
usage_fee: string;
}
-export type AuthenticationProviderStatusEmpty = {
+export type AuthenticationProviderStatusNotContacted = {
status: "not-contacted";
};
export interface AuthenticationProviderStatusOk {
status: "ok";
+
annual_fee: string;
business_name: string;
currency: string;
@@ -320,8 +323,13 @@ export interface AuthenticationProviderStatusOk {
// FIXME: add timestamp?
}
+export interface AuthenticationProviderStatusDisabled {
+ status: "disabled";
+}
+
export interface AuthenticationProviderStatusError {
status: "error";
+
http_status?: number;
code: number;
hint?: string;
@@ -329,7 +337,8 @@ export interface AuthenticationProviderStatusError {
}
export type AuthenticationProviderStatus =
- | AuthenticationProviderStatusEmpty
+ | AuthenticationProviderStatusNotContacted
+ | AuthenticationProviderStatusDisabled
| AuthenticationProviderStatusError
| AuthenticationProviderStatusOk;
@@ -486,7 +495,6 @@ export interface PolicyMetaInfo {
secret_name?: string;
}
-
/**
* Aggregated / de-duplicated policy meta info.
*/
@@ -495,7 +503,7 @@ export interface AggregatedPolicyMetaInfo {
policy_hash: string;
attribute_mask: number;
providers: {
- provider_url: string;
+ url: string;
version: number;
}[];
}