Skip to content

Commit

Permalink
chg: refactor tag selector to use tags table not taxonomies
Browse files Browse the repository at this point in the history
  • Loading branch information
righel committed Jan 22, 2025
1 parent c3c634f commit 434e5cf
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 100 deletions.
9 changes: 7 additions & 2 deletions api/app/repositories/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
from app.models import user as user_models
from app.schemas import tag as tag_schemas
from fastapi import HTTPException, status
from fastapi_pagination.ext.sqlalchemy import paginate
from pymisp import MISPTag
from sqlalchemy import func
from sqlalchemy.orm import Session


def get_tags(db: Session, skip: int = 0, limit: int = 100):
return db.query(tag_models.Tag).offset(skip).limit(limit).all()
def get_tags(
db: Session,
):
query = db.query(tag_models.Tag)

return paginate(query)


def get_tag_by_id(db: Session, tag_id: int):
Expand Down
70 changes: 50 additions & 20 deletions api/app/repositories/taxonomies.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def get_or_create_predicate(db: Session, db_taxonomy, raw_predicate):
return db_predicate


def get_or_create_predicate_tag(db: Session, db_taxonomy, raw_predicate):
predicate_tag = f'{db_taxonomy.namespace}:{raw_predicate["value"]}'
def get_or_create_predicate_tag(db: Session, db_taxonomy, db_predicate):
predicate_tag = f"{db_taxonomy.namespace}:{db_predicate.value}"
db_predicate_tag = (
db.query(tags_models.Tag)
.filter(
Expand All @@ -73,7 +73,7 @@ def get_or_create_predicate_tag(db: Session, db_taxonomy, raw_predicate):
if db_predicate_tag is None:
db_predicate_tag = tags_models.Tag(
name=predicate_tag,
colour=(raw_predicate["colour"] if "colour" in raw_predicate else ""),
colour=db_predicate.colour,
exportable=False,
hide_tag=False,
is_galaxy=False,
Expand Down Expand Up @@ -108,13 +108,16 @@ def get_or_create_entry(db: Session, db_predicate, raw_entry):
raw_entry["description"] if "description" in raw_entry else ""
),
)

return db_entry


def get_or_create_predicate_entry_tag(
db: Session, db_predicate_tag, raw_entry, db_predicate
db: Session, db_taxonomy, db_predicate, db_predicate_entry
):
predicate_entry_tag = f'{db_predicate_tag}:{raw_entry["value"]}'
predicate_entry_tag = (
f"{db_taxonomy.namespace}:{db_predicate.value}:{db_predicate_entry.value}"
)
db_predicate_entry_tag = (
db.query(tags_models.Tag)
.filter(
Expand All @@ -126,15 +129,14 @@ def get_or_create_predicate_entry_tag(
if db_predicate_entry_tag is None:
db_predicate_entry_tag = tags_models.Tag(
name=predicate_entry_tag,
colour=(
raw_entry["colour"] if "colour" in raw_entry else db_predicate.colour
),
colour=db_predicate_entry.colour,
exportable=False,
hide_tag=False,
is_galaxy=False,
is_custom_galaxy=False,
local_only=False,
)

return db_predicate_entry_tag


Expand Down Expand Up @@ -198,12 +200,6 @@ def update_taxonomies(db: Session):

predicates.append(db_predicate)

# check if the predicate exists in the tags table
db_predicate_tag = get_or_create_predicate_tag(
db, db_taxonomy, raw_predicate
)
db.add(db_predicate_tag)

# process entries
if "values" not in raw_taxonomy:
continue
Expand All @@ -221,17 +217,45 @@ def update_taxonomies(db: Session):
db_entry = get_or_create_entry(db, db_predicate, raw_entry)
db.add(db_entry)

# check if the predicate entry exists in the tags table
db_predicate_entry_tag = get_or_create_predicate_entry_tag(
db, db_predicate_tag, raw_entry, db_predicate
)
db.add(db_predicate_entry_tag)

db.commit()

return taxonomies


def enable_taxonomy_tags(db: Session, db_taxonomy):

for db_predicate in db_taxonomy.predicates:
# check if the predicate exists in the tags table
db_predicate_tag = get_or_create_predicate_tag(db, db_taxonomy, db_predicate)
db.add(db_predicate_tag)

for db_predicate_entry in db_predicate.entries:
# check if the predicate entry exists in the tags table
db_predicate_entry_tag = get_or_create_predicate_entry_tag(
db,
db_taxonomy,
db_predicate,
db_predicate_entry,
)
db.add(db_predicate_entry_tag)

db.commit()


def disable_taxonomy_tags(db: Session, db_taxonomy):
# delete all tags from the taxonomy
db_taxonomy_tags = (
db.query(tags_models.Tag)
.filter(tags_models.Tag.name.ilike(f"{db_taxonomy.namespace}:%"))
.all()
)

for tag in db_taxonomy_tags:
db.delete(tag)

db.commit()


def update_taxonomy(
db: Session,
taxonomy_id: int,
Expand All @@ -248,6 +272,12 @@ def update_taxonomy(
for key, value in taxonomy_patch.items():
setattr(db_taxonomy, key, value)

# if taxonomy is enabled, update the tags
if db_taxonomy.enabled and taxonomy_patch["enabled"]:
enable_taxonomy_tags(db, db_taxonomy)
elif not db_taxonomy.enabled and not taxonomy_patch["enabled"]:
disable_taxonomy_tags(db, db_taxonomy)

db.add(db_taxonomy)
db.commit()
db.refresh(db_taxonomy)
Expand Down
20 changes: 15 additions & 5 deletions api/app/routers/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@
from app.repositories import tags as tags_repository
from app.schemas import tag as tag_schemas
from app.schemas import user as user_schemas
from fastapi import APIRouter, Depends, HTTPException, Security, status
from fastapi import APIRouter, Depends, HTTPException, Query, Security, status
from fastapi_pagination import Page
from fastapi_pagination.customization import (
CustomizedPage,
UseModelConfig,
UseParamsFields,
)
from sqlalchemy.orm import Session

router = APIRouter()

Page = CustomizedPage[
Page,
UseModelConfig(extra="allow"),
UseParamsFields(size=Query(le=1000, default=20)),
]


@router.get("/tags/", response_model=list[tag_schemas.Tag])
@router.get("/tags/", response_model=Page[tag_schemas.Tag])
def get_tags(
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
user: user_schemas.User = Security(get_current_active_user, scopes=["tags:read"]),
):
return tags_repository.get_tags(db, skip=skip, limit=limit)
return tags_repository.get_tags(db)


@router.get("/tags/{tag_id}", response_model=tag_schemas.Tag)
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/attributes/AttributesIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import CopyToClipboard from "@/components/misc/CopyToClipboard.vue";
import Timestamp from "@/components/misc/Timestamp.vue";
import { Modal } from 'bootstrap';
const props = defineProps(['event_id', 'taxonomies', 'page_size']);
const props = defineProps(['event_id', 'page_size']);
const attributesStore = useAttributesStore();
const { page_count, attributes, status } = storeToRefs(attributesStore);
Expand Down Expand Up @@ -79,8 +79,7 @@ function handleAttributesUpdated(event) {
{{ attribute.value }}
</td>
<td class="d-none d-sm-table-cell">
<TagsSelect :modelClass="'attribute'" :model="attribute" :tags="attribute.tags"
:taxonomies="taxonomies" />
<TagsSelect :modelClass="'attribute'" :model="attribute" :selectedTags="attribute.tags" />
</td>
<td>{{ attribute.type }}</td>
<td class="d-none d-sm-table-cell">
Expand Down
29 changes: 17 additions & 12 deletions frontend/src/components/events/EventView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import ThreatLevel from "@/components/enums/ThreatLevel.vue";
import AnalysisLevel from "@/components/enums/AnalysisLevel.vue";
import DeleteEventModal from "@/components/events/DeleteEventModal.vue";
import { router } from "@/router";
import { useModulesStore, useTaxonomiesStore } from "@/stores";
import { useModulesStore, useTaxonomiesStore, useGalaxiesStore } from "@/stores";
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faTrash, faPen, faDownLong, faGlobe, faTags, faShapes, faCubesStacked } from '@fortawesome/free-solid-svg-icons'
const props = defineProps(['event_id', 'event', 'status']);
Expand All @@ -20,7 +22,11 @@ modulesStore.get({ enabled: true });
const taxonomiesStore = useTaxonomiesStore();
taxonomiesStore.get({ enabled: true, size: 1000 }); // FIXME: get all taxonomies
const galaxiesStore = useGalaxiesStore();
galaxiesStore.get({ enabled: true, size: 1000 }); // FIXME: get all galaxies
const { taxonomies } = storeToRefs(taxonomiesStore);
const { galaxies } = storeToRefs(galaxiesStore);
function handleEventDeleted(event) {
router.push(`/events`);
Expand Down Expand Up @@ -55,13 +61,13 @@ div.row h3 {
:class="{ 'btn-group-vertical': $isMobile, 'btn-group me-2': !$isMobile }"
aria-label="Event Actions">
<RouterLink :to="`/events/update/${event.id}`" tag="button" class="btn btn-outline-primary">
<font-awesome-icon icon="fa-solid fa-pen" />
<FontAwesomeIcon :icon="faPen" />
</RouterLink>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-danger" data-bs-toggle="modal"
:data-bs-target="'#deleteEventModal-' + event.id">
<font-awesome-icon icon="fa-solid fa-trash" />
<FontAwesomeIcon :icon="faTrash" />
</button>
</div>
</div>
Expand Down Expand Up @@ -145,12 +151,12 @@ div.row h3 {
<div class="mt-2">
<div class="card h-100">
<div class="card-header">
<font-awesome-icon icon="fa-solid fa-tags" /> tags
<FontAwesomeIcon :icon="faTags" /> tags
</div>
<div class="card-body d-flex flex-column">
<div class="card-text">
<TagsSelect v-if="taxonomies.items" :modelClass="'event'" :model="event"
:tags="event.tags" :taxonomies="taxonomies.items" />
:selectedTags="event.tags" />
</div>
</div>
</div>
Expand Down Expand Up @@ -182,7 +188,7 @@ div.row h3 {
<h2>8,523</h2>
</div>
<span class="badge badge-pill badge-cyan badge-red bg-warning fs-5">
<font-awesome-icon icon="fa-solid fa-down-long" />
<FontAwesomeIcon :icon="faDownLong" />
<span class="font-weight-semibold ml-1">6.71%</span>
</span>
</div>
Expand All @@ -203,7 +209,7 @@ div.row h3 {
<h2>423</h2>
</div>
<span class="badge badge-pill badge-cyan badge-red bg-danger fs-5">
<font-awesome-icon icon="fa-solid fa-up-long" />
<FontAwesomeIcon :icon="faDownLong" />
<span class="font-weight-semibold ml-1">16.71%</span>
</span>
</div>
Expand Down Expand Up @@ -231,11 +237,10 @@ div.row h3 {
<div class="col-12">
<div class="card">
<div class="card-header">
<font-awesome-icon icon="fa-solid fa-shapes" /> objects
<FontAwesomeIcon :icon="faShapes" /> objects
</div>
<div class="card-body d-flex flex-column">
<ObjectsIndex :event_id="event_id" :taxonomies="taxonomies.items"
:total_size="event.object_count" :page_size="10" />
<ObjectsIndex :event_id="event_id" :total_size="event.object_count" :page_size="10" />
</div>
</div>
</div>
Expand All @@ -244,10 +249,10 @@ div.row h3 {
<div class="col-12">
<div class="card">
<div class="card-header">
<font-awesome-icon icon="fa-solid fa-cubes-stacked" /> attributes
<FontAwesomeIcon :icon="faCubesStacked" /> attributes
</div>
<div class="card-body d-flex flex-column">
<AttributesIndex :event_id="event_id" :taxonomies="taxonomies.items" :page_size="10" />
<AttributesIndex :event_id="event_id" :page_size="10" />
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/objects/ObjectAttributesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Timestamp from "@/components/misc/Timestamp.vue";
import CopyToClipboard from "@/components/misc/CopyToClipboard.vue";
import AttributeActions from "@/components/attributes/AttributeActions.vue";
const props = defineProps(['object_id', 'attributes', 'taxonomies']);
const props = defineProps(['object_id', 'attributes']);
const emit = defineEmits(['attribute-created', 'attribute-updated', 'attribute-deleted', 'object-created', 'attribute-enriched']);
const attributes = ref(props.attributes);
Expand Down Expand Up @@ -57,7 +57,7 @@ function handleAttributeEnriched(attribute_id) {
{{ attribute.value }}
</td>
<td style="width: 20%" class="d-none d-sm-table-cell">
<TagsSelect :modelClass="'attribute'" :model="attribute" :tags="attribute.tags" :taxonomies="taxonomies"/>
<TagsSelect :modelClass="'attribute'" :model="attribute" :selectedTags="attribute.tags" >
</td>
<td style="width: 10%">{{ attribute.type }}</td>
<td style="width: 10%" class="d-none d-sm-table-cell">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/objects/ObjectsIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import DeleteObjectModal from "@/components/objects/DeleteObjectModal.vue";
import Spinner from "@/components/misc/Spinner.vue";
import Paginate from "vuejs-paginate-next";
const props = defineProps(['event_id','taxonomies', 'total_size', 'page_size']);
const props = defineProps(['event_id','total_size', 'page_size']);
let page_count = Math.ceil(props.total_size / props.page_size);
const objectsStore = useObjectsStore();
Expand Down
Loading

0 comments on commit 434e5cf

Please sign in to comment.