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:
pascal-fischer
2023-12-05 13:17:40 +01:00
committed by GitHub
parent a20894092b
commit 0199ea81f3
5 changed files with 139 additions and 6 deletions

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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":

View File

@@ -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>
);
};

View File

@@ -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={{