Skip to content

Commit 74686a2

Browse files
authored
Add support for ResourceIds with 11 elements (#85)
* Use a longer resourceId for sqlsecurityauditevents.json * Implement a new object for longer resourceIds * Use the ResourceIdWithSubtype for SQLSecurityAuditEventsType * Add a test for 12 segment long ResourceIdWithSubtype * Add tests for checking the Exceptions from ResourceId* methods that throw them * Add braces to ResourceIdWithSubtype.equals if statement
1 parent 33e0335 commit 74686a2

7 files changed

Lines changed: 263 additions & 19 deletions

File tree

src/main/java/com/teragrep/nlf_01/types/SQLSecurityAuditEventsType.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,7 @@
4747

4848
import com.teragrep.akv_01.event.ParsedEvent;
4949
import com.teragrep.akv_01.plugin.PluginException;
50-
import com.teragrep.nlf_01.util.ASCIIString;
51-
import com.teragrep.nlf_01.util.DefaultSDElements;
52-
import com.teragrep.nlf_01.util.MD5Hash;
53-
import com.teragrep.nlf_01.util.ResourceId;
54-
import com.teragrep.nlf_01.util.SDElements;
55-
import com.teragrep.nlf_01.util.ValidKey;
56-
import com.teragrep.nlf_01.util.ValidRFC5424AppName;
57-
import com.teragrep.nlf_01.util.ValidRFC5424Hostname;
58-
import com.teragrep.nlf_01.util.ValidRFC5424Timestamp;
59-
import com.teragrep.nlf_01.util.ValidStringKey;
50+
import com.teragrep.nlf_01.util.*;
6051
import com.teragrep.rlo_14.Facility;
6152
import com.teragrep.rlo_14.SDElement;
6253
import com.teragrep.rlo_14.Severity;
@@ -97,7 +88,7 @@ public String hostname() throws PluginException {
9788
final ValidKey<String> validKey = new ValidStringKey(record, "resourceId");
9889

9990
return new ValidRFC5424Hostname(
100-
"md5-".concat(new MD5Hash(validKey.value()).md5().concat("-").concat(new ASCIIString(new ResourceId(validKey.value()).resourceName()).withNonAsciiCharsRemoved()))
91+
"md5-".concat(new MD5Hash(validKey.value()).md5().concat("-").concat(new ASCIIString(new ResourceIdWithSubtype(validKey.value()).resourceName()).withNonAsciiCharsRemoved()))
10192
).hostnameWithInvalidCharsRemoved();
10293
}
10394

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Teragrep Neon log format plugin for AKV_01
3+
* Copyright (C) 2025 Suomen Kanuuna Oy
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
*
19+
* Additional permission under GNU Affero General Public License version 3
20+
* section 7
21+
*
22+
* If you modify this Program, or any covered work, by linking or combining it
23+
* with other code, such other code is not for that reason alone subject to any
24+
* of the requirements of the GNU Affero GPL version 3 as long as this Program
25+
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
26+
* modifications.
27+
*
28+
* Supplemented terms under GNU Affero General Public License version 3
29+
* section 7
30+
*
31+
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
32+
* versions must be marked as "Modified version of" The Program.
33+
*
34+
* Names of the licensors and authors may not be used for publicity purposes.
35+
*
36+
* No rights are granted for use of trade names, trademarks, or service marks
37+
* which are in The Program if any.
38+
*
39+
* Licensee must indemnify licensors and authors for any liability that these
40+
* contractual assumptions impose on licensors and authors.
41+
*
42+
* To the extent this program is licensed as part of the Commercial versions of
43+
* Teragrep, the applicable Commercial License may apply to this file if you as
44+
* a licensee so wish it.
45+
*/
46+
package com.teragrep.nlf_01.util;
47+
48+
import com.teragrep.akv_01.plugin.PluginException;
49+
50+
import java.util.Arrays;
51+
52+
public final class ResourceIdWithSubtype {
53+
54+
private final String[] splitResourceId;
55+
56+
public ResourceIdWithSubtype(final String resourceId) {
57+
this(resourceId.split("/"));
58+
}
59+
60+
public ResourceIdWithSubtype(final String[] splitResourceId) {
61+
this.splitResourceId = splitResourceId;
62+
}
63+
64+
private void validate() throws PluginException {
65+
if (splitResourceId.length != 11) {
66+
throw new PluginException(new IllegalArgumentException("ResourceIdWithSubtype must have 11 elements"));
67+
}
68+
}
69+
70+
public String subscriptionId() throws PluginException {
71+
validate();
72+
return splitResourceId[2];
73+
}
74+
75+
public String resourceGroupName() throws PluginException {
76+
validate();
77+
return splitResourceId[4];
78+
}
79+
80+
public String resourceProviderNamespace() throws PluginException {
81+
validate();
82+
return splitResourceId[6];
83+
}
84+
85+
public String resourceType() throws PluginException {
86+
validate();
87+
return splitResourceId[7];
88+
}
89+
90+
public String resourceName() throws PluginException {
91+
validate();
92+
return splitResourceId[8];
93+
}
94+
95+
public String subtype() throws PluginException {
96+
validate();
97+
return splitResourceId[9];
98+
}
99+
100+
public String subtypeName() throws PluginException {
101+
validate();
102+
return splitResourceId[10];
103+
}
104+
105+
@Override
106+
public boolean equals(final Object o) {
107+
if (o == null || getClass() != o.getClass()) {
108+
return false;
109+
}
110+
final ResourceIdWithSubtype that = (ResourceIdWithSubtype) o;
111+
return Arrays.deepEquals(splitResourceId, that.splitResourceId);
112+
}
113+
114+
@Override
115+
public int hashCode() {
116+
return Arrays.hashCode(splitResourceId);
117+
}
118+
}

src/test/java/com/teragrep/nlf_01/NLFPluginTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,11 +1037,11 @@ void sqlSecurityAuditEventsTypeTest() {
10371037
"{\n" + " \"category\": \"SQLSecurityAuditEvents\",\n"
10381038
+ " \"operationName\": \"Operation-1\",\n"
10391039
+ " \"originalEventTimestamp\": \"2025-10-06T00:00:00.0000000Z\",\n"
1040-
+ " \"resourceId\": \"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}\"\n"
1040+
+ " \"resourceId\": \"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}\"\n"
10411041
+ "}",
10421042
syslogMessage.getMsg()
10431043
);
1044-
Assertions.assertEquals("md5-0ded52ef915af563e25778bf26b0f129-resourceName", syslogMessage.getHostname());
1044+
Assertions.assertEquals("md5-63a8be7673efd1bb7439550f2ad118ce-resourceName", syslogMessage.getHostname());
10451045
Assertions.assertEquals("Operation-1", syslogMessage.getAppName());
10461046
Assertions.assertEquals("2025-10-06T00:00:00Z", syslogMessage.getTimestamp());
10471047

src/test/java/com/teragrep/nlf_01/ResourceIdTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,29 @@ void testWithValidResourceId() {
6969
@Test
7070
void testWithInvalidResourceId() {
7171
final ResourceId r = new ResourceId("/foo/bar");
72-
Assertions.assertThrows(PluginException.class, r::subscriptionId);
72+
final PluginException subscriptionIdPluginException = Assertions
73+
.assertThrows(PluginException.class, r::subscriptionId);
74+
final Throwable cause = subscriptionIdPluginException.getCause();
75+
Assertions.assertEquals(IllegalArgumentException.class, cause.getClass());
76+
Assertions.assertEquals("ResourceId must have 9 elements", cause.getMessage());
77+
78+
Assertions.assertThrows(PluginException.class, r::resourceGroupName);
79+
Assertions.assertThrows(PluginException.class, r::resourceProviderNamespace);
80+
Assertions.assertThrows(PluginException.class, r::resourceType);
81+
Assertions.assertThrows(PluginException.class, r::resourceName);
82+
}
83+
84+
@Test
85+
void testWithLongerResourceId() {
86+
final ResourceId r = new ResourceId(
87+
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}"
88+
);
89+
PluginException subscriptionIdPluginException = Assertions
90+
.assertThrows(PluginException.class, r::subscriptionId);
91+
final Throwable cause = subscriptionIdPluginException.getCause();
92+
Assertions.assertEquals(IllegalArgumentException.class, cause.getClass());
93+
Assertions.assertEquals("ResourceId must have 9 elements", cause.getMessage());
94+
7395
Assertions.assertThrows(PluginException.class, r::resourceGroupName);
7496
Assertions.assertThrows(PluginException.class, r::resourceProviderNamespace);
7597
Assertions.assertThrows(PluginException.class, r::resourceType);

src/test/java/com/teragrep/nlf_01/types/SQLSecurityAuditEventsTypeTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,10 @@ void testIdealCase() {
159159

160160
Assertions.assertEquals("Operation-1", actualAppName);
161161
Assertions.assertEquals(Facility.AUDIT, actualFacility);
162-
Assertions.assertEquals("md5-0ded52ef915af563e25778bf26b0f129-resourceName", actualHostname);
162+
Assertions.assertEquals("md5-63a8be7673efd1bb7439550f2ad118ce-resourceName", actualHostname);
163163
Assertions
164164
.assertEquals(
165-
"{\"category\":\"SQLSecurityAuditEvents\",\"operationName\":\"Operation-1\",\"originalEventTimestamp\":\"2025-10-06T00:00:00.0000000Z\",\"resourceId\":\"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}\"}",
165+
"{\"category\":\"SQLSecurityAuditEvents\",\"operationName\":\"Operation-1\",\"originalEventTimestamp\":\"2025-10-06T00:00:00.0000000Z\",\"resourceId\":\"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}\"}",
166166
actualMsg
167167
);
168168
Assertions.assertEquals("12345678900", actualMsgId);
@@ -217,10 +217,10 @@ void testWithAllMetadataStubs() {
217217

218218
Assertions.assertEquals("Operation-1", actualAppName);
219219
Assertions.assertEquals(Facility.AUDIT, actualFacility);
220-
Assertions.assertEquals("md5-0ded52ef915af563e25778bf26b0f129-resourceName", actualHostname);
220+
Assertions.assertEquals("md5-63a8be7673efd1bb7439550f2ad118ce-resourceName", actualHostname);
221221
Assertions
222222
.assertEquals(
223-
"{\"category\":\"SQLSecurityAuditEvents\",\"operationName\":\"Operation-1\",\"originalEventTimestamp\":\"2025-10-06T00:00:00.0000000Z\",\"resourceId\":\"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}\"}",
223+
"{\"category\":\"SQLSecurityAuditEvents\",\"operationName\":\"Operation-1\",\"originalEventTimestamp\":\"2025-10-06T00:00:00.0000000Z\",\"resourceId\":\"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}\"}",
224224
actualMsg
225225
);
226226
Assertions.assertEquals("", actualMsgId);
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Teragrep Neon log format plugin for AKV_01
3+
* Copyright (C) 2025 Suomen Kanuuna Oy
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
*
19+
* Additional permission under GNU Affero General Public License version 3
20+
* section 7
21+
*
22+
* If you modify this Program, or any covered work, by linking or combining it
23+
* with other code, such other code is not for that reason alone subject to any
24+
* of the requirements of the GNU Affero GPL version 3 as long as this Program
25+
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
26+
* modifications.
27+
*
28+
* Supplemented terms under GNU Affero General Public License version 3
29+
* section 7
30+
*
31+
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
32+
* versions must be marked as "Modified version of" The Program.
33+
*
34+
* Names of the licensors and authors may not be used for publicity purposes.
35+
*
36+
* No rights are granted for use of trade names, trademarks, or service marks
37+
* which are in The Program if any.
38+
*
39+
* Licensee must indemnify licensors and authors for any liability that these
40+
* contractual assumptions impose on licensors and authors.
41+
*
42+
* To the extent this program is licensed as part of the Commercial versions of
43+
* Teragrep, the applicable Commercial License may apply to this file if you as
44+
* a licensee so wish it.
45+
*/
46+
package com.teragrep.nlf_01.util;
47+
48+
import com.teragrep.akv_01.plugin.PluginException;
49+
import nl.jqno.equalsverifier.EqualsVerifier;
50+
import org.junit.jupiter.api.Assertions;
51+
import org.junit.jupiter.api.Test;
52+
53+
final class ResourceIdWithSubtypeTest {
54+
55+
@Test
56+
void equalsContractTest() {
57+
EqualsVerifier.forClass(ResourceIdWithSubtype.class).verify();
58+
}
59+
60+
@Test
61+
void testWithValidResourceId() {
62+
final ResourceIdWithSubtype r = new ResourceIdWithSubtype(
63+
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}"
64+
);
65+
Assertions.assertEquals("{subscriptionId}", Assertions.assertDoesNotThrow(r::subscriptionId));
66+
Assertions.assertEquals("{resourceGroupName}", Assertions.assertDoesNotThrow(r::resourceGroupName));
67+
Assertions
68+
.assertEquals("{resourceProviderNamespace}", Assertions.assertDoesNotThrow(r::resourceProviderNamespace));
69+
Assertions.assertEquals("{resourceType}", Assertions.assertDoesNotThrow(r::resourceType));
70+
Assertions.assertEquals("{resourceName}", Assertions.assertDoesNotThrow(r::resourceName));
71+
Assertions.assertEquals("{resourceSubtype}", Assertions.assertDoesNotThrow(r::subtype));
72+
Assertions.assertEquals("{subtypeName}", Assertions.assertDoesNotThrow(r::subtypeName));
73+
}
74+
75+
@Test
76+
void testWithInvalidResourceId() {
77+
// 9 segments long, which shouldn't work with the subtype
78+
final ResourceIdWithSubtype r = new ResourceIdWithSubtype(
79+
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}"
80+
);
81+
final PluginException subscriptionIdPluginException = Assertions
82+
.assertThrows(PluginException.class, r::subscriptionId);
83+
final Throwable cause = subscriptionIdPluginException.getCause();
84+
Assertions.assertEquals(IllegalArgumentException.class, cause.getClass());
85+
Assertions.assertEquals("ResourceIdWithSubtype must have 11 elements", cause.getMessage());
86+
87+
Assertions.assertThrows(PluginException.class, r::resourceGroupName);
88+
Assertions.assertThrows(PluginException.class, r::resourceProviderNamespace);
89+
Assertions.assertThrows(PluginException.class, r::resourceType);
90+
Assertions.assertThrows(PluginException.class, r::resourceName);
91+
Assertions.assertThrows(PluginException.class, r::subtype);
92+
Assertions.assertThrows(PluginException.class, r::subtypeName);
93+
}
94+
95+
@Test
96+
void testWithLongerResourceId() {
97+
final ResourceIdWithSubtype r = new ResourceIdWithSubtype(
98+
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}/{tooLongId}"
99+
);
100+
final PluginException subscriptionIdPluginException = Assertions
101+
.assertThrows(PluginException.class, r::subscriptionId);
102+
final Throwable cause = subscriptionIdPluginException.getCause();
103+
Assertions.assertEquals(IllegalArgumentException.class, cause.getClass());
104+
Assertions.assertEquals("ResourceIdWithSubtype must have 11 elements", cause.getMessage());
105+
106+
Assertions.assertThrows(PluginException.class, r::resourceGroupName);
107+
Assertions.assertThrows(PluginException.class, r::resourceProviderNamespace);
108+
Assertions.assertThrows(PluginException.class, r::resourceType);
109+
Assertions.assertThrows(PluginException.class, r::resourceName);
110+
Assertions.assertThrows(PluginException.class, r::subtype);
111+
Assertions.assertThrows(PluginException.class, r::subtypeName);
112+
}
113+
}

src/test/resources/sqlsecurityauditevents.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"category": "SQLSecurityAuditEvents",
33
"operationName": "Operation-1",
44
"originalEventTimestamp": "2025-10-06T00:00:00.0000000Z",
5-
"resourceId": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}"
5+
"resourceId": "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/{resourceSubtype}/{subtypeName}"
66
}

0 commit comments

Comments
 (0)