From 143bd4c400126d13d16c92db5ceca37426261ac2 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Mon, 8 Jan 2024 17:43:25 +0100 Subject: [PATCH] [backend/frontend] First bunch of work on TTPs (#506) --- .../migration/V2_66__Attack_patterns.java | 78 ++++++++ .../rest/attack_pattern/AttackPatternApi.java | 51 +++++ .../form/AttackPatternCreateInput.java | 82 ++++++++ .../rest/group/form/GroupCreateInput.java | 4 +- .../kill_chain_phase/KillChainPhaseApi.java | 50 +++++ .../form/KillChainPhaseCreateInput.java | 46 +++++ .../admin/components/nav/TopMenuSettings.tsx | 16 +- .../src/admin/components/settings/Index.tsx | 3 +- .../components/settings/TaxonomiesMenu.tsx | 76 ++++++++ .../components/settings/tags/CreateTag.js | 10 +- .../admin/components/settings/tags/Tags.js | 12 +- openex-front/src/components/ThemeDark.ts | 3 +- openex-front/src/components/ThemeLight.ts | 3 +- openex-front/src/utils/Localization.js | 11 +- openex-model/pom.xml | 5 + .../openex/database/model/AttackPattern.java | 180 ++++++++++++++++++ .../openex/database/model/KillChainPhase.java | 120 ++++++++++++ .../repository/AttackPatternRepository.java | 16 ++ .../repository/KillChainPhaseRepository.java | 16 ++ 19 files changed, 750 insertions(+), 32 deletions(-) create mode 100644 openex-api/src/main/java/io/openex/migration/V2_66__Attack_patterns.java create mode 100644 openex-api/src/main/java/io/openex/rest/attack_pattern/AttackPatternApi.java create mode 100644 openex-api/src/main/java/io/openex/rest/attack_pattern/form/AttackPatternCreateInput.java create mode 100644 openex-api/src/main/java/io/openex/rest/kill_chain_phase/KillChainPhaseApi.java create mode 100644 openex-api/src/main/java/io/openex/rest/kill_chain_phase/form/KillChainPhaseCreateInput.java create mode 100644 openex-front/src/admin/components/settings/TaxonomiesMenu.tsx create mode 100644 openex-model/src/main/java/io/openex/database/model/AttackPattern.java create mode 100644 openex-model/src/main/java/io/openex/database/model/KillChainPhase.java create mode 100644 openex-model/src/main/java/io/openex/database/repository/AttackPatternRepository.java create mode 100644 openex-model/src/main/java/io/openex/database/repository/KillChainPhaseRepository.java diff --git a/openex-api/src/main/java/io/openex/migration/V2_66__Attack_patterns.java b/openex-api/src/main/java/io/openex/migration/V2_66__Attack_patterns.java new file mode 100644 index 0000000000..4c070dc33e --- /dev/null +++ b/openex-api/src/main/java/io/openex/migration/V2_66__Attack_patterns.java @@ -0,0 +1,78 @@ +package io.openex.migration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; + +@Component +public class V2_66__Attack_patterns extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Connection connection = context.getConnection(); + Statement select = connection.createStatement(); + // Kill chain phases + select.execute(""" + CREATE TABLE kill_chain_phases ( + phase_id varchar(255) not null constraint kill_chain_phases_pkey primary key, + phase_created_at timestamp not null default now(), + phase_updated_at timestamp not null default now(), + phase_name varchar(255) not null, + phase_kill_chain_name varchar(255) not null, + phase_order bigint not null + ); + CREATE INDEX idx_kill_chain_phases on kill_chain_phases (phase_id); + CREATE UNIQUE INDEX kill_chain_phases_unique on kill_chain_phases (phase_name, phase_kill_chain_name); + """); + // Attack patterns + select.execute(""" + CREATE TABLE attack_patterns ( + attack_pattern_id varchar(255) not null constraint attack_patterns_pkey primary key, + attack_pattern_created_at timestamp not null default now(), + attack_pattern_updated_at timestamp not null default now(), + attack_pattern_name varchar(255) not null, + attack_pattern_description text, + attack_pattern_external_id varchar(255) not null, + attack_pattern_platforms text[], + attack_pattern_permissions_required text[], + attack_pattern_parent varchar(255) + constraint attack_pattern_parent_fk + references attack_patterns + on delete cascade + ); + CREATE INDEX idx_attack_patterns on attack_patterns (attack_pattern_id); + CREATE UNIQUE INDEX attack_patterns_unique on attack_patterns (attack_pattern_external_id); + """); + select.execute(""" + CREATE TABLE attack_patterns_kill_chain_phases ( + attack_pattern_id varchar(255) not null + constraint attack_pattern_id_fk + references attack_patterns + on delete cascade, + phase_id varchar(255) not null + constraint phase_id_fk + references kill_chain_phases + on delete cascade, + primary key (attack_pattern_id, phase_id) + ); + CREATE INDEX idx_attack_patterns_kill_chain_phases_attack_pattern on attack_patterns_kill_chain_phases (attack_pattern_id); + CREATE INDEX idx_attack_patterns_kill_chain_phases_kill_chain_phase on attack_patterns_kill_chain_phases (phase_id); + """); + // Cleanup a bit indexes + select.execute(""" + CREATE INDEX idx_teams on teams (team_id); + ALTER INDEX idx_medias RENAME TO idx_channels; + ALTER INDEX idx_4e039727729413d0 RENAME TO idx_comchecks; + ALTER INDEX idx_article_media_exercise RENAME TO idx_articles_channel_exercise; + ALTER INDEX idx_1483a5e941221f7e RENAME TO idx_users_organization; + ALTER INDEX idx_cfb417fca76ed395 RENAME TO idx_users_teams_user; + ALTER INDEX idx_cfb417fccb0ca5a3 RENAME TO idx_users_teams_team; + """); + } +} diff --git a/openex-api/src/main/java/io/openex/rest/attack_pattern/AttackPatternApi.java b/openex-api/src/main/java/io/openex/rest/attack_pattern/AttackPatternApi.java new file mode 100644 index 0000000000..232ad37cb8 --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/attack_pattern/AttackPatternApi.java @@ -0,0 +1,51 @@ +package io.openex.rest.attack_pattern; + +import io.openex.database.model.AttackPattern; +import io.openex.database.repository.AttackPatternRepository; +import io.openex.rest.attack_pattern.form.AttackPatternCreateInput; +import io.openex.rest.helper.RestBehavior; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.security.RolesAllowed; +import javax.validation.Valid; + +import static io.openex.database.model.User.ROLE_ADMIN; +import static io.openex.database.model.User.ROLE_USER; + +@RestController +@RolesAllowed(ROLE_USER) +public class AttackPatternApi extends RestBehavior { + + private AttackPatternRepository attackPatternRepository; + + @Autowired + public void setAttackPatternRepository(AttackPatternRepository attackPatternRepository) { + this.attackPatternRepository = attackPatternRepository; + } + + @GetMapping("/api/attack_patterns") + public Iterable attackPatterns() { + return attackPatternRepository.findAll(); + } + + @GetMapping("/api/attack_patterns/{attackPatternId}") + public AttackPattern attackPattern(@PathVariable String attackPatternId) { + return attackPatternRepository.findById(attackPatternId).orElseThrow(); + } + + @RolesAllowed(ROLE_ADMIN) + @PostMapping("/api/attack_patterns") + public AttackPattern createAttackPattern(@Valid @RequestBody AttackPatternCreateInput input) { + AttackPattern attackPattern = new AttackPattern(); + attackPattern.setUpdateAttributes(input); + attackPattern.setParent(attackPatternRepository.findById(input.getParentId()).orElseThrow()); + return attackPatternRepository.save(attackPattern); + } + + @RolesAllowed(ROLE_ADMIN) + @DeleteMapping("/api/attack_patterns/{attackPatternId}") + public void deleteAttackPattern(@PathVariable String attackPatternId) { + attackPatternRepository.deleteById(attackPatternId); + } +} diff --git a/openex-api/src/main/java/io/openex/rest/attack_pattern/form/AttackPatternCreateInput.java b/openex-api/src/main/java/io/openex/rest/attack_pattern/form/AttackPatternCreateInput.java new file mode 100644 index 0000000000..bad8cbee20 --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/attack_pattern/form/AttackPatternCreateInput.java @@ -0,0 +1,82 @@ +package io.openex.rest.attack_pattern.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +import javax.validation.constraints.NotBlank; +import java.util.ArrayList; +import java.util.List; + +import static io.openex.config.AppConfig.MANDATORY_MESSAGE; + +@Getter +public class AttackPatternCreateInput { + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("attack_pattern_name") + private String name; + + @JsonProperty("attack_pattern_description") + private String description; + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("attack_pattern_external_id") + private String externalId; + + @JsonProperty("attack_pattern_platforms") + private List platforms = new ArrayList<>(); + + @JsonProperty("attack_pattern_permissions_required") + private List permissionsRequired = new ArrayList<>(); + + @JsonProperty("attack_pattern_parent") + private String parentId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getExternalId() { + return externalId; + } + + public void setExternalId(String externalId) { + this.externalId = externalId; + } + + public List getPlatforms() { + return platforms; + } + + public void setPlatforms(List platforms) { + this.platforms = platforms; + } + + public List getPermissionsRequired() { + return permissionsRequired; + } + + public void setPermissionsRequired(List permissionsRequired) { + this.permissionsRequired = permissionsRequired; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } +} \ No newline at end of file diff --git a/openex-api/src/main/java/io/openex/rest/group/form/GroupCreateInput.java b/openex-api/src/main/java/io/openex/rest/group/form/GroupCreateInput.java index 74ff78b452..7a5575bacf 100644 --- a/openex-api/src/main/java/io/openex/rest/group/form/GroupCreateInput.java +++ b/openex-api/src/main/java/io/openex/rest/group/form/GroupCreateInput.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.openex.database.model.Grant; +import lombok.Getter; import javax.validation.constraints.NotBlank; import java.util.ArrayList; @@ -9,6 +10,7 @@ import static io.openex.config.AppConfig.MANDATORY_MESSAGE; +@Getter public class GroupCreateInput { @NotBlank(message = MANDATORY_MESSAGE) @@ -77,4 +79,4 @@ public boolean isDefaultExercisePlanner() { public void setDefaultExercisePlanner(boolean defaultExercisePlanner) { this.defaultExercisePlanner = defaultExercisePlanner; } -} +} \ No newline at end of file diff --git a/openex-api/src/main/java/io/openex/rest/kill_chain_phase/KillChainPhaseApi.java b/openex-api/src/main/java/io/openex/rest/kill_chain_phase/KillChainPhaseApi.java new file mode 100644 index 0000000000..e3817ae11e --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/kill_chain_phase/KillChainPhaseApi.java @@ -0,0 +1,50 @@ +package io.openex.rest.kill_chain_phase; + +import io.openex.database.model.KillChainPhase; +import io.openex.database.repository.KillChainPhaseRepository; +import io.openex.rest.helper.RestBehavior; +import io.openex.rest.kill_chain_phase.form.KillChainPhaseCreateInput; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.security.RolesAllowed; +import javax.validation.Valid; + +import static io.openex.database.model.User.ROLE_ADMIN; +import static io.openex.database.model.User.ROLE_USER; + +@RestController +@RolesAllowed(ROLE_USER) +public class KillChainPhaseApi extends RestBehavior { + + private KillChainPhaseRepository killChainPhaseRepository; + + @Autowired + public void setKillChainPhaseRepository(KillChainPhaseRepository killChainPhaseRepository) { + this.killChainPhaseRepository = killChainPhaseRepository; + } + + @GetMapping("/api/kill_chain_phases") + public Iterable killChainPhases() { + return killChainPhaseRepository.findAll(); + } + + @GetMapping("/api/kill_chain_phases/{killChainPhaseId}") + public KillChainPhase killChainPhase(@PathVariable String killChainPhaseId) { + return killChainPhaseRepository.findById(killChainPhaseId).orElseThrow(); + } + + @RolesAllowed(ROLE_ADMIN) + @PostMapping("/api/kill_chain_phases") + public KillChainPhase createKillChainPhase(@Valid @RequestBody KillChainPhaseCreateInput input) { + KillChainPhase killChainPhase = new KillChainPhase(); + killChainPhase.setUpdateAttributes(input); + return killChainPhaseRepository.save(killChainPhase); + } + + @RolesAllowed(ROLE_ADMIN) + @DeleteMapping("/api/kill_chain_phases/{killChainPhaseId}") + public void deleteKillChainPhase(@PathVariable String killChainPhaseId) { + killChainPhaseRepository.deleteById(killChainPhaseId); + } +} diff --git a/openex-api/src/main/java/io/openex/rest/kill_chain_phase/form/KillChainPhaseCreateInput.java b/openex-api/src/main/java/io/openex/rest/kill_chain_phase/form/KillChainPhaseCreateInput.java new file mode 100644 index 0000000000..904096778a --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/kill_chain_phase/form/KillChainPhaseCreateInput.java @@ -0,0 +1,46 @@ +package io.openex.rest.kill_chain_phase.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +import javax.validation.constraints.NotBlank; + +import static io.openex.config.AppConfig.MANDATORY_MESSAGE; + +@Getter +public class KillChainPhaseCreateInput { + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("phase_name") + private String name; + + @JsonProperty("phase_kill_chain_name") + private String killChainName; + + @JsonProperty("phase_order") + private Long order; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getKillChainName() { + return killChainName; + } + + public void setKillChainName(String killChainName) { + this.killChainName = killChainName; + } + + public Long getOrder() { + return order; + } + + public void setOrder(Long order) { + this.order = order; + } +} \ No newline at end of file diff --git a/openex-front/src/admin/components/nav/TopMenuSettings.tsx b/openex-front/src/admin/components/nav/TopMenuSettings.tsx index 0070e4723c..20e02c1c52 100644 --- a/openex-front/src/admin/components/nav/TopMenuSettings.tsx +++ b/openex-front/src/admin/components/nav/TopMenuSettings.tsx @@ -44,21 +44,13 @@ const TopMenuSettings: React.FC = () => { ); diff --git a/openex-front/src/admin/components/settings/Index.tsx b/openex-front/src/admin/components/settings/Index.tsx index 86629f6c2e..148715535d 100644 --- a/openex-front/src/admin/components/settings/Index.tsx +++ b/openex-front/src/admin/components/settings/Index.tsx @@ -12,7 +12,8 @@ const Index = () => ( } /> - + } /> + ); diff --git a/openex-front/src/admin/components/settings/TaxonomiesMenu.tsx b/openex-front/src/admin/components/settings/TaxonomiesMenu.tsx new file mode 100644 index 0000000000..f3c1657ec6 --- /dev/null +++ b/openex-front/src/admin/components/settings/TaxonomiesMenu.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { Drawer, MenuList, MenuItem, ListItemIcon, ListItemText } from '@mui/material'; +import { LockPattern } from 'mdi-material-ui'; +import { StyleOutlined, RouteOutlined } from '@mui/icons-material'; +import { makeStyles } from '@mui/styles'; +import { useFormatter } from '../../../components/i18n'; +import type { Theme } from '../../../components/Theme'; + +const useStyles = makeStyles((theme: Theme) => ({ + drawer: { + minHeight: '100vh', + width: 200, + position: 'fixed', + overflow: 'auto', + padding: 0, + backgroundColor: theme.palette.background.nav, + }, + toolbar: theme.mixins.toolbar, + item: { + paddingTop: 10, + paddingBottom: 10, + }, +})); + +const DefinitionMenu: React.FC = () => { + const location = useLocation(); + const classes = useStyles(); + const { t } = useFormatter(); + return ( + +
+ + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default DefinitionMenu; diff --git a/openex-front/src/admin/components/settings/tags/CreateTag.js b/openex-front/src/admin/components/settings/tags/CreateTag.js index 992c9e726c..7735256212 100644 --- a/openex-front/src/admin/components/settings/tags/CreateTag.js +++ b/openex-front/src/admin/components/settings/tags/CreateTag.js @@ -2,23 +2,19 @@ import React, { Component } from 'react'; import * as PropTypes from 'prop-types'; import { connect } from 'react-redux'; import * as R from 'ramda'; -import { Dialog, DialogTitle, DialogContent, Slide, Fab } from '@mui/material'; +import { Dialog, DialogTitle, DialogContent, Fab } from '@mui/material'; import { Add } from '@mui/icons-material'; import withStyles from '@mui/styles/withStyles'; import { addTag } from '../../../../actions/Tag'; import TagForm from './TagForm'; import inject18n from '../../../../components/i18n'; - -const Transition = React.forwardRef((props, ref) => ( - -)); -Transition.displayName = 'TransitionSlide'; +import Transition from '../../../../components/common/Transition'; const styles = () => ({ createButton: { position: 'fixed', bottom: 30, - right: 30, + right: 230, }, }); diff --git a/openex-front/src/admin/components/settings/tags/Tags.js b/openex-front/src/admin/components/settings/tags/Tags.js index 2fc5ddcc5c..9582a4e85e 100644 --- a/openex-front/src/admin/components/settings/tags/Tags.js +++ b/openex-front/src/admin/components/settings/tags/Tags.js @@ -13,15 +13,20 @@ import SearchFilter from '../../../../components/SearchFilter'; import CreateTag from './CreateTag'; import TagPopover from './TagPopover'; import { storeHelper } from '../../../../actions/Schema'; +import TaxonomiesMenu from '../TaxonomiesMenu'; const interval$ = interval(FIVE_SECONDS); const styles = (theme) => ({ + container: { + margin: 0, + padding: '0 200px 50px 0', + }, parameters: { float: 'left', marginTop: -10, }, - container: { + list: { marginTop: 10, }, itemHead: { @@ -169,7 +174,8 @@ class Tags extends Component { ); const sortedTags = R.pipe(R.filter(filterByKeyword), sort)(tags); return ( -
+
+
- + jackson-datatype-jsr310 ${jackson.version} + + io.hypersistence + hypersistence-utils-hibernate-55 + 3.7.0 + io.minio minio diff --git a/openex-model/src/main/java/io/openex/database/model/AttackPattern.java b/openex-model/src/main/java/io/openex/database/model/AttackPattern.java new file mode 100644 index 0000000000..59d9359ca0 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/model/AttackPattern.java @@ -0,0 +1,180 @@ +package io.openex.database.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.hypersistence.utils.hibernate.type.array.ListArrayType; +import io.openex.database.audit.ModelBaseListener; +import io.openex.helper.MonoIdDeserializer; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Type; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static java.time.Instant.now; + +@Getter +@Entity +@Table(name = "attack_patterns") +@EntityListeners(ModelBaseListener.class) +public class AttackPattern implements Base { + + @Getter + @Setter + @Id + @Column(name = "attack_pattern_id") + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") + @JsonProperty("attack_pattern_id") + @NotBlank + private String id; + + @Getter + @Setter + @Column(name = "attack_pattern_name") + @JsonProperty("attack_pattern_name") + @NotBlank + private String name; + + @Getter + @Setter + @Column(name = "attack_pattern_description") + @JsonProperty("attack_pattern_description") + private String description; + + @Getter + @Setter + @Column(name = "attack_pattern_external_id") + @JsonProperty("attack_pattern_external_id") + @NotBlank + private String externalId; + + @Getter + @Setter + @Type(type = "list-array") + // @Type(ListArrayType.class) + // TODO: For migration to Hibernate 6 + @Column(name = "attack_pattern_platforms", columnDefinition = "text[]") + @JsonProperty("attack_pattern_platforms") + private List platforms = new ArrayList<>(); + + @Getter + @Setter + @Type(type = "list-array") + // @Type(ListArrayType.class) + // TODO: For migration to Hibernate 6 + @Column(name = "attack_pattern_permissions_required") + @JsonProperty("attack_pattern_permissions_required") + private ArrayList permissionsRequired = new ArrayList<>(); + + @Getter + @Setter + @Column(name = "attack_pattern_created_at") + @JsonProperty("attack_pattern_created_at") + private Instant createdAt = now(); + + @Getter + @Setter + @Column(name = "attack_pattern_updated_at") + @JsonProperty("attack_pattern_updated_at") + private Instant updatedAt = now(); + + @Getter + @Setter + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "attack_pattern_parent") + @JsonSerialize(using = MonoIdDeserializer.class) + @JsonProperty("attack_pattern_parent") + private AttackPattern parent; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getExternalId() { + return externalId; + } + + public void setExternalId(String externalId) { + this.externalId = externalId; + } + + public List getPlatforms() { + return platforms; + } + + public void setPlatforms(List platforms) { + this.platforms = platforms; + } + + public ArrayList getPermissionsRequired() { + return permissionsRequired; + } + + public void setPermissionsRequired(ArrayList permissionsRequired) { + this.permissionsRequired = permissionsRequired; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = updatedAt; + } + + public AttackPattern getParent() { + return parent; + } + + public void setParent(AttackPattern parent) { + this.parent = parent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !Base.class.isAssignableFrom(o.getClass())) return false; + Base base = (Base) o; + return id.equals(base.getId()); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/openex-model/src/main/java/io/openex/database/model/KillChainPhase.java b/openex-model/src/main/java/io/openex/database/model/KillChainPhase.java new file mode 100644 index 0000000000..4d34d285d7 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/model/KillChainPhase.java @@ -0,0 +1,120 @@ +package io.openex.database.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.openex.database.audit.ModelBaseListener; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.time.Instant; +import java.util.Objects; + +import static java.time.Instant.now; + +@Entity +@Table(name = "kill_chain_phases") +@EntityListeners(ModelBaseListener.class) +public class KillChainPhase implements Base { + @Getter + @Setter + @Id + @Column(name = "phase_id") + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") + @JsonProperty("phase_id") + private String id; + + @Getter + @Setter + @Column(name = "phase_name") + @JsonProperty("phase_name") + private String name; + + @Getter + @Setter + @Column(name = "phase_kill_chain_name") + @JsonProperty("phase_kill_chain_name") + private String killChainName; + + @Getter + @Setter + @Column(name = "phase_order") + @JsonProperty("phase_order") + private Long order; + + @Getter + @Setter + @Column(name = "phase_created_at") + @JsonProperty("phase_created_at") + private Instant createdAt = now(); + + @Getter + @Setter + @Column(name = "phase_updated_at") + @JsonProperty("phase_updated_at") + private Instant updatedAt = now(); + // endregion + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getKillChainName() { + return killChainName; + } + + public void setKillChainName(String killChainName) { + this.killChainName = killChainName; + } + + public Long getOrder() { + return order; + } + + public void setOrder(Long order) { + this.order = order; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = updatedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !Base.class.isAssignableFrom(o.getClass())) return false; + Base base = (Base) o; + return id.equals(base.getId()); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/openex-model/src/main/java/io/openex/database/repository/AttackPatternRepository.java b/openex-model/src/main/java/io/openex/database/repository/AttackPatternRepository.java new file mode 100644 index 0000000000..ec58158a93 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/repository/AttackPatternRepository.java @@ -0,0 +1,16 @@ +package io.openex.database.repository; + +import io.openex.database.model.AttackPattern; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import javax.validation.constraints.NotNull; +import java.util.Optional; + +@Repository +public interface AttackPatternRepository extends CrudRepository, JpaSpecificationExecutor { + + @NotNull + Optional findById(@NotNull String id); +} diff --git a/openex-model/src/main/java/io/openex/database/repository/KillChainPhaseRepository.java b/openex-model/src/main/java/io/openex/database/repository/KillChainPhaseRepository.java new file mode 100644 index 0000000000..e985d6f61d --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/repository/KillChainPhaseRepository.java @@ -0,0 +1,16 @@ +package io.openex.database.repository; + +import io.openex.database.model.KillChainPhase; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import javax.validation.constraints.NotNull; +import java.util.Optional; + +@Repository +public interface KillChainPhaseRepository extends CrudRepository, JpaSpecificationExecutor { + + @NotNull + Optional findById(@NotNull String id); +}