Skip to content

Commit

Permalink
Added legacy support for SNS messages. (#225)
Browse files Browse the repository at this point in the history
* Added legacy support for SNS messages.

Signed-off-by: AWSHurneyt <[email protected]>

* Added license header to new classes.

Signed-off-by: AWSHurneyt <[email protected]>

* Fixed style errors.

Signed-off-by: AWSHurneyt <[email protected]>

Signed-off-by: AWSHurneyt <[email protected]>
  • Loading branch information
AWSHurneyt authored Oct 6, 2022
1 parent 7dcb3a0 commit 9c2621e
Show file tree
Hide file tree
Showing 6 changed files with 351 additions and 1 deletion.
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");
}
}

0 comments on commit 9c2621e

Please sign in to comment.