full role functionality
This commit is contained in:
parent
e44f170c6e
commit
186c2633d8
|
@ -82,6 +82,9 @@
|
|||
"name": "Name",
|
||||
"rules": "Rule count",
|
||||
"description": "Description",
|
||||
"actions": "Actions",
|
||||
"editbtn": "Edit",
|
||||
"delete": "Delete",
|
||||
"add": {
|
||||
"any": "Any",
|
||||
"name": "Role name",
|
||||
|
@ -105,7 +108,34 @@
|
|||
"role": "Allowed role",
|
||||
"desc": "Description",
|
||||
"add": "Add rule",
|
||||
"edit": "Edit rule",
|
||||
"edit": "Save edit",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"any": "Any",
|
||||
"name": "Role name",
|
||||
"desc": "Role description",
|
||||
"button": "Save",
|
||||
"cancel": "Cancel",
|
||||
"rules": "Inbound firewall rules",
|
||||
"rulesexplainer": "Inbound traffic is denied by default. Add rules to allow traffic from hosts belonging to specific roles.",
|
||||
"rulescols": {
|
||||
"description": "Description",
|
||||
"protocol": "Protocol",
|
||||
"portrange": "Port range",
|
||||
"allowedrole": "Allowed role"
|
||||
},
|
||||
"ruleremove": "Remove rule",
|
||||
"ruleedit": "Edit rule",
|
||||
"rulesadd": "Add rule",
|
||||
"editrule": {
|
||||
"protocol": "Protocol",
|
||||
"range": "Port or port range",
|
||||
"role": "Allowed role",
|
||||
"desc": "Description",
|
||||
"add": "Add rule",
|
||||
"edit": "Save edit",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,23 @@
|
|||
async function roleAdd() {
|
||||
window.location.href = "/roles/add";
|
||||
}
|
||||
|
||||
async function roleDelete(id: string) {
|
||||
const configuration = new Configuration({
|
||||
basePath: PUBLIC_BASE_URL,
|
||||
accessToken: window.localStorage.getItem("session") + " " + window.localStorage.getItem("mfa")
|
||||
});
|
||||
|
||||
const rolesApi = new RolesApi(configuration);
|
||||
|
||||
await rolesApi.roleDelete(
|
||||
{
|
||||
roleID: id
|
||||
}
|
||||
);
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -98,12 +115,21 @@
|
|||
<th>{$t("roles.name")}</th>
|
||||
<th>{$t("roles.rules")}</th>
|
||||
<th>{$t("roles.description")}</th>
|
||||
<th>{$t("roles.actions")}</th>
|
||||
</tr>
|
||||
{#each roles.data as role}
|
||||
<tr>
|
||||
<td>{role.name}</td>
|
||||
<td>
|
||||
<a href="/roles/{role.id}/edit">
|
||||
{role.name}
|
||||
</a>
|
||||
</td>
|
||||
<td>{role.firewallRules.length}</td>
|
||||
<td>{role.description}</td>
|
||||
<td>
|
||||
<button on:click={() => {window.location.href = `/roles/${role.id}/edit`}}>{$t("roles.editbtn")}</button>
|
||||
<button on:click={() => {roleDelete(role.id)}}>{$t("roles.delete")}</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
<script lang="ts">
|
||||
import {isLoading, t} from "svelte-i18n";
|
||||
import LoadingWrapper from "$components/LoadingWrapper.svelte";
|
||||
import {onMount} from "svelte";
|
||||
import {APIResult, isAuthedMFA, isAuthedSession} from "$lib/auth.ts";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import type {APIError} from "$lib/auth.ts";
|
||||
import {PUBLIC_BASE_URL} from "$env/static/public";
|
||||
import {Configuration, NetworksApi, RolesApi, FirewallRuleProtocolEnum} from "$lib/api";
|
||||
import type {FirewallRule} from "$lib/api";
|
||||
import AdminBar from "$components/AdminBar.svelte";
|
||||
import {page} from "$app/stores";
|
||||
|
||||
let loading = true;
|
||||
let isError = false;
|
||||
let error = '';
|
||||
$: currentlyLoading = $isLoading || loading;
|
||||
|
||||
logSetup();
|
||||
let logger = new Logger("roles/edit/+page.svelte");
|
||||
|
||||
let roles;
|
||||
let role;
|
||||
let roleName = '';
|
||||
let roleDescription = '';
|
||||
|
||||
|
||||
enum RuleProtocol {
|
||||
ANY = 0,
|
||||
TCP = 1,
|
||||
UDP = 2,
|
||||
ICMP = 3
|
||||
}
|
||||
|
||||
function protoToStringName(proto: RuleProtocol): string {
|
||||
console.log(`proto: ${proto}`);
|
||||
if (proto == RuleProtocol.ANY) {
|
||||
return $t("roles.edit.any");
|
||||
} else if (proto == RuleProtocol.TCP) {
|
||||
return "TCP";
|
||||
} else if (proto == RuleProtocol.UDP) {
|
||||
return "UDP";
|
||||
} else if (proto == RuleProtocol.ICMP) {
|
||||
return "ICMP";
|
||||
}
|
||||
}
|
||||
|
||||
interface Rule {
|
||||
description: string,
|
||||
protocol: RuleProtocol,
|
||||
portRange: null | number | [number, number]
|
||||
allowedRole: string | null
|
||||
}
|
||||
|
||||
function prettyPortRange(range: null | number | [number, number]): string {
|
||||
if (range === null) {
|
||||
return $t("roles.edit.any");
|
||||
} else if (typeof range === 'number') {
|
||||
return String(range);
|
||||
} else {
|
||||
return String(range[0]) + "-" + String(range[1])
|
||||
}
|
||||
}
|
||||
|
||||
function findRole(role: string): string {
|
||||
if (role === null) {
|
||||
return $t("roles.edit.any");
|
||||
}
|
||||
for (let i = 0; i < roles.data.length; i++) {
|
||||
if (roles.data[i].id == role) {
|
||||
return roles.data[i].name;
|
||||
}
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
let rules: Rule[] = [];
|
||||
|
||||
function removeRule(rule: Rule) {
|
||||
let newRules: Rule[] = [];
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
if (rules[i] != rule) {
|
||||
newRules.push(rules[i]);
|
||||
}
|
||||
}
|
||||
rules = newRules;
|
||||
}
|
||||
|
||||
let isEditingRule = false;
|
||||
let editingRuleDesc = "";
|
||||
let editingRuleProtocol = RuleProtocol.ANY;
|
||||
let editingRulePortRange = "any";
|
||||
let editingRuleAllowedRole = "any";
|
||||
let editingExistingRule = false;
|
||||
let editingExistingTheRule = null;
|
||||
|
||||
$: allowClickingOtherStuff = isEditingRule;
|
||||
|
||||
function editRule(rule: Rule) {
|
||||
if (isEditingRule) return;
|
||||
|
||||
isEditingRule = true;
|
||||
editingRuleDesc = rule.description;
|
||||
editingRuleProtocol = rule.protocol;
|
||||
editingRulePortRange = prettyPortRange(rule.portRange);
|
||||
editingRuleAllowedRole = (rule.allowedRole === null) ? "any" : rule.allowedRole;
|
||||
editingExistingRule = true;
|
||||
editingExistingTheRule = rule;
|
||||
}
|
||||
|
||||
function addRule() {
|
||||
if (isEditingRule) return;
|
||||
isEditingRule = true;
|
||||
editingRuleDesc = "";
|
||||
editingRuleProtocol = RuleProtocol.ANY;
|
||||
editingRulePortRange = "any";
|
||||
editingRuleAllowedRole = "any";
|
||||
editingExistingRule = false;
|
||||
editingExistingTheRule = null;
|
||||
}
|
||||
|
||||
function finishEdit() {
|
||||
if (!isEditingRule) return;
|
||||
|
||||
// parse port range
|
||||
let portRange;
|
||||
if (editingRulePortRange == "any" || editingRulePortRange == "Any") {
|
||||
portRange = null;
|
||||
} else {
|
||||
let split = editingRulePortRange.split("-");
|
||||
if (split.length == 1) {
|
||||
portRange = parseInt(split[0]);
|
||||
} else {
|
||||
portRange = [parseInt(split[0]), parseInt(split[1])];
|
||||
}
|
||||
}
|
||||
|
||||
rules.push({
|
||||
allowedRole: editingRuleAllowedRole,
|
||||
description: editingRuleDesc,
|
||||
portRange: portRange,
|
||||
protocol: editingRuleProtocol
|
||||
});
|
||||
|
||||
if (editingExistingRule) {
|
||||
removeRule(editingExistingTheRule);
|
||||
}
|
||||
|
||||
isEditingRule = false;
|
||||
editingRuleDesc = "";
|
||||
editingRuleProtocol = RuleProtocol.ANY;
|
||||
editingRulePortRange = "any";
|
||||
editingRuleAllowedRole = "any";
|
||||
editingExistingRule = false;
|
||||
editingExistingTheRule = null;
|
||||
}
|
||||
|
||||
function cancelEdit() {
|
||||
if (!isEditingRule) return;
|
||||
isEditingRule = false;
|
||||
editingRuleDesc = "";
|
||||
editingRuleProtocol = RuleProtocol.ANY;
|
||||
editingRulePortRange = "any";
|
||||
editingRuleAllowedRole = "any";
|
||||
editingExistingRule = false;
|
||||
editingExistingTheRule = null;
|
||||
}
|
||||
|
||||
function convertRule(rule: Rule): FirewallRule {
|
||||
let protocol;
|
||||
if (rule.protocol == RuleProtocol.ANY) {
|
||||
protocol = FirewallRuleProtocolEnum.Any;
|
||||
} else if (rule.protocol == RuleProtocol.TCP) {
|
||||
protocol = FirewallRuleProtocolEnum.Tcp;
|
||||
} else if (rule.protocol == RuleProtocol.UDP) {
|
||||
protocol = FirewallRuleProtocolEnum.Udp;
|
||||
} else if (rule.protocol == RuleProtocol.ICMP) {
|
||||
protocol = FirewallRuleProtocolEnum.Icmp;
|
||||
}
|
||||
|
||||
let allowedRole = undefined;
|
||||
|
||||
if (rule.allowedRole == null) {
|
||||
allowedRole = undefined;
|
||||
} else {
|
||||
allowedRole = rule.allowedRole;
|
||||
}
|
||||
|
||||
let portRange;
|
||||
|
||||
if (typeof rule.portRange === "number") {
|
||||
portRange = {
|
||||
from: rule.portRange,
|
||||
to: rule.portRange
|
||||
}
|
||||
} else if (rule.portRange === null) {
|
||||
portRange = undefined;
|
||||
} else {
|
||||
portRange = {
|
||||
from: rule.portRange[0],
|
||||
to: rule.portRange[1]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
protocol: protocol,
|
||||
description: rule.description,
|
||||
allowedRoleID: allowedRole,
|
||||
portRange: portRange
|
||||
};
|
||||
}
|
||||
|
||||
async function roleEdit() {
|
||||
const configuration = new Configuration({
|
||||
basePath: PUBLIC_BASE_URL,
|
||||
accessToken: window.localStorage.getItem("session") + " " + window.localStorage.getItem("mfa")
|
||||
});
|
||||
|
||||
const rolesApi = new RolesApi(configuration);
|
||||
|
||||
let apirules = [];
|
||||
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
apirules.push(convertRule(rules[i]));
|
||||
}
|
||||
|
||||
await rolesApi.roleEdit(
|
||||
{
|
||||
roleID: $page.params.role_id,
|
||||
roleEditRequest: {
|
||||
name: roleName,
|
||||
description: roleDescription,
|
||||
firewallRules: apirules
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
window.location.href = "/roles"
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
let session_load_info = await isAuthedSession();
|
||||
if (session_load_info[0] == APIResult.Failed) {
|
||||
let err = session_load_info[1] as APIError;
|
||||
logger.error(`session load failed: ${err.code} ${err.message}`);
|
||||
window.location.href = '/login';
|
||||
return;
|
||||
}
|
||||
|
||||
let mfa_load_info = await isAuthedMFA();
|
||||
if (mfa_load_info[0] == APIResult.Failed) {
|
||||
let err = mfa_load_info[1] as APIError;
|
||||
logger.error(`mfa load failed: ${err.code} ${err.message}`);
|
||||
window.location.href = '/2fa';
|
||||
return;
|
||||
}
|
||||
|
||||
// pull networks
|
||||
const configuration = new Configuration({
|
||||
basePath: PUBLIC_BASE_URL,
|
||||
accessToken: window.localStorage.getItem("session") + " " + window.localStorage.getItem("mfa")
|
||||
});
|
||||
|
||||
const networksApi = new NetworksApi(configuration);
|
||||
let networks;
|
||||
try {
|
||||
networks = await networksApi.networksList();
|
||||
} catch (e) {
|
||||
let resp_json = await e.response.json();
|
||||
if (resp_json.errors[0].code == "ERR_NO_ORG") {
|
||||
window.location.href = "/networkcreate";
|
||||
return;
|
||||
} else {
|
||||
isError = true;
|
||||
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log(networks);
|
||||
|
||||
if (networks.data?.length == 0) {
|
||||
window.location.href = '/networkcreate';
|
||||
return;
|
||||
}
|
||||
|
||||
const rolesApi = new RolesApi(configuration);
|
||||
roles = await rolesApi.rolesList();
|
||||
|
||||
// pull our role
|
||||
role = await rolesApi.roleGet({
|
||||
roleID: $page.params.role_id
|
||||
});
|
||||
|
||||
roleName = role.data.name;
|
||||
roleDescription = role.data.description;
|
||||
|
||||
for (let i = 0; i < role.data.firewallRules.length; i++) {
|
||||
let rule = role.data.firewallRules[i];
|
||||
|
||||
let portRange;
|
||||
|
||||
if (rule.portRange === undefined) {
|
||||
portRange = null;
|
||||
} else if (rule.portRange.from == rule.portRange.to) {
|
||||
portRange = rule.portRange.from;
|
||||
} else {
|
||||
portRange = [rule.portRange.from, rule.portRange.to];
|
||||
}
|
||||
|
||||
let protocol;
|
||||
|
||||
if (rule.protocol === "ANY") {
|
||||
protocol = RuleProtocol.ANY;
|
||||
} else if (rule.protocol === "TCP") {
|
||||
protocol = RuleProtocol.TCP;
|
||||
} else if (rule.protocol === "UDP") {
|
||||
protocol = RuleProtocol.UDP;
|
||||
} else if (rule.protocol === "ICMP") {
|
||||
protocol = RuleProtocol.ICMP;
|
||||
}
|
||||
|
||||
let allowedRole;
|
||||
|
||||
if (rule.allowedRoleID === undefined) {
|
||||
allowedRole = null;
|
||||
} else {
|
||||
allowedRole = rule.allowedRoleID;
|
||||
}
|
||||
|
||||
rules.push({
|
||||
description: rule.description,
|
||||
protocol: protocol,
|
||||
portRange: portRange,
|
||||
allowedRole: allowedRole
|
||||
});
|
||||
}
|
||||
console.log(role);
|
||||
|
||||
loading = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{$t("common.title", {values: {title: $t("common.page.roles")}})}</title>
|
||||
</svelte:head>
|
||||
|
||||
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
|
||||
<AdminBar selected="roles" />
|
||||
|
||||
<form on:submit|preventDefault={roleEdit}>
|
||||
<label for="roleName">{$t("roles.edit.name")}</label>
|
||||
<input disabled bind:value={roleName} type="text" id="roleName" />
|
||||
<label for="roleDesc">{$t("roles.edit.desc")}</label>
|
||||
<input bind:value={roleDescription} type="text" id="roleDesc" />
|
||||
<h3>{$t("roles.edit.rules")}</h3>
|
||||
<p>{$t("roles.edit.rulesexplainer")}</p>
|
||||
|
||||
<!-- firewall rules -->
|
||||
<table>
|
||||
<tr>
|
||||
<th>{$t("roles.edit.rulescols.description")}</th>
|
||||
<th>{$t("roles.edit.rulescols.protocol")}</th>
|
||||
<th>{$t("roles.edit.rulescols.portrange")}</th>
|
||||
<th>{$t("roles.edit.rulescols.allowedrole")}</th>
|
||||
</tr>
|
||||
{#each rules as rule}
|
||||
<tr>
|
||||
<td>{rule.description}</td>
|
||||
<td>{protoToStringName(rule.protocol)}</td>
|
||||
<td>{prettyPortRange(rule.portRange)}</td>
|
||||
<td>{findRole(rule.allowedRole)}</td>
|
||||
<button disabled="{allowClickingOtherStuff}" on:click={() => {editRule(rule)}}>{$t("roles.edit.ruleedit")}</button>
|
||||
<button disabled="{allowClickingOtherStuff}" on:click={() => {removeRule(rule)}}>{$t("roles.edit.ruleremove")}</button>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
|
||||
<button disabled="{allowClickingOtherStuff}" on:click={() => {addRule()}}>{$t("roles.edit.rulesadd")}</button>
|
||||
|
||||
{#if isEditingRule}
|
||||
<form on:submit|preventDefault>
|
||||
<label for="ruleProtocol">{$t("roles.edit.editrule.protocol")}</label>
|
||||
<select id="ruleProtocol" bind:value="{editingRuleProtocol}">
|
||||
<option value="{RuleProtocol.ANY}">{$t("roles.edit.any")}</option>
|
||||
<option value="{RuleProtocol.TCP}">TCP</option>
|
||||
<option value="{RuleProtocol.UDP}">UDP</option>
|
||||
<option value="{RuleProtocol.ICMP}">ICMP</option>
|
||||
</select>
|
||||
<label for="rulePortRange">{$t("roles.edit.editrule.range")}</label>
|
||||
<input type="text" bind:value="{editingRulePortRange}" id="rulePortRange"/>
|
||||
<label for="ruleAllowedRole">{$t("roles.edit.editrule.role")}</label>
|
||||
<select id="ruleAllowedRole" bind:value="{editingRuleAllowedRole}">
|
||||
<option value="any">{$t("roles.edit.any")}</option>
|
||||
{#each roles.data as role}
|
||||
<option value="{role.id}">{role.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<label for="ruleDescription">{$t("roles.edit.editrule.desc")}</label>
|
||||
<input type="text" bind:value="{editingRuleDesc}" id="ruleDescription"/>
|
||||
<button on:click|preventDefault={finishEdit}>{editingExistingRule ? $t("roles.edit.editrule.edit") : $t("roles.edit.editrule.add")}</button>
|
||||
<button on:click|preventDefault={cancelEdit}>{$t("roles.edit.editrule.cancel")}</button>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<br/>
|
||||
|
||||
<button disabled="{allowClickingOtherStuff}">{$t("roles.edit.button")}</button>
|
||||
<button disabled="{allowClickingOtherStuff}" on:click|preventDefault={() => {window.location.href = "/roles";}}>{$t("roles.edit.cancel")}</button>
|
||||
</form>
|
||||
</LoadingWrapper>
|
|
@ -301,7 +301,7 @@
|
|||
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
|
||||
<AdminBar selected="roles" />
|
||||
|
||||
<form on:submit|preventDefault={roleAdd}>
|
||||
<form>
|
||||
<label for="roleName">{$t("roles.add.name")}</label>
|
||||
<input bind:value={roleName} type="text" id="roleName" />
|
||||
<label for="roleDesc">{$t("roles.add.desc")}</label>
|
||||
|
@ -358,7 +358,7 @@
|
|||
|
||||
<br/>
|
||||
|
||||
<button disabled="{allowClickingOtherStuff}">{$t("roles.add.button")}</button>
|
||||
<button on:click={roleAdd} disabled="{allowClickingOtherStuff}">{$t("roles.add.button")}</button>
|
||||
<button disabled="{allowClickingOtherStuff}" on:click|preventDefault={() => {window.location.href = "/roles";}}>{$t("roles.add.cancel")}</button>
|
||||
</form>
|
||||
</LoadingWrapper>
|
||||
|
|
Loading…
Reference in New Issue