-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Update node Docker tag to v22 [42ee05bee] * Update dependency org.eclipse.jgit:org.eclipse.jgit to v6 [447b855d2] * Cleanup left-over 'authorization.admin.role' in profiles [da3e72785] * Update dependency org.sonarqube:org.sonarqube.gradle.plugin to v5 [1adb4a140] * Update dependency io.sentry:sentry-bom to v7 [d3a369f76] * Update dependency commons-codec:commons-codec to v1.17.0 [df69caf44] * Refactor Mac initialisation [f29622783] * Update dependency com.gorylenko.gradle-git-properties:com.gorylenko.gradle-git-properties.gradle.plugin to v2.4.2 [6f8670965] * More Sonarqube quirks [38a6f0e38] * Sonarqube issues [b69e7e8bc] * Better error messages [50baec128] * Formatting [b8f2f3aa4] * Make tokens URL-safe [8315ccd6b] * Use proper hmac, higher entropy secrets [f8063063c] * Include a unique id from the CA into the email unsubscribe token [76d67e240] * Use global secret instead of per-configuration token [d790f1417] * Include URL in RRDP duration tracker [d379c9d3c] * DBProvider 1.6 [c1344ea79] * Update dependency org.wiremock:wiremock-jetty12 to v3.5.4 [0fb6ccc99] * Fix compilation [89122afc0] * Fix migration numbers [11d0c758b] * Add authentication-first URL [ea5f7806e] * Revert error processing in the method [32f355f88] * Syntax fix [c1dd5814d] * Fix response building [c1d4c0959] * Error handling [a37cf9900] * Introduce separate alertUnsubscribeUri property [c7e8a1cbf] * Use PathVariables like all the other code does [de7f52739] * Fix NULL value [05db29b46] * Fix tests [a9b551ad1] * Delete unused method [2bd5eb3ca] * Improve email template tests [e4aa937d7] * Extend templates with unsubscribe URL [ebff2c8ec] * Pass around unsubscribeToken [9885d1432] * Cleanup smells [8be42f963] * Cleanup imports [cac48e104] * Refactor [5378dd72c] * Add unsubscribeToken [cd583ee56] * Formatting [5c9adc0d3] * Add unsubscribeToken field [58278c3c9] * Use commands to unsubscribe [005824705] * Fix broken tests [185ac4e72] * Add unsubscribe API end-point [0c89379ad]
- Loading branch information
RPKI Team at RIPE NCC
committed
May 16, 2024
1 parent
e1f4451
commit 5bcbdb4
Showing
37 changed files
with
464 additions
and
212 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
src/main/java/net/ripe/rpki/rest/service/EmailService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package net.ripe.rpki.rest.service; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.ws.rs.core.MediaType; | ||
import lombok.extern.slf4j.Slf4j; | ||
import net.ripe.rpki.domain.alerts.RoaAlertConfigurationRepository; | ||
import net.ripe.rpki.server.api.commands.UnsubscribeFromRoaAlertCommand; | ||
import net.ripe.rpki.server.api.dto.RoaAlertSubscriptionData; | ||
import net.ripe.rpki.server.api.services.command.CommandService; | ||
import net.ripe.rpki.services.impl.email.EmailTokens; | ||
import org.apache.logging.log4j.util.Strings; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Scope; | ||
import org.springframework.http.ResponseEntity; | ||
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.RestController; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import static org.springframework.http.HttpStatus.NOT_FOUND; | ||
|
||
@Slf4j | ||
@Scope("prototype") | ||
@RestController | ||
@RequestMapping(path = "/api/email", produces = MediaType.APPLICATION_JSON) | ||
@Tag(name = "/api/email", description = "Manage email subscriptions") | ||
public class EmailService extends RestService { | ||
|
||
public static final String ERROR = "error"; | ||
|
||
private final CommandService commandService; | ||
private final RoaAlertConfigurationRepository roaAlertConfigurationRepository; | ||
private final EmailTokens emailTokens; | ||
|
||
@Autowired | ||
public EmailService(CommandService commandService, | ||
EmailTokens emailTokens, | ||
RoaAlertConfigurationRepository roaAlertConfigurationRepository) { | ||
this.commandService = commandService; | ||
this.roaAlertConfigurationRepository = roaAlertConfigurationRepository; | ||
this.emailTokens = emailTokens; | ||
} | ||
|
||
@PostMapping("/unsubscribe/{email}/{token}") | ||
@Operation(summary = "Implement one-click unsubscribe functionality.") | ||
public ResponseEntity<?> unsubscribe( | ||
@PathVariable("email") final String email, | ||
@PathVariable("token") final String token) { | ||
|
||
if (Strings.isBlank(token)) { | ||
return ResponseEntity.status(NOT_FOUND).body(Map.of(ERROR, "Unknown or invalid token: " + token)); | ||
} | ||
var configurations = roaAlertConfigurationRepository.findByEmail(email); | ||
if (configurations.isEmpty()) { | ||
return ResponseEntity.status(NOT_FOUND).body(Map.of(ERROR, "Unknown email " + email)); | ||
} | ||
var unsubscribedAnyone = new AtomicBoolean(false); | ||
configurations.forEach(configuration -> { | ||
RoaAlertSubscriptionData subscriptionOrNull = configuration.getSubscriptionOrNull(); | ||
var ca = configuration.getCertificateAuthority(); | ||
var configurationToken = emailTokens.createUnsubscribeToken(EmailTokens.uniqueId(ca.getUuid()), email); | ||
if (subscriptionOrNull != null | ||
&& subscriptionOrNull.getEmails().contains(email) | ||
&& token.equals(configurationToken)) { | ||
commandService.execute(new UnsubscribeFromRoaAlertCommand(ca.getVersionedId(), email)); | ||
unsubscribedAnyone.set(true); | ||
} | ||
}); | ||
if (unsubscribedAnyone.get()) { | ||
return ResponseEntity.ok().body(Map.of("success", "Unsubscribed " + email)); | ||
} | ||
return ResponseEntity.status(NOT_FOUND).body(Map.of(ERROR, "Unknown token " + token)); | ||
} | ||
} |
25 changes: 6 additions & 19 deletions
25
src/main/java/net/ripe/rpki/server/api/dto/RoaAlertSubscriptionData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,27 @@ | ||
package net.ripe.rpki.server.api.dto; | ||
|
||
import lombok.Getter; | ||
import net.ripe.rpki.commons.validation.roa.RouteValidityState; | ||
import net.ripe.rpki.domain.alerts.RoaAlertFrequency; | ||
import net.ripe.rpki.server.api.support.objects.ValueObjectSupport; | ||
|
||
import java.util.*; | ||
|
||
|
||
@Getter | ||
public class RoaAlertSubscriptionData extends ValueObjectSupport { | ||
|
||
private final List<String> emails; | ||
private final EnumSet<RouteValidityState> routeValidityStates; | ||
private final RoaAlertFrequency frequency; | ||
private final EnumSet<RouteValidityState> routeValidityStates; | ||
|
||
public RoaAlertSubscriptionData(String email, Collection<RouteValidityState> routeValidityStates, RoaAlertFrequency frequency) { | ||
this.emails = new ArrayList<>(); | ||
emails.add(email); | ||
this.routeValidityStates = EnumSet.copyOf(routeValidityStates); | ||
this.frequency = frequency; | ||
this(List.of(email), routeValidityStates, frequency); | ||
} | ||
|
||
public RoaAlertSubscriptionData(List<String> emails, Collection<RouteValidityState> routeValidityStates, RoaAlertFrequency frequency) { | ||
public RoaAlertSubscriptionData(List<String> emails, Collection<RouteValidityState> routeValidityStates, | ||
RoaAlertFrequency frequency) { | ||
this.emails = new ArrayList<>(emails); | ||
this.routeValidityStates = EnumSet.copyOf(routeValidityStates); | ||
this.frequency = frequency; | ||
} | ||
|
||
public List<String> getEmails() { | ||
return emails; | ||
} | ||
|
||
public RoaAlertFrequency getFrequency() { | ||
return frequency; | ||
} | ||
|
||
public Set<RouteValidityState> getRouteValidityStates() { | ||
return routeValidityStates; | ||
} | ||
} |
84 changes: 0 additions & 84 deletions
84
src/main/java/net/ripe/rpki/services/impl/EmailSenderBean.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 10 additions & 9 deletions
19
.../ripe/rpki/services/impl/EmailSender.java → ...rpki/services/impl/email/EmailSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,27 @@ | ||
package net.ripe.rpki.services.impl; | ||
package net.ripe.rpki.services.impl.email; | ||
|
||
import java.util.Map; | ||
|
||
public interface EmailSender { | ||
|
||
void sendEmail(String emailTo, String subject, EmailTemplates template, Map<String, Object> parameters); | ||
void sendEmail(String emailTo, String subject, EmailTemplates template, Map<String, Object> parameters, String uniqueId); | ||
|
||
// Limit the number of possible inputs to allow us to check all templates in tests. | ||
enum EmailTemplates { | ||
ROA_ALERT_SUBSCRIBE_CONFIRMATION_WEEKLY("email-templates/subscribe-confirmation-weekly.txt", "Your Resource Certification (RPKI) alerts subscription"), | ||
ROA_ALERT_SUBSCRIBE_CONFIRMATION_DAILY("email-templates/subscribe-confirmation-daily.txt", "Your Resource Certification (RPKI) alerts subscription"), | ||
ROA_ALERT_UNSUBSCRIBE("email-templates/unsubscribe-confirmation.txt", "Unsubscribe from Resource Certification (RPKI) alerts"), | ||
ROA_ALERT("email-templates/roa-alert-email.txt", "Resource Certification (RPKI) alerts for %s"); | ||
ROA_ALERT_SUBSCRIBE_CONFIRMATION_WEEKLY("email-templates/subscribe-confirmation-weekly.txt", "Your Resource Certification (RPKI) alerts subscription", true), | ||
ROA_ALERT_SUBSCRIBE_CONFIRMATION_DAILY("email-templates/subscribe-confirmation-daily.txt", "Your Resource Certification (RPKI) alerts subscription", true), | ||
ROA_ALERT_UNSUBSCRIBE("email-templates/unsubscribe-confirmation.txt", "Unsubscribe from Resource Certification (RPKI) alerts", false), | ||
ROA_ALERT("email-templates/roa-alert-email.txt", "Resource Certification (RPKI) alerts for %s", true); | ||
|
||
public final String templateName; | ||
public final String templateSubject; | ||
public final boolean generateUnsubcribeUrl; | ||
|
||
private EmailTemplates(String templateName, String subject) { | ||
EmailTemplates(String templateName, String subject, boolean generateUnsubcribeUrl) { | ||
this.templateName = templateName; | ||
this.templateSubject = subject; | ||
this.generateUnsubcribeUrl = generateUnsubcribeUrl; | ||
} | ||
|
||
|
||
} | ||
|
||
} |
Oops, something went wrong.