Skip to content

Commit

Permalink
Add support for peer approval (#299)
Browse files Browse the repository at this point in the history
* add support for peer approval

* use gold color for tag

---------

Co-authored-by: Maycon Santos <[email protected]>
  • Loading branch information
pascal-fischer and mlsmaycon authored Dec 5, 2023
1 parent a208940 commit 0199ea8
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 6 deletions.
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

0 comments on commit 0199ea8

Please sign in to comment.