Skip to content

Commit

Permalink
feat: complete grants management
Browse files Browse the repository at this point in the history
  • Loading branch information
yankeguo committed Feb 1, 2024
1 parent 19b8830 commit e539486
Show file tree
Hide file tree
Showing 14 changed files with 669 additions and 295 deletions.
66 changes: 66 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bunker

import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"net/http"
Expand Down Expand Up @@ -340,6 +341,68 @@ func (a *App) routeUpdateUser(c ufx.Context) {
c.JSON(map[string]any{})
}

func (a *App) routeListGrants(c ufx.Context) {
_, _ = a.requireAdmin(c)

var data struct {
UserID string `json:"user_id"`
}

c.Bind(&data)

db := dao.Use(a.db)

grants := rg.Must(db.Grant.Where(db.Grant.UserID.Eq(data.UserID)).Find())

c.JSON(map[string]any{"grants": grants})
}

func (a *App) routeCreateGrant(c ufx.Context) {
_, _ = a.requireAdmin(c)

db := dao.Use(a.db)

var data struct {
UserID string `json:"user_id" validate:"required"`
ServerUser string `json:"server_user" validate:"required"`
ServerID string `json:"server_id" validate:"required"`
}
c.Bind(&data)

digest := sha256.Sum256([]byte(data.UserID + "::" + data.ServerUser + "@" + data.ServerID))
id := hex.EncodeToString(digest[:])

grant := &model.Grant{
ID: id,
UserID: data.UserID,
ServerUser: data.ServerUser,
ServerID: data.ServerID,
}

rg.Must0(db.Grant.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoNothing: true,
}).Create(grant))

c.JSON(map[string]any{"grant": grant})
}

func (a *App) routeDeleteGrant(c ufx.Context) {
_, _ = a.requireAdmin(c)

db := dao.Use(a.db)

var data struct {
ID string `json:"id" validate:"required"`
}

c.Bind(&data)

rg.Must(db.Grant.Where(db.Grant.ID.Eq(data.ID)).Delete())

c.JSON(map[string]any{})
}

func InstallAppToRouter(a *App, ur ufx.Router) {
ur.HandleFunc("/backend/current_user", a.routeCurrentUser)
ur.HandleFunc("/backend/sign_in", a.routeSignIn)
Expand All @@ -353,4 +416,7 @@ func InstallAppToRouter(a *App, ur ufx.Router) {
ur.HandleFunc("/backend/users", a.routeListUsers)
ur.HandleFunc("/backend/users/create", a.routeCreateUser)
ur.HandleFunc("/backend/users/update", a.routeUpdateUser)
ur.HandleFunc("/backend/grants", a.routeListGrants)
ur.HandleFunc("/backend/grants/create", a.routeCreateGrant)
ur.HandleFunc("/backend/grants/delete", a.routeDeleteGrant)
}
4 changes: 2 additions & 2 deletions ui/components/simple/fields.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ const props = defineProps<{

<template>
<template v-for="field in fields" v-bind:key="field.name">
<div class="my-2">
<div class="mb-1">
<label class="text-gray-900 dark:text-white font-semibold">{{
field.name
}}</label>
</div>
<div>
<div class="mb-2">
<span class="text-gray-500 dark:text-gray-400">{{ field.content }}</span>
</div>
</template>
Expand Down
55 changes: 24 additions & 31 deletions ui/components/skeleton/dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@ const links = [
},
...(user.value.user?.is_admin
? [
{
label: "Servers",
icon: "i-mdi-server",
to: { name: "dashboard-servers" },
},
{
label: "Users",
icon: "i-mdi-account-multiple",
to: { name: "dashboard-users" },
},
]
{
label: "Servers",
icon: "i-mdi-server",
to: { name: "dashboard-servers" },
},
{
label: "Users",
icon: "i-mdi-account-multiple",
to: { name: "dashboard-users" },
},
]
: []),
],
[
{
label: "My SSH Keys",
label: "SSH Keys",
icon: "i-mdi-key-chain",
to: { name: "dashboard-profile-keys" },
},
{
label: "My Profile",
label: "Profile",
icon: "i-mdi-account-circle",
to: { name: "dashboard-profile" },
},
Expand All @@ -46,31 +46,24 @@ const links = [
</script>

<template>
<div class="flex flex-col md:flex-row my-6">
<div class="flex flex-col my-6">

<Head>
<Title>Bunker - {{ titleName }}</Title>
</Head>

<div
class="w-full md:w-1/3 lg:w-1/5 px-3 py-6 flex flex-col items-center md:items-end"
>
<UVerticalNavigation
class="w-full"
:ui="{ size: 'text-lg' }"
:links="links"
/>
<UHorizontalNavigation :links="links" class="w-full border-b border-gray-200 dark:border-gray-800" />

<div class="flex flex-row items-center mt-8 px-2.5">
<UIcon :name="titleIcon" class="text-2xl font-semibold me-2" size="lg"></UIcon>
<span class="text-2xl font-semibold">{{ titleName }}</span>
</div>

<div class="w-full md:w-2/3 lg:w-4/5 px-12 py-6">
<div class="flex flex-row items-center">
<UIcon
:name="titleIcon"
class="text-2xl font-semibold me-2"
size="lg"
></UIcon>
<span class="text-2xl font-semibold">{{ titleName }}</span>
<div class="flex flex-row mt-8 px-2.5">
<div class="w-80 me-8">
<slot name="left"></slot>
</div>
<div class="flex flex-col mt-8 px-1">
<div class="flex-grow">
<slot></slot>
</div>
</div>
Expand Down
15 changes: 15 additions & 0 deletions ui/composables/grant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const useGrants = (userId: string) => {
return useAsyncData<{ grants: BGrant[] }>(
"grants-" + userId,
() => $fetch("/backend/grants", {
query: {
user_id: userId,
}
}),
{
default() {
return { grants: [] };
},
}
);
};
11 changes: 11 additions & 0 deletions ui/composables/ui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const uiCard = {
header: {
padding: 'py-2 px-1'
},
body: {
padding: 'py-1 px-1'
},
footer: {
padding: 'py-2 px-1'
}
}
6 changes: 3 additions & 3 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
"@vueuse/components": "^10.7.2",
"@vueuse/core": "^10.7.2",
"@vueuse/nuxt": "^10.7.2",
"nuxt": "^3.9.3",
"nuxt": "^3.10.0",
"vue": "^3.4.15",
"vue-router": "^4.2.5"
},
"dependencies": {
"@iconify-json/mdi": "^1.1.64",
"@iconify-json/noto-v1": "^1.1.11",
"@iconify-json/simple-icons": "^1.1.89",
"@nuxt/ui": "^2.12.3",
"@iconify-json/simple-icons": "^1.1.90",
"@nuxt/ui": "^2.13.0",
"@tailwindcss/typography": "^0.5.10"
}
}
17 changes: 8 additions & 9 deletions ui/pages/dashboard/profile/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@ async function doSignOut() {

<template>
<SkeletonDashboard title-name="Profile" title-icon="i-mdi-account-circle">
<SimpleFields :fields="fields"></SimpleFields>
<div class="mt-6">
<UButton
icon="i-mdi-logout"
label="Sign out"
color="red"
@click="doSignOut"
></UButton>
</div>
<template #left>
<UCard :ui="uiCard">
<SimpleFields :fields="fields"></SimpleFields>
<div class="mt-6">
<UButton icon="i-mdi-logout" label="Sign out" color="red" @click="doSignOut"></UButton>
</div>
</UCard>
</template>
</SkeletonDashboard>
</template>
69 changes: 55 additions & 14 deletions ui/pages/dashboard/profile/keys/index.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<script setup lang="ts">
import type { FormError, FormSubmitEvent } from "#ui/types";
import { guardWorking } from "~/composables/error";
definePageMeta({
middleware: ["auth"],
});
Expand Down Expand Up @@ -38,27 +41,65 @@ async function deleteKey(id: string) {
}
refreshKeys();
}
const state = reactive({
display_name: undefined,
public_key: undefined,
});
const validate = (state: any): FormError[] => {
const errors = [];
if (!state.display_name)
errors.push({ path: "display_name", message: "Required" });
if (!state.public_key)
errors.push({ path: "public_key", message: "Required" });
return errors;
};
const working = ref(0);
async function onSubmit(event: FormSubmitEvent<any>) {
return guardWorking(working, async () => {
await $fetch("/backend/keys/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(event.data),
});
await refreshKeys()
})
}
</script>

<template>
<SkeletonDashboard title-name="SSH Keys" title-icon="i-mdi-key-chain">
<div class="mb-4">
<UButton
:to="{ name: 'dashboard-profile-keys-new' }"
icon="i-mdi-plus"
label="Add SSH Key"
></UButton>
</div>
<template #left>
<UCard :ui="uiCard">
<template #header>
<UIcon name="i-mdi-key-plus" class="me-1"></UIcon>
<span>Add Key</span>
</template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit">
<UFormGroup label="Name" name="display_name">
<UInput v-model="state.display_name" placeholder="Input name" />
</UFormGroup>

<UFormGroup label="Public Key" name="public_key">
<UTextarea v-model="state.public_key" :rows="12" placeholder="Input ssh public key" />
</UFormGroup>

<UButton type="submit" :disabled="!!working" :loading="!!working" icon="i-mdi-check" label="Submit">
</UButton>
</UForm>
</UCard>
</template>

<UTable :rows="keys.keys" :columns="columns">
<template #actions-data="{ row }">
<UButton
variant="link"
color="red"
icon="i-mdi-trash"
label="Delete"
@click="deleteKey(row.id)"
></UButton>
<UButton variant="link" color="red" icon="i-mdi-trash" label="Delete" @click="deleteKey(row.id)"></UButton>
</template>
</UTable>

</SkeletonDashboard>
</template>
54 changes: 0 additions & 54 deletions ui/pages/dashboard/profile/keys/new.vue

This file was deleted.

Loading

0 comments on commit e539486

Please sign in to comment.