mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Add support for peer approval (#299)
* add support for peer approval * use gold color for tag --------- Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
This commit is contained in:
@@ -8,6 +8,9 @@ export interface Account {
|
||||
jwt_groups_enabled: boolean;
|
||||
groups_propagation_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
extra: {
|
||||
peer_approval_enabled: boolean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,4 +20,5 @@ export interface FormAccount extends Account {
|
||||
groups_propagation_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
peer_login_expiration_formatted: ExpiresInValue;
|
||||
peer_approval_enabled: boolean;
|
||||
}
|
||||
@@ -16,7 +16,8 @@ export interface Peer {
|
||||
dns_label: string,
|
||||
last_login: string,
|
||||
login_expired: boolean,
|
||||
login_expiration_enabled: boolean
|
||||
login_expiration_enabled: boolean,
|
||||
approval_required: boolean
|
||||
}
|
||||
|
||||
export interface FormPeer extends Peer {
|
||||
|
||||
@@ -338,6 +338,8 @@ export const Activity = () => {
|
||||
case "peer.login.expiration.enable":
|
||||
case "user.peer.login":
|
||||
case "peer.login.expire":
|
||||
case "peer.approve":
|
||||
case "peer.approval.revoke":
|
||||
return renderMultiRowSpan(event.meta.fqdn, event.meta.ip);
|
||||
case "route.add":
|
||||
case "route.delete":
|
||||
@@ -387,6 +389,8 @@ export const Activity = () => {
|
||||
case "account.setting.peer.login.expiration.enable":
|
||||
case "account.setting.peer.login.expiration.disable":
|
||||
case "account.setting.peer.login.expiration.update":
|
||||
case "account.setting.peer.approval.enable":
|
||||
case "account.setting.peer.approval.disable":
|
||||
case "integration.create":
|
||||
case "integration.update":
|
||||
case "integration.delete":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { capitalize, formatOS, timeAgo } from "../utils/common";
|
||||
import {capitalize, formatOS, isLocalDev, isNetBirdHosted, timeAgo} from "../utils/common";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import { actions as peerActions } from "../store/peer";
|
||||
@@ -79,6 +79,7 @@ export const Peers = () => {
|
||||
(state: RootState) => state.peer.updateGroupsVisible
|
||||
);
|
||||
const users = useSelector((state: RootState) => state.user.data);
|
||||
const account = useSelector((state: RootState) => state.account.data);
|
||||
const [addPeerModalOpen, setAddPeerModalOpen] = useState(false);
|
||||
const { oidcUser } = useOidcUser();
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
@@ -100,6 +101,7 @@ export const Peers = () => {
|
||||
const optionsOnOff = [
|
||||
{ label: "Online", value: "on" },
|
||||
{ label: "All", value: "all" },
|
||||
// ...((isNetBirdHosted() || isLocalDev()) && account[0].settings.extra.peer_approval_enabled ? [{ label: "Needs approval", value: "approval" }] : []),
|
||||
];
|
||||
|
||||
const transformDataTable = (d: Peer[]): PeerDataTable[] => {
|
||||
@@ -291,6 +293,16 @@ export const Peers = () => {
|
||||
);
|
||||
}) as Peer[];
|
||||
|
||||
// switch (optionOnOff) {
|
||||
// case "on":
|
||||
// f = filter(f, (f: Peer) => f.connected);
|
||||
// break;
|
||||
// case "approval":
|
||||
// f = filter(f, (f: Peer) => f.approval_required);
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
if (optionOnOff === "on") {
|
||||
f = filter(f, (f: Peer) => f.connected);
|
||||
}
|
||||
@@ -410,6 +422,36 @@ export const Peers = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const showConfirmApprove = (record: PeerDataTable) => {
|
||||
setPeerToAction(record);
|
||||
|
||||
let content = (
|
||||
<Paragraph>
|
||||
Are you sure you want to approve this peer?
|
||||
</Paragraph>
|
||||
);
|
||||
|
||||
let name = record ? record.name : "";
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
title: <span className="font-500">Approve peer {name}</span>,
|
||||
width: 600,
|
||||
content: content,
|
||||
onOk() {
|
||||
record.approval_required = false
|
||||
dispatch(
|
||||
peerActions.updatePeer.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: record,
|
||||
})
|
||||
);
|
||||
},
|
||||
onCancel() {
|
||||
setPeerToAction(null);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const showConfirmEnableSSH = (record: PeerDataTable) => {
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
@@ -601,6 +643,19 @@ export const Peers = () => {
|
||||
""
|
||||
);
|
||||
|
||||
let approval = peer.approval_required ? (
|
||||
<Tooltip title="The peer needs to be approved by an administrator before it can connect to other peers">
|
||||
<Tag color="gold">
|
||||
<Text
|
||||
style={{ fontSize: "10px", color: "rgba(212, 136, 6, 1)" }}
|
||||
type={"secondary"}
|
||||
>needs approval</Text>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
const userEmail = users?.find((u) => u.id === peer.user_id)?.email;
|
||||
let expiry = !peer.login_expiration_enabled ? (
|
||||
<Tag>
|
||||
@@ -622,9 +677,9 @@ export const Peers = () => {
|
||||
<Row>
|
||||
<Text className="font-500"> {status}</Text>
|
||||
</Row>
|
||||
<Row>{loginExpire}</Row>
|
||||
</span>
|
||||
</Button>
|
||||
<Row>{loginExpire}</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -643,11 +698,18 @@ export const Peers = () => {
|
||||
<Row>
|
||||
<Text type="secondary">{userEmail}</Text>
|
||||
</Row>
|
||||
<Row style={{ minWidth: "195px" }}>
|
||||
{expiry} {loginExpire}
|
||||
</Row>
|
||||
</span>
|
||||
</Button>
|
||||
<Row style={{ minWidth: "195px", paddingLeft: "15px" }}>
|
||||
{expiry} {loginExpire} <Button
|
||||
type="text"
|
||||
style={{ height: "auto", whiteSpace: "normal", textAlign: "center", padding: "0px", margin: "0px" }}
|
||||
onClick={() => showConfirmApprove(peer)}
|
||||
className={!isAdmin ? "nohover" : ""}
|
||||
>
|
||||
{approval}
|
||||
</Button>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -127,6 +127,8 @@ export const Settings = () => {
|
||||
|
||||
const [formPeerExpirationEnabled, setFormPeerExpirationEnabled] =
|
||||
useState(true);
|
||||
const [formPeerApprovalEnabled, setFormPeerApprovalEnabled] =
|
||||
useState(false);
|
||||
const [jwtGroupsEnabled, setJwtGroupsEnabled] = useState(true);
|
||||
const [groupsPropagationEnabled, setGroupsPropagationEnabled] =
|
||||
useState(true);
|
||||
@@ -221,9 +223,11 @@ export const Settings = () => {
|
||||
jwt_groups_enabled: account.settings.jwt_groups_enabled,
|
||||
jwt_groups_claim_name: account.settings.jwt_groups_claim_name,
|
||||
groups_propagation_enabled: account.settings.groups_propagation_enabled,
|
||||
peer_approval_enabled: account.settings.extra ? account.settings.extra.peer_approval_enabled : false,
|
||||
} as FormAccount;
|
||||
setFormAccount(fAccount);
|
||||
setFormPeerExpirationEnabled(fAccount.peer_login_expiration_enabled);
|
||||
setFormPeerApprovalEnabled(fAccount.peer_approval_enabled);
|
||||
setJwtGroupsEnabled(fAccount.jwt_groups_enabled);
|
||||
setGroupsPropagationEnabled(fAccount.groups_propagation_enabled);
|
||||
setJwtGroupsClaimName(fAccount.jwt_groups_claim_name);
|
||||
@@ -394,6 +398,7 @@ export const Settings = () => {
|
||||
updatedAccount.data.settings.jwt_groups_claim_name,
|
||||
groups_propagation_enabled:
|
||||
updatedAccount.data.settings.groups_propagation_enabled,
|
||||
peer_approval_enabled: updatedAccount.data.settings.extra.peer_approval_enabled
|
||||
} as FormAccount;
|
||||
setFormAccount(fAccount);
|
||||
} else if (updatedAccount.error) {
|
||||
@@ -428,6 +433,7 @@ export const Settings = () => {
|
||||
jwt_groups_enabled: jwtGroupsEnabled,
|
||||
jwt_groups_claim_name: jwtGroupsClaimName,
|
||||
groups_propagation_enabled: groupsPropagationEnabled,
|
||||
peer_approval_enabled: formPeerApprovalEnabled,
|
||||
});
|
||||
})
|
||||
.catch((errorInfo) => {
|
||||
@@ -453,6 +459,9 @@ export const Settings = () => {
|
||||
jwt_groups_enabled: jwtGroupsEnabled,
|
||||
jwt_groups_claim_name: jwtGroupsClaimName,
|
||||
groups_propagation_enabled: groupsPropagationEnabled,
|
||||
extra: {
|
||||
peer_approval_enabled: values.peer_approval_enabled
|
||||
}
|
||||
},
|
||||
} as Account;
|
||||
};
|
||||
@@ -624,6 +633,59 @@ export const Settings = () => {
|
||||
<div className={groupsClicked ? "d-none" : ""}>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
{(isNetBirdHosted() || isLocalDev()) && <Form.Item name="peer_approval_enabled" label="">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
onChange={(checked) => {
|
||||
setFormPeerApprovalEnabled(checked);
|
||||
}}
|
||||
size="small"
|
||||
checked={formPeerApprovalEnabled}
|
||||
/>
|
||||
<div>
|
||||
<label
|
||||
style={{
|
||||
color: "rgba(0, 0, 0, 0.88)",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Peer approval{" "}
|
||||
<Tooltip
|
||||
title="Peer approval requires that every newly added peer
|
||||
will require approval by an administrator before it can connect to other peers.
|
||||
You can approve peers in the peers tab."
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: "5px",
|
||||
fontSize: "14px",
|
||||
color: "#bdbdbe",
|
||||
}}
|
||||
type={"secondary"}
|
||||
>
|
||||
<QuestionCircleFilled />
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<Paragraph
|
||||
type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-2",
|
||||
fontWeight: "400",
|
||||
marginBottom: "0",
|
||||
}}
|
||||
>
|
||||
Require peers to be approved by an administrator
|
||||
</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Item>}
|
||||
<Form.Item name="peer_login_expiration_enabled" label="">
|
||||
<div
|
||||
style={{
|
||||
|
||||
Reference in New Issue
Block a user