Skip to content

Commit

Permalink
Merge branch 'main' into container-image
Browse files Browse the repository at this point in the history
  • Loading branch information
fwilhe authored May 7, 2024
2 parents 0da67bd + e133719 commit b56fc7d
Show file tree
Hide file tree
Showing 18 changed files with 395 additions and 57 deletions.
45 changes: 44 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ on:
branches: [ "main" ]
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:

Expand All @@ -28,7 +40,13 @@ jobs:
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

- name: Build with Gradle Wrapper
run: ./gradlew build
run: ./gradlew build asciidoctor

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: 'build'

dependency-submission:

Expand All @@ -48,3 +66,28 @@ jobs:
# See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

deploy-pages:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs:
- build
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- uses: actions/download-artifact@v4
with:
name: build-artifacts
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: 'docs/asciidoc'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

14 changes: 13 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@ ext {
set('snippetsDir', file("build/generated-snippets"))
}

configurations {
asciidoctorExtensions
}

dependencies {
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation 'org.springframework.restdocs:spring-restdocs-restassured'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
testImplementation 'io.rest-assured:rest-assured:5.4.0'
Expand All @@ -40,6 +45,13 @@ tasks.named('test') {
}

tasks.named('asciidoctor') {
configurations "asciidoctorExtensions"
inputs.dir snippetsDir
dependsOn test
}
bootJar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
41 changes: 41 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
= Garden Linux Vulnerability Database Rest API
Garden Linux Authors;
:doctype: book
:icons: font
:source-highlighter: highlightjs

== API Endpoints

This document describes the HTTP API endpoints of Garden Linux Vulnerability Database.

CAUTION: This document and the API are work in progress and subject to change at any time.

=== Check running app

For verifying that the app is running and has a connection to the database, you may query the `readiness` endpoint:

include::{snippets}/readiness/curl-request.adoc[]

The expected response looks like this:

include::{snippets}/readiness/http-response.adoc[]

=== Get a CVE by id

To query a single CVE by its id, you maye use the `cves` endpoint:

include::{snippets}/getCve/curl-request.adoc[]

The expected response looks like this:

include::{snippets}/getCve/http-response.adoc[]

=== Get a list of CVEs by distro

To query all CVEs for a given distribution, you may use this endpoint:

include::{snippets}/getCveForDistro/curl-request.adoc[]

The expected response looks like this:

include::{snippets}/getCveForDistro/http-response.adoc[]
24 changes: 24 additions & 0 deletions src/main/java/io/gardenlinux/glvd/AppController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.dto.Readiness;
import io.gardenlinux.glvd.exceptions.DbNotConnectedException;
import jakarta.annotation.Nonnull;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AppController {

@Nonnull
private final GlvdService glvdService;

public AppController(@Nonnull GlvdService glvdService) {
this.glvdService = glvdService;
}

@GetMapping("/readiness")
public Readiness readiness() throws DbNotConnectedException {
return glvdService.getReadiness();
}

}
7 changes: 0 additions & 7 deletions src/main/java/io/gardenlinux/glvd/CveRepository.java

This file was deleted.

15 changes: 12 additions & 3 deletions src/main/java/io/gardenlinux/glvd/GlvdController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.dto.Cve;
import io.gardenlinux.glvd.exceptions.NotFoundException;
import jakarta.annotation.Nonnull;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -8,6 +10,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping(value = "/v1/cves", produces = MediaType.APPLICATION_JSON_VALUE)
public class GlvdController {
Expand All @@ -20,9 +24,14 @@ public GlvdController(@Nonnull GlvdService glvdService) {
}

@GetMapping("/{cveId}")
ResponseEntity<?> getCveId(@PathVariable("cveId") final String cveId) {
var cve = glvdService.getCve(cveId);
return ResponseEntity.ok().body(cve);
ResponseEntity<Cve> getCveId(@PathVariable("cveId") final String cveId) throws NotFoundException {
return ResponseEntity.ok().body(glvdService.getCve(cveId));
}

@GetMapping("/{vendor}/{product}/{codename}")
ResponseEntity<List<String>> getCveDistro(@PathVariable final String vendor, @PathVariable final String product,
@PathVariable final String codename) {
return ResponseEntity.ok().body(glvdService.getCveForDistribution(vendor, product, codename));
}

}
36 changes: 32 additions & 4 deletions src/main/java/io/gardenlinux/glvd/GlvdService.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,48 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.db.CveRepository;
import io.gardenlinux.glvd.db.HealthCheckRepository;
import io.gardenlinux.glvd.dto.Cve;
import io.gardenlinux.glvd.dto.Readiness;
import io.gardenlinux.glvd.exceptions.DbNotConnectedException;
import io.gardenlinux.glvd.exceptions.NotFoundException;
import jakarta.annotation.Nonnull;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class GlvdService {

@Nonnull
private final CveRepository cveRepository;

public GlvdService(@Nonnull CveRepository cveRepository) {
@Nonnull
private final HealthCheckRepository healthCheckRepository;

public GlvdService(@Nonnull CveRepository cveRepository, @Nonnull HealthCheckRepository healthCheckRepository) {
this.cveRepository = cveRepository;
this.healthCheckRepository = healthCheckRepository;
}

public Cve getCve(String cveId) {
var cve = cveRepository.findById(cveId);
return cve.orElseThrow();
public Readiness getReadiness() throws DbNotConnectedException {
try {
var connection = healthCheckRepository.checkDbConnection();
return new Readiness(connection);
} catch (Exception e) {
throw new DbNotConnectedException(e);
}
}

public Cve getCve(String cveId) throws NotFoundException {
var cveEntity = cveRepository.findById(cveId).orElseThrow(NotFoundException::new);
// Todo: more specific transformation from db type 'cve' to response type 'cve'
return new Cve(cveEntity.getId(), cveEntity.getLastModified(), cveEntity.getData());

}

public List<String> getCveForDistribution(String vendor, String product, String codename) {
return cveRepository.cvesForDistribution(vendor, product, codename);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.gardenlinux.glvd;
package io.gardenlinux.glvd.db;

import jakarta.annotation.Nonnull;
import jakarta.persistence.Column;
Expand All @@ -8,7 +8,8 @@
import java.util.Objects;

@Entity(name = "all_cve")
public class Cve {
public class CveEntity {

@Id
@Column(name = "cve_id", nullable = false)
private String id;
Expand All @@ -21,10 +22,10 @@ public class Cve {
@Nonnull
private String data;

public Cve() {
public CveEntity() {
}

public Cve(String id, @Nonnull String lastModified, @Nonnull String data) {
public CveEntity(String id, @Nonnull String lastModified, @Nonnull String data) {
this.id = id;
this.lastModified = lastModified;
this.data = data;
Expand All @@ -46,11 +47,14 @@ public String getData() {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;

Cve cve = (Cve) o;
return Objects.equals(id, cve.id) && lastModified.equals(cve.lastModified) && data.equals(cve.data);
CveEntity cveEntity = (CveEntity) o;
return Objects.equals(id, cveEntity.id) && lastModified.equals(cveEntity.lastModified)
&& data.equals(cveEntity.data);
}

@Override
Expand All @@ -60,4 +64,5 @@ public int hashCode() {
result = 31 * result + data.hashCode();
return result;
}

}
26 changes: 26 additions & 0 deletions src/main/java/io/gardenlinux/glvd/db/CveRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.gardenlinux.glvd.db;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface CveRepository extends JpaRepository<CveEntity, String> {

@Query(value = """
SELECT
all_cve.data AS cveEntity
FROM
all_cve
INNER JOIN deb_cve USING (cve_id)
INNER JOIN dist_cpe ON (deb_cve.dist_id = dist_cpe.id)
WHERE
dist_cpe.cpe_vendor = ?1 AND
dist_cpe.cpe_product = ?2 and
dist_cpe.deb_codename = ?3
ORDER BY
all_cve.cve_id
""", nativeQuery = true)
List<String> cvesForDistribution(String vendor, String product, String codename);

}
12 changes: 12 additions & 0 deletions src/main/java/io/gardenlinux/glvd/db/HealthCheckEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.gardenlinux.glvd.db;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class HealthCheckEntity {

@Id
private String id;

}
11 changes: 11 additions & 0 deletions src/main/java/io/gardenlinux/glvd/db/HealthCheckRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.gardenlinux.glvd.db;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface HealthCheckRepository extends JpaRepository<HealthCheckEntity, String> {

@Query(value = "SELECT TRUE", nativeQuery = true)
String checkDbConnection();

}
Loading

0 comments on commit b56fc7d

Please sign in to comment.