Skip to content

Commit

Permalink
RIPE NCC has merged c2001cf
Browse files Browse the repository at this point in the history
* Make non-hosted publishers optional [e765ea30]
* Add another test [ecf961af]
* Fix test [b700c289]
* Add a test for adding ROA for already valid announcement [88dbca1f]
* Change the definition of affectedByChange [c9c00ee3]
  • Loading branch information
RPKI Team at RIPE NCC committed Nov 7, 2023
1 parent 722e933 commit 3afa85c
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ public ResponseEntity<?> stageRoaChanges(@PathVariable("caName") final CaName ca
final boolean isSuppressed = ignoredAnnouncements.contains(announcedRoute);
result.add(new BgpAnnouncementChange(bgp.getOrigin().toString(), bgp.getPrefix().toString(),
bgp.getVisibility(), isSuppressed, currentValidityState, futureValidityState,
affectedRanges.contains(bgp.getPrefix()) && currentValidityState != futureValidityState,
affectedRanges.contains(bgp.getPrefix()),
verifiedOrNot));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,13 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.google.common.collect.ImmutableMap.of;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static net.ripe.rpki.rest.service.AbstractCaRestService.API_URL_PREFIX;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.*;
import static org.springframework.http.MediaType.TEXT_XML;

@Slf4j
Expand All @@ -63,31 +61,35 @@ public class PublisherRepositoriesService extends AbstractCaRestService {

private final CertificateAuthorityViewService certificateAuthorityViewService;
private final CommandService commandService;
private final NonHostedPublisherRepositoryService nonHostedPublisherRepositoryService;
private final Optional<NonHostedPublisherRepositoryService> maybeNonHostedPublisherRepositoryService;


@Autowired
public PublisherRepositoriesService(CertificateAuthorityViewService certificateAuthorityViewService,
CommandService commandService,
NonHostedPublisherRepositoryService nonHostedPublisherRepositoryService) {
Optional<NonHostedPublisherRepositoryService> maybeNonHostedPublisherRepositoryService) {
this.certificateAuthorityViewService = certificateAuthorityViewService;
this.commandService = commandService;
this.nonHostedPublisherRepositoryService = nonHostedPublisherRepositoryService;
this.maybeNonHostedPublisherRepositoryService = maybeNonHostedPublisherRepositoryService;
}

@GetMapping(path = "non-hosted/publisher-repositories")
@Operation(summary = "lists all active publisher repositories for this non-hosted CA")
public ResponseEntity<?> listNonHostedPublicationRepositories(@PathVariable("caName") final CaName caName) {
log.debug("List all publishers for CA: {}", caName);

if (maybeNonHostedPublisherRepositoryService.isEmpty()) {
return ResponseEntity.ok().body(Map.of("available", false, "repositories", Map.of()));
}

try {
Map<UUID, RepositoryResponseDto> repositories = certificateAuthorityViewService
.findNonHostedPublisherRepositories(caName.getPrincipal())
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> RepositoryResponseDto.of(entry.getValue())));

return ResponseEntity.ok().body(of("repositories", repositories));
return ResponseEntity.ok().body(Map.of("available", true, "repositories", repositories));
} catch (EntityNotFoundException e) {
throw new CaNotFoundException(e.getMessage());
}
Expand All @@ -102,6 +104,11 @@ public ResponseEntity<?> provisionNonHostedPublicationRepository(
) {
log.info("Publisher request for non-hosted CA: {}", caName);

if (maybeNonHostedPublisherRepositoryService.isEmpty()) {
return ResponseEntity.status(NOT_ACCEPTABLE).body("non hosted publishers are not available for this instance.");
}
var nonHostedPublisherRepositoryService = this.maybeNonHostedPublisherRepositoryService.orElseThrow();

NonHostedCertificateAuthorityData ca = getCa(NonHostedCertificateAuthorityData.class, caName);
if (certificateAuthorityViewService.findNonHostedPublisherRepositories(ca.getName()).size() >= NonHostedCertificateAuthority.PUBLISHER_REPOSITORIES_LIMIT) {
return ResponseEntity.status(FORBIDDEN).body(bodyForError("maximum number of publisher repositories limit exceeded"));
Expand Down Expand Up @@ -150,6 +157,9 @@ public ResponseEntity<?> downloadNonHostedPublicationRepositoryResponse(
@PathVariable("caName") final CaName caName,
@PathVariable("publisherHandle") UUID publisherHandle
) {
if (maybeNonHostedPublisherRepositoryService.isEmpty()) {
return ResponseEntity.status(NOT_ACCEPTABLE).body("non hosted publishers are not available for this instance.");
}
log.info("Download repository non-hosted publication response for CA: {}", caName);

try {
Expand Down Expand Up @@ -177,6 +187,11 @@ public ResponseEntity<?> deleteNonHostedPublicationRepository(
@PathVariable("caName") final CaName caName,
@PathVariable("publisherHandle") UUID publisherHandle
) {
if (maybeNonHostedPublisherRepositoryService.isEmpty()) {
return ResponseEntity.status(NOT_ACCEPTABLE).body("non hosted publishers are not available for this instance.");
}
var nonHostedPublisherRepositoryService = this.maybeNonHostedPublisherRepositoryService.orElseThrow();

log.info("Delete non-hosted publication repository for CA: {}", caName);

NonHostedCertificateAuthorityData ca = getCa(NonHostedCertificateAuthorityData.class, caName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,71 @@ public void shouldRejectROAWithMaxLengthOverflowingIntWhenStaging() throws Excep
.andExpect(status().isBadRequest());
}

@Test
public void shouldReturnChangesEvenForUnchagedAnnouncementStatus() throws Exception {
// Have a large ROA covering everything
when(roaViewService.getRoaConfiguration(CA_ID)).thenReturn(new RoaConfigurationData(Arrays.asList(
new RoaConfigurationPrefixData(new Asn(11), IpRange.parse("148.139.0.0/16"), 24))));

// this resource set here doesn't matter, it's only used for `findMostSpecificContainedAndNotContained`
ImmutableResourceSet ipResourceSet = ImmutableResourceSet.parse("127.0.0.1, ::1");
when(certificateAuthorityData.getResources()).thenReturn(ipResourceSet);

Map<Boolean, Collection<BgpRisEntry>> bgpRisEntries = new HashMap<>();
bgpRisEntries.put(true, Collections.singletonList(new BgpRisEntry(new Asn(11), IpRange.parse("148.139.0.0/24"), 16)));
when(bgpRisEntryViewService.findMostSpecificContainedAndNotContained(ipResourceSet)).thenReturn(bgpRisEntries);

// Submit another ROA that is more specific for the announcement
mockMvc.perform(Rest.post(API_URL_PREFIX + "/123/roas/stage")
.content("[" +
"{\"asn\" : \"AS11\", \"prefix\" : \"148.139.0.0/16\", \"maximalLength\" : \"24\"}," +
"{\"asn\" : \"AS11\", \"prefix\" : \"148.139.0.0/24\", \"maximalLength\" : \"24\"}" +
"]"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value("1"))
.andExpect(jsonPath("$.[0].asn").value("AS11"))
.andExpect(jsonPath("$.[0].prefix").value("148.139.0.0/24"))
.andExpect(jsonPath("$.[0].visibility").value("16"))
.andExpect(jsonPath("$.[0].suppressed").value("false"))
.andExpect(jsonPath("$.[0].currentState").value("VALID"))
.andExpect(jsonPath("$.[0].futureState").value("VALID"))
.andExpect(jsonPath("$.[0].affectedByChange").value("true"))
.andExpect(jsonPath("$.[0].verified").value("true"));
}

@Test
public void shouldReturnNotChangesForExactlySameROAs() throws Exception {
// Have a large ROA covering everything
when(roaViewService.getRoaConfiguration(CA_ID)).thenReturn(new RoaConfigurationData(Arrays.asList(
new RoaConfigurationPrefixData(new Asn(11), IpRange.parse("148.139.0.0/16"), 24),
new RoaConfigurationPrefixData(new Asn(11), IpRange.parse("148.139.0.0/24"), 24))));

// this resource set here doesn't matter, it's only used for `findMostSpecificContainedAndNotContained`
ImmutableResourceSet ipResourceSet = ImmutableResourceSet.parse("127.0.0.1, ::1");
when(certificateAuthorityData.getResources()).thenReturn(ipResourceSet);

Map<Boolean, Collection<BgpRisEntry>> bgpRisEntries = new HashMap<>();
bgpRisEntries.put(true, Collections.singletonList(new BgpRisEntry(new Asn(11), IpRange.parse("148.139.0.0/24"), 16)));
when(bgpRisEntryViewService.findMostSpecificContainedAndNotContained(ipResourceSet)).thenReturn(bgpRisEntries);

// Submit another ROA that is more specific for the announcement
mockMvc.perform(Rest.post(API_URL_PREFIX + "/123/roas/stage")
.content("[" +
"{\"asn\" : \"AS11\", \"prefix\" : \"148.139.0.0/16\", \"maximalLength\" : \"24\"}," +
"{\"asn\" : \"AS11\", \"prefix\" : \"148.139.0.0/24\", \"maximalLength\" : \"24\"}" +
"]"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value("1"))
.andExpect(jsonPath("$.[0].asn").value("AS11"))
.andExpect(jsonPath("$.[0].prefix").value("148.139.0.0/24"))
.andExpect(jsonPath("$.[0].visibility").value("16"))
.andExpect(jsonPath("$.[0].suppressed").value("false"))
.andExpect(jsonPath("$.[0].currentState").value("VALID"))
.andExpect(jsonPath("$.[0].futureState").value("VALID"))
.andExpect(jsonPath("$.[0].affectedByChange").value("false"))
.andExpect(jsonPath("$.[0].verified").value("true"));
}

@Test
public void shouldReturnAffectingROAsAllIsFine() throws Exception {

Expand Down

0 comments on commit 3afa85c

Please sign in to comment.