aboutsummaryrefslogtreecommitdiff
path: root/packages/anastasis-core
diff options
context:
space:
mode:
Diffstat (limited to 'packages/anastasis-core')
-rw-r--r--packages/anastasis-core/src/anastasis-data.ts17
-rw-r--r--packages/anastasis-core/src/crypto.ts2
-rw-r--r--packages/anastasis-core/src/index.ts124
-rw-r--r--packages/anastasis-core/src/policy-suggestion.test.ts44
-rw-r--r--packages/anastasis-core/src/policy-suggestion.ts25
-rw-r--r--packages/anastasis-core/src/reducer-types.ts13
6 files changed, 209 insertions, 16 deletions
diff --git a/packages/anastasis-core/src/anastasis-data.ts b/packages/anastasis-core/src/anastasis-data.ts
index 4946e9dfd..c67883a2e 100644
--- a/packages/anastasis-core/src/anastasis-data.ts
+++ b/packages/anastasis-core/src/anastasis-data.ts
@@ -1,6 +1,7 @@
// This file is auto-generated, do not modify.
// Generated from v0.2.0-4-g61ea83c on Tue, 05 Oct 2021 10:40:32 +0200
// To re-generate, run contrib/gen-ts.sh from the main anastasis code base.
+// XXX: Modified for demo, allowing demo providers for EUR
export const anastasisData = {
providersList: {
@@ -16,6 +17,22 @@ export const anastasisData = {
currency: "KUDOS",
},
{
+ url: "https://anastasis.demo.taler.net/",
+ currency: "EUR",
+ },
+ {
+ url: "https://kudos.demo.anastasis.lu/",
+ currency: "EUR",
+ },
+ {
+ url: "https://anastasis.demo.taler.net/",
+ currency: "CHF",
+ },
+ {
+ url: "https://kudos.demo.anastasis.lu/",
+ currency: "CHF",
+ },
+ {
url: "http://localhost:8086/",
currency: "TESTKUDOS",
},
diff --git a/packages/anastasis-core/src/crypto.ts b/packages/anastasis-core/src/crypto.ts
index 206d9eca8..75bd4b323 100644
--- a/packages/anastasis-core/src/crypto.ts
+++ b/packages/anastasis-core/src/crypto.ts
@@ -11,8 +11,6 @@ import {
stringToBytes,
secretbox_open,
hash,
- Logger,
- j2s,
} from "@gnu-taler/taler-util";
import { argon2id } from "hash-wasm";
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts
index 362ac3317..15e1e5d97 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -65,6 +65,8 @@ import {
ActionArgsChangeVersion,
TruthMetaData,
ActionArgsUpdatePolicy,
+ ActionArgsAddProvider,
+ ActionArgsDeleteProvider,
} from "./reducer-types.js";
import fetchPonyfill from "fetch-ponyfill";
import {
@@ -109,13 +111,23 @@ export * from "./challenge-feedback-types.js";
const logger = new Logger("anastasis-core:index.ts");
-function getContinents(): ContinentInfo[] {
+function getContinents(
+ opts: { requireProvider?: boolean } = {},
+): ContinentInfo[] {
+ const currenciesWithProvider = new Set<string>();
+ anastasisData.providersList.anastasis_provider.forEach((x) => {
+ currenciesWithProvider.add(x.currency);
+ });
const continentSet = new Set<string>();
const continents: ContinentInfo[] = [];
for (const country of anastasisData.countriesList.countries) {
if (continentSet.has(country.continent)) {
continue;
}
+ if (opts.requireProvider && !currenciesWithProvider.has(country.currency)) {
+ // Country's currency doesn't have any providers => skip
+ continue;
+ }
continentSet.add(country.continent);
continents.push({
...{ name_i18n: country.continent_i18n },
@@ -148,9 +160,18 @@ export class ReducerError extends Error {
* Get countries for a continent, abort with ReducerError
* exception when continent doesn't exist.
*/
-function getCountries(continent: string): CountryInfo[] {
+function getCountries(
+ continent: string,
+ opts: { requireProvider?: boolean } = {},
+): CountryInfo[] {
+ const currenciesWithProvider = new Set<string>();
+ anastasisData.providersList.anastasis_provider.forEach((x) => {
+ currenciesWithProvider.add(x.currency);
+ });
const countries = anastasisData.countriesList.countries.filter(
- (x) => x.continent === continent,
+ (x) =>
+ x.continent === continent &&
+ (!opts.requireProvider || currenciesWithProvider.has(x.currency)),
);
if (countries.length <= 0) {
throw new ReducerError({
@@ -164,14 +185,18 @@ function getCountries(continent: string): CountryInfo[] {
export async function getBackupStartState(): Promise<ReducerStateBackup> {
return {
backup_state: BackupStates.ContinentSelecting,
- continents: getContinents(),
+ continents: getContinents({
+ requireProvider: true,
+ }),
};
}
export async function getRecoveryStartState(): Promise<ReducerStateRecovery> {
return {
recovery_state: RecoveryStates.ContinentSelecting,
- continents: getContinents(),
+ continents: getContinents({
+ requireProvider: true,
+ }),
};
}
@@ -952,6 +977,21 @@ async function requestTruth(
}
if (resp.status === HttpStatusCode.Forbidden) {
+ const body = await resp.json();
+ if (
+ body.code === TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED
+ ) {
+ return {
+ ...state,
+ recovery_state: RecoveryStates.ChallengeSolving,
+ challenge_feedback: {
+ ...state.challenge_feedback,
+ [truth.uuid]: {
+ state: ChallengeFeedbackStatus.Pending,
+ },
+ },
+ };
+ }
return {
...state,
recovery_state: RecoveryStates.ChallengeSolving,
@@ -959,7 +999,7 @@ async function requestTruth(
...state.challenge_feedback,
[truth.uuid]: {
state: ChallengeFeedbackStatus.Message,
- message: "Challenge should be solved",
+ message: body.hint ?? "Challenge should be solved",
},
},
};
@@ -1022,9 +1062,15 @@ async function recoveryEnterUserAttributes(
args: ActionArgsEnterUserAttributes,
): Promise<ReducerStateRecovery | ReducerStateError> {
// FIXME: validate attributes
+ const providerUrls = Object.keys(state.authentication_providers ?? {});
+ const newProviders = state.authentication_providers ?? {};
+ for (const url of providerUrls) {
+ newProviders[url] = await getProviderInfo(url);
+ }
const st: ReducerStateRecovery = {
...state,
identity_attributes: args.identity_attributes,
+ authentication_providers: newProviders,
};
return downloadPolicy(st);
}
@@ -1058,7 +1104,9 @@ async function backupSelectContinent(
state: ReducerStateBackup,
args: ActionArgsSelectContinent,
): Promise<ReducerStateBackup | ReducerStateError> {
- const countries = getCountries(args.continent);
+ const countries = getCountries(args.continent, {
+ requireProvider: true,
+ });
if (countries.length <= 0) {
return {
code: TalerErrorCode.ANASTASIS_REDUCER_INPUT_INVALID,
@@ -1077,7 +1125,9 @@ async function recoverySelectContinent(
state: ReducerStateRecovery,
args: ActionArgsSelectContinent,
): Promise<ReducerStateRecovery | ReducerStateError> {
- const countries = getCountries(args.continent);
+ const countries = getCountries(args.continent, {
+ requireProvider: true,
+ });
return {
...state,
recovery_state: RecoveryStates.CountrySelecting,
@@ -1132,6 +1182,60 @@ function transitionRecoveryJump(
};
}
+//FIXME: doest the same that addProviderRecovery, but type are not generic enough
+async function addProviderBackup(
+ state: ReducerStateBackup,
+ args: ActionArgsAddProvider,
+): Promise<ReducerStateBackup> {
+ const info = await getProviderInfo(args.provider_url)
+ return {
+ ...state,
+ authentication_providers: {
+ ...(state.authentication_providers ?? {}),
+ [args.provider_url]: info,
+ },
+ };
+}
+
+//FIXME: doest the same that deleteProviderRecovery, but type are not generic enough
+async function deleteProviderBackup(
+ state: ReducerStateBackup,
+ args: ActionArgsDeleteProvider,
+): Promise<ReducerStateBackup> {
+ const authentication_providers = {... state.authentication_providers ?? {} }
+ delete authentication_providers[args.provider_url]
+ return {
+ ...state,
+ authentication_providers,
+ };
+}
+
+async function addProviderRecovery(
+ state: ReducerStateRecovery,
+ args: ActionArgsAddProvider,
+): Promise<ReducerStateRecovery> {
+ const info = await getProviderInfo(args.provider_url)
+ return {
+ ...state,
+ authentication_providers: {
+ ...(state.authentication_providers ?? {}),
+ [args.provider_url]: info,
+ },
+ };
+}
+
+async function deleteProviderRecovery(
+ state: ReducerStateRecovery,
+ args: ActionArgsDeleteProvider,
+): Promise<ReducerStateRecovery> {
+ const authentication_providers = {... state.authentication_providers ?? {} }
+ delete authentication_providers[args.provider_url]
+ return {
+ ...state,
+ authentication_providers,
+ };
+}
+
async function addAuthentication(
state: ReducerStateBackup,
args: ActionArgsAddAuthentication,
@@ -1366,6 +1470,8 @@ const backupTransitions: Record<
...transitionBackupJump("back", BackupStates.UserAttributesCollecting),
...transition("add_authentication", codecForAny(), addAuthentication),
...transition("delete_authentication", codecForAny(), deleteAuthentication),
+ ...transition("add_provider", codecForAny(), addProviderBackup),
+ ...transition("delete_provider", codecForAny(), deleteProviderBackup),
...transition("next", codecForAny(), nextFromAuthenticationsEditing),
},
[BackupStates.PoliciesReviewing]: {
@@ -1434,6 +1540,8 @@ const recoveryTransitions: Record<
[RecoveryStates.SecretSelecting]: {
...transitionRecoveryJump("back", RecoveryStates.UserAttributesCollecting),
...transitionRecoveryJump("next", RecoveryStates.ChallengeSelecting),
+ ...transition("add_provider", codecForAny(), addProviderRecovery),
+ ...transition("delete_provider", codecForAny(), deleteProviderRecovery),
...transition(
"change_version",
codecForActionArgsChangeVersion(),
diff --git a/packages/anastasis-core/src/policy-suggestion.test.ts b/packages/anastasis-core/src/policy-suggestion.test.ts
new file mode 100644
index 000000000..6370825da
--- /dev/null
+++ b/packages/anastasis-core/src/policy-suggestion.test.ts
@@ -0,0 +1,44 @@
+import { j2s } from "@gnu-taler/taler-util";
+import test from "ava";
+import { ProviderInfo, suggestPolicies } from "./policy-suggestion.js";
+
+test("policy suggestion", async (t) => {
+ const methods = [
+ {
+ challenge: "XXX",
+ instructions: "SMS to 123",
+ type: "sms",
+ },
+ {
+ challenge: "XXX",
+ instructions: "What is the meaning of life?",
+ type: "question",
+ },
+ {
+ challenge: "XXX",
+ instructions: "email to foo@bar.com",
+ type: "email",
+ },
+ ];
+ const providers: ProviderInfo[] = [
+ {
+ methodCost: {
+ sms: "KUDOS:1",
+ },
+ url: "prov1",
+ },
+ {
+ methodCost: {
+ question: "KUDOS:1",
+ },
+ url: "prov2",
+ },
+ ];
+ const res1 = suggestPolicies(methods, providers);
+ t.assert(res1.policies.length === 1);
+ const res2 = suggestPolicies([...methods].reverse(), providers);
+ t.assert(res2.policies.length === 1);
+
+ const res3 = suggestPolicies(methods, [...providers].reverse());
+ t.assert(res3.policies.length === 1);
+});
diff --git a/packages/anastasis-core/src/policy-suggestion.ts b/packages/anastasis-core/src/policy-suggestion.ts
index 7eb6c21cc..2c25caaa4 100644
--- a/packages/anastasis-core/src/policy-suggestion.ts
+++ b/packages/anastasis-core/src/policy-suggestion.ts
@@ -84,9 +84,16 @@ function assignProviders(
for (const provSel of providerSelections) {
// First, check if selection is even possible with the methods offered
let possible = true;
- for (const methIndex in provSel) {
- const provIndex = provSel[methIndex];
+ for (const methSelIndex in provSel) {
+ const provIndex = provSel[methSelIndex];
+ if (typeof provIndex !== "number") {
+ throw Error("invariant failed");
+ }
+ const methIndex = methodSelection[methSelIndex];
const meth = methods[methIndex];
+ if (!meth) {
+ throw Error("invariant failed");
+ }
const prov = providers[provIndex];
if (!prov.methodCost[meth.type]) {
possible = false;
@@ -96,7 +103,6 @@ function assignProviders(
if (!possible) {
continue;
}
-
// Evaluate diversity, always prefer policies
// that increase diversity.
const providerSet = new Set<string>();
@@ -163,10 +169,19 @@ function assignProviders(
/**
* A provider selection maps a method selection index to a provider index.
+ *
+ * I.e. "PSEL[i] = x" means that provider with index "x" should be used
+ * for method with index "MSEL[i]"
*/
type ProviderSelection = number[];
/**
+ * A method selection "MSEL[j] = y" means that policy method j
+ * should use method y.
+ */
+type MethodSelection = number[];
+
+/**
* Compute provider mappings.
* Enumerates all n-combinations with repetition of m providers.
*/
@@ -184,7 +199,7 @@ function enumerateProviderMappings(
}
for (let j = start; j < m; j++) {
a[i] = j;
- sel(i + 1, j);
+ sel(i + 1, 0);
if (limit && selections.length >= limit) {
break;
}
@@ -199,8 +214,6 @@ interface PolicySelectionResult {
policy_providers: PolicyProvider[];
}
-type MethodSelection = number[];
-
/**
* Compute method selections.
* Enumerates all n-combinations without repetition of m methods.
diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts
index 0f64be4eb..3e6d6c852 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -50,6 +50,11 @@ export interface SuccessDetails {
export interface CoreSecret {
mime: string;
value: string;
+ /**
+ * Filename, only set if the secret comes from
+ * a file. Should be set unless the mime type is "text/plain";
+ */
+ filename?: string;
}
export interface ReducerStateBackup {
@@ -329,6 +334,14 @@ export const codecForActionArgsEnterUserAttributes = () =>
.property("identity_attributes", codecForAny())
.build("ActionArgsEnterUserAttributes");
+export interface ActionArgsAddProvider {
+ provider_url: string;
+}
+
+export interface ActionArgsDeleteProvider {
+ provider_url: string;
+}
+
export interface ActionArgsAddAuthentication {
authentication_method: {
type: string;