mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-06 07:12:46 +01:00
Improve user info (#47)
This commit is contained in:
6
cmd/catalyst-dev/images.go
Normal file
6
cmd/catalyst-dev/images.go
Normal file
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/api"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/generated/pointer"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
"github.com/SecurityBrewery/catalyst/test"
|
||||
@@ -39,12 +41,35 @@ func main() {
|
||||
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
|
||||
middlewares := []func(next http.Handler) http.Handler{
|
||||
catalyst.Authenticate(theCatalyst.DB, config.Auth),
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
|
||||
63
ui/src/components/User.vue
Normal file
63
ui/src/components/User.vue
Normal 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>
|
||||
@@ -12,8 +12,7 @@
|
||||
<v-icon small class="mr-1" :color="statusColor">{{ statusIcon }}</v-icon>
|
||||
<span :class="statusColor + '--text'">{{ ticket.status | capitalize }}</span>
|
||||
|
||||
<v-icon small class="mx-1">mdi-account</v-icon>
|
||||
{{ ticket.owner ? ticket.owner : 'unassigned' }}
|
||||
<User :id="ticket.owner" :size="16" class="mx-2"></User>
|
||||
<v-spacer></v-spacer>
|
||||
<v-icon small class="mr-1">mdi-source-branch</v-icon>
|
||||
<span class="mr-1">{{ ticket.playbooks ? lodash.size(ticket.playbooks) : 0 }}</span>
|
||||
@@ -38,10 +37,14 @@
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import {Playbook, Task, Type, TypeColorEnum} from "@/client";
|
||||
import User from "@/components/User.vue";
|
||||
|
||||
export default Vue.extend({
|
||||
name: "TicketSnippet",
|
||||
props: ["ticket", "to", "action"],
|
||||
components: {
|
||||
User
|
||||
},
|
||||
computed: {
|
||||
opentaskcount: function() {
|
||||
let count = 0;
|
||||
|
||||
@@ -103,38 +103,6 @@
|
||||
</v-list>
|
||||
</v-menu>
|
||||
·
|
||||
<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>
|
||||
·
|
||||
<v-icon small class="mr-1">mdi-calendar-plus</v-icon>
|
||||
{{ ticket.created | formatdate($store.state.settings.timeformat) }}
|
||||
·
|
||||
@@ -243,6 +211,63 @@
|
||||
>
|
||||
<v-icon class="mr-1">mdi-graph</v-icon> open graph
|
||||
</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">
|
||||
<span class="text--disabled">Playbooks</span>
|
||||
<v-spacer></v-spacer>
|
||||
@@ -796,19 +821,19 @@ import {alg, Graph} from "graphlib";
|
||||
import ArtifactSnippet from "../components/snippets/ArtifactSnippet.vue";
|
||||
import TicketSnippet from "../components/snippets/TicketSnippet.vue";
|
||||
import TicketList from "../components/TicketList.vue";
|
||||
import User from "../components/User.vue";
|
||||
import ArtifactPopup from "./ArtifactPopup.vue";
|
||||
|
||||
import {
|
||||
Ticket,
|
||||
TicketResponse,
|
||||
TicketTemplate,
|
||||
ModelFile,
|
||||
PlaybookTemplate,
|
||||
Reference,
|
||||
Task,
|
||||
Type,
|
||||
TypeColorEnum,
|
||||
TaskResponse, PlaybookResponse, UserResponse, TaskTypeEnum, TicketWithTickets,
|
||||
TaskResponse, PlaybookResponse, UserResponse, TicketWithTickets,
|
||||
} from "@/client";
|
||||
import {API} from "@/services/api";
|
||||
|
||||
@@ -818,7 +843,7 @@ import Tus from "@uppy/tus";
|
||||
import "@uppy/core/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 {DateTime} from "luxon";
|
||||
import VueMarkdown from "vue-markdown";
|
||||
@@ -897,7 +922,8 @@ export default Vue.extend({
|
||||
"vue-markdown": VueMarkdown,
|
||||
TicketList,
|
||||
ArtifactPopup,
|
||||
JSONHTML
|
||||
JSONHTML,
|
||||
User
|
||||
},
|
||||
data: (): State => ({
|
||||
valid: false,
|
||||
@@ -1447,12 +1473,16 @@ export default Vue.extend({
|
||||
this.editName = "";
|
||||
});
|
||||
},
|
||||
setOwner: function(owner: string) {
|
||||
setOwner: function(owner?: string) {
|
||||
if (!this.ticket || !this.ticket.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner === undefined) {
|
||||
this.lodash.unset(this.ticket, "owner")
|
||||
} else {
|
||||
this.ticket.owner = owner;
|
||||
}
|
||||
|
||||
API.updateTicket(this.ticket.id, this.toTicket(this.ticket)).then(response => {
|
||||
this.$store.dispatch("alertSuccess", { name: "Ticket owner changed" });
|
||||
|
||||
Reference in New Issue
Block a user