Skip to content

Commit

Permalink
Feature owen dashboard session key invalid check (#897)
Browse files Browse the repository at this point in the history
* feat: add filterd provider

* feat: ok for isSessionKeyExpired

* feat: fix lint error

* feat: ok for dashboard session key valid check
  • Loading branch information
yubing744 authored Oct 2, 2023
1 parent 3802733 commit 22beffe
Show file tree
Hide file tree
Showing 14 changed files with 494 additions and 11 deletions.
3 changes: 2 additions & 1 deletion crates/rooch-framework/doc/session_key.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,9 @@ The lengths of the parts of the session's scope do not match.
<pre><code><b>public</b> <b>fun</b> <a href="session_key.md#0x3_session_key_is_expired_session_key">is_expired_session_key</a>(ctx: &StorageContext, account_address: <b>address</b>, authentication_key: <a href="">vector</a>&lt;u8&gt;) : bool {
<b>let</b> session_key_option = <a href="session_key.md#0x3_session_key_get_session_key">get_session_key</a>(ctx, account_address, authentication_key);
<b>if</b> (<a href="_is_none">option::is_none</a>(&session_key_option)){
<b>return</b> <b>false</b>
<b>return</b> <b>true</b>
};

<b>let</b> <a href="session_key.md#0x3_session_key">session_key</a> = <a href="_extract">option::extract</a>(&<b>mut</b> session_key_option);
<a href="session_key.md#0x3_session_key_is_expired">is_expired</a>(ctx, &<a href="session_key.md#0x3_session_key">session_key</a>)
}
Expand Down
3 changes: 2 additions & 1 deletion crates/rooch-framework/sources/session_key.move
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ module rooch_framework::session_key {
public fun is_expired_session_key(ctx: &StorageContext, account_address: address, authentication_key: vector<u8>) : bool {
let session_key_option = get_session_key(ctx, account_address, authentication_key);
if (option::is_none(&session_key_option)){
return false
return true
};

let session_key = option::extract(&mut session_key_option);
is_expired(ctx, &session_key)
}
Expand Down
63 changes: 57 additions & 6 deletions dashboard/src/context/session/SessionContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import { hexlify } from '@ethersproject/bytes'
import { useEffect, useState } from 'react'
import { useEffect, useState, useMemo } from 'react'
import { useAuth } from 'src/hooks/useAuth'
import { useRooch } from 'src/hooks/useRooch'

Expand All @@ -13,11 +13,17 @@ import {
bcsTypes,
IAccount,
IProvider,
FilteredProvider,
ITransactionFilterChain,
FilterFunc,
FuncFilter,
Account,
PrivateKeyAuth,
Ed25519Keypair,
encodeMoveCallData,
addressToSeqNumber,
parseRoochErrorSubStatus,
ErrorCategory,
} from '@rooch/sdk'

// ** React Imports
Expand Down Expand Up @@ -64,18 +70,59 @@ const loadSessionAccountFromSessionStorage = (provider: IProvider, roochAddress:
return null
}

const clearSessionAccountInSessionStorage = (roochAddress: string) => {
try {
window.sessionStorage.setItem(makeSessionAccountStoreKey(roochAddress), '')
} catch (error) {
// If error also return initialValue
console.log(error)
}

return null
}

const SessionProvider = ({ children }: Props) => {
const auth = useAuth()
const rooch = useRooch()

const [loading, setLoading] = useState(false)

const filterdProvider = useMemo(() => {
const sessionKeyInvalidFilterFunc: FilterFunc = async (
req: any,
chain: ITransactionFilterChain,
): Promise<any> => {
try {
return await chain.doFilter(req)
} catch (e: any) {
console.log('sessionKeyInvalidFilterFunc catch error:', e)
const subStatus = parseRoochErrorSubStatus(e.message)
if (
subStatus &&
((subStatus.category === ErrorCategory.INVALID_ARGUMENT && subStatus.reason === 1001) ||
subStatus.category === ErrorCategory.PERMISSION_DENIED)
) {
setSessionAccount(null)

const defaultAccount = auth.defaultAccount
if (defaultAccount) {
clearSessionAccountInSessionStorage(defaultAccount.roochAddress)
}
}

throw e
}
}

return new FilteredProvider(rooch.provider!, [new FuncFilter(sessionKeyInvalidFilterFunc)])
}, [rooch.provider, auth.defaultAccount])

const [sessionAccount, setSessionAccount] = useState<IAccount | null>(() => {
const defaultAccount = auth.defaultAccount

if (defaultAccount) {
const sessionAccount = loadSessionAccountFromSessionStorage(
rooch.provider!,
filterdProvider,
defaultAccount.roochAddress,
)

Expand All @@ -90,15 +137,15 @@ const SessionProvider = ({ children }: Props) => {

if (defaultAccount) {
const sessionAccount = loadSessionAccountFromSessionStorage(
rooch.provider!,
filterdProvider,
defaultAccount.roochAddress,
)

if (sessionAccount) {
setSessionAccount(sessionAccount)
}
}
}, [auth.defaultAccount, rooch.provider])
}, [auth.defaultAccount, filterdProvider])

const waitTxConfirmed = async (ethereum: any, txHash: string) => {
let receipt
Expand Down Expand Up @@ -194,6 +241,7 @@ const SessionProvider = ({ children }: Props) => {
}

const requestWalletCreateSessionKey = async (
provider: IProvider,
account: AccountDataType,
scope: Array<string>,
maxInactiveInterval: number,
Expand All @@ -214,7 +262,7 @@ const SessionProvider = ({ children }: Props) => {
window.sessionStorage.setItem(key, pk.export().privateKey)
const authorizer = new PrivateKeyAuth(pk)

return new Account(rooch.provider!, account.roochAddress, authorizer)
return new Account(provider, account.roochAddress, authorizer)
} catch (err: any) {
console.log(`registerSessionKey error:`, err)

Expand All @@ -223,6 +271,7 @@ const SessionProvider = ({ children }: Props) => {
}

const requestPrivateCreateSessionKey = async (
provider: IProvider,
account: IAccount,
scope: Array<string>,
maxInactiveInterval: number,
Expand All @@ -237,7 +286,7 @@ const SessionProvider = ({ children }: Props) => {
window.sessionStorage.setItem(key, pk.export().privateKey)
const authorizer = new PrivateKeyAuth(pk)

return new Account(rooch.provider!, roochAddress, authorizer)
return new Account(provider, roochAddress, authorizer)
} catch (err: any) {
console.log(`registerSessionKey error:`, err)

Expand All @@ -263,6 +312,7 @@ const SessionProvider = ({ children }: Props) => {
const account = new Account(rooch.provider!, roochAddress, authorizer)

const sessionAccount = await requestPrivateCreateSessionKey(
filterdProvider,
account,
scope,
maxInactiveInterval,
Expand All @@ -273,6 +323,7 @@ const SessionProvider = ({ children }: Props) => {
}
} else if (defaultAccount.type === 'ETH') {
const sessionAccount = await requestWalletCreateSessionKey(
filterdProvider,
defaultAccount,
scope,
maxInactiveInterval,
Expand Down
67 changes: 65 additions & 2 deletions dashboard/src/views/feature/SessionKeyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Button from '@mui/material/Button'
import Snackbar from '@mui/material/Snackbar'
import Dialog from '@mui/material/Dialog'
import DialogActions from '@mui/material/DialogActions'
import DialogContent from '@mui/material/DialogContent'
import DialogContentText from '@mui/material/DialogContentText'
import DialogTitle from '@mui/material/DialogTitle'
import {
DataGrid,
GridColDef,
Expand Down Expand Up @@ -83,11 +88,12 @@ export default function SessionKeyList() {
headerName: 'Action',
flex: 1,
align: 'right',
headerAlign: 'right',
renderCell: (params: GridRenderCellParams) => (
<Button
variant="contained"
color="secondary"
onClick={() => handleRemove(params.row.authentication_key)}
onClick={() => showConfirmDeleteDialog(params.row.authentication_key)}
>
Remove
</Button>
Expand Down Expand Up @@ -172,7 +178,16 @@ export default function SessionKeyList() {
)
}

const handleRemove = (authentication_key: string) => {
const handleConfirmRemove = (authentication_key: string | undefined) => {
setConfirmDeleteDialog({
open: false,
authKey: undefined,
})

if (!authentication_key) {
return false
}

const defaultAccount = auth.defaultAccount
if (!defaultAccount) {
return false
Expand All @@ -190,6 +205,28 @@ export default function SessionKeyList() {
return false
}

const [confirmDeleteDialog, setConfirmDeleteDialog] = useState<{
open: boolean
authKey: string | undefined
}>({
open: false,
authKey: undefined,
})

const showConfirmDeleteDialog = (authKey: string) => {
setConfirmDeleteDialog({
open: true,
authKey: authKey,
})
}

const handleConfirmDeleteDialogClose = () => {
setConfirmDeleteDialog({
open: false,
authKey: undefined,
})
}

return (
<Grid item xs={12}>
<Card>
Expand Down Expand Up @@ -220,6 +257,32 @@ export default function SessionKeyList() {
message={error}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
/>
<Dialog
open={confirmDeleteDialog.open}
onClose={handleConfirmDeleteDialogClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{'Confirm Deletion'}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this Session Key? Once deleted, the user will be
logged out and this action cannot be undone.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleConfirmDeleteDialogClose} color="primary">
Cancel
</Button>
<Button
onClick={() => handleConfirmRemove(confirmDeleteDialog.authKey)}
color="primary"
autoFocus
>
Confirm
</Button>
</DialogActions>
</Dialog>
</CardContent>
</Card>
</Grid>
Expand Down
28 changes: 28 additions & 0 deletions sdk/typescript/src/account/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,32 @@ export class Account implements IAccount {

return result
}

/**
* Check session key whether expired
*
* @param authKey the auth key
*/
async isSessionKeyExpired(authKey: AccountAddress): Promise<boolean> {
const result = await this.provider.executeViewFunction(
'0x3::session_key::is_expired_session_key',
[],
[
{
type: 'Address',
value: this.address,
},
{
type: { Vector: 'U8' },
value: addressToSeqNumber(authKey),
},
],
)

if (result && result.vm_status !== 'Executed') {
throw new Error('view 0x3::session_key::is_expired_session_key fail')
}

return result.return_values![0].move_value as boolean
}
}
7 changes: 7 additions & 0 deletions sdk/typescript/src/account/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,11 @@ export interface IAccount {
* @param limit The page limit
*/
querySessionKeys(cursor: Bytes | null, limit: number): Promise<IPage<ISessionKey>>

/**
* Check session key whether expired
*
* @param authKey the auth key
*/
isSessionKeyExpired(authKey: AccountAddress): Promise<boolean>
}
Loading

0 comments on commit 22beffe

Please sign in to comment.