Improve user info (#47)

This commit is contained in:
Jonas Plum
2022-03-20 12:57:15 +01:00
committed by GitHub
parent 68618d2bdb
commit 2158899983
5 changed files with 168 additions and 41 deletions

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"log" "log"
"net/http" "net/http"
"strings"
"github.com/arangodb/go-driver" "github.com/arangodb/go-driver"
@@ -12,6 +13,7 @@ import (
"github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/api" "github.com/SecurityBrewery/catalyst/generated/api"
"github.com/SecurityBrewery/catalyst/generated/model" "github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/generated/pointer"
"github.com/SecurityBrewery/catalyst/hooks" "github.com/SecurityBrewery/catalyst/hooks"
"github.com/SecurityBrewery/catalyst/role" "github.com/SecurityBrewery/catalyst/role"
"github.com/SecurityBrewery/catalyst/test" "github.com/SecurityBrewery/catalyst/test"
@@ -39,12 +41,35 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "eve", Roles: []string{"admin"}})
_ = theCatalyst.DB.UserDataCreate(context.Background(), "eve", &model.UserData{
Name: pointer.String("Eve"),
Email: pointer.String("eve@example.com"),
Image: &avatarEve,
})
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "kevin", Roles: []string{"admin"}})
_ = theCatalyst.DB.UserDataCreate(context.Background(), "kevin", &model.UserData{
Name: pointer.String("Kevin"),
Email: pointer.String("kevin@example.com"),
Image: &avatarKevin,
})
// proxy static requests // proxy static requests
middlewares := []func(next http.Handler) http.Handler{ middlewares := []func(next http.Handler) http.Handler{
catalyst.Authenticate(theCatalyst.DB, config.Auth), catalyst.Authenticate(theCatalyst.DB, config.Auth),
catalyst.AuthorizeBlockedUser(), catalyst.AuthorizeBlockedUser(),
} }
theCatalyst.Server.With(middlewares...).NotFound(api.Proxy("http://localhost:8080")) theCatalyst.Server.With(middlewares...).NotFound(func(writer http.ResponseWriter, request *http.Request) {
var handler http.Handler = http.HandlerFunc(api.Proxy("http://localhost:8080/static/"))
if strings.HasPrefix(request.URL.Path, "/static/") {
handler = http.StripPrefix("/static/", handler)
} else {
request.URL.Path = "/"
}
handler.ServeHTTP(writer, request)
})
if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil { if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil {
log.Fatal(err) log.Fatal(err)

View File

@@ -0,0 +1,63 @@
<template>
<span>
<span v-if="id === undefined">
<v-icon small class="mr-1">mdi-account</v-icon>
unassigned
</span>
<span v-else-if="user === undefined">
<v-icon small class="mr-1">mdi-account</v-icon>
{{ id }}
</span>
<span v-else>
<v-avatar v-if="user.image" :size="lodash.isInteger(size) ? size : 24">
<v-img :src="user.image"></v-img>
</v-avatar>
<v-icon v-else small class="">mdi-account</v-icon>
{{ user.name ? user.name : id }}
</span>
</span>
</template>
<script lang="ts">
import Vue from "vue";
import {UserData} from "@/client";
import {API} from "@/services/api";
import {AxiosTransformer} from "axios";
interface State {
user?: UserData,
}
export default Vue.extend({
name: "User",
props: ["id", "size"],
data: (): State => ({
user: undefined,
}),
watch: {
id: function(): void {
this.loadUserData();
}
},
methods: {
loadUserData: function () {
if (this.id === undefined) {
this.user = undefined;
return
}
let defaultTransformers = this.axios.defaults.transformResponse as AxiosTransformer[]
let transformResponse = defaultTransformers.concat((data) => {
data.notoast = true;
return data
});
API.getUserData(this.id, {transformResponse: transformResponse}).then(response => {
this.user = response.data;
})
}
},
mounted() {
this.loadUserData();
}
});
</script>

View File

@@ -12,8 +12,7 @@
<v-icon small class="mr-1" :color="statusColor">{{ statusIcon }}</v-icon> <v-icon small class="mr-1" :color="statusColor">{{ statusIcon }}</v-icon>
<span :class="statusColor + '--text'">{{ ticket.status | capitalize }}</span> <span :class="statusColor + '--text'">{{ ticket.status | capitalize }}</span>
<v-icon small class="mx-1">mdi-account</v-icon> <User :id="ticket.owner" :size="16" class="mx-2"></User>
{{ ticket.owner ? ticket.owner : 'unassigned' }}
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-icon small class="mr-1">mdi-source-branch</v-icon> <v-icon small class="mr-1">mdi-source-branch</v-icon>
<span class="mr-1">{{ ticket.playbooks ? lodash.size(ticket.playbooks) : 0 }}</span> <span class="mr-1">{{ ticket.playbooks ? lodash.size(ticket.playbooks) : 0 }}</span>
@@ -38,10 +37,14 @@
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from "vue";
import {Playbook, Task, Type, TypeColorEnum} from "@/client"; import {Playbook, Task, Type, TypeColorEnum} from "@/client";
import User from "@/components/User.vue";
export default Vue.extend({ export default Vue.extend({
name: "TicketSnippet", name: "TicketSnippet",
props: ["ticket", "to", "action"], props: ["ticket", "to", "action"],
components: {
User
},
computed: { computed: {
opentaskcount: function() { opentaskcount: function() {
let count = 0; let count = 0;

View File

@@ -103,38 +103,6 @@
</v-list> </v-list>
</v-menu> </v-menu>
&middot; &middot;
<v-menu offset-y class="mr-2">
<template v-slot:activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">
<v-icon small class="mr-1">mdi-account</v-icon>
{{ ticket.owner ? ticket.owner : "unassigned" }}
</span>
</template>
<v-list>
<v-list-item
dense
link
v-for="user in otherUsers(ticket.owner)"
:key="user.id"
@click="setOwner(user.id)"
>
<v-list-item-title>
Change owner to {{ user.id }}
</v-list-item-title>
</v-list-item>
<v-list-item
v-if="ticket.owner"
dense
link
@click="setOwner(null)"
>
<v-list-item-title>
Unassign ticket
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
&middot;
<v-icon small class="mr-1">mdi-calendar-plus</v-icon> <v-icon small class="mr-1">mdi-calendar-plus</v-icon>
{{ ticket.created | formatdate($store.state.settings.timeformat) }} {{ ticket.created | formatdate($store.state.settings.timeformat) }}
&middot; &middot;
@@ -243,6 +211,63 @@
> >
<v-icon class="mr-1">mdi-graph</v-icon> open graph <v-icon class="mr-1">mdi-graph</v-icon> open graph
</v-btn> </v-btn>
<v-list dense color="background">
<v-list-item class="pa-0 ma-0" style="min-height: 32px">
<span class="text--disabled" style="width: 50px;">Owner</span>
<v-menu offset-y class="mr-2">
<template v-slot:activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">
<User :id="ticket.owner" class="ml-3"></User>
</span>
</template>
<v-list>
<v-list-item
dense
link
v-for="user in otherUsers(ticket.owner)"
:key="user.id"
@click="setOwner(user.id)"
>
<v-list-item-title>
Change owner to <User :id="user.id"></User>
</v-list-item-title>
</v-list-item>
<v-list-item
v-if="ticket.owner"
dense
link
@click="setOwner(undefined)"
>
<v-list-item-title>
Unassign ticket
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
<!--v-list-item class="pa-0 ma-0" style="min-height: 32px">
<span class="text--disabled" style="width: 50px;">Editors</span>
<span v-for="writer in ticket.write" :key="writer">
<User :id="writer" class="ml-3"></User>
</span>
<v-btn v-if="!ticket.write || ticket.write.length === 0" small text elevation="0" @click="referenceDialog = true">
<v-icon small>mdi-plus</v-icon> Add editor
</v-btn>
</v-list-item>
<v-list-item class="pa-0 ma-0" style="min-height: 32px">
<span class="text--disabled" style="width: 50px;">Viewers</span>
<span v-for="reader in ticket.read" :key="reader">
<User :id="reader" class="ml-3"></User>
</span>
<v-btn v-if="!ticket.read || ticket.read.length === 0" small text elevation="0" @click="referenceDialog = true">
<v-icon small>mdi-plus</v-icon> Add viewer
</v-btn>
</v-list-item>
</v-list-->
<v-divider class="mb-5"></v-divider>
<div style="align-items: center" class="d-flex pb-1"> <div style="align-items: center" class="d-flex pb-1">
<span class="text--disabled">Playbooks</span> <span class="text--disabled">Playbooks</span>
<v-spacer></v-spacer> <v-spacer></v-spacer>
@@ -796,19 +821,19 @@ import {alg, Graph} from "graphlib";
import ArtifactSnippet from "../components/snippets/ArtifactSnippet.vue"; import ArtifactSnippet from "../components/snippets/ArtifactSnippet.vue";
import TicketSnippet from "../components/snippets/TicketSnippet.vue"; import TicketSnippet from "../components/snippets/TicketSnippet.vue";
import TicketList from "../components/TicketList.vue"; import TicketList from "../components/TicketList.vue";
import User from "../components/User.vue";
import ArtifactPopup from "./ArtifactPopup.vue"; import ArtifactPopup from "./ArtifactPopup.vue";
import { import {
Ticket, Ticket,
TicketResponse, TicketResponse,
TicketTemplate, TicketTemplate,
ModelFile,
PlaybookTemplate, PlaybookTemplate,
Reference, Reference,
Task, Task,
Type, Type,
TypeColorEnum, TypeColorEnum,
TaskResponse, PlaybookResponse, UserResponse, TaskTypeEnum, TicketWithTickets, TaskResponse, PlaybookResponse, UserResponse, TicketWithTickets,
} from "@/client"; } from "@/client";
import {API} from "@/services/api"; import {API} from "@/services/api";
@@ -818,7 +843,7 @@ import Tus from "@uppy/tus";
import "@uppy/core/dist/style.css"; import "@uppy/core/dist/style.css";
import "@uppy/dashboard/dist/style.css"; import "@uppy/dashboard/dist/style.css";
import {Uppy, UppyFile} from "@uppy/core"; import {Uppy} from "@uppy/core";
import {AxiosError, AxiosResponse} from "axios"; import {AxiosError, AxiosResponse} from "axios";
import {DateTime} from "luxon"; import {DateTime} from "luxon";
import VueMarkdown from "vue-markdown"; import VueMarkdown from "vue-markdown";
@@ -897,7 +922,8 @@ export default Vue.extend({
"vue-markdown": VueMarkdown, "vue-markdown": VueMarkdown,
TicketList, TicketList,
ArtifactPopup, ArtifactPopup,
JSONHTML JSONHTML,
User
}, },
data: (): State => ({ data: (): State => ({
valid: false, valid: false,
@@ -1447,12 +1473,16 @@ export default Vue.extend({
this.editName = ""; this.editName = "";
}); });
}, },
setOwner: function(owner: string) { setOwner: function(owner?: string) {
if (!this.ticket || !this.ticket.id) { if (!this.ticket || !this.ticket.id) {
return; return;
} }
if (owner === undefined) {
this.lodash.unset(this.ticket, "owner")
} else {
this.ticket.owner = owner; this.ticket.owner = owner;
}
API.updateTicket(this.ticket.id, this.toTicket(this.ticket)).then(response => { API.updateTicket(this.ticket.id, this.toTicket(this.ticket)).then(response => {
this.$store.dispatch("alertSuccess", { name: "Ticket owner changed" }); this.$store.dispatch("alertSuccess", { name: "Ticket owner changed" });