mirror of
https://github.com/balzack/databag.git
synced 2025-02-15 04:59:16 +00:00
invoke mfa in browser
This commit is contained in:
parent
19248eee7c
commit
e5fe393b43
@ -35,6 +35,7 @@ func GetAccountStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
status.Disabled = account.Disabled
|
status.Disabled = account.Disabled
|
||||||
status.ForwardingAddress = account.Forward
|
status.ForwardingAddress = account.Forward
|
||||||
status.Searchable = account.Searchable
|
status.Searchable = account.Searchable
|
||||||
|
status.MFAEnabled = account.MFAEnabled && account.MFAConfirmed
|
||||||
status.Sealable = true
|
status.Sealable = true
|
||||||
status.EnableIce = getBoolConfigValue(CNFEnableIce, false)
|
status.EnableIce = getBoolConfigValue(CNFEnableIce, false)
|
||||||
status.AllowUnsealed = getBoolConfigValue(CNFAllowUnsealed, false)
|
status.AllowUnsealed = getBoolConfigValue(CNFAllowUnsealed, false)
|
||||||
|
@ -35,6 +35,8 @@ type AccountStatus struct {
|
|||||||
|
|
||||||
Searchable bool `json:"searchable"`
|
Searchable bool `json:"searchable"`
|
||||||
|
|
||||||
|
MFAEnabled bool `json:"mfaEnabled"`
|
||||||
|
|
||||||
PushEnabled bool `json:"pushEnabled"`
|
PushEnabled bool `json:"pushEnabled"`
|
||||||
|
|
||||||
Sealable bool `json:"sealable"`
|
Sealable bool `json:"sealable"`
|
||||||
|
8
net/web/src/api/addAccountMFA.js
Normal file
8
net/web/src/api/addAccountMFA.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function addAccountMFA(token) {
|
||||||
|
const mfa = await fetchWithTimeout(`/account/mfauth?agent=${token}`, { method: 'POST' })
|
||||||
|
checkResponse(mfa);
|
||||||
|
return mfa.json();
|
||||||
|
}
|
||||||
|
|
7
net/web/src/api/removeAccountMFA.js
Normal file
7
net/web/src/api/removeAccountMFA.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function removeAccountMFA(token) {
|
||||||
|
let res = await fetchWithTimeout(`/account/mfauth?agent=${token}`, { method: 'DELETE' })
|
||||||
|
checkResponse(res);
|
||||||
|
}
|
||||||
|
|
7
net/web/src/api/setAccountMFA.js
Normal file
7
net/web/src/api/setAccountMFA.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function setAccountMFA(token, code) {
|
||||||
|
let res = await fetchWithTimeout(`/account/mfauth?agent=${token}&code=${code}`, { method: 'PUT' })
|
||||||
|
checkResponse(res);
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,9 @@ import { setAccountSearchable } from 'api/setAccountSearchable';
|
|||||||
import { setAccountSeal } from 'api/setAccountSeal';
|
import { setAccountSeal } from 'api/setAccountSeal';
|
||||||
import { getAccountStatus } from 'api/getAccountStatus';
|
import { getAccountStatus } from 'api/getAccountStatus';
|
||||||
import { setAccountLogin } from 'api/setAccountLogin';
|
import { setAccountLogin } from 'api/setAccountLogin';
|
||||||
|
import { addAccountMFA } from 'api/addAccountMFA';
|
||||||
|
import { setAccountMFA } from 'api/setAccountMFA';
|
||||||
|
import { removeAccountMFA } from 'api/removeAccountMFA';
|
||||||
import { StoreContext } from './StoreContext';
|
import { StoreContext } from './StoreContext';
|
||||||
|
|
||||||
export function useAccountContext() {
|
export function useAccountContext() {
|
||||||
@ -72,6 +75,17 @@ export function useAccountContext() {
|
|||||||
setSearchable: async (flag) => {
|
setSearchable: async (flag) => {
|
||||||
await setAccountSearchable(access.current, flag);
|
await setAccountSearchable(access.current, flag);
|
||||||
},
|
},
|
||||||
|
enableMFA: async () => {
|
||||||
|
const secret = await addAccountMFA(access.current);
|
||||||
|
console.log("SECRET ", secret);
|
||||||
|
return secret;
|
||||||
|
},
|
||||||
|
disableMFA: async () => {
|
||||||
|
await removeAccountMFA(access.current);
|
||||||
|
},
|
||||||
|
confirmMFA: async (code) => {
|
||||||
|
await setAccountMFA(access.current, code);
|
||||||
|
},
|
||||||
setSeal: async (seal, sealKey) => {
|
setSeal: async (seal, sealKey) => {
|
||||||
await setAccountSeal(access.current, seal);
|
await setAccountSeal(access.current, seal);
|
||||||
await storeContext.actions.setValue("sealKey", sealKey);
|
await storeContext.actions.setValue("sealKey", sealKey);
|
||||||
|
@ -39,6 +39,26 @@ export function AccountAccess() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enableMFA = async (enable) => {
|
||||||
|
console.log("ENABLE: ", enable);
|
||||||
|
try {
|
||||||
|
if (enable) {
|
||||||
|
await actions.enableMFA();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await actions.disableMFA();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
modal.error({
|
||||||
|
title: <span style={state.menuStyle}>{state.strings.operationFailed}</span>,
|
||||||
|
content: <span style={state.menuStyle}>{state.strings.tryAgain}</span>,
|
||||||
|
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const saveLogin = async () => {
|
const saveLogin = async () => {
|
||||||
try {
|
try {
|
||||||
await actions.setLogin();
|
await actions.setLogin();
|
||||||
@ -84,6 +104,12 @@ export function AccountAccess() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="switchLabel">{state.strings.registry}</div>
|
<div className="switchLabel">{state.strings.registry}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="switch">
|
||||||
|
<div className="control">
|
||||||
|
<Switch size="small" checked={state.mfaEnabled} onChange={enable => enableMFA(enable)} />
|
||||||
|
</div>
|
||||||
|
<div className="switchLabel">Multi-Factor Authentication</div>
|
||||||
|
</div>
|
||||||
<div className="link" onClick={actions.setEditSeal}>
|
<div className="link" onClick={actions.setEditSeal}>
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<SettingOutlined />
|
<SettingOutlined />
|
||||||
@ -238,6 +264,9 @@ export function AccountAccess() {
|
|||||||
</div>
|
</div>
|
||||||
</LoginModal>
|
</LoginModal>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal centerd closable={false} footer={null} visible={state.mfaModal} bodyStyle={{ borderRadius: 8, padding: 16, ...state.menuStyle }} onCancel={actions.dismissMFA}>
|
||||||
|
<div>{ state.mfaSecret }</div>
|
||||||
|
</Modal>
|
||||||
</AccountAccessWrapper>
|
</AccountAccessWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,11 @@ export function useAccountAccess() {
|
|||||||
videoId: null,
|
videoId: null,
|
||||||
videoInputs: [],
|
videoInputs: [],
|
||||||
|
|
||||||
|
mfaModal: false,
|
||||||
|
mfaEnabled: null,
|
||||||
|
mfaSecret: null,
|
||||||
|
mfaCode: null,
|
||||||
|
|
||||||
seal: null,
|
seal: null,
|
||||||
sealKey: null,
|
sealKey: null,
|
||||||
});
|
});
|
||||||
@ -60,7 +65,7 @@ export function useAccountAccess() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { seal, sealKey, status } = account.state;
|
const { seal, sealKey, status } = account.state;
|
||||||
updateState({ searchable: status?.searchable, seal, sealKey });
|
updateState({ searchable: status?.searchable, mfaEnabled: status?.mfaEnabled, seal, sealKey });
|
||||||
}, [account.state]);
|
}, [account.state]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -307,6 +312,54 @@ export function useAccountAccess() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setCode: async (code) => {
|
||||||
|
updateState({ mfaCode: code });
|
||||||
|
},
|
||||||
|
enableMFA: async () => {
|
||||||
|
if (!state.busy) {
|
||||||
|
try {
|
||||||
|
updateState({ busy: true });
|
||||||
|
const secret = await account.actions.enableMFA();
|
||||||
|
updateState({ busy: false, mfaModal: true, mfaSecret: secret, mfaCode: '' });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
updateState({ busy: false });
|
||||||
|
throw new Error('faild to enable mfa');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disableMFA: async () => {
|
||||||
|
if (!state.busy) {
|
||||||
|
try {
|
||||||
|
updateState({ busy: true });
|
||||||
|
await account.actions.disableMFA();
|
||||||
|
updateState({ busy: false });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
updateState({ busy: false });
|
||||||
|
throw new Error('failed to disable mfa');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmMFA: async () => {
|
||||||
|
if (!state.busy) {
|
||||||
|
try {
|
||||||
|
updateState({ busy: true });
|
||||||
|
await account.actions.confirmMFA(state.code);
|
||||||
|
updateState({ busy: false });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
updateState({ busy: false });
|
||||||
|
throw new Error('failed to confirm mfa');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissMFA: async () => {
|
||||||
|
updateState({ mfaModal: false });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return { state, actions };
|
return { state, actions };
|
||||||
|
Loading…
Reference in New Issue
Block a user