full role functionality
This commit is contained in:
parent
e44f170c6e
commit
186c2633d8
|
@ -82,6 +82,9 @@
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"rules": "Rule count",
|
"rules": "Rule count",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
|
"actions": "Actions",
|
||||||
|
"editbtn": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
"add": {
|
"add": {
|
||||||
"any": "Any",
|
"any": "Any",
|
||||||
"name": "Role name",
|
"name": "Role name",
|
||||||
|
@ -105,7 +108,34 @@
|
||||||
"role": "Allowed role",
|
"role": "Allowed role",
|
||||||
"desc": "Description",
|
"desc": "Description",
|
||||||
"add": "Add rule",
|
"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"
|
"cancel": "Cancel"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,23 @@
|
||||||
async function roleAdd() {
|
async function roleAdd() {
|
||||||
window.location.href = "/roles/add";
|
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>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -98,12 +115,21 @@
|
||||||
<th>{$t("roles.name")}</th>
|
<th>{$t("roles.name")}</th>
|
||||||
<th>{$t("roles.rules")}</th>
|
<th>{$t("roles.rules")}</th>
|
||||||
<th>{$t("roles.description")}</th>
|
<th>{$t("roles.description")}</th>
|
||||||
|
<th>{$t("roles.actions")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each roles.data as role}
|
{#each roles.data as role}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{role.name}</td>
|
<td>
|
||||||
|
<a href="/roles/{role.id}/edit">
|
||||||
|
{role.name}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
<td>{role.firewallRules.length}</td>
|
<td>{role.firewallRules.length}</td>
|
||||||
<td>{role.description}</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>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</table>
|
</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}>
|
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
|
||||||
<AdminBar selected="roles" />
|
<AdminBar selected="roles" />
|
||||||
|
|
||||||
<form on:submit|preventDefault={roleAdd}>
|
<form>
|
||||||
<label for="roleName">{$t("roles.add.name")}</label>
|
<label for="roleName">{$t("roles.add.name")}</label>
|
||||||
<input bind:value={roleName} type="text" id="roleName" />
|
<input bind:value={roleName} type="text" id="roleName" />
|
||||||
<label for="roleDesc">{$t("roles.add.desc")}</label>
|
<label for="roleDesc">{$t("roles.add.desc")}</label>
|
||||||
|
@ -358,7 +358,7 @@
|
||||||
|
|
||||||
<br/>
|
<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>
|
<button disabled="{allowClickingOtherStuff}" on:click|preventDefault={() => {window.location.href = "/roles";}}>{$t("roles.add.cancel")}</button>
|
||||||
</form>
|
</form>
|
||||||
</LoadingWrapper>
|
</LoadingWrapper>
|
||||||
|
|
Loading…
Reference in New Issue