Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for peer approval #299

Merged
merged 3 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/store/account/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
}

Expand All @@ -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;
}
3 changes: 2 additions & 1 deletion src/store/peer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions src/views/Activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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":
Expand Down
72 changes: 67 additions & 5 deletions src/views/Peers.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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);
Expand All @@ -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[] => {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 />,
Expand Down Expand Up @@ -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>
Expand All @@ -622,9 +677,9 @@ export const Peers = () => {
<Row>
<Text className="font-500"> {status}</Text>
</Row>
<Row>{loginExpire}</Row>
</span>
</Button>
<Row>{loginExpire}</Row>
</>
);
}
Expand All @@ -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>
);
};
Expand Down
62 changes: 62 additions & 0 deletions src/views/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) => {
Expand All @@ -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;
};
Expand Down Expand Up @@ -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={{
Expand Down