Skip to content

Commit

Permalink
[backend/frontend] First bunch of work on TTPs (#506)
Browse files Browse the repository at this point in the history
  • Loading branch information
SamuelHassine committed Jan 8, 2024
1 parent 6592b24 commit 143bd4c
Show file tree
Hide file tree
Showing 19 changed files with 750 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -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;
""");
}
}
Original file line number Diff line number Diff line change
@@ -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<AttackPattern> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> platforms = new ArrayList<>();

@JsonProperty("attack_pattern_permissions_required")
private List<String> 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<String> getPlatforms() {
return platforms;
}

public void setPlatforms(List<String> platforms) {
this.platforms = platforms;
}

public List<String> getPermissionsRequired() {
return permissionsRequired;
}

public void setPermissionsRequired(List<String> permissionsRequired) {
this.permissionsRequired = permissionsRequired;
}

public String getParentId() {
return parentId;
}

public void setParentId(String parentId) {
this.parentId = parentId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import io.openex.database.model.Grant;
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 GroupCreateInput {

@NotBlank(message = MANDATORY_MESSAGE)
Expand Down Expand Up @@ -77,4 +79,4 @@ public boolean isDefaultExercisePlanner() {
public void setDefaultExercisePlanner(boolean defaultExercisePlanner) {
this.defaultExercisePlanner = defaultExercisePlanner;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<KillChainPhase> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
16 changes: 4 additions & 12 deletions openex-front/src/admin/components/nav/TopMenuSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,13 @@ const TopMenuSettings: React.FC = () => {
</Button>
<Button
component={Link}
to="/admin/settings/tags"
variant={
location.pathname.includes('/admin/settings/tags')
? 'contained'
: 'text'
}
to="/admin/settings/taxonomies"
variant={location.pathname.includes('/admin/settings/taxonomies') ? 'contained' : 'text'}
size="small"
color={
location.pathname.includes('/admin/settings/tags')
? 'secondary'
: 'primary'
}
color="primary"
classes={{ root: classes.button }}
>
{t('Tags')}
{t('Taxonomies')}
</Button>
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion openex-front/src/admin/components/settings/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const Index = () => (
<Route path="security" element={<Navigate to="groups" replace={true} />} />
<Route path="security/groups" element={errorWrapper(Groups)()} />
<Route path="security/users" element={errorWrapper(Users)()} />
<Route path="tags" element={errorWrapper(Tags)()} />
<Route path="taxonomies" element={<Navigate to="tags" replace={true} />} />
<Route path="taxonomies/tags" element={errorWrapper(Tags)()} />
</Routes>
);

Expand Down
Loading

0 comments on commit 143bd4c

Please sign in to comment.