Skip to content

Commit 07802ac

Browse files
Username validation for special characters (#2277) (#2322)
* Only prevent user creation on colon characters, separate out tests Signed-off-by: Rutuja Surve <rutuja@amazon.com> Signed-off-by: Rutuja Surve <110013621+rutuja-amazon@users.noreply.github.com> Signed-off-by: Peter Nied <petern@amazon.com> Co-authored-by: Peter Nied <petern@amazon.com> (cherry picked from commit efbc48b) Co-authored-by: rutuja-amazon <110013621+rutuja-amazon@users.noreply.github.com>
1 parent ad3656c commit 07802ac

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.io.IOException;
1515
import java.nio.file.Path;
1616
import java.util.List;
17+
import java.util.stream.Collectors;
1718

1819
import com.fasterxml.jackson.databind.JsonNode;
1920
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -49,6 +50,10 @@
4950
import static org.opensearch.security.dlic.rest.support.Utils.hash;
5051

5152
public class InternalUsersApiAction extends PatchableResourceApiAction {
53+
static final List<String> RESTRICTED_FROM_USERNAME = ImmutableList.of(
54+
":" // Not allowed in basic auth, see https://stackoverflow.com/a/33391003/533057
55+
);
56+
5257
private static final List<Route> routes = addRoutesPrefix(ImmutableList.of(
5358
new Route(Method.GET, "/user/{name}"),
5459
new Route(Method.GET, "/user/"),
@@ -93,6 +98,13 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C
9398
return;
9499
}
95100

101+
final List<String> foundRestrictedContents = RESTRICTED_FROM_USERNAME.stream().filter(username::contains).collect(Collectors.toList());
102+
if (!foundRestrictedContents.isEmpty()) {
103+
final String restrictedContents = foundRestrictedContents.stream().map(s -> "'" + s + "'").collect(Collectors.joining(","));
104+
badRequestResponse(channel, "Username has restricted characters " + restrictedContents + " that are not permitted.");
105+
return;
106+
}
107+
96108
// TODO it might be sensible to consolidate this with the overridden method in
97109
// order to minimize duplicated logic
98110

src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
import org.opensearch.security.test.helper.file.FileHelper;
2929
import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
3030

31+
import static org.hamcrest.MatcherAssert.assertThat;
32+
import static org.hamcrest.Matchers.containsString;
33+
import static org.hamcrest.Matchers.equalTo;
3134
import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;
35+
import static org.opensearch.security.dlic.rest.api.InternalUsersApiAction.RESTRICTED_FROM_USERNAME;
36+
3237

3338
public class UserApiTest extends AbstractRestApiUnitTest {
3439
private final String ENDPOINT;
@@ -468,7 +473,7 @@ public void testPasswordRules() throws Exception {
468473
addUserWithPassword("$1aAAAAAAAac", "$1aAAAAAAAAC", HttpStatus.SC_BAD_REQUEST);
469474
addUserWithPassword(URLEncoder.encode("$1aAAAAAAAac%", "UTF-8"), "$1aAAAAAAAAC%", HttpStatus.SC_BAD_REQUEST);
470475
addUserWithPassword(URLEncoder.encode("$1aAAAAAAAac%!=\"/\\;:test&~@^", "UTF-8").replace("+", "%2B"), "$1aAAAAAAAac%!=\\\"/\\\\;:test&~@^", HttpStatus.SC_BAD_REQUEST);
471-
addUserWithPassword(URLEncoder.encode("$1aAAAAAAAac%!=\"/\\;: test&", "UTF-8"), "$1aAAAAAAAac%!=\\\"/\\\\;: test&123", HttpStatus.SC_CREATED);
476+
addUserWithPassword(URLEncoder.encode("$1aAAAAAAAac%!=\"/\\;: test&", "UTF-8"), "$1aAAAAAAAac%!=\\\"/\\\\;: test&123", HttpStatus.SC_BAD_REQUEST);
472477

473478
response = rh.executeGetRequest(PLUGINS_PREFIX + "/api/internalusers/nothinghthere?pretty", new Header[0]);
474479
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
@@ -624,7 +629,24 @@ public void testUserApiForNonSuperAdmin() throws Exception {
624629
// Patch multiple hidden users
625630
response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/hide/description\", \"value\": \"foo\" }]", new Header[0]);
626631
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
632+
}
633+
634+
@Test
635+
public void restrictedUsernameContents() throws Exception {
636+
setup();
637+
638+
rh.keystore = "restapi/kirk-keystore.jks";
639+
rh.sendAdminCertificate = true;
640+
641+
RESTRICTED_FROM_USERNAME.stream().forEach(restrictedTerm -> {
642+
final String username = "nag" + restrictedTerm + "ilum";
643+
final String url = ENDPOINT + "/internalusers/" + username;
644+
final String bodyWithDefaultPasswordHash = "{\"hash\": \"456\"}";
645+
final HttpResponse response = rh.executePutRequest(url, bodyWithDefaultPasswordHash);
627646

647+
assertThat("Expected " + username + " to be rejected", response.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST));
648+
assertThat(response.getBody(), containsString(restrictedTerm));
649+
});
628650
}
629651

630652
@Test

0 commit comments

Comments
 (0)