"some" wokr on roles (this is last major roadblock to milestone:feat-org-keys)
This commit is contained in:
parent
cde5b73907
commit
39978c3579
|
@ -0,0 +1,44 @@
|
|||
<script lang="ts">
|
||||
export let selected;
|
||||
export let orgid;
|
||||
|
||||
let hostsstyle = (selected == "hosts" ? "dark:bg-slate-600 bg-slate-200" : "dark:hover:bg-slate-600 hover:bg-slate-200");
|
||||
let lighthousestyle = (selected == "lighthouses" ? "dark:bg-slate-600 bg-slate-200" : "dark:hover:bg-slate-600 hover:bg-slate-200");
|
||||
let relaysstyle = (selected == "relays" ? "dark:bg-slate-600 bg-slate-200" : "dark:hover:bg-slate-600 hover:bg-slate-200");
|
||||
let rolesstyle = (selected == "roles" ? "dark:bg-slate-600 bg-slate-200" : "dark:hover:bg-slate-600 hover:bg-slate-200");
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen flex flex-row overflow-y-hidden">
|
||||
<div class="max-h flex flex-col w-56 overflow-hidden border-r-2 border-slate-500">
|
||||
<ul class="flex flex-col py-4">
|
||||
<li>
|
||||
<a href="/org/{orgid}/hosts" class="{hostsstyle} mt-2 flex flex-row items-center h-12 transition">
|
||||
<span class="ml-10 text-sm font-medium">Hosts</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/org/{orgid}/lighthouses" class="{lighthousestyle} mt-2 flex flex-row items-center h-12 dark:hover:bg-slate-600 hover:bg-slate-200 transition">
|
||||
<span class="ml-10 text-sm font-medium">Lighthouses</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/org/{orgid}/relays" class="{relaysstyle} mt-2 flex flex-row items-center h-12 dark:hover:bg-slate-600 hover:bg-slate-200 transition">
|
||||
<span class="ml-10 text-sm font-medium">Relays</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/org/{orgid}/roles" class="{rolesstyle} mt-2 flex flex-row items-center h-12 dark:hover:bg-slate-600 hover:bg-slate-200 transition">
|
||||
<span class="ml-10 text-sm font-medium">Roles</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden w-full min-w-screen min-h-screen ml-5 mr-5 m-2">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
|
@ -106,5 +106,48 @@
|
|||
"loadownedorg": "Unable to load organizations",
|
||||
"this user does not have permission to access this org": "You do not have permission to access one of the orgs associated with your account. Your access may have been revoked. Please reload the page."
|
||||
}
|
||||
},
|
||||
|
||||
"org": {
|
||||
"apierror": {
|
||||
"notyourorg": "Access denied",
|
||||
"notyourorgexplainer": "You do not have permission to manage this org. Ask the owner of this org to add you, or go back to the homepage.",
|
||||
"loadroles": "Unable to load roles",
|
||||
"TypeError: NetworkError when attempting to fetch resource": "Unable to contact the server. Please try again later",
|
||||
"Unable to create default role firewall rules": "Unable to create the default role. Please try again later."
|
||||
},
|
||||
|
||||
"nohosts": "You don't have any hosts set up",
|
||||
"nohostsdesc": "Create and enroll a host to begin using it in your network.",
|
||||
"nohostsdesc2": "Be sure to also create and enroll a lighthouse, or your hosts wont be able to communicate.",
|
||||
"nohosts_cta": "Add host",
|
||||
|
||||
"nolighthouses": "You don't have any lighthouses set up",
|
||||
"nolighthousesdesc": "Lighthouses keep track of potential routes to overlay network hosts, and they assist in establishing connections between hosts.",
|
||||
"nolighthouses_list_item1": "Each network needs at least one lighthouse.",
|
||||
"nolighthouses_list_item2": "If you plan to access hosts over the Internet, at least one lighthouse will need a static, public IPv4 address with its firewall configured to allow inbound udp traffic on a specific port.",
|
||||
"nolighthouses_list_item3": "A modestly-sized cloud instance should be sufficient for most users.",
|
||||
"nolighthouses_list_item4": "Lighthouses are also hosts - they will have an IP and you will be able to access them over the network.",
|
||||
"nolighthouses_cta": "Add lighthouse",
|
||||
|
||||
"norelays": "Adding a relay is recommended",
|
||||
"norelays_list_item1": "Relays ensure connectivity for scenarios when direct connections fail.",
|
||||
"norelays_list_item2": "Without a relay, some devices may be unable to communicate.",
|
||||
"norelays_list_item3": "Your lighthouses also act as relays.",
|
||||
"norelays_lighthouse": "You currently have {} lighthouses acting as relays",
|
||||
"norelays_cta": "Add relay",
|
||||
|
||||
"roles_title": "Roles",
|
||||
"roles_bar": "Roles control how hosts, lighthouses, and relays communicate through firewall rules.",
|
||||
"roles_cta": "Add",
|
||||
|
||||
"guide": "Read the guide →",
|
||||
|
||||
"noroles": "Wow, such empty",
|
||||
"noroles_cta": "You don't have any roles. Consider creating one with the button above.",
|
||||
|
||||
"roleaddprompt": "What's the name of your new role?",
|
||||
"roleaddpromptdesc": "What's the description of your new role?",
|
||||
"roleadd_cta": "Create role"
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
import {finishTOTPSetup} from "$lib/totp";
|
||||
import {setCookie} from "$lib/cookie";
|
||||
|
||||
let logger = new Logger("auth/mfa/+page.svelte");
|
||||
let logger = new Logger("auth/mfasetup/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let api_token = "";
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {enforce_auth, enforce_session} from "$lib/auth";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import {list_org} from "$lib/orgs";
|
||||
import {t} from "$lib/i18n";
|
||||
import FullPageError from "../../../components/FullPageError.svelte";
|
||||
import {get_user_info} from "$lib/auth";
|
||||
import {org} from "$lib/orgs";
|
||||
import type {Organization} from "$lib/orgs";
|
||||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
import {page} from "$app/stores";
|
||||
|
||||
let logger = new Logger("org/[orgid]/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let orgid = $page.params.orgid;
|
||||
|
||||
let api_token = "";
|
||||
|
||||
let fullPageError = false;
|
||||
let fullPageErrorTitle = "";
|
||||
let fullPageErrorSubtitle = "";
|
||||
|
||||
let user_info;
|
||||
|
||||
// this page requires session and mfa auth.
|
||||
onMount(async () => {
|
||||
let st_result = await enforce_session();
|
||||
if (!st_result[0]) {
|
||||
// Session token is invalid. redirect to login
|
||||
window.location = "/auth/login";
|
||||
return;
|
||||
}
|
||||
let at_result = await enforce_auth();
|
||||
if (!at_result[0]) {
|
||||
// Auth token is invalid. Redirect to mfa page.
|
||||
window.location = "/auth/mfa";
|
||||
return;
|
||||
}
|
||||
// user is fully authenticated and permitted to proceed
|
||||
api_token = at_result[1];
|
||||
logger.info("User authenticated successfully");
|
||||
|
||||
// GET USER INFO
|
||||
user_info = await get_user_info(api_token);
|
||||
if (typeof user_info === "string") {
|
||||
logger.error(user_info);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('admin.apierror.loaduser');
|
||||
fullPageErrorSubtitle = t('admin.apierror.' + user_info);
|
||||
return;
|
||||
}
|
||||
|
||||
let org_list: string | number[] = await list_org(api_token);
|
||||
if (typeof org_list === "string") {
|
||||
// Error
|
||||
logger.error(org_list);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + org_list);
|
||||
return;
|
||||
}
|
||||
let list: number[] = org_list;
|
||||
|
||||
if (!list.includes(Number(orgid))) {
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.notyourorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.notyourorgexplainer');
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = `/org/${orgid}/lighthouses`;
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if fullPageError}
|
||||
<FullPageError title="{fullPageErrorTitle}" subtitle="{fullPageErrorSubtitle}"/>
|
||||
{:else}
|
||||
<!-- BREAKING THE LAYOUT! -->
|
||||
|
||||
<!-- Sidenav :O -->
|
||||
|
||||
|
||||
{/if}
|
|
@ -0,0 +1,86 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {enforce_auth, enforce_session} from "$lib/auth";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import {list_org} from "$lib/orgs";
|
||||
import {t} from "$lib/i18n";
|
||||
import FullPageError from "../../../../components/FullPageError.svelte";
|
||||
import {get_user_info} from "$lib/auth";
|
||||
import {org} from "$lib/orgs";
|
||||
import type {Organization} from "$lib/orgs";
|
||||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
import {page} from "$app/stores";
|
||||
import OrgWrapper from "$components/org/OrgWrapper.svelte";
|
||||
|
||||
let logger = new Logger("org/[orgid]/hosts/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let orgid = $page.params.orgid;
|
||||
|
||||
let api_token = "";
|
||||
|
||||
let fullPageError = false;
|
||||
let fullPageErrorTitle = "";
|
||||
let fullPageErrorSubtitle = "";
|
||||
|
||||
let user_info;
|
||||
|
||||
// this page requires session and mfa auth.
|
||||
onMount(async () => {
|
||||
let st_result = await enforce_session();
|
||||
if (!st_result[0]) {
|
||||
// Session token is invalid. redirect to login
|
||||
window.location = "/auth/login";
|
||||
return;
|
||||
}
|
||||
let at_result = await enforce_auth();
|
||||
if (!at_result[0]) {
|
||||
// Auth token is invalid. Redirect to mfa page.
|
||||
window.location = "/auth/mfa";
|
||||
return;
|
||||
}
|
||||
// user is fully authenticated and permitted to proceed
|
||||
api_token = at_result[1];
|
||||
logger.info("User authenticated successfully");
|
||||
|
||||
// GET USER INFO
|
||||
user_info = await get_user_info(api_token);
|
||||
if (typeof user_info === "string") {
|
||||
logger.error(user_info);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('admin.apierror.loaduser');
|
||||
fullPageErrorSubtitle = t('admin.apierror.' + user_info);
|
||||
return;
|
||||
}
|
||||
|
||||
let org_list: string | number[] = await list_org(api_token);
|
||||
if (typeof org_list === "string") {
|
||||
// Error
|
||||
logger.error(org_list);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + org_list);
|
||||
return;
|
||||
}
|
||||
let list: number[] = org_list;
|
||||
|
||||
if (!list.includes(Number(orgid))) {
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.notyourorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.notyourorgexplainer');
|
||||
return;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if fullPageError}
|
||||
<FullPageError title="{fullPageErrorTitle}" subtitle="{fullPageErrorSubtitle}"/>
|
||||
{:else}
|
||||
<!-- BREAKING THE LAYOUT! -->
|
||||
|
||||
<!-- Sidenav :O -->
|
||||
<OrgWrapper selected="hosts" orgid="{orgid}">
|
||||
|
||||
</OrgWrapper>
|
||||
{/if}
|
|
@ -0,0 +1,86 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {enforce_auth, enforce_session} from "$lib/auth";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import {list_org} from "$lib/orgs";
|
||||
import {t} from "$lib/i18n";
|
||||
import FullPageError from "../../../../components/FullPageError.svelte";
|
||||
import {get_user_info} from "$lib/auth";
|
||||
import {org} from "$lib/orgs";
|
||||
import type {Organization} from "$lib/orgs";
|
||||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
import {page} from "$app/stores";
|
||||
import OrgWrapper from "../../../../components/org/OrgWrapper.svelte";
|
||||
|
||||
let logger = new Logger("org/[orgid]/lighthouses/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let orgid = $page.params.orgid;
|
||||
|
||||
let api_token = "";
|
||||
|
||||
let fullPageError = false;
|
||||
let fullPageErrorTitle = "";
|
||||
let fullPageErrorSubtitle = "";
|
||||
|
||||
let user_info;
|
||||
|
||||
// this page requires session and mfa auth.
|
||||
onMount(async () => {
|
||||
let st_result = await enforce_session();
|
||||
if (!st_result[0]) {
|
||||
// Session token is invalid. redirect to login
|
||||
window.location = "/auth/login";
|
||||
return;
|
||||
}
|
||||
let at_result = await enforce_auth();
|
||||
if (!at_result[0]) {
|
||||
// Auth token is invalid. Redirect to mfa page.
|
||||
window.location = "/auth/mfa";
|
||||
return;
|
||||
}
|
||||
// user is fully authenticated and permitted to proceed
|
||||
api_token = at_result[1];
|
||||
logger.info("User authenticated successfully");
|
||||
|
||||
// GET USER INFO
|
||||
user_info = await get_user_info(api_token);
|
||||
if (typeof user_info === "string") {
|
||||
logger.error(user_info);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('admin.apierror.loaduser');
|
||||
fullPageErrorSubtitle = t('admin.apierror.' + user_info);
|
||||
return;
|
||||
}
|
||||
|
||||
let org_list: string | number[] = await list_org(api_token);
|
||||
if (typeof org_list === "string") {
|
||||
// Error
|
||||
logger.error(org_list);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + org_list);
|
||||
return;
|
||||
}
|
||||
let list: number[] = org_list;
|
||||
|
||||
if (!list.includes(Number(orgid))) {
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.notyourorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.notyourorgexplainer');
|
||||
return;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if fullPageError}
|
||||
<FullPageError title="{fullPageErrorTitle}" subtitle="{fullPageErrorSubtitle}"/>
|
||||
{:else}
|
||||
<!-- BREAKING THE LAYOUT! -->
|
||||
|
||||
<!-- Sidenav :O -->
|
||||
<OrgWrapper selected="lighthouses" orgid="{orgid}">
|
||||
|
||||
</OrgWrapper>
|
||||
{/if}
|
|
@ -0,0 +1,86 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {enforce_auth, enforce_session} from "$lib/auth";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import {list_org} from "$lib/orgs";
|
||||
import {t} from "$lib/i18n";
|
||||
import FullPageError from "../../../../components/FullPageError.svelte";
|
||||
import {get_user_info} from "$lib/auth";
|
||||
import {org} from "$lib/orgs";
|
||||
import type {Organization} from "$lib/orgs";
|
||||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
import {page} from "$app/stores";
|
||||
import OrgWrapper from "$components/org/OrgWrapper.svelte";
|
||||
|
||||
let logger = new Logger("org/[orgid]/relays/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let orgid = $page.params.orgid;
|
||||
|
||||
let api_token = "";
|
||||
|
||||
let fullPageError = false;
|
||||
let fullPageErrorTitle = "";
|
||||
let fullPageErrorSubtitle = "";
|
||||
|
||||
let user_info;
|
||||
|
||||
// this page requires session and mfa auth.
|
||||
onMount(async () => {
|
||||
let st_result = await enforce_session();
|
||||
if (!st_result[0]) {
|
||||
// Session token is invalid. redirect to login
|
||||
window.location = "/auth/login";
|
||||
return;
|
||||
}
|
||||
let at_result = await enforce_auth();
|
||||
if (!at_result[0]) {
|
||||
// Auth token is invalid. Redirect to mfa page.
|
||||
window.location = "/auth/mfa";
|
||||
return;
|
||||
}
|
||||
// user is fully authenticated and permitted to proceed
|
||||
api_token = at_result[1];
|
||||
logger.info("User authenticated successfully");
|
||||
|
||||
// GET USER INFO
|
||||
user_info = await get_user_info(api_token);
|
||||
if (typeof user_info === "string") {
|
||||
logger.error(user_info);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('admin.apierror.loaduser');
|
||||
fullPageErrorSubtitle = t('admin.apierror.' + user_info);
|
||||
return;
|
||||
}
|
||||
|
||||
let org_list: string | number[] = await list_org(api_token);
|
||||
if (typeof org_list === "string") {
|
||||
// Error
|
||||
logger.error(org_list);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + org_list);
|
||||
return;
|
||||
}
|
||||
let list: number[] = org_list;
|
||||
|
||||
if (!list.includes(Number(orgid))) {
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.notyourorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.notyourorgexplainer');
|
||||
return;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if fullPageError}
|
||||
<FullPageError title="{fullPageErrorTitle}" subtitle="{fullPageErrorSubtitle}"/>
|
||||
{:else}
|
||||
<!-- BREAKING THE LAYOUT! -->
|
||||
|
||||
<!-- Sidenav :O -->
|
||||
<OrgWrapper selected="relays" orgid="{orgid}">
|
||||
|
||||
</OrgWrapper>
|
||||
{/if}
|
|
@ -0,0 +1,169 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {enforce_auth, enforce_session} from "$lib/auth";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import {list_org} from "$lib/orgs";
|
||||
import {t} from "$lib/i18n";
|
||||
import FullPageError from "../../../../components/FullPageError.svelte";
|
||||
import {get_user_info} from "$lib/auth";
|
||||
import {org} from "$lib/orgs";
|
||||
import type {Organization} from "$lib/orgs";
|
||||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
import {page} from "$app/stores";
|
||||
import OrgWrapper from "../../../../components/org/OrgWrapper.svelte";
|
||||
|
||||
let logger = new Logger("org/[orgid]/roles/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let orgid = $page.params.orgid;
|
||||
|
||||
let api_token = "";
|
||||
|
||||
let fullPageError = false;
|
||||
let fullPageErrorTitle = "";
|
||||
let fullPageErrorSubtitle = "";
|
||||
|
||||
let user_info;
|
||||
|
||||
let roles = {
|
||||
'data': []
|
||||
};
|
||||
|
||||
// this page requires session and mfa auth.
|
||||
onMount(async () => {
|
||||
let st_result = await enforce_session();
|
||||
if (!st_result[0]) {
|
||||
// Session token is invalid. redirect to login
|
||||
window.location = "/auth/login";
|
||||
return;
|
||||
}
|
||||
let at_result = await enforce_auth();
|
||||
if (!at_result[0]) {
|
||||
// Auth token is invalid. Redirect to mfa page.
|
||||
window.location = "/auth/mfa";
|
||||
return;
|
||||
}
|
||||
// user is fully authenticated and permitted to proceed
|
||||
api_token = at_result[1];
|
||||
logger.info("User authenticated successfully");
|
||||
|
||||
// GET USER INFO
|
||||
user_info = await get_user_info(api_token);
|
||||
if (typeof user_info === "string") {
|
||||
logger.error(user_info);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('admin.apierror.loaduser');
|
||||
fullPageErrorSubtitle = t('admin.apierror.' + user_info);
|
||||
return;
|
||||
}
|
||||
|
||||
let org_list: string | number[] = await list_org(api_token);
|
||||
if (typeof org_list === "string") {
|
||||
// Error
|
||||
logger.error(org_list);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + org_list);
|
||||
return;
|
||||
}
|
||||
let list: number[] = org_list;
|
||||
|
||||
if (!list.includes(Number(orgid))) {
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.notyourorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.notyourorgexplainer');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let resp = await fetch_timeout(`${API_ROOT}/v1/org/${orgid}/roles`, {
|
||||
'method': 'GET',
|
||||
'headers': {
|
||||
'Authorization': `Bearer ${api_token}`
|
||||
}
|
||||
});
|
||||
if (!resp.ok) {
|
||||
let err = JSON.parse(await resp.text()).errors[0].message;
|
||||
logger.error(`${err}`);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadroles');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + err);
|
||||
return;
|
||||
}
|
||||
roles = JSON.parse(await resp.text());
|
||||
} catch (e) {
|
||||
logger.error(`${e}`);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadroles');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + `${e}`.replaceAll('.', ''));
|
||||
return;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if fullPageError}
|
||||
<FullPageError title="{fullPageErrorTitle}" subtitle="{fullPageErrorSubtitle}"/>
|
||||
{:else}
|
||||
<!-- BREAKING THE LAYOUT! -->
|
||||
|
||||
<!-- Sidenav :O -->
|
||||
<OrgWrapper selected="roles" orgid="{orgid}">
|
||||
<div class="w-full">
|
||||
<!-- Top bar -->
|
||||
<h1 class="align-middle inline mt-10 font-semibold text-2xl">{t('org.roles_title')}</h1>
|
||||
<button on:click={() => {window.location.href = `/org/${orgid}/roles/add`}} class="align-middle inline w-20 h-31 float-right dark:bg-purple-700 rounded font-bold text-sm p-2 w-25 border-2 dark:border-purple-600 border-purple-700 bg-purple-200">{t('org.roles_cta')}</button>
|
||||
|
||||
<!-- Infobox -->
|
||||
<div class="mt-5 p-4 bg-purple-800/20 text-sm rounded">
|
||||
<span>{t('org.roles_bar')}</span>
|
||||
<a class="inline text-purple-600 hover:text-purple-700 transition font-semibold"
|
||||
href="/docs/roles">{t('org.guide')}</a>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<!-- roles.data.length === 0 -->
|
||||
{#if roles.data.length === 0}
|
||||
<div class="mt-5">
|
||||
<h2 class="text-center text-xl font-semibold">{t('org.noroles')}</h2>
|
||||
<h3 class="text-center text-md">{t('org.noroles_cta')}</h3>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mt-5">
|
||||
<table class="rounded w-full bg-white dark:bg-slate-800 text-sm shadow-sm">
|
||||
<tr>
|
||||
<th class="w-1/4 border-b font-semibold p-4 text-slate-900 dark:text-slate-200 text-left">
|
||||
Name
|
||||
</th>
|
||||
<th class="w-1/8 border-b font-semibold p-4 text-slate-900 dark:text-slate-200 text-left">ID
|
||||
</th>
|
||||
<th class="w-1/8 border-b font-semibold p-4 text-slate-900 dark:text-slate-200 text-left">
|
||||
Rules
|
||||
</th>
|
||||
<th class="w-3/4 border-b font-semibold p-4 text-slate-900 dark:text-slate-200 text-left">
|
||||
Description
|
||||
</th>
|
||||
<th class="border-b p-4"></th>
|
||||
</tr>
|
||||
|
||||
{#each roles.data as role}
|
||||
<tr>
|
||||
<td class="p-4 text-slate-600 dark:text-slate-300 text-left">{role.name}</td>
|
||||
<td class="p-4 text-slate-600 dark:text-slate-300 text-left">{role.id}</td>
|
||||
<td class="p-4 text-slate-600 dark:text-slate-300 text-left">{role.firewall_rules.length}</td>
|
||||
<td class="p-4 text-slate-600 dark:text-slate-300 text-left">{role.description}</td>
|
||||
<td>
|
||||
<button class="w-16 border p-2 rounded m-2 hover:bg-slate-100 transition dark:hover:bg-slate-700" on:click={() => {
|
||||
window.location.href = `/org/${orgid}/roles/${role.id}`
|
||||
}}>
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</OrgWrapper>
|
||||
{/if}
|
|
@ -0,0 +1,144 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {enforce_auth, enforce_session} from "$lib/auth";
|
||||
import {Logger, logSetup} from "$lib/logger";
|
||||
import {list_org} from "$lib/orgs";
|
||||
import {t} from "$lib/i18n";
|
||||
import FullPageError from "../../../../../components/FullPageError.svelte";
|
||||
import {get_user_info} from "$lib/auth";
|
||||
import {org} from "$lib/orgs";
|
||||
import type {Organization} from "$lib/orgs";
|
||||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
import {page} from "$app/stores";
|
||||
import OrgWrapper from "../../../../../components/org/OrgWrapper.svelte";
|
||||
|
||||
let logger = new Logger("org/[orgid]/roles/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let orgid = $page.params.orgid;
|
||||
|
||||
let api_token = "";
|
||||
|
||||
let fullPageError = false;
|
||||
let fullPageErrorTitle = "";
|
||||
let fullPageErrorSubtitle = "";
|
||||
|
||||
let user_info;
|
||||
|
||||
let roles = {
|
||||
'data': []
|
||||
};
|
||||
|
||||
// this page requires session and mfa auth.
|
||||
onMount(async () => {
|
||||
let st_result = await enforce_session();
|
||||
if (!st_result[0]) {
|
||||
// Session token is invalid. redirect to login
|
||||
window.location = "/auth/login";
|
||||
return;
|
||||
}
|
||||
let at_result = await enforce_auth();
|
||||
if (!at_result[0]) {
|
||||
// Auth token is invalid. Redirect to mfa page.
|
||||
window.location = "/auth/mfa";
|
||||
return;
|
||||
}
|
||||
// user is fully authenticated and permitted to proceed
|
||||
api_token = at_result[1];
|
||||
logger.info("User authenticated successfully");
|
||||
|
||||
// GET USER INFO
|
||||
user_info = await get_user_info(api_token);
|
||||
if (typeof user_info === "string") {
|
||||
logger.error(user_info);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('admin.apierror.loaduser');
|
||||
fullPageErrorSubtitle = t('admin.apierror.' + user_info);
|
||||
return;
|
||||
}
|
||||
|
||||
let org_list: string | number[] = await list_org(api_token);
|
||||
if (typeof org_list === "string") {
|
||||
// Error
|
||||
logger.error(org_list);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + org_list);
|
||||
return;
|
||||
}
|
||||
let list: number[] = org_list;
|
||||
|
||||
if (!list.includes(Number(orgid))) {
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.notyourorg');
|
||||
fullPageErrorSubtitle = t('org.apierror.notyourorgexplainer');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
let name = "";
|
||||
let description = "";
|
||||
|
||||
async function createRole() {
|
||||
try {
|
||||
let resp = await fetch_timeout(`${API_ROOT}/v1/org/${orgid}/role`, {
|
||||
'method': 'POST',
|
||||
'headers': {
|
||||
'Authorization': `Bearer ${api_token}`
|
||||
},
|
||||
'body': JSON.stringify({
|
||||
'name': name,
|
||||
'description': description
|
||||
})
|
||||
});
|
||||
if (!resp.ok) {
|
||||
let err = JSON.parse(await resp.text()).errors[0].message;
|
||||
logger.error(`${err}`);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.addrole');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + err);
|
||||
return;
|
||||
}
|
||||
roles = JSON.parse(await resp.text());
|
||||
window.location.href = `/org/${orgid}/roles`
|
||||
} catch (e) {
|
||||
logger.error(`${e}`);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('org.apierror.loadroles');
|
||||
fullPageErrorSubtitle = t('org.apierror.' + `${e}`.replaceAll('.', ''));
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if fullPageError}
|
||||
<FullPageError title="{fullPageErrorTitle}" subtitle="{fullPageErrorSubtitle}"/>
|
||||
{:else}
|
||||
<div class="flex in-h-full items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="w-full max-w-md">
|
||||
|
||||
<!-- The actual form -->
|
||||
<form class="mt-5" action="#" method="POST" on:submit|preventDefault={createRole}>
|
||||
<div class="rounded-md shadow-sm">
|
||||
<label for="name">{t('org.roleaddprompt')}</label>
|
||||
<input bind:value={name} id="name"
|
||||
class="dark:bg-slate-500 bg-gray-200 w-full rounded px-3 py-2 focus:outline-none focus:ring-purple-500 appearance-none">
|
||||
</div>
|
||||
|
||||
<div class="rounded-md shadow-sm mt-5">
|
||||
<label for="desc">{t('org.roleaddpromptdesc')}</label>
|
||||
<input bind:value={description} id="desc"
|
||||
class="dark:bg-slate-500 bg-gray-200 w-full rounded px-3 py-2 focus:outline-none focus:ring-purple-500 appearance-none">
|
||||
</div>
|
||||
|
||||
<button class="bg-purple-400 dark:bg-purple-600 mt-4 w-full py-2 -space-y-px rounded-md shadow-sm place-content-center">
|
||||
{t('org.roleadd_cta')}
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
|
@ -11,7 +11,7 @@
|
|||
import {API_ROOT} from "$lib/config";
|
||||
import {fetch_timeout} from "$lib/util";
|
||||
|
||||
let logger = new Logger("admin/+page.svelte");
|
||||
let logger = new Logger("org/new/+page.svelte");
|
||||
logSetup();
|
||||
|
||||
let api_token = "";
|
||||
|
@ -111,15 +111,18 @@
|
|||
fullPageErrorSubtitle = t('neworg.apierror.invaliddata');
|
||||
return;
|
||||
}
|
||||
let err = JSON.parse(await resp.text()).errors[0].message;
|
||||
logger.error(`${await resp.json().errors[0]}`);
|
||||
let text = await resp.text();
|
||||
console.log(text);
|
||||
let err = JSON.parse(text).errors[0].message;
|
||||
logger.error(`${err}`);
|
||||
fullPageError = true;
|
||||
fullPageErrorTitle = t('neworg.apierror.orgcreate');
|
||||
fullPageErrorSubtitle = t('neworg.apierror.' + err);
|
||||
return;
|
||||
}
|
||||
let resp_objectified = JSON.parse(await resp.text());
|
||||
console.log(JSON.stringify(resp_objectified));
|
||||
let text = await resp.text();
|
||||
console.log(text);
|
||||
let resp_objectified = JSON.parse(text);
|
||||
created_org_id = resp_objectified.org_id;
|
||||
logger.info("Able to create organization with id " + created_org_id);
|
||||
logger.info("Org create success! Redirecting to manage page");
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
],
|
||||
"$lib/*": [
|
||||
"./src/lib/*"
|
||||
],
|
||||
"$components": [
|
||||
"./src/components"
|
||||
],
|
||||
"$components/*": [
|
||||
"./src/components/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
-- trifid-api, an open source reimplementation of the Defined Networking nebula management server.
|
||||
-- Copyright (C) 2023 c0repwn3r
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <https:--www.gnu.org/licenses/>.
|
||||
|
||||
CREATE TABLE roles (
|
||||
id SERIAL NOT NULL PRIMARY KEY,
|
||||
org SERIAL NOT NULL REFERENCES organizations(id),
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(4096) NOT NULL
|
||||
);
|
|
@ -0,0 +1,27 @@
|
|||
-- trifid-api, an open source reimplementation of the Defined Networking nebula management server.
|
||||
-- Copyright (C) 2023 c0repwn3r
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <https:--www.gnu.org/licenses/>.
|
||||
|
||||
CREATE TABLE roles_firewall_rules (
|
||||
id SERIAL NOT NULL PRIMARY KEY,
|
||||
role SERIAL NOT NULL REFERENCES roles(id),
|
||||
protocol INTEGER NOT NULL, -- 0: any 1: tcp 2: udp 3: icmp
|
||||
port_range_start INTEGER NOT NULL, -- min: 1 max: 65535. Ignored if protocol==3
|
||||
port_range_end INTEGER NOT NULL, -- min: 1 max: 65535, must be greater than or equal to port_range_start. Ignored if protocol==3
|
||||
allow_from INTEGER NOT NULL, -- Allow traffic goverened by above rules from who?
|
||||
-- -1: anybody
|
||||
-- (a role, anything else): only that role
|
||||
description VARCHAR(4096) NOT NULL
|
||||
);
|
|
@ -41,6 +41,7 @@ pub mod auth;
|
|||
pub mod crypto;
|
||||
pub mod org;
|
||||
pub mod kv;
|
||||
pub mod role;
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
|
@ -183,7 +184,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
crate::routes::v1::user::options,
|
||||
crate::routes::v1::organization::createorgoptions,
|
||||
crate::routes::v1::ca::get_cas_for_org,
|
||||
crate::routes::v1::ca::options
|
||||
crate::routes::v1::ca::options,
|
||||
crate::routes::v1::roles::get_roles,
|
||||
crate::routes::v1::roles::options,
|
||||
crate::routes::v1::roles::options_roleadd,
|
||||
crate::routes::v1::roles::role_add
|
||||
])
|
||||
.register("/", catchers![
|
||||
crate::routes::handler_400,
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
// trifid-api, an open source reimplementation of the Defined Networking nebula management server.
|
||||
// Copyright (C) 2023 c0repwn3r
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::error::Error;
|
||||
use sqlx::PgPool;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[repr(i32)]
|
||||
pub enum Protocol {
|
||||
Any = 0,
|
||||
TCP = 1,
|
||||
UDP = 2,
|
||||
ICMP = 3
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum AllowFrom {
|
||||
Anyone,
|
||||
SpecificRole(i32)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FirewallRule {
|
||||
pub id: i32,
|
||||
pub protocol: Protocol,
|
||||
pub port_start: u16,
|
||||
pub port_end: u16,
|
||||
pub allow_from: AllowFrom,
|
||||
pub description: String
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Role {
|
||||
pub id: i32,
|
||||
pub org_id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub firewall_rules: Vec<FirewallRule>
|
||||
}
|
||||
|
||||
pub async fn get_role(role_id: i32, db: &PgPool) -> Result<Option<Role>, Box<dyn Error>> {
|
||||
let query_result: Option<_> = sqlx::query!("SELECT * FROM roles WHERE id = $1", role_id).fetch_optional(db).await?;
|
||||
|
||||
if let Some(res) = query_result {
|
||||
// get all firewall rules
|
||||
let rules_res = sqlx::query!("SELECT * FROM roles_firewall_rules WHERE role = $1", Some(role_id)).fetch_all(db).await?;
|
||||
|
||||
let mut rules = vec![];
|
||||
|
||||
for rule in rules_res {
|
||||
rules.push(FirewallRule {
|
||||
id: rule.id,
|
||||
protocol: match rule.protocol {
|
||||
0 => Protocol::Any,
|
||||
1 => Protocol::TCP,
|
||||
2 => Protocol::UDP,
|
||||
3 => Protocol::ICMP,
|
||||
_ => return Err(format!("invalid protocol on a firewall rule {}", rule.id).into())
|
||||
},
|
||||
port_start: rule.port_range_start as u16,
|
||||
port_end: rule.port_range_end as u16,
|
||||
allow_from: match rule.allow_from {
|
||||
-1 => AllowFrom::Anyone,
|
||||
_ => AllowFrom::SpecificRole(rule.allow_from)
|
||||
},
|
||||
description: rule.description,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(Some(Role {
|
||||
id: role_id,
|
||||
org_id: res.org,
|
||||
name: res.name,
|
||||
description: res.description,
|
||||
firewall_rules: rules,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_role_ids_for_ca(org_id: i32, db: &PgPool) -> Result<Vec<i32>, Box<dyn Error>> {
|
||||
Ok(sqlx::query!("SELECT id FROM roles WHERE org = $1", org_id).fetch_all(db).await?.iter().map(|r| r.id).collect())
|
||||
}
|
|
@ -22,3 +22,4 @@ pub mod verify_totp_authenticator;
|
|||
pub mod organization;
|
||||
pub mod user;
|
||||
pub mod ca;
|
||||
pub mod roles;
|
|
@ -162,6 +162,23 @@ pub async fn create_org(req: Json<CreateCARequest>, user: TOTPAuthenticatedUserI
|
|||
|
||||
let result = sqlx::query!("INSERT INTO organizations (owner, ca_key, ca_crt, iv) VALUES ($1, $2, $3, $4) RETURNING id", owner_id, ca_key, ca_crt, iv_hex).fetch_one(db.inner()).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))?;
|
||||
|
||||
// last step: create a default role to allow pings from all hosts
|
||||
let role_id = match sqlx::query!("INSERT INTO roles (org, name, description) VALUES ($1, 'Default', 'Allow pings from other hosts. Default role for new hosts.') RETURNING id", result.id).fetch_one(db.inner()).await {
|
||||
Ok(r) => r.id,
|
||||
Err(e) => {
|
||||
error!("[tfapi] dberror: {}", e);
|
||||
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_INTERNAL_SERVER_ERROR", "Unable to create default role")));
|
||||
}
|
||||
};
|
||||
|
||||
match sqlx::query!("INSERT INTO roles_firewall_rules (role, protocol, port_range_start, port_range_end, allow_from, description) VALUES ($1, 3, 1, 1, -1, 'Allow pings from anyone on the network')", role_id).execute(db.inner()).await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
error!("[tfapi] dberror: {} inserting on roleid {}", e, role_id);
|
||||
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_INTERNAL_SERVER_ERROR", "Unable to create default role firewall rules")));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((ContentType::JSON, Json(
|
||||
OrginfoStruct {
|
||||
org_id: result.id,
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
// trifid-api, an open source reimplementation of the Defined Networking nebula management server.
|
||||
// Copyright (C) 2023 c0repwn3r
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use log::error;
|
||||
use rocket::{options, get, State, post};
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::serde::json::Json;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sqlx::PgPool;
|
||||
use crate::auth::TOTPAuthenticatedUserInfo;
|
||||
use crate::org::user_has_org_assoc;
|
||||
use crate::role::{get_role, get_role_ids_for_ca, Role};
|
||||
|
||||
#[options("/v1/org/<_org>/roles")]
|
||||
pub fn options(_org: i32) -> &'static str { "" }
|
||||
#[options("/v1/org/<_org>/role")]
|
||||
pub fn options_roleadd(_org: i32) -> &'static str { "" }
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct RolesResponse {
|
||||
pub data: Vec<Role>
|
||||
}
|
||||
|
||||
#[get("/v1/org/<org>/roles")]
|
||||
pub async fn get_roles(org: i32, user: TOTPAuthenticatedUserInfo, db: &State<PgPool>) -> Result<(ContentType, Json<RolesResponse>), (Status, String)> {
|
||||
if !user_has_org_assoc(user.user_id, org, db.inner()).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))? {
|
||||
return Err((Status::Unauthorized, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_MISSING_ORG_AUTHORIZATION", "this user does not have permission to access this org")));
|
||||
}
|
||||
|
||||
let roles = match get_role_ids_for_ca(org, db.inner()).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_INTERNAL_SERVER_ERR", "there was an error querying the db, please try again later", e)))
|
||||
}
|
||||
};
|
||||
|
||||
let mut resp = RolesResponse {
|
||||
data: vec![]
|
||||
};
|
||||
|
||||
for role in roles {
|
||||
let role_info = match get_role(role, db.inner()).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_INTERNAL_SERVER_ERR", "there was an error querying the db, please try again later", e)))
|
||||
}
|
||||
};
|
||||
if let Some(info) = role_info {
|
||||
resp.data.push(info);
|
||||
} else {
|
||||
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_INTERNAL_SERVER_ERR", "there was an error querying the db, please try again later", "missing role as returned by server - possibly deleted inbetween?")))
|
||||
}
|
||||
}
|
||||
|
||||
Ok((ContentType::JSON, Json(resp)))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct RoleaddReq {
|
||||
pub name: String,
|
||||
pub description: String
|
||||
}
|
||||
|
||||
#[post("/v1/org/<org>/role", data = "<req>")]
|
||||
pub async fn role_add(req: Json<RoleaddReq>, org: i32, user: TOTPAuthenticatedUserInfo, db: &State<PgPool>) -> Result<(ContentType, String), (Status, String)> {
|
||||
if !user_has_org_assoc(user.user_id, org, db.inner()).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))? {
|
||||
return Err((Status::Unauthorized, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_MISSING_ORG_AUTHORIZATION", "this user does not have permission to access this org")));
|
||||
}
|
||||
|
||||
match sqlx::query!("INSERT INTO roles (org, name, description) VALUES ($1, $2, $3)", org, req.name, req.description).execute(db.inner()).await {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
error!("[tfapi] dberror: {}", e);
|
||||
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_INTERNAL_SERVER_ERR", "database returned error while trying to create role", "unable to insert role")))
|
||||
}
|
||||
}
|
||||
|
||||
Ok((ContentType::JSON, "{}".to_string()))
|
||||
}
|
Loading…
Reference in New Issue