From d76642beb8701c40de6443ef7709d4c466686f2b Mon Sep 17 00:00:00 2001 From: Romuald Lemesle Date: Thu, 28 Dec 2023 11:44:18 +0100 Subject: [PATCH] [backend] Asset, Endpoint & AssetGroup (#268) --- openex-api/pom.xml | 1 - .../migration/V2_64__Assets_Asset_Groups.java | 59 ++++++++++++ .../rest/asset/endpoint/EndpointApi.java | 54 +++++++++++ .../asset/endpoint/form/EndpointInput.java | 28 ++++++ .../io/openex/rest/asset/form/AssetInput.java | 20 ++++ .../asset_group/form/AssetGroupInput.java | 21 +++++ .../io/openex/service/AssetGroupService.java | 42 +++++++++ .../openex/service/asset/EndpointService.java | 42 +++++++++ .../openex/service/AssetGroupServiceTest.java | 91 +++++++++++++++++++ .../openex/service/EndpointServiceTest.java | 90 ++++++++++++++++++ openex-model/pom.xml | 5 + .../annotation/Ipv4OrIpv6Constraint.java | 24 +++++ .../java/io/openex/database/model/Asset.java | 48 ++++++++++ .../io/openex/database/model/AssetGroup.java | 55 +++++++++++ .../io/openex/database/model/Endpoint.java | 39 ++++++++ .../repository/AssetGroupRepository.java | 10 ++ .../database/repository/AssetRepository.java | 8 ++ .../repository/EndpointRepository.java | 8 ++ .../openex/validator/Ipv4OrIpv6Validator.java | 17 ++++ pom.xml | 1 + 20 files changed, 662 insertions(+), 1 deletion(-) create mode 100644 openex-api/src/main/java/io/openex/migration/V2_64__Assets_Asset_Groups.java create mode 100644 openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java create mode 100644 openex-api/src/main/java/io/openex/rest/asset/endpoint/form/EndpointInput.java create mode 100644 openex-api/src/main/java/io/openex/rest/asset/form/AssetInput.java create mode 100644 openex-api/src/main/java/io/openex/rest/asset_group/form/AssetGroupInput.java create mode 100644 openex-api/src/main/java/io/openex/service/AssetGroupService.java create mode 100644 openex-api/src/main/java/io/openex/service/asset/EndpointService.java create mode 100644 openex-api/src/test/java/io/openex/service/AssetGroupServiceTest.java create mode 100644 openex-api/src/test/java/io/openex/service/EndpointServiceTest.java create mode 100644 openex-model/src/main/java/io/openex/annotation/Ipv4OrIpv6Constraint.java create mode 100644 openex-model/src/main/java/io/openex/database/model/Asset.java create mode 100644 openex-model/src/main/java/io/openex/database/model/AssetGroup.java create mode 100644 openex-model/src/main/java/io/openex/database/model/Endpoint.java create mode 100644 openex-model/src/main/java/io/openex/database/repository/AssetGroupRepository.java create mode 100644 openex-model/src/main/java/io/openex/database/repository/AssetRepository.java create mode 100644 openex-model/src/main/java/io/openex/database/repository/EndpointRepository.java create mode 100644 openex-model/src/main/java/io/openex/validator/Ipv4OrIpv6Validator.java diff --git a/openex-api/pom.xml b/openex-api/pom.xml index 3107982e94..b045856cc7 100644 --- a/openex-api/pom.xml +++ b/openex-api/pom.xml @@ -18,7 +18,6 @@ 1.16.0 4.4 1.5 - 1.7 9.0.3 4.1.1 1.7.0 diff --git a/openex-api/src/main/java/io/openex/migration/V2_64__Assets_Asset_Groups.java b/openex-api/src/main/java/io/openex/migration/V2_64__Assets_Asset_Groups.java new file mode 100644 index 0000000000..87c024ceb8 --- /dev/null +++ b/openex-api/src/main/java/io/openex/migration/V2_64__Assets_Asset_Groups.java @@ -0,0 +1,59 @@ +package io.openex.migration; + +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.Statement; + +@Component +public class V2_64__Assets_Asset_Groups extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Connection connection = context.getConnection(); + Statement select = connection.createStatement(); + // Create table asset + select.execute(""" + CREATE TABLE IF NOT EXISTS assets ( + asset_id varchar(255) not null constraint assets_pkey primary key, + asset_external_id varchar(255), + asset_name varchar(255) not null, + asset_description text, + asset_created_at timestamp not null default now(), + asset_updated_at timestamp not null default now() + ); + CREATE INDEX IF NOT EXISTS idx_assets on assets (asset_id); + """); + // Create table endpoint + select.execute(""" + CREATE TABLE IF NOT EXISTS endpoints ( + endpoint_ip varchar(255) not null constraint endpoint_pkey primary key, + endpoint_hostname varchar(255), + endpoint_os varchar(255) + ) INHERITS (assets); + """); + // Create table asset groups + select.execute(""" + CREATE TABLE IF NOT EXISTS asset_groups ( + asset_group_id varchar(255) not null constraint asset_groups_pkey primary key, + asset_group_name varchar(255) not null, + asset_group_description text, + asset_group_created_at timestamp not null default now(), + asset_group_updated_at timestamp not null default now() + ); + CREATE INDEX IF NOT EXISTS idx_asset_groups on asset_groups (asset_group_id); + """); + // Add association table between asset and asset groups + select.execute(""" + CREATE TABLE IF NOT EXISTS asset_groups_assets ( + asset_group_id varchar(255) not null constraint asset_group_id_fk references asset_groups on delete cascade, + asset_id varchar(255) not null constraint asset_id_fk references endpoints on delete cascade, + constraint asset_groups_assets_pkey primary key (asset_group_id, asset_id) + ); + CREATE INDEX IF NOT EXISTS idx_asset_groups_assets_asset on asset_groups_assets (asset_group_id); + CREATE INDEX IF NOT EXISTS idx_asset_groups_assets_asset on asset_groups_assets (asset_id); + """); + } +} diff --git a/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java b/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java new file mode 100644 index 0000000000..bfc4312182 --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java @@ -0,0 +1,54 @@ +package io.openex.rest.asset.endpoint; + +import io.openex.database.model.Endpoint; +import io.openex.rest.asset.endpoint.form.EndpointInput; +import io.openex.service.asset.EndpointService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.security.RolesAllowed; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; + +import static io.openex.database.model.User.ROLE_ADMIN; + +@RequiredArgsConstructor +@RestController +public class EndpointApi { + + public static final String ENDPOINT_URI = "/api/endpoints"; + + private final EndpointService endpointService; + + @PostMapping(ENDPOINT_URI) + @RolesAllowed(ROLE_ADMIN) + public Endpoint createEndpoint(@Valid @RequestBody final EndpointInput input) { + Endpoint endpoint = new Endpoint(); + endpoint.setUpdateAttributes(input); + return this.endpointService.createEndpoint(endpoint); + } + + @GetMapping(ENDPOINT_URI) + @PreAuthorize("isObserver()") + public List endpoints() { + return this.endpointService.endpoints(); + } + + @PutMapping(ENDPOINT_URI + "/{endpointId}") + @RolesAllowed(ROLE_ADMIN) + public Endpoint updateEndpoint( + @PathVariable @NotBlank final String endpointId, + @Valid @RequestBody final EndpointInput input) { + Endpoint endpoint = this.endpointService.endpoint(endpointId); + endpoint.setUpdateAttributes(input); + return this.endpointService.updateEndpoint(endpoint); + } + + @DeleteMapping(ENDPOINT_URI + "/{endpointId}") + @RolesAllowed(ROLE_ADMIN) + public void deleteEndpoint(@PathVariable @NotBlank final String endpointId) { + this.endpointService.deleteEndpoint(endpointId); + } +} diff --git a/openex-api/src/main/java/io/openex/rest/asset/endpoint/form/EndpointInput.java b/openex-api/src/main/java/io/openex/rest/asset/endpoint/form/EndpointInput.java new file mode 100644 index 0000000000..287ff26629 --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/asset/endpoint/form/EndpointInput.java @@ -0,0 +1,28 @@ +package io.openex.rest.asset.endpoint.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.openex.rest.asset.form.AssetInput; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; + +import static io.openex.config.AppConfig.MANDATORY_MESSAGE; + +@EqualsAndHashCode(callSuper = true) +@Data +public class EndpointInput extends AssetInput { + + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("endpoint_ip") + private String ip; + + @JsonProperty("endpoint_hostname") + private String hostname; + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("endpoint_os") + private String os; + +} diff --git a/openex-api/src/main/java/io/openex/rest/asset/form/AssetInput.java b/openex-api/src/main/java/io/openex/rest/asset/form/AssetInput.java new file mode 100644 index 0000000000..32af424dc4 --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/asset/form/AssetInput.java @@ -0,0 +1,20 @@ +package io.openex.rest.asset.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +import static io.openex.config.AppConfig.MANDATORY_MESSAGE; + +@Data +public abstract class AssetInput { + + @JsonProperty("asset_external_id") + private String externalId; + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("asset_name") + private String name; + +} diff --git a/openex-api/src/main/java/io/openex/rest/asset_group/form/AssetGroupInput.java b/openex-api/src/main/java/io/openex/rest/asset_group/form/AssetGroupInput.java new file mode 100644 index 0000000000..fee57f4065 --- /dev/null +++ b/openex-api/src/main/java/io/openex/rest/asset_group/form/AssetGroupInput.java @@ -0,0 +1,21 @@ +package io.openex.rest.asset_group.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +import static io.openex.config.AppConfig.MANDATORY_MESSAGE; + +@Getter +@Setter +public class AssetGroupInput { + + @NotBlank(message = MANDATORY_MESSAGE) + @JsonProperty("asset_group_name") + private String name; + + @JsonProperty("asset_group_description") + private String description; +} diff --git a/openex-api/src/main/java/io/openex/service/AssetGroupService.java b/openex-api/src/main/java/io/openex/service/AssetGroupService.java new file mode 100644 index 0000000000..1039678c14 --- /dev/null +++ b/openex-api/src/main/java/io/openex/service/AssetGroupService.java @@ -0,0 +1,42 @@ +package io.openex.service; + +import io.openex.database.model.AssetGroup; +import io.openex.database.repository.AssetGroupRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +import static io.openex.helper.StreamHelper.fromIterable; +import static java.time.Instant.now; + +@RequiredArgsConstructor +@Service +public class AssetGroupService { + + private final AssetGroupRepository assetGroupRepository; + + public AssetGroup createAssetGroup(@NotNull final AssetGroup assetGroup) { + return this.assetGroupRepository.save(assetGroup); + } + + public AssetGroup assetGroup(@NotBlank final String assetGroupId) { + return this.assetGroupRepository.findById(assetGroupId).orElseThrow(); + } + + public List assetGroups() { + return fromIterable(this.assetGroupRepository.findAll()); + } + + public AssetGroup updateAssetGroup(@NotNull final AssetGroup assetGroup) { + assetGroup.setUpdatedAt(now()); + return this.assetGroupRepository.save(assetGroup); + } + + public void deleteAssetGroup(@NotBlank final String assetGroupId) { + this.assetGroupRepository.deleteById(assetGroupId); + } + +} diff --git a/openex-api/src/main/java/io/openex/service/asset/EndpointService.java b/openex-api/src/main/java/io/openex/service/asset/EndpointService.java new file mode 100644 index 0000000000..f17016cd1e --- /dev/null +++ b/openex-api/src/main/java/io/openex/service/asset/EndpointService.java @@ -0,0 +1,42 @@ +package io.openex.service.asset; + +import io.openex.database.model.Endpoint; +import io.openex.database.repository.EndpointRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +import static io.openex.helper.StreamHelper.fromIterable; +import static java.time.Instant.now; + +@RequiredArgsConstructor +@Service +public class EndpointService { + + private final EndpointRepository endpointRepository; + + public Endpoint createEndpoint(@NotNull final Endpoint endpoint) { + return this.endpointRepository.save(endpoint); + } + + public Endpoint endpoint(@NotBlank final String endpointId) { + return this.endpointRepository.findById(endpointId).orElseThrow(); + } + + public List endpoints() { + return fromIterable(this.endpointRepository.findAll()); + } + + public Endpoint updateEndpoint(@NotNull final Endpoint endpoint) { + endpoint.setUpdatedAt(now()); + return this.endpointRepository.save(endpoint); + } + + public void deleteEndpoint(@NotBlank final String endpointId) { + this.endpointRepository.deleteById(endpointId); + } + +} diff --git a/openex-api/src/test/java/io/openex/service/AssetGroupServiceTest.java b/openex-api/src/test/java/io/openex/service/AssetGroupServiceTest.java new file mode 100644 index 0000000000..faf47cef6b --- /dev/null +++ b/openex-api/src/test/java/io/openex/service/AssetGroupServiceTest.java @@ -0,0 +1,91 @@ +package io.openex.service; + +import io.openex.database.model.Endpoint; +import io.openex.database.model.AssetGroup; +import io.openex.service.asset.EndpointService; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; +import java.util.NoSuchElementException; + +import static io.openex.database.model.Endpoint.OS_TYPE.LINUX; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class AssetGroupServiceTest { + + @Autowired + private AssetGroupService assetGroupService; + @Autowired + private EndpointService endpointService; + + static String ASSET_GROUP_ID; + + @DisplayName("Create asset group") + @Test + @Order(1) + void createAssetGroupTest() { + // -- PREPARE -- + AssetGroup assetGroup = new AssetGroup(); + String name = "Personal network"; + assetGroup.setName(name); + + // -- EXECUTE -- + AssetGroup assetGroupCreated = this.assetGroupService.createAssetGroup(assetGroup); + ASSET_GROUP_ID = assetGroupCreated.getId(); + assertNotNull(assetGroupCreated); + assertNotNull(assetGroupCreated.getId()); + assertNotNull(assetGroupCreated.getCreatedAt()); + assertNotNull(assetGroupCreated.getUpdatedAt()); + assertEquals(name, assetGroupCreated.getName()); + } + + @DisplayName("Retrieve asset group") + @Test + @Order(2) + void retrieveAssetGroupTest() { + AssetGroup assetGroup = this.assetGroupService.assetGroup(ASSET_GROUP_ID); + assertNotNull(assetGroup); + + List assetGroups = this.assetGroupService.assetGroups(); + assertNotNull(assetGroups); + assertEquals(ASSET_GROUP_ID, assetGroups.get(0).getId()); + } + + @DisplayName("Update asset group") + @Test + @Order(3) + void updateAssetGroupTest() { + // -- PREPARE -- + Endpoint endpoint = new Endpoint(); + String name = "Personal PC"; + endpoint.setName(name); + endpoint.setIp("127.0.0.1"); + endpoint.setHostname("hostname"); + endpoint.setOs(LINUX); + endpoint.setHostname("hostname"); + endpoint.setOs(LINUX); + Endpoint endpointCreated = this.endpointService.createEndpoint(endpoint); + + AssetGroup assetGroup = this.assetGroupService.assetGroup(ASSET_GROUP_ID); + String value = "Professional network"; + assetGroup.setName(value); + assetGroup.setAssets(List.of(endpointCreated)); + + // -- EXECUTE -- + AssetGroup assetGroupUpdated = this.assetGroupService.updateAssetGroup(assetGroup); + assertNotNull(assetGroup); + assertEquals(value, assetGroupUpdated.getName()); + } + + @DisplayName("Delete asset group") + @Test + @Order(4) + void deleteAssetGroupTest() { + this.assetGroupService.deleteAssetGroup(ASSET_GROUP_ID); + assertThrows(NoSuchElementException.class, () -> this.assetGroupService.assetGroup(ASSET_GROUP_ID)); + } +} diff --git a/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java b/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java new file mode 100644 index 0000000000..f7756a08aa --- /dev/null +++ b/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java @@ -0,0 +1,90 @@ +package io.openex.service; + +import io.openex.database.model.Endpoint; +import io.openex.service.asset.EndpointService; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.TransactionSystemException; + +import java.util.List; +import java.util.NoSuchElementException; + +import static io.openex.database.model.Endpoint.OS_TYPE.LINUX; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class EndpointServiceTest { + + @Autowired + private EndpointService endpointService; + + static String ENDPOINT_ID; + + @DisplayName("Create endpoint") + @Test + @Order(1) + void createEndpointTest() { + // -- PREPARE -- + Endpoint endpoint = new Endpoint(); + String name = "Personal PC"; + endpoint.setName(name); + endpoint.setIp("wrong ip"); + endpoint.setHostname("hostname"); + endpoint.setOs(LINUX); + endpoint.setHostname("hostname"); + endpoint.setOs(LINUX); + + // -- EXECUTE -- + assertThrows(TransactionSystemException.class, () -> this.endpointService.createEndpoint(endpoint)); + + // -- PREPARE -- + endpoint.setIp("127.0.0.1"); + + // -- EXECUTE -- + Endpoint endpointCreated = this.endpointService.createEndpoint(endpoint); + ENDPOINT_ID = endpointCreated.getId(); + assertNotNull(endpointCreated); + assertNotNull(endpointCreated.getId()); + assertNotNull(endpointCreated.getCreatedAt()); + assertNotNull(endpointCreated.getUpdatedAt()); + assertEquals(name, endpointCreated.getName()); + } + + @DisplayName("Retrieve endpoint") + @Test + @Order(2) + void retrieveEndpointTest() { + Endpoint endpoint = this.endpointService.endpoint(ENDPOINT_ID); + assertNotNull(endpoint); + + List endpoints = this.endpointService.endpoints(); + assertNotNull(endpoints); + assertEquals(ENDPOINT_ID, endpoints.get(0).getId()); + } + + @DisplayName("Update endpoint") + @Test + @Order(3) + void updateEndpointTest() { + // -- PREPARE -- + Endpoint endpoint = this.endpointService.endpoint(ENDPOINT_ID); + String value = "Professional PC"; + endpoint.setName(value); + + // -- EXECUTE -- + Endpoint endpointUpdated = this.endpointService.updateEndpoint(endpoint); + assertNotNull(endpoint); + assertEquals(value, endpointUpdated.getName()); + } + + @DisplayName("Delete endpoint") + @Test + @Order(4) + void deleteEndpointTest() { + this.endpointService.deleteEndpoint(ENDPOINT_ID); + assertThrows(NoSuchElementException.class, () -> this.endpointService.endpoint(ENDPOINT_ID)); + } + +} diff --git a/openex-model/pom.xml b/openex-model/pom.xml index 04aec32176..453dc9374a 100644 --- a/openex-model/pom.xml +++ b/openex-model/pom.xml @@ -52,5 +52,10 @@ validation-api ${validation-api.version} + + commons-validator + commons-validator + ${commons-validator.version} + diff --git a/openex-model/src/main/java/io/openex/annotation/Ipv4OrIpv6Constraint.java b/openex-model/src/main/java/io/openex/annotation/Ipv4OrIpv6Constraint.java new file mode 100644 index 0000000000..dc4507f1c5 --- /dev/null +++ b/openex-model/src/main/java/io/openex/annotation/Ipv4OrIpv6Constraint.java @@ -0,0 +1,24 @@ +package io.openex.annotation; + +import io.openex.validator.Ipv4OrIpv6Validator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import javax.validation.ReportAsSingleViolation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target(FIELD) +@Retention(RUNTIME) +@Constraint(validatedBy = Ipv4OrIpv6Validator.class) +@ReportAsSingleViolation +public @interface Ipv4OrIpv6Constraint { + String message() default "must be ipv4 or ipv6"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/openex-model/src/main/java/io/openex/database/model/Asset.java b/openex-model/src/main/java/io/openex/database/model/Asset.java new file mode 100644 index 0000000000..7e77085326 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/model/Asset.java @@ -0,0 +1,48 @@ +package io.openex.database.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.openex.database.audit.ModelBaseListener; +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.time.Instant; + +import static java.time.Instant.now; + +@Data +@Entity +@Table(name = "assets") +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@EntityListeners(ModelBaseListener.class) +public class Asset implements Base { + + @Id + @Column(name = "asset_id") + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") + @JsonProperty("asset_id") + private String id; + + @Column(name = "asset_external_id") + @JsonProperty("asset_external_id") + private String externalId; + + @NotBlank + @Column(name = "asset_name") + @JsonProperty("asset_name") + private String name; + + @Column(name = "asset_description") + @JsonProperty("asset_description") + private String description; + + @Column(name = "asset_created_at") + @JsonProperty("asset_created_at") + private Instant createdAt = now(); + + @Column(name = "asset_updated_at") + @JsonProperty("asset_updated_at") + private Instant updatedAt = now(); +} diff --git a/openex-model/src/main/java/io/openex/database/model/AssetGroup.java b/openex-model/src/main/java/io/openex/database/model/AssetGroup.java new file mode 100644 index 0000000000..5537239ae3 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/model/AssetGroup.java @@ -0,0 +1,55 @@ +package io.openex.database.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.openex.database.audit.ModelBaseListener; +import io.openex.helper.MultiIdDeserializer; +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import static java.time.Instant.now; + +@Data +@Entity +@Table(name = "asset_groups") +@EntityListeners(ModelBaseListener.class) +public class AssetGroup implements Base { + + @Id + @Column(name = "asset_group_id") + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") + @JsonProperty("asset_group_id") + private String id; + + @NotBlank + @Column(name = "asset_group_name") + @JsonProperty("asset_group_name") + private String name; + + @Column(name = "asset_group_description") + @JsonProperty("asset_group_description") + private String description; + + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable(name = "asset_groups_assets", + joinColumns = @JoinColumn(name = "asset_group_id"), + inverseJoinColumns = @JoinColumn(name = "asset_id")) + @JsonSerialize(using = MultiIdDeserializer.class) + @JsonProperty("asset_group_assets") + private List assets = new ArrayList<>(); + + @Column(name = "asset_group_created_at") + @JsonProperty("asset_group_created_at") + private Instant createdAt = now(); + + @Column(name = "asset_group_updated_at") + @JsonProperty("asset_group_updated_at") + private Instant updatedAt = now(); +} diff --git a/openex-model/src/main/java/io/openex/database/model/Endpoint.java b/openex-model/src/main/java/io/openex/database/model/Endpoint.java new file mode 100644 index 0000000000..59658d6b09 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/model/Endpoint.java @@ -0,0 +1,39 @@ +package io.openex.database.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.openex.annotation.Ipv4OrIpv6Constraint; +import io.openex.database.audit.ModelBaseListener; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; + +@EqualsAndHashCode(callSuper = true) +@Data +@Entity +@Table(name = "endpoints") +@EntityListeners(ModelBaseListener.class) +public class Endpoint extends Asset { + + public enum OS_TYPE { + LINUX, + WINDOWS, + } + + @NotBlank + @Ipv4OrIpv6Constraint + @Column(name = "endpoint_ip") + @JsonProperty("endpoint_ip") + private String ip; + + @Column(name = "endpoint_hostname") + @JsonProperty("endpoint_hostname") + private String hostname; + + @Column(name = "endpoint_os") + @JsonProperty("endpoint_os") + @Enumerated(EnumType.STRING) + private OS_TYPE os; + +} diff --git a/openex-model/src/main/java/io/openex/database/repository/AssetGroupRepository.java b/openex-model/src/main/java/io/openex/database/repository/AssetGroupRepository.java new file mode 100644 index 0000000000..3e05f4f3e9 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/repository/AssetGroupRepository.java @@ -0,0 +1,10 @@ +package io.openex.database.repository; + +import io.openex.database.model.AssetGroup; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AssetGroupRepository extends CrudRepository { + +} diff --git a/openex-model/src/main/java/io/openex/database/repository/AssetRepository.java b/openex-model/src/main/java/io/openex/database/repository/AssetRepository.java new file mode 100644 index 0000000000..3d06196b95 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/repository/AssetRepository.java @@ -0,0 +1,8 @@ +package io.openex.database.repository; + +import io.openex.database.model.Asset; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AssetRepository extends CrudRepository { } diff --git a/openex-model/src/main/java/io/openex/database/repository/EndpointRepository.java b/openex-model/src/main/java/io/openex/database/repository/EndpointRepository.java new file mode 100644 index 0000000000..1b046b9dd7 --- /dev/null +++ b/openex-model/src/main/java/io/openex/database/repository/EndpointRepository.java @@ -0,0 +1,8 @@ +package io.openex.database.repository; + +import io.openex.database.model.Endpoint; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EndpointRepository extends CrudRepository { } diff --git a/openex-model/src/main/java/io/openex/validator/Ipv4OrIpv6Validator.java b/openex-model/src/main/java/io/openex/validator/Ipv4OrIpv6Validator.java new file mode 100644 index 0000000000..ce83d864f7 --- /dev/null +++ b/openex-model/src/main/java/io/openex/validator/Ipv4OrIpv6Validator.java @@ -0,0 +1,17 @@ +package io.openex.validator; + +import io.openex.annotation.Ipv4OrIpv6Constraint; +import org.apache.commons.validator.routines.InetAddressValidator; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class Ipv4OrIpv6Validator implements ConstraintValidator { + + @Override + public boolean isValid(final String ip, final ConstraintValidatorContext cxt) { + InetAddressValidator validator = InetAddressValidator.getInstance(); + return validator.isValid(ip); + } + +} diff --git a/pom.xml b/pom.xml index 3f1a1afbd7..502fa87687 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ 8.5.7 2.0.1.Final 2.15.0 + 1.8.0