Skip to content

Commit

Permalink
RIPE NCC has merged 4569ffd
Browse files Browse the repository at this point in the history
* deploy-check: fix deployments selection [c5ca1df4]
* Update dependency io.freefair.lombok:io.freefair.lombok.gradle.plugin to v8.7.1 [0ccd4427]
* Add integration tests to cover ROA update scenarios [e17644f3]
* Update dependency net.logstash.logback:logstash-logback-encoder to v8 [24108932]
* Update dependency io.sentry:sentry-bom to v7.13.0 [55c816f4]
* Update dependency org.wiremock:wiremock-jetty12 to v3.9.1 [68ae162e]
* Refactor for sonarqube [d5967a81]
* Cleanup imports [b183d3c5]
* Use a thread pool of fixed size [e1b08933]
* Revert ignored test [252ec562]
* Cleanup [40792e39]
* Renaming [788aa678]
* Revert disabled security [c77eb8ca]
* Fix date JSON serialisation [4d8b2175]
* Ignore breaking test [5afbfa78]
* Compilation fixes [576f84cf]
* Add lastUpdate timestamp [5df6a6f3]
* Comment out security again [077a9580]
* Revert security [99acfb4d]
* Remove security again [92ef220c]
* Cleanup [7c8c9a43]
* Revert disabled security [0a396415]
* Fix DTO for Krill API [33d506e1]
* Fix non-existent publishers [3d1cf2fa]
* More testing [688522e3]
* Disable security more or less [67a810d3]
* Ignore broken test [ae3efe8b]
* Disable security for easy testing [bed7a4f5]
* Make some Krill communication parallel [8193b94f]
* Fix publishers listing [8df1828e]
* Naming [fcc3500b]
* Fix compilation [39916d2d]
* Cleanup [b1b6e0b3]
* Initial version [662c8972]
* Update dependency org.wiremock:wiremock-jetty12 to v3.9.0 [bea428c1]
  • Loading branch information
RPKI Team at RIPE NCC committed Aug 9, 2024
1 parent a960ed6 commit edbf194
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 31 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ rpki-ripe-ncc-ui/bin/
logfile
.metals
bin/
.jqwik-database
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ dependencies {
implementation "org.thymeleaf:thymeleaf:3.1.2.RELEASE"
implementation "org.thymeleaf:thymeleaf-spring6:3.1.2.RELEASE"

implementation platform('io.sentry:sentry-bom:7.12.0')
implementation platform('io.sentry:sentry-bom:7.13.0')
implementation 'io.sentry:sentry-spring-boot-starter'
implementation 'io.sentry:sentry-logback'

Expand All @@ -62,7 +62,7 @@ dependencies {
implementation 'commons-io:commons-io:2.16.1'
implementation 'ch.qos.logback.contrib:logback-json-classic:0.1.5'
implementation 'ch.qos.logback.contrib:logback-jackson:0.1.5'
implementation 'net.logstash.logback:logstash-logback-encoder:7.3'
implementation 'net.logstash.logback:logstash-logback-encoder:8.0'
implementation 'commons-lang:commons-lang:2.6'

testImplementation('org.springframework.boot:spring-boot-starter-test') {
Expand All @@ -72,7 +72,7 @@ dependencies {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}

testImplementation "org.wiremock:wiremock-jetty12:3.8.0"
testImplementation "org.wiremock:wiremock-jetty12:3.9.1"
testImplementation 'net.jqwik:jqwik:1.9.0'
testImplementation "net.ripe.rpki:rpki-commons:$rpki_commons_version:tests"
testImplementation 'org.assertj:assertj-core'
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repositories {
}

dependencies {
implementation 'io.freefair.lombok:io.freefair.lombok.gradle.plugin:8.6'
implementation 'io.freefair.lombok:io.freefair.lombok.gradle.plugin:8.7.1'
implementation('com.gorylenko.gradle-git-properties:com.gorylenko.gradle-git-properties.gradle.plugin:2.4.2') {
exclude group: 'org.eclipse.jgit', module: 'org.eclipse.jgit'
}
Expand Down
3 changes: 2 additions & 1 deletion scripts/gitlab-deploy-check
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const Calendar = {
return xd > yd;
},
max: (x, y) => Calendar.isAfter(x, y) ? x : y,
min: (x, y) => Calendar.isAfter(x, y) ? y : x,
compare: (key=(x)=>x) => (x, y) =>
Calendar.isAfter(key(x), key(y)) ? 1 : Calendar.isAfter(key(y), key(x)) ? -1 : 0,
showTimestamp: (x) => {
Expand Down Expand Up @@ -180,7 +181,7 @@ const main = async (ctx) => {

const deployments = await gitlab.combinators.findAll(gitlab.queries.deployments)({
environment: ctx.stagingEnv,
finished_after: Calendar.max(lastCommit.created_at, mergeRequest.created_at),
finished_after: Calendar.min(lastCommit.created_at, mergeRequest.created_at),
order_by: "finished_at",
status: "success",
})();
Expand Down
142 changes: 142 additions & 0 deletions src/integration/java/net/ripe/rpki/ripencc/RoaIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package net.ripe.rpki.ripencc;

import net.ripe.ipresource.Asn;
import net.ripe.ipresource.ImmutableResourceSet;
import net.ripe.ipresource.IpRange;
import net.ripe.rpki.bgpris.BgpRisEntryRepositoryBean;
import net.ripe.rpki.commons.util.VersionedId;
import net.ripe.rpki.domain.*;
import net.ripe.rpki.rest.service.Rest;
import net.ripe.rpki.ripencc.cache.JpaResourceCacheImpl;
import net.ripe.rpki.server.api.commands.CertificateAuthorityCommand;
import net.ripe.rpki.server.api.commands.UpdateAllIncomingResourceCertificatesCommand;
import net.ripe.rpki.server.api.dto.BgpRisEntry;
import net.ripe.rpki.server.api.services.command.CommandService;
import net.ripe.rpki.server.api.services.command.CommandStatus;
import net.ripe.rpki.server.api.support.objects.CaName;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
import javax.security.auth.x500.X500Principal;
import java.util.*;
import static net.ripe.ipresource.ImmutableResourceSet.parse;
import static net.ripe.rpki.rest.service.RestService.API_URL_PREFIX;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;


@Transactional
@AutoConfigureMockMvc
@AutoConfigureWebMvc
public class RoaIT extends CertificationDomainTestCase {
private static final long HOSTED_CA_ID = 454L;
private static final X500Principal CHILD_CA_NAME = new X500Principal("CN=child");
public static final ImmutableResourceSet CHILD_CA_RESOURCES = ImmutableResourceSet.parse("fc00::/12, 192.168.0.0/16");
private static final BgpRisEntry BGP_RIS_ENTRY_1 = new BgpRisEntry(Asn.parse("AS3549"), IpRange.parse("fc00::/12"), 100);
private static final BgpRisEntry BGP_RIS_ENTRY_2 = new BgpRisEntry(Asn.parse("AS3549"), IpRange.parse("192.168.0.0/16"), 100);
@Autowired
private JpaResourceCacheImpl resourceCache;
@Autowired
private CommandService subject;
@Autowired
private MockMvc mockMvc;
@Autowired
private BgpRisEntryRepositoryBean bgpRisEntryRepository;
private ProductionCertificateAuthority parent;
private HostedCertificateAuthority child;

@Before
public void setUp() throws Exception {
clearDatabase();
parent = createInitializedAllResourcesAndProductionCertificateAuthority();
child = new HostedCertificateAuthority(HOSTED_CA_ID, CHILD_CA_NAME, UUID.randomUUID(), parent);
issueCertificateForNewKey(parent, child, CHILD_CA_RESOURCES);
certificateAuthorityRepository.add(child);
resourceCache.updateEntry(CaName.of(CHILD_CA_NAME), parse("fc00::/12, 192.168.0.0/16"));
execute(new UpdateAllIncomingResourceCertificatesCommand(new VersionedId(HOSTED_CA_ID, VersionedId.INITIAL_VERSION), Integer.MAX_VALUE));
bgpRisEntryRepository.resetEntries(Arrays.asList(BGP_RIS_ENTRY_1));
// Add initial ROA first
mockMvc.perform(Rest.post(API_URL_PREFIX + "/" + child.getName() + "/roas/publish")
.content("{ " +
"\"added\" : [{\"asn\" : \"AS3549\", \"prefix\" : \"fc00::/12\", \"maximalLength\" : \"12\"}], " +
"\"deleted\" : [] " +
"}"))
.andExpect(status().is(204));
}

@Test
public void itIsNotPossibleToStageExistingRoaWhenAnnouncementNotFound() throws Exception {
// All of the sudden we do not see initial announcement anymore, it has changed.
bgpRisEntryRepository.resetEntries(Arrays.asList(BGP_RIS_ENTRY_2));

// Ok, announcement has changed, let's see if we can alter existing ROA and add new to cover new announcement, staging
mockMvc.perform(Rest.post(API_URL_PREFIX + "/" + child.getName() + "/roas/stage")
.content("[{\"asn\" : \"AS3549\", \"prefix\" : \"fc00::/12\", \"maximalLength\" : \"14\"}," +
"{\"asn\" : \"AS3549\", \"prefix\" : \"192.168.0.0/16\", \"maximalLength\" : \"16\"}]"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value("1"))
.andExpect(jsonPath("$.[0].asn").value("AS3549"))
.andExpect(jsonPath("$.[0].prefix").value("192.168.0.0/16"))
.andExpect(jsonPath("$.[0].visibility").value("100"))
.andExpect(jsonPath("$.[0].suppressed").value("false"))
.andExpect(jsonPath("$.[0].verified").value("true"))
.andExpect(jsonPath("$.[0].currentState").value("UNKNOWN"))
.andExpect(jsonPath("$.[0].futureState").value("VALID"))
.andExpect(jsonPath("$.[0].affectedByChange").value("true"));
}

@Test
public void itIsPossibleToUpdateExistingRoaWhenAnnouncementNotFound() throws Exception {
// All of the sudden we do not see initial announcement anymore, it has changed.
bgpRisEntryRepository.resetEntries(Arrays.asList(BGP_RIS_ENTRY_2));

// Is it still possible to update initial ROA?
mockMvc.perform(Rest.post(API_URL_PREFIX + "/" + child.getName() + "/roas/publish")
.content("{ " +
"\"added\" : [{\"asn\" : \"AS3549\", \"prefix\" : \"fc00::/12\", \"maximalLength\" : \"14\"}], " +
"\"deleted\" : [{\"asn\" : \"AS3549\", \"prefix\" : \"fc00::/12\", \"maximalLength\" : \"12\"}] " +
"}"))
.andExpect(status().is(204));

// Initial ROA should not be VALID anyways.
mockMvc.perform(Rest.get(API_URL_PREFIX + "/" + child.getName() + "/roas"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value("1"))
.andExpect(jsonPath("$.[0].asn").value("AS3549"))
.andExpect(jsonPath("$.[0].prefix").value("fc00::/12"))
.andExpect(jsonPath("$.[0]._numberOfValidsCaused").value("0"))
.andExpect(jsonPath("$.[0]._numberOfInvalidsCaused").value("0"))
.andExpect(jsonPath("$.[0].maximalLength").value("14"));
}

@Test
public void roaIsValidatingAgainAfterCorrespondingAnnouncementIsVisibleAgain() throws Exception {
// First announcement is visible again, so both announcements are visible now.
bgpRisEntryRepository.resetEntries(Arrays.asList(BGP_RIS_ENTRY_1, BGP_RIS_ENTRY_2));

// It is expected to have two ROAs and both valid
mockMvc.perform(Rest.get(API_URL_PREFIX + "/" + child.getName() + "/roas"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value("1"))
.andExpect(jsonPath("$.[0].asn").value("AS3549"))
.andExpect(jsonPath("$.[0].prefix").value("fc00::/12"))
.andExpect(jsonPath("$.[0]._numberOfValidsCaused").value("1"))
.andExpect(jsonPath("$.[0]._numberOfInvalidsCaused").value("0"))
.andExpect(jsonPath("$.[0].maximalLength").value("12"));
}

protected CommandStatus execute(CertificateAuthorityCommand command) {
try {
return subject.execute(command);
} finally {
entityManager.flush();
}
}

private static Collection<BgpRisEntry> entries(BgpRisEntry... entries) {
return new HashSet<>(Arrays.asList(entries));
}
}
1 change: 0 additions & 1 deletion src/main/java/net/ripe/rpki/config/OpenAPIConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ private Components getSecuritySchemes() {
}

private Info getInfo() {

return new Info().title("Resource Certification (RPKI) API")
.termsOfService("https://www.ripe.net/lir-services/resource-management/certification/legal/ripe-ncc-certification-service-terms-and-conditions")
.description("Rest API for RIPE NCC Resource Certification (RPKI)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public SecurityFilterChain webSecurityFilterChainRequireApiKey(HttpSecurity http
.build();
}


@Order(2)
@Bean
public SecurityFilterChain webSecurityFilterChainAllowProvisioningEndpoint(HttpSecurity http) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.persistence.EntityNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.core.MediaType;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import net.ripe.rpki.commons.provisioning.identity.IdentitySerializer;
import net.ripe.rpki.commons.provisioning.identity.PublisherRequest;
import net.ripe.rpki.commons.provisioning.identity.PublisherRequestSerializer;
import net.ripe.rpki.commons.provisioning.identity.RepositoryResponse;
import net.ripe.rpki.commons.provisioning.identity.RepositoryResponseSerializer;
import net.ripe.rpki.commons.provisioning.identity.*;
import net.ripe.rpki.domain.NonHostedCertificateAuthority;
import net.ripe.rpki.rest.exception.CaNotFoundException;
import net.ripe.rpki.rest.exception.ObjectNotFoundException;
Expand All @@ -27,25 +26,17 @@
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import jakarta.persistence.EntityNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;

import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
Expand All @@ -65,6 +56,8 @@ public class PublisherRepositoriesService extends AbstractCaRestService {
private final CommandService commandService;
private final Optional<NonHostedPublisherRepositoryService> maybeNonHostedPublisherRepositoryService;

private static final ForkJoinPool krillCommunicationPool = new ForkJoinPool(4);

/**
* A workaround for the long-standing issue https://github.com/NLnetLabs/krill/issues/984 that appears to be a wont-fix.
*
Expand Down Expand Up @@ -231,6 +224,30 @@ public ResponseEntity<?> deleteNonHostedPublicationRepository(
}
}

@GetMapping(path = "non-hosted/publisher-content")
@Operation(summary = "Get content for every publisher for the CA")
public ResponseEntity<?> getPublishers(@PathVariable("caName") final CaName caName) {
if (maybeNonHostedPublisherRepositoryService.isEmpty()) {
return ResponseEntity.status(NOT_ACCEPTABLE).body(NON_HOSTED_PUBLISHERS_ARE_NOT_AVAILABLE);
}
var nonHostedPublisherRepositoryService = this.maybeNonHostedPublisherRepositoryService.orElseThrow();

log.info("Getting full information about publishers for CA: {}", caName);

NonHostedCertificateAuthorityData ca = getCa(NonHostedCertificateAuthorityData.class, caName);

var nonHostedPublisherRepositories = certificateAuthorityViewService.findNonHostedPublisherRepositories(ca.getName());
var publisherContent = krillCommunicationPool.submit(() ->
nonHostedPublisherRepositories
.keySet()
.parallelStream()
.flatMap(handle -> nonHostedPublisherRepositoryService.publisherInfo(handle).stream())
.toList()
).join();

return ResponseEntity.ok(publisherContent);
}

@Value
private static class RepositoryResponseDto {
@JsonInclude(JsonInclude.Include.NON_NULL)
Expand All @@ -252,5 +269,4 @@ static RepositoryResponseDto of(RepositoryResponse repositoryResponse) {
);
}
}

}
Loading

0 comments on commit edbf194

Please sign in to comment.