Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added legacy support for SNS messages. #225

Merged
merged 3 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public enum LegacyDestinationType {
LEGACY_CHIME,
LEGACY_SLACK,
LEGACY_CUSTOM_WEBHOOK,
LEGACY_EMAIL
LEGACY_EMAIL,
LEGACY_SNS
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.commons.destination.message;

import java.io.IOException;

import org.opensearch.common.Strings;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.commons.destination.util.Util;

/**
* This class holds the content of an SNS message
*/
public class LegacySNSMessage extends LegacyBaseMessage {

private final String subject;
private final String message;
private final String roleArn;
private final String topicArn;
private final String clusterName;

private LegacySNSMessage(
final String destinationName,
final String roleArn,
final String topicArn,
final String clusterName,
final String subject,
final String message
) {
super(LegacyDestinationType.LEGACY_SNS, destinationName, message);

if (Strings.isNullOrEmpty(message)) {
throw new IllegalArgumentException("Message content is missing");
}
if (Strings.isNullOrEmpty(roleArn) || !Util.isValidIAMArn(roleArn)) {
throw new IllegalArgumentException("Role arn is missing/invalid: " + roleArn);
}

if (Strings.isNullOrEmpty(topicArn) || !Util.isValidSNSArn(topicArn)) {
throw new IllegalArgumentException("Topic arn is missing/invalid: " + topicArn);
}

if (Strings.isNullOrEmpty(message)) {
throw new IllegalArgumentException("Message content is missing");
}

this.subject = subject;
this.message = message;
this.roleArn = roleArn;
this.topicArn = topicArn;
this.clusterName = clusterName;
}

public LegacySNSMessage(StreamInput streamInput) throws java.io.IOException {
super(streamInput);
this.subject = streamInput.readString();
this.message = super.getMessageContent();
this.roleArn = streamInput.readString();
this.topicArn = streamInput.readString();
this.clusterName = streamInput.readString();
}

@Override
public String toString() {
return "DestinationType: "
+ getChannelType()
+ ", DestinationName: "
+ destinationName
+ ", RoleARn: "
+ roleArn
+ ", TopicArn: "
+ topicArn
+ ", ClusterName: "
+ clusterName
+ ", Subject: "
+ subject
+ ", Message: "
+ message;
}

public static class Builder {
private final String destinationName;
private String subject;
private String message;
private String roleArn;
private String topicArn;
private String clusterName;

public Builder(String destinationName) {
this.destinationName = destinationName;
}

public Builder withSubject(String subject) {
this.subject = subject;
return this;
}

public Builder withMessage(String message) {
this.message = message;
return this;
}

public Builder withRole(String roleArn) {
this.roleArn = roleArn;
return this;
}

public Builder withTopicArn(String topicArn) {
this.topicArn = topicArn;
return this;
}

public Builder withClusterName(String clusterName) {
this.clusterName = clusterName;
return this;
}

public LegacySNSMessage build() {
return new LegacySNSMessage(this.destinationName, this.roleArn, this.topicArn, this.clusterName, this.subject, this.message);
}
}

public String getSubject() {
return subject;
}

public String getMessage() {
return message;
}

public String getRoleArn() {
return roleArn;
}

public String getTopicArn() {
return topicArn;
}

public String getClusterName() {
return clusterName;
}

@Override
public void writeTo(StreamOutput streamOutput) throws IOException {
super.writeTo(streamOutput);
streamOutput.writeString(subject);
streamOutput.writeString(message);
streamOutput.writeString(roleArn);
streamOutput.writeString(topicArn);
streamOutput.writeString(clusterName);
}
}
35 changes: 35 additions & 0 deletions src/main/java/org/opensearch/commons/destination/util/Util.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.commons.destination.util;

import java.util.regex.Pattern;

import org.opensearch.common.Strings;
import org.opensearch.common.ValidationException;

public class Util {
private Util() {}

public static final Pattern SNS_ARN_REGEX = Pattern
.compile("^arn:aws(-[^:]+)?:sns:([a-zA-Z0-9-]+):([0-9]{12}):([a-zA-Z0-9-_]+)(\\.fifo)?$");
public static final Pattern IAM_ARN_REGEX = Pattern.compile("^arn:aws(-[^:]+)?:iam::([0-9]{12}):([a-zA-Z0-9-/_+=@.,]+)$");

public static String getRegion(String arn) {
// sample topic arn arn:aws:sns:us-west-2:075315751589:test-notification
if (isValidSNSArn(arn)) {
return arn.split(":")[3];
}
throw new IllegalArgumentException("Unable to retrieve region from ARN " + arn);
}

public static boolean isValidIAMArn(String arn) {
return Strings.hasLength(arn) && IAM_ARN_REGEX.matcher(arn).find();
}

public static boolean isValidSNSArn(String arn) throws ValidationException {
return Strings.hasLength(arn) && SNS_ARN_REGEX.matcher(arn).find();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.opensearch.commons.destination.message.LegacyChimeMessage
import org.opensearch.commons.destination.message.LegacyCustomWebhookMessage
import org.opensearch.commons.destination.message.LegacyDestinationType
import org.opensearch.commons.destination.message.LegacyEmailMessage
import org.opensearch.commons.destination.message.LegacySNSMessage
import org.opensearch.commons.destination.message.LegacySlackMessage
import java.io.IOException

Expand Down Expand Up @@ -52,6 +53,7 @@ class LegacyPublishNotificationRequest : ActionRequest {
LegacyDestinationType.LEGACY_CUSTOM_WEBHOOK -> LegacyCustomWebhookMessage(input)
LegacyDestinationType.LEGACY_SLACK -> LegacySlackMessage(input)
LegacyDestinationType.LEGACY_EMAIL -> LegacyEmailMessage(input)
LegacyDestinationType.LEGACY_SNS -> LegacySNSMessage(input)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.commons.destination.message;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class LegacySNSMessageTest {

@Test
public void testCreateRoleArnMissingMessage() {
try {
LegacySNSMessage message = new LegacySNSMessage.Builder("sms").withMessage("dummyMessage").build();
} catch (Exception ex) {
assertEquals("Role arn is missing/invalid: null", ex.getMessage());
throw ex;
}
}

@Test
public void testCreateTopicArnMissingMessage() {
try {
LegacySNSMessage message = new LegacySNSMessage.Builder("sms")
.withMessage("dummyMessage")
.withRole("arn:aws:iam::853806060000:role/domain/abc")
.build();
} catch (Exception ex) {
assertEquals("Topic arn is missing/invalid: null", ex.getMessage());
throw ex;
}
}

@Test
public void testCreateContentMissingMessage() {
try {
LegacySNSMessage message = new LegacySNSMessage.Builder("sms")
.withRole("arn:aws:iam::853806060000:role/domain/abc")
.withTopicArn("arn:aws:sns:us-west-2:475313751589:test-notification")
.build();
} catch (Exception ex) {
assertEquals("Message content is missing", ex.getMessage());
throw ex;
}
}

@Test
public void testInValidRoleMessage() {
try {
LegacySNSMessage message = new LegacySNSMessage.Builder("sms")
.withMessage("dummyMessage")
.withRole("dummyRole")
.withTopicArn("arn:aws:sns:us-west-2:475313751589:test-notification")
.build();
} catch (Exception ex) {
assertEquals("Role arn is missing/invalid: dummyRole", ex.getMessage());
throw ex;
}
}

@Test
public void testValidMessage() {
LegacySNSMessage message = new LegacySNSMessage.Builder("sms")
.withMessage("dummyMessage")
.withRole("arn:aws:iam::853806060000:role/domain/abc")
.withTopicArn("arn:aws:sns:us-west-2:475313751589:test-notification")
.build();
assertEquals(LegacyDestinationType.LEGACY_SNS, message.getChannelType());
assertEquals("sms", message.getChannelName());
assertEquals("dummyMessage", message.getMessage());
assertEquals("arn:aws:iam::853806060000:role/domain/abc", message.getRoleArn());
assertEquals("arn:aws:sns:us-west-2:475313751589:test-notification", message.getTopicArn());
}

@Test
public void testInValidChannelName() {
try {
LegacySNSMessage message = new LegacySNSMessage.Builder("")
.withMessage("dummyMessage")
.withRole("arn:aws:iam::853806060000:role/domain/abc")
.withTopicArn("arn:aws:sns:us-west-2:475313751589:test-notification")
.build();
} catch (Exception ex) {
assertEquals("Channel name must be defined", ex.getMessage());
throw ex;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.commons.destination.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

public class UtilTest {

@Test
public void testValidSNSTopicArn() {
String topicArn = "arn:aws:sns:us-west-2:475313751589:test-notification";
assertTrue("topic arn should be valid", Util.isValidSNSArn(topicArn));
topicArn = "arn:aws-cn:sns:us-west-2:475313751589:test-notification";
assertTrue("topic arn should be valid", Util.isValidSNSArn(topicArn));
topicArn = "arn:aws-cn:sns:us-west-2:475313751589:test-notification.fifo";
assertTrue("topic arn should be valid", Util.isValidSNSArn(topicArn));
}

@Test
public void testInvalidSNSTopicArn() {
String topicArn = "arn:aws:sns1:us-west-2:475313751589:test-notification";
assertFalse("topic arn should be Invalid", Util.isValidSNSArn(topicArn));
topicArn = "arn:aws:sns:us-west-2:475313751589:test-notification.fifo.fifo";
assertFalse("topic arn should be Invalid", Util.isValidSNSArn(topicArn));
topicArn = "arn:aws:sns:us-west-2:475313751589:test-notification.fi";
assertFalse("topic arn should be Invalid", Util.isValidSNSArn(topicArn));
topicArn = "arn:aws:sns:us-west-2:475313751589:test-notifica.tion";
assertFalse("topic arn should be Invalid", Util.isValidSNSArn(topicArn));
topicArn = "arn:aws:sns:us-west-2:475313751589:test-notification&fifo";
assertFalse("topic arn should be Invalid", Util.isValidSNSArn(topicArn));
}

@Test
public void testIAMRoleArn() {
String roleArn = "arn:aws:iam::853806060000:role/domain/abc";
assertTrue("IAM role arn should be valid", Util.isValidIAMArn(roleArn));
roleArn = "arn:aws:iam::853806060000:role/domain/a@+=.,-_bc";
assertTrue("IAM role arn should be valid", Util.isValidIAMArn(roleArn));
}

@Test
public void testInvalidIAMRoleArn() {
String roleArn = "arn:aws:iam::85380606000000000:role/domain/010-asdf";
assertFalse("IAM role arn should be Invalid", Util.isValidIAMArn(roleArn));
}

@Test
public void testGetRegion() {
String topicArn = "arn:aws:sns:us-west-2:475313751589:test-notification";
assertEquals(Util.getRegion(topicArn), "us-west-2");
}

@Test(expected = IllegalArgumentException.class)
public void testInvalidGetRegion() {
String topicArn = "arn:aws:abs:us-west-2:475313751589:test-notification";
assertEquals(Util.getRegion(topicArn), "us-west-2");
}
}