2022-06-06 16:46:49 +02:00
/ *
This file is part of GNU Anastasis
( C ) 2021 - 2022 Anastasis SARL
GNU Anastasis is free software ; you can redistribute it and / or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation ; either version 3 , or ( at your option ) any later version .
GNU Anastasis is distributed in the hope that it will be useful , but WITHOUT ANY
WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE . See the GNU Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License along with
GNU Anastasis ; see the file COPYING . If not , see < http : / / www.gnu.org / licenses / >
* /
2022-01-24 18:39:27 +01:00
import {
2022-08-26 17:59:00 +02:00
AggregatedPolicyMetaInfo ,
2022-01-24 18:39:27 +01:00
AuthenticationProviderStatus ,
AuthenticationProviderStatusOk ,
} from "@gnu-taler/anastasis-core" ;
2021-10-19 15:56:52 +02:00
import { h , VNode } from "preact" ;
2022-04-12 12:54:57 +02:00
import { useEffect , useState } from "preact/hooks" ;
2022-06-06 05:54:55 +02:00
import { AsyncButton } from "../../components/AsyncButton.js" ;
import { PhoneNumberInput } from "../../components/fields/NumberInput.js" ;
import { useAnastasisContext } from "../../context/anastasis.js" ;
2022-06-12 00:10:26 +02:00
import AddingProviderScreen from "./AddingProviderScreen/index.js" ;
2022-06-06 05:54:55 +02:00
import { AnastasisClientFrame } from "./index.js" ;
2021-10-19 15:56:52 +02:00
2022-08-26 17:59:00 +02:00
export function SecretSelectionScreenFound ( {
policies ,
onManageProvider ,
onNext ,
} : {
policies : AggregatedPolicyMetaInfo [ ] ;
onManageProvider : ( ) = > void ;
onNext : ( version : AggregatedPolicyMetaInfo ) = > void ;
} ) : VNode {
2021-11-10 14:20:52 +01:00
const reducer = useAnastasisContext ( ) ;
2022-04-12 12:54:57 +02:00
if ( ! reducer ) {
return < div > no reducer in context < / div > ;
}
if (
! reducer . currentReducerState ||
2022-04-13 08:44:37 +02:00
reducer . currentReducerState . reducer_type !== "recovery"
2022-04-12 12:54:57 +02:00
) {
return < div > invalid state < / div > ;
}
return (
2022-04-13 08:44:37 +02:00
< AnastasisClientFrame
title = "Recovery: Select secret"
hideNext = "Please select version to recover"
>
2022-06-09 21:11:49 +02:00
< div class = "columns" >
< div class = "column" >
< p class = "block" > Found versions : < / p >
{ policies . map ( ( version , i ) = > (
< div key = { i } class = "box" >
< div
class = "block"
style = { { display : "flex" , justifyContent : "space-between" } }
>
< div
style = { {
display : "flex" ,
flexDirection : "column" ,
} }
>
< div style = { { display : "flex" , alignItems : "center" } } >
< b > Name : < / b > & nbsp ; < span > { version . secret_name } < / span >
< / div >
< div style = { { display : "flex" , alignItems : "center" } } >
< b > Id : < / b > & nbsp ;
< span
class = "icon has-tooltip-top"
2022-08-26 17:59:00 +02:00
data - tooltip = { version . policy_hash }
2022-06-09 21:11:49 +02:00
>
< i class = "mdi mdi-information" / >
< / span >
< span > { version . policy_hash . substring ( 0 , 22 ) } . . . < / span >
< / div >
< / div >
< div >
< AsyncButton
class = "button"
2022-08-26 17:59:00 +02:00
onClick = { async ( ) = > onNext ( version ) }
2022-06-09 21:11:49 +02:00
>
Recover
< / AsyncButton >
< / div >
< / div >
< / div >
) ) }
2022-04-12 12:54:57 +02:00
< / div >
2022-06-09 21:11:49 +02:00
< div class = "column" >
< p >
Secret found , you can select another version or continue to the
challenges solving
< / p >
< p class = "block" >
2022-08-26 17:59:00 +02:00
< a onClick = { onManageProvider } > Manage recovery providers < / a >
2022-06-09 21:11:49 +02:00
< / p >
< / div >
< / div >
2022-04-12 12:54:57 +02:00
< / AnastasisClientFrame >
) ;
}
2021-10-22 06:31:46 +02:00
2022-08-26 17:59:00 +02:00
export function SecretSelectionScreen ( ) : VNode {
2022-04-12 12:54:57 +02:00
const reducer = useAnastasisContext ( ) ;
2021-11-10 14:20:52 +01:00
const [ manageProvider , setManageProvider ] = useState ( false ) ;
2022-04-12 12:54:57 +02:00
useEffect ( ( ) = > {
async function f() {
if ( reducer ) {
await reducer . discoverStart ( ) ;
}
}
f ( ) . catch ( ( e ) = > console . log ( e ) ) ;
} , [ ] ) ;
2021-10-22 06:31:46 +02:00
if ( ! reducer ) {
2021-11-10 14:20:52 +01:00
return < div > no reducer in context < / div > ;
2021-10-22 06:31:46 +02:00
}
2022-08-26 17:59:00 +02:00
2021-11-10 14:20:52 +01:00
if (
! reducer . currentReducerState ||
2022-04-13 08:44:37 +02:00
reducer . currentReducerState . reducer_type !== "recovery"
2021-11-10 14:20:52 +01:00
) {
return < div > invalid state < / div > ;
2021-10-22 06:31:46 +02:00
}
2022-08-26 17:59:00 +02:00
if ( manageProvider ) {
2021-11-10 14:20:52 +01:00
return (
2022-08-26 17:59:00 +02:00
< AddingProviderScreen onCancel = { async ( ) = > setManageProvider ( false ) } / >
2021-11-10 14:20:52 +01:00
) ;
2021-10-19 15:56:52 +02:00
}
2021-11-04 20:51:19 +01:00
2022-08-26 17:59:00 +02:00
if (
reducer . discoveryState . state === "none" ||
reducer . discoveryState . state === "active"
) {
// Can this even happen?
return < SecretSelectionScreenWaiting / > ;
2021-11-04 20:51:19 +01:00
}
2022-08-26 17:59:00 +02:00
const policies = reducer . discoveryState . aggregatedPolicies ? ? [ ] ;
if ( policies . length === 0 ) {
2022-06-12 00:10:26 +02:00
return (
2022-08-26 17:59:00 +02:00
< AddingProviderScreen
onCancel = { async ( ) = > setManageProvider ( false ) }
notifications = { [
{
message : "Secret not found" ,
type : "ERROR" ,
description :
"With the information you provided we could not found secret in any of the providers. You can try adding more providers if you think the data is correct." ,
} ,
] }
/ >
2022-06-12 00:10:26 +02:00
) ;
2021-11-09 04:19:50 +01:00
}
2021-10-19 15:56:52 +02:00
return (
2022-08-26 17:59:00 +02:00
< SecretSelectionScreenFound
policies = { policies }
onNext = { ( version ) = > reducer . transition ( "select_version" , version ) }
onManageProvider = { async ( ) = > setManageProvider ( false ) }
/ >
2021-10-19 15:56:52 +02:00
) ;
}
2021-11-05 15:17:42 +01:00
2022-08-26 17:59:00 +02:00
// export function OldSecretSelectionScreen(): VNode {
// const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
// const reducer = useAnastasisContext();
// const [manageProvider, setManageProvider] = useState(false);
// useEffect(() => {
// async function f() {
// if (reducer) {
// await reducer.discoverStart();
// }
// }
// f().catch((e) => console.log(e));
// }, []);
// const currentVersion =
// (reducer?.currentReducerState &&
// "recovery_document" in reducer.currentReducerState &&
// reducer.currentReducerState.recovery_document?.version) ||
// 0;
// if (!reducer) {
// return <div>no reducer in context</div>;
// }
// if (
// !reducer.currentReducerState ||
// reducer.currentReducerState.reducer_type !== "recovery"
// ) {
// return <div>invalid state</div>;
// }
// async function doSelectVersion(p: string, n: number): Promise<void> {
// if (!reducer) return Promise.resolve();
// return reducer.runTransaction(async (tx) => {
// await tx.transition("select_version", {
// version: n,
// provider_url: p,
// });
// setSelectingVersion(false);
// });
// }
// const provs = reducer.currentReducerState.authentication_providers ?? {};
// const recoveryDocument = reducer.currentReducerState.recovery_document;
// if (!recoveryDocument) {
// return (
// <ChooseAnotherProviderScreen
// providers={provs}
// selected=""
// onChange={(newProv) => doSelectVersion(newProv, 0)}
// />
// );
// }
// if (selectingVersion) {
// return (
// <SelectOtherVersionProviderScreen
// providers={provs}
// provider={recoveryDocument.provider_url}
// version={recoveryDocument.version}
// onCancel={() => setSelectingVersion(false)}
// onConfirm={doSelectVersion}
// />
// );
// }
// if (manageProvider) {
// return (
// <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
// );
// }
// const providerInfo = provs[
// recoveryDocument.provider_url
// ] as AuthenticationProviderStatusOk;
// return (
// <AnastasisClientFrame title="Recovery: Select secret">
// <div class="columns">
// <div class="column">
// <div class="box" style={{ border: "2px solid green" }}>
// <h1 class="subtitle">{providerInfo.business_name}</h1>
// <div class="block">
// {currentVersion === 0 ? (
// <p>Set to recover the latest version</p>
// ) : (
// <p>Set to recover the version number {currentVersion}</p>
// )}
// </div>
// <div class="buttons is-right">
// <button class="button" onClick={(e) => setSelectingVersion(true)}>
// Change secret's version
// </button>
// </div>
// </div>
// </div>
// <div class="column">
// <p>
// Secret found, you can select another version or continue to the
// challenges solving
// </p>
// <p class="block">
// <a onClick={() => setManageProvider(true)}>
// Manage recovery providers
// </a>
// </p>
// </div>
// </div>
// </AnastasisClientFrame>
// );
// }
2021-11-10 14:20:52 +01:00
function ChooseAnotherProviderScreen ( {
onChange ,
} : {
onChange : ( prov : string ) = > void ;
} ) : VNode {
2022-08-26 17:59:00 +02:00
const reducer = useAnastasisContext ( ) ;
if ( ! reducer ) {
return < div > no reducer in context < / div > ;
}
if (
! reducer . currentReducerState ||
reducer . currentReducerState . reducer_type !== "recovery"
) {
return < div > invalid state < / div > ;
}
const providers = reducer . currentReducerState . authentication_providers ? ? { } ;
2021-11-05 15:17:42 +01:00
return (
2021-11-10 14:20:52 +01:00
< AnastasisClientFrame
hideNext = "Recovery document not found"
title = "Recovery: Problem"
>
2021-11-05 15:17:42 +01:00
< p > No recovery document found , try with another provider < / p >
< div class = "field" >
< label class = "label" > Provider < / label >
< div class = "control is-expanded has-icons-left" >
< div class = "select is-fullwidth" >
2022-08-26 17:59:00 +02:00
< select onChange = { ( e ) = > onChange ( e . currentTarget . value ) } value = "" >
2021-11-10 14:20:52 +01:00
< option key = "none" disabled selected value = "" >
2022-08-26 17:59:00 +02:00
Choose a provider
2021-11-10 14:20:52 +01:00
< / option >
2021-11-11 17:22:14 +01:00
{ Object . keys ( providers ) . map ( ( url ) = > {
2022-01-24 18:39:27 +01:00
const p = providers [ url ] ;
if ( ! ( "methods" in p ) ) return null ;
return (
< option key = { url } value = { url } >
{ p . business_name }
< / option >
) ;
2021-11-11 17:22:14 +01:00
} ) }
2021-11-05 15:17:42 +01:00
< / select >
< div class = "icon is-small is-left" >
< i class = "mdi mdi-earth" / >
< / div >
< / div >
< / div >
< / div >
< / AnastasisClientFrame >
) ;
}
2021-11-10 14:20:52 +01:00
function SelectOtherVersionProviderScreen ( {
providers ,
provider ,
version ,
onConfirm ,
onCancel ,
} : {
onCancel : ( ) = > void ;
provider : string ;
version : number ;
2021-11-11 17:22:14 +01:00
providers : { [ url : string ] : AuthenticationProviderStatus } ;
2021-11-10 14:20:52 +01:00
onConfirm : ( prov : string , v : number ) = > Promise < void > ;
} ) : VNode {
2021-11-05 15:17:42 +01:00
const [ otherProvider , setOtherProvider ] = useState < string > ( provider ) ;
2021-11-10 14:20:52 +01:00
const [ otherVersion , setOtherVersion ] = useState (
version > 0 ? String ( version ) : "" ,
) ;
2022-01-24 18:39:27 +01:00
const otherProviderInfo = providers [
otherProvider
] as AuthenticationProviderStatusOk ;
2021-11-05 15:17:42 +01:00
return (
< AnastasisClientFrame hideNav title = "Recovery: Select secret" >
< div class = "columns" >
< div class = "column" >
< div class = "box" >
2021-11-11 17:22:14 +01:00
< h1 class = "subtitle" > Provider { otherProviderInfo . business_name } < / h1 >
2021-11-05 15:17:42 +01:00
< div class = "block" >
2021-11-10 14:20:52 +01:00
{ version === 0 ? (
< p > Set to recover the latest version < / p >
) : (
< p > Set to recover the version number { version } < / p >
) }
2021-11-05 15:17:42 +01:00
< p > Specify other version below or use the latest < / p >
< / div >
< div class = "field" >
< label class = "label" > Provider < / label >
< div class = "control is-expanded has-icons-left" >
< div class = "select is-fullwidth" >
2021-11-10 14:20:52 +01:00
< select
onChange = { ( e ) = > setOtherProvider ( e . currentTarget . value ) }
value = { otherProvider }
>
< option key = "none" disabled selected value = "" >
{ " " }
Choose a provider { " " }
< / option >
2021-11-11 17:22:14 +01:00
{ Object . keys ( providers ) . map ( ( url ) = > {
2022-01-24 18:39:27 +01:00
const p = providers [ url ] ;
if ( ! ( "methods" in p ) ) return null ;
return (
< option key = { url } value = { url } >
{ p . business_name }
< / option >
) ;
2021-11-11 17:22:14 +01:00
} ) }
2021-11-05 15:17:42 +01:00
< / select >
< div class = "icon is-small is-left" >
< i class = "mdi mdi-earth" / >
< / div >
< / div >
< / div >
< / div >
< div class = "container" >
2021-11-09 23:14:44 +01:00
< PhoneNumberInput
2021-11-05 15:17:42 +01:00
label = "Version"
placeholder = "version number to recover"
grabFocus
2021-11-10 14:20:52 +01:00
bind = { [ otherVersion , setOtherVersion ] }
/ >
2021-11-05 15:17:42 +01:00
< / div >
< / div >
2021-11-10 14:20:52 +01:00
< div
style = { {
marginTop : "2em" ,
display : "flex" ,
justifyContent : "space-between" ,
} }
>
< button class = "button" onClick = { onCancel } >
Cancel
< / button >
2021-11-05 15:17:42 +01:00
< div class = "buttons" >
2021-11-10 14:20:52 +01:00
< AsyncButton
class = "button"
onClick = { ( ) = > onConfirm ( otherProvider , 0 ) }
>
Use latest
< / AsyncButton >
< AsyncButton
class = "button is-info"
onClick = { ( ) = >
onConfirm ( o therProvider , parseInt ( otherVersion , 10 ) )
}
>
Confirm
< / AsyncButton >
2021-11-05 15:17:42 +01:00
< / div >
< / div >
< / div >
< / div >
< / AnastasisClientFrame >
) ;
}
2022-08-26 17:59:00 +02:00
function SecretSelectionScreenWaiting ( ) : VNode {
return (
< AnastasisClientFrame title = "Recovery: Select secret" >
< div > loading secret versions < / div >
< / AnastasisClientFrame >
) ;
}