gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: fix #8276


From: gnunet
Subject: [taler-wallet-core] branch master updated: fix #8276
Date: Fri, 05 Apr 2024 22:53:44 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new cc3899880 fix #8276
cc3899880 is described below

commit cc38998803141c42511e878441a5a8b15a387436
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Apr 5 17:53:39 2024 -0300

    fix #8276
---
 .../merchant-backoffice-ui/src/Application.tsx     |   2 +-
 .../merchant-backoffice-ui/src/context/session.ts  |   2 +-
 .../paths/instance/accounts/create/CreatePage.tsx  |  25 ++--
 .../src/paths/instance/accounts/create/index.tsx   | 162 +++++++++++----------
 .../paths/instance/accounts/update/UpdatePage.tsx  |  95 ++++++++----
 .../src/paths/instance/accounts/update/index.tsx   |  16 +-
 .../taler-util/src/http-client/bank-revenue.ts     |  21 ++-
 7 files changed, 184 insertions(+), 139 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/Application.tsx 
b/packages/merchant-backoffice-ui/src/Application.tsx
index 1a4bd6708..d5a05e821 100644
--- a/packages/merchant-backoffice-ui/src/Application.tsx
+++ b/packages/merchant-backoffice-ui/src/Application.tsx
@@ -64,7 +64,7 @@ export function Application(): VNode {
           de: strings["de"].completeness,
         }}
       >
-        <MerchantApiProvider baseUrl={new URL("/", baseUrl)} 
frameOnError={OnConfigError} evictors={{
+        <MerchantApiProvider baseUrl={new URL("./", baseUrl)} 
frameOnError={OnConfigError} evictors={{
           management: swrCacheEvictor
         }}>
           <SWRConfig
diff --git a/packages/merchant-backoffice-ui/src/context/session.ts 
b/packages/merchant-backoffice-ui/src/context/session.ts
index 7a5ef33d7..f3349bf83 100644
--- a/packages/merchant-backoffice-ui/src/context/session.ts
+++ b/packages/merchant-backoffice-ui/src/context/session.ts
@@ -188,7 +188,7 @@ export function useSessionContext(): SessionStateHandler {
       if (state.impersonate === undefined) {
         return;
       }
-      const newURL = new URL(`/`, state.impersonate.originalBackendUrl);
+      const newURL = new URL(`./`, state.impersonate.originalBackendUrl);
       changeBackend(newURL);
       const nextState: SessionState = {
         status: "loggedIn",
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
index 255caa375..d05375b6c 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
@@ -32,6 +32,7 @@ import { Input } from "../../../../components/form/Input.js";
 import { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
+import { safeConvertURL } from "../update/UpdatePage.js";
 
 type Entity = TalerMerchantApi.AccountAddDetails & { repeatPassword: string };
 
@@ -42,19 +43,11 @@ interface Props {
 
 const accountAuthType = ["none", "basic"];
 
-function isValidURL(s: string): boolean {
-  try {
-    const parsed = new URL("/", s);
-    return parsed instanceof URL;
-  } catch (e) {
-    return false;
-  }
-}
-
 export function CreatePage({ onCreate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
 
   const [state, setState] = useState<Partial<Entity>>({});
+  const facadeURL = safeConvertURL(state.credit_facade_url);
   const errors: FormErrors<Entity> = {
     payto_uri: !state.payto_uri ? i18n.str`required` : undefined,
 
@@ -74,9 +67,15 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
         }),
     credit_facade_url: !state.credit_facade_url
       ? undefined
-      : !isValidURL(state.credit_facade_url)
-        ? i18n.str`not valid url`
-        : undefined,
+      : !facadeURL
+        ? i18n.str`Invalid url`
+        : !facadeURL.href.endsWith("/")
+          ? i18n.str`URL should end with a '/'`
+          : facadeURL.searchParams.size > 0
+            ? i18n.str`URL should not contain params`
+            : facadeURL.hash
+              ? i18n.str`URL should not hash param`
+              : undefined,
     repeatPassword: !state.credit_facade_credentials
       ? undefined
       : state.credit_facade_credentials.type === "basic" &&
@@ -94,7 +93,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     if (hasErrors) return Promise.reject();
     const credit_facade_url = !state.credit_facade_url
       ? undefined
-      : new URL("/", state.credit_facade_url).href;
+      : facadeURL?.href;
     const credit_facade_credentials:
       | TalerMerchantApi.FacadeCredentials
       | undefined =
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index 96ca8bf5e..fb50ab995 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -62,7 +62,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
         onCreate={async (request: Entity) => {
           const revenueAPI = !request.credit_facade_url
             ? undefined
-            : new URL("/", request.credit_facade_url);
+            : new URL("./", request.credit_facade_url);
 
           if (revenueAPI) {
             const resp = await testRevenueAPI(
@@ -71,7 +71,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
             );
             if (resp.type === "fail") {
               switch (resp.case) {
-                case "no-config": {
+                case TestRevenueErrorType.NO_CONFIG: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -79,7 +79,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
                   });
                   return;
                 }
-                case "client-bad-request": {
+                case TestRevenueErrorType.CLIENT_BAD_REQUEST: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -87,7 +87,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
                   });
                   return;
                 }
-                case "unauthorized": {
+                case TestRevenueErrorType.UNAUTHORIZED: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -95,7 +95,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
                   });
                   return;
                 }
-                case "not-found": {
+                case TestRevenueErrorType.NOT_FOUND: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -103,7 +103,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
                   });
                   return;
                 }
-                case "error": {
+                case TestRevenueErrorType.GENERIC_ERROR: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -112,7 +112,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
                   return;
                 }
                 default: {
-                  assertUnreachable(resp)
+                  assertUnreachable(resp.case);
                 }
               }
             }
@@ -136,92 +136,102 @@ export default function CreateValidator({ onConfirm, 
onBack }: Props): VNode {
   );
 }
 
+export enum TestRevenueErrorType {
+  NO_CONFIG,
+  CLIENT_BAD_REQUEST,
+  UNAUTHORIZED,
+  NOT_FOUND,
+  GENERIC_ERROR,
+}
+
 export async function testRevenueAPI(
   revenueAPI: URL,
   creds: FacadeCredentials | undefined,
-): Promise<
-  | OperationOk<void>
-  | OperationFail<"no-config">
-  | OperationFail<"client-bad-request">
-  | OperationFail<"unauthorized">
-  | OperationFail<"not-found">
-  | OperationFail<"error">
-> {
+): Promise<OperationOk<void> | OperationFail<TestRevenueErrorType>> {
   const api = new TalerRevenueHttpClient(
     revenueAPI.href,
     new BrowserFetchHttpLib(),
   );
+  const auth =
+    creds === undefined
+      ? undefined
+      : creds.type === "none"
+        ? undefined
+        : creds.type === "basic"
+          ? {
+              username: creds.username,
+              password: creds.password,
+            }
+          : undefined;
+
   try {
-    const config = await api.getConfig();
+    const config = await api.getConfig(auth);
+
     if (config.type === "fail") {
-      return {
-        type: "fail",
-        case: "no-config",
-        detail: {
-          code: 1,
-        },
-      };
+      switch (config.case) {
+        case HttpStatusCode.Unauthorized: {
+          return {
+            type: "fail",
+            case: TestRevenueErrorType.UNAUTHORIZED,
+            detail: {
+              code: 1,
+            },
+          };
+        }
+        case HttpStatusCode.NotFound: {
+          return {
+            type: "fail",
+            case: TestRevenueErrorType.NO_CONFIG,
+            detail: {
+              code: 1,
+            },
+          };
+        }
+      }
+    }
+
+    const history = await api.getHistory(auth);
+
+    if (history.type === "fail") {
+      switch (history.case) {
+        case HttpStatusCode.BadRequest: {
+          return {
+            type: "fail",
+            case: TestRevenueErrorType.CLIENT_BAD_REQUEST,
+            detail: {
+              code: 1,
+            },
+          };
+        }
+        case HttpStatusCode.Unauthorized: {
+          return {
+            type: "fail",
+            case: TestRevenueErrorType.UNAUTHORIZED,
+            detail: {
+              code: 1,
+            },
+          };
+        }
+        case HttpStatusCode.NotFound: {
+          return {
+            type: "fail",
+            case: TestRevenueErrorType.NOT_FOUND,
+            detail: {
+              code: 1,
+            },
+          };
+        }
+      }
     }
   } catch (err) {
     if (err instanceof TalerError) {
       return {
         type: "fail",
-        case: "error",
+        case: TestRevenueErrorType.GENERIC_ERROR,
         detail: err.errorDetail,
       };
     }
   }
-  if (creds) {
-    const auth =
-      creds.type === "basic"
-        ? {
-            username: creds.username,
-            password: creds.password,
-          }
-        : undefined;
-
-    try {
-      const history = await api.getHistory(auth);
-      if (history.type === "fail") {
-        switch (history.case) {
-          case HttpStatusCode.BadRequest: {
-            return {
-              type: "fail",
-              case: "client-bad-request",
-              detail: {
-                code: 1,
-              },
-            };
-          }
-          case HttpStatusCode.Unauthorized: {
-            return {
-              type: "fail",
-              case: "unauthorized",
-              detail: {
-                code: 1,
-              },
-            };
-          }
-          case HttpStatusCode.NotFound: {
-            return {
-              type: "fail",
-              case: "not-found",
-              detail: {
-                code: 1,
-              },
-            };
-          }
-        }
-      }
-    } catch (err) {
-      if (err instanceof TalerError) {
-        return {
-          type: "fail",
-          case: "error",
-          detail: err.errorDetail,
-        };
-      }
-    }
-  }
+
   return opFixedSuccess(undefined);
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
index 6dd264f29..1a8e9bdc1 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
@@ -33,8 +33,7 @@ import { InputPaytoForm } from 
"../../../../components/form/InputPaytoForm.js";
 import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 
-type Entity = TalerMerchantApi.BankAccountEntry
-  & WithId;
+type Entity = TalerMerchantApi.BankAccountEntry & WithId;
 
 const accountAuthType = ["unedit", "none", "basic"];
 interface Props {
@@ -43,32 +42,56 @@ interface Props {
   account: Entity;
 }
 
-
 export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
 
-  const [state, setState] = 
useState<Partial<TalerMerchantApi.AccountPatchDetails>>(account);
+  const [state, setState] =
+    useState<Partial<TalerMerchantApi.AccountPatchDetails>>(account);
 
   // @ts-expect-error "unedit" is fine since is part of the accountAuthType 
values
   if (state.credit_facade_credentials?.type === "unedit") {
     // we use this to set creds to undefined but server don't get this type
-    state.credit_facade_credentials = undefined
+    state.credit_facade_credentials = undefined;
   }
 
+  const facadeURL = safeConvertURL(state.credit_facade_url);
+
   const errors: FormErrors<TalerMerchantApi.AccountPatchDetails> = {
-    credit_facade_url: !state.credit_facade_url ? undefined : 
!isValidURL(state.credit_facade_url) ? i18n.str`invalid url` : undefined,
+    credit_facade_url: !state.credit_facade_url
+      ? undefined
+      : !facadeURL
+        ? i18n.str`Invalid url`
+        : !facadeURL.href.endsWith("/")
+          ? i18n.str`URL should end with a '/'`
+          : facadeURL.searchParams.size > 0
+            ? i18n.str`URL should not contain params`
+            : facadeURL.hash
+              ? i18n.str`URL should not hash param`
+                : undefined,
     credit_facade_credentials: undefinedIfEmpty({
+      username:
+        state.credit_facade_credentials?.type !== "basic"
+          ? undefined
+          : !state.credit_facade_credentials.username
+            ? i18n.str`required`
+            : undefined,
 
-      username: state.credit_facade_credentials?.type !== "basic" ? undefined
-        : !state.credit_facade_credentials.username ? i18n.str`required` : 
undefined,
-
-      password: state.credit_facade_credentials?.type !== "basic" ? undefined
-        : !state.credit_facade_credentials.password ? i18n.str`required` : 
undefined,
-
-      repeatPassword: state.credit_facade_credentials?.type !== "basic" ? 
undefined
-        : !(state.credit_facade_credentials as any).repeatPassword ? 
i18n.str`required` :
-          (state.credit_facade_credentials as any).repeatPassword !== 
state.credit_facade_credentials.password ? i18n.str`doesn't match`
+      password:
+        state.credit_facade_credentials?.type !== "basic"
+          ? undefined
+          : !state.credit_facade_credentials.password
+            ? i18n.str`required`
             : undefined,
+
+      repeatPassword:
+        state.credit_facade_credentials?.type !== "basic"
+          ? undefined
+          : !(state.credit_facade_credentials as any).repeatPassword
+            ? i18n.str`required`
+            : (state.credit_facade_credentials as any).repeatPassword !==
+                state.credit_facade_credentials.password
+              ? i18n.str`doesn't match`
+              : undefined,
     }),
   };
 
@@ -78,18 +101,25 @@ export function UpdatePage({ account, onUpdate, onBack }: 
Props): VNode {
 
   const submitForm = () => {
     if (hasErrors) return Promise.reject();
-
-    const credit_facade_url = !state.credit_facade_url ? undefined : new 
URL("/", state.credit_facade_url).href
-
-    const credit_facade_credentials: TalerMerchantApi.FacadeCredentials | 
undefined =
-      credit_facade_url == undefined || state.credit_facade_credentials === 
undefined ? undefined :
-        state.credit_facade_credentials.type === "basic" ? {
-          type: "basic",
-          password: state.credit_facade_credentials.password,
-          username: state.credit_facade_credentials.username,
-        } : {
-          type: "none"
-        };
+    const credit_facade_url = !state.credit_facade_url
+      ? undefined
+      : facadeURL?.href;
+
+    const credit_facade_credentials:
+      | TalerMerchantApi.FacadeCredentials
+      | undefined =
+      credit_facade_url == undefined ||
+      state.credit_facade_credentials === undefined
+        ? undefined
+        : state.credit_facade_credentials.type === "basic"
+          ? {
+              type: "basic",
+              password: state.credit_facade_credentials.password,
+              username: state.credit_facade_credentials.username,
+            }
+          : {
+              type: "none",
+            };
 
     return onUpdate({ credit_facade_credentials, credit_facade_url });
   };
@@ -140,7 +170,7 @@ export function UpdatePage({ account, onUpdate, onBack }: 
Props): VNode {
                   toStr={(str) => {
                     if (str === "none") return "Without authentication";
                     if (str === "basic") return "With authentication";
-                    return "Do not change"
+                    return "Do not change";
                   }}
                 />
                 {state.credit_facade_credentials?.type === "basic" ? (
@@ -191,11 +221,12 @@ export function UpdatePage({ account, onUpdate, onBack }: 
Props): VNode {
   );
 }
 
-function isValidURL(s: string): boolean {
+//TODO: move to utils
+export function safeConvertURL(s?: string): URL | undefined {
+  if (!s) return undefined;
   try {
-    const u = new URL("/", s)
-    return true;
+    return new URL(s);
   } catch (e) {
-    return false;
+    return undefined;
   }
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
index cf1d5b0c7..519c9f56a 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -35,7 +35,7 @@ import { Notification } from "../../../../utils/types.js";
 import { LoginPage } from "../../../login/index.js";
 import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { testRevenueAPI } from "../create/index.js";
+import { TestRevenueErrorType, testRevenueAPI } from "../create/index.js";
 
 export type Entity = TalerMerchantApi.AccountPatchDetails & WithId;
 
@@ -83,7 +83,7 @@ export default function UpdateValidator({
         onUpdate={async (request) => {
           const revenueAPI = !request.credit_facade_url
             ? undefined
-            : new URL("/", request.credit_facade_url);
+            : new URL("./", request.credit_facade_url);
 
           if (revenueAPI) {
             const resp = await testRevenueAPI(
@@ -92,7 +92,7 @@ export default function UpdateValidator({
             );
             if (resp.type === "fail") {
               switch (resp.case) {
-                case "no-config": {
+                case TestRevenueErrorType.NO_CONFIG: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -100,7 +100,7 @@ export default function UpdateValidator({
                   });
                   return;
                 }
-                case "client-bad-request": {
+                case TestRevenueErrorType.CLIENT_BAD_REQUEST: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -108,7 +108,7 @@ export default function UpdateValidator({
                   });
                   return;
                 }
-                case "unauthorized": {
+                case TestRevenueErrorType.UNAUTHORIZED: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -116,7 +116,7 @@ export default function UpdateValidator({
                   });
                   return;
                 }
-                case "not-found": {
+                case TestRevenueErrorType.NOT_FOUND: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -124,7 +124,7 @@ export default function UpdateValidator({
                   });
                   return;
                 }
-                case "error": {
+                case TestRevenueErrorType.GENERIC_ERROR: {
                   setNotif({
                     message: i18n.str`Could not create account`,
                     type: "ERROR",
@@ -133,7 +133,7 @@ export default function UpdateValidator({
                   return;
                 }
                 default: {
-                  assertUnreachable(resp)
+                  assertUnreachable(resp.case)
                 }
               }
             }
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts 
b/packages/taler-util/src/http-client/bank-revenue.ts
index b195e8c8f..34afe7d86 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -44,6 +44,10 @@ export type TalerBankRevenueErrorsByMethod<
   prop extends keyof TalerRevenueHttpClient,
 > = FailCasesByMethod<TalerRevenueHttpClient, prop>;
 
+type UsernameAndPassword = {
+  username: string;
+  password: string;
+};
 /**
  * The API is used by the merchant (or other parties) to query
  * for incoming transactions to their account.
@@ -69,33 +73,34 @@ export class TalerRevenueHttpClient {
    * https://docs.taler.net/core/api-bank-revenue.html#get--config
    *
    */
-  async getConfig() {
+  async getConfig(auth?: UsernameAndPassword) {
     const url = new URL(`config`, this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "GET",
+      headers: {
+        Authorization: auth
+          ? makeBasicAuthHeader(auth.username, auth.password)
+          : undefined,
+      },
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
         return opSuccessFromHttp(resp, codecForRevenueConfig());
+      case HttpStatusCode.Unauthorized:
+        return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       default:
         return opUnknownFailure(resp, await readTalerErrorResponse(resp));
     }
   }
-
   /**
    * https://docs.taler.net/core/api-bank-revenue.html#get--history
    *
    * @returns
    */
   async getHistory(
-    auth:
-      | {
-          username: string;
-          password: string;
-        }
-      | undefined,
+    auth?: UsernameAndPassword,
     params?: PaginationParams & LongPollParams,
   ) {
     const url = new URL(`history`, this.baseUrl);

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]