Skip to content

Commit 7b5681b

Browse files
authored
Merge pull request #1274 from baranowb/UNDERTOW-1994
[UNDERTOW-1994] impl declareRoles in ServletContext + testcase
2 parents a8088df + 1f77acf commit 7b5681b

File tree

5 files changed

+253
-0
lines changed

5 files changed

+253
-0
lines changed

servlet/src/main/java/io/undertow/servlet/UndertowServletMessages.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,13 @@ public interface UndertowServletMessages {
235235

236236
@Message(id = 10063, value = "Path %s must start with a / to get the request dispatcher")
237237
IllegalArgumentException pathMustStartWithSlashForRequestDispatcher(String path);
238+
239+
@Message(id = 10064, value = "Servlet context for context path '%s' in deployment '%s' has already been initialized, can not declare roles.")
240+
IllegalStateException servletAlreadyInitialize(String deploymentName, String contextPath);
241+
242+
@Message(id = 10065, value = "Can not set empty/null role in servlet context for context path '%s' in deployment '%s' ")
243+
IllegalArgumentException roleMustNotBeEmpty(String deploymentName, String contextPath);
244+
245+
@Message(id = 10066, value = "Can not set invoke 'declareRoles' from dynamic listener in servlet context for context path '%s' in deployment '%s' ")
246+
UnsupportedOperationException cantCallFromDynamicListener(String deploymentName, String contextPath);
238247
}

servlet/src/main/java/io/undertow/servlet/spec/ServletContextImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,24 @@ public ClassLoader getClassLoader() {
792792

793793
@Override
794794
public void declareRoles(final String... roleNames) {
795+
final DeploymentInfo di = this.getDeploymentInfo();
796+
if (isInitialized()) {
797+
throw UndertowServletMessages.MESSAGES.servletAlreadyInitialize(di.getDeploymentName(), di.getContextPath());
798+
}
799+
800+
for (String role : roleNames) {
801+
if (role == null || role.isEmpty()) {
802+
throw UndertowServletMessages.MESSAGES.roleMustNotBeEmpty(di.getDeploymentName(), di.getContextPath());
803+
}
804+
}
805+
806+
if (ApplicationListeners.listenerState() == PROGRAMATIC_LISTENER) {
807+
//NOTE: its either null or false for non-programatic? - null in case its not ApplicationListener
808+
throw UndertowServletMessages.MESSAGES.cantCallFromDynamicListener(di.getDeploymentName(), di.getContextPath());
809+
}
810+
811+
deploymentInfo.addSecurityRoles(roleNames);
812+
795813
}
796814

797815
@Override
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2021 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package io.undertow.servlet.test.listener.servletcontext;
19+
20+
import java.io.IOException;
21+
import java.io.PrintWriter;
22+
23+
import javax.servlet.ServletConfig;
24+
import javax.servlet.ServletException;
25+
import javax.servlet.http.HttpServlet;
26+
import javax.servlet.http.HttpServletRequest;
27+
import javax.servlet.http.HttpServletResponse;
28+
29+
/**
30+
*
31+
* @author baranowb
32+
*
33+
*/
34+
public class CheckRolesServlet extends HttpServlet {
35+
36+
37+
@Override
38+
public void init(final ServletConfig config) throws ServletException {
39+
super.init(config);
40+
}
41+
42+
@Override
43+
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
44+
PrintWriter writer = resp.getWriter();
45+
for(String role:DeclareRolesServletContextListener.ROLES) {
46+
final boolean isInRole = req.isUserInRole(role);
47+
writer.write(role+":"+isInRole+"\n");
48+
}
49+
writer.close();
50+
}
51+
52+
@Override
53+
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
54+
doGet(req, resp);
55+
}
56+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2021 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package io.undertow.servlet.test.listener.servletcontext;
19+
20+
import javax.servlet.ServletContextEvent;
21+
import javax.servlet.ServletContextListener;
22+
/**
23+
*
24+
* @author baranowb
25+
*
26+
*/
27+
public class DeclareRolesServletContextListener implements ServletContextListener{
28+
29+
public static final String[] ROLES = new String[] {"dobby","was","here"};
30+
@Override
31+
public void contextInitialized(ServletContextEvent sce) {
32+
System.err.println(sce.getServletContext());
33+
sce.getServletContext().declareRoles(ROLES);
34+
}
35+
36+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2021 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package io.undertow.servlet.test.listener.servletcontext;
20+
21+
import static io.undertow.util.Headers.AUTHORIZATION;
22+
import static io.undertow.util.Headers.BASIC;
23+
import static org.junit.Assert.assertEquals;
24+
25+
import java.nio.charset.Charset;
26+
import java.nio.charset.StandardCharsets;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
import javax.servlet.ServletException;
31+
32+
import org.apache.http.HttpResponse;
33+
import org.apache.http.client.methods.HttpGet;
34+
import org.junit.BeforeClass;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
38+
import io.undertow.server.handlers.PathHandler;
39+
import io.undertow.servlet.api.AuthMethodConfig;
40+
import io.undertow.servlet.api.DeploymentInfo;
41+
import io.undertow.servlet.api.DeploymentManager;
42+
import io.undertow.servlet.api.ListenerInfo;
43+
import io.undertow.servlet.api.LoginConfig;
44+
import io.undertow.servlet.api.SecurityConstraint;
45+
import io.undertow.servlet.api.SecurityInfo;
46+
import io.undertow.servlet.api.ServletContainer;
47+
import io.undertow.servlet.api.ServletInfo;
48+
import io.undertow.servlet.api.WebResourceCollection;
49+
import io.undertow.servlet.test.security.constraint.ServletIdentityManager;
50+
import io.undertow.servlet.test.util.TestClassIntrospector;
51+
import io.undertow.testutils.DefaultServer;
52+
import io.undertow.testutils.HttpClientUtils;
53+
import io.undertow.testutils.TestHttpClient;
54+
import io.undertow.util.FlexBase64;
55+
import io.undertow.util.Headers;
56+
57+
/**
58+
* @author baranowb
59+
*/
60+
@RunWith(DefaultServer.class)
61+
public class ServletContextRolesTestCase {
62+
private static final String REALM_NAME = "Servlet_Realm-1";
63+
static DeploymentManager manager;
64+
65+
@BeforeClass
66+
public static void setup() throws ServletException {
67+
68+
final PathHandler root = new PathHandler();
69+
final ServletContainer container = ServletContainer.Factory.newInstance();
70+
final ServletIdentityManager identityManager = new ServletIdentityManager();
71+
identityManager.addUser("user1", "password1", "unspecified-role");
72+
73+
LoginConfig loginConfig = new LoginConfig(REALM_NAME);
74+
Map<String, String> props = new HashMap<>();
75+
props.put("charset", "ISO_8859_1");
76+
props.put("user-agent-charsets", "Chrome,UTF-8,OPR,UTF-8");
77+
loginConfig.addFirstAuthMethod(new AuthMethodConfig("BASIC", props));
78+
79+
DeploymentInfo builder = new DeploymentInfo()
80+
.setClassLoader(ServletContextRolesTestCase.class.getClassLoader())
81+
.setContextPath("/servletContext")
82+
.setClassIntrospecter(TestClassIntrospector.INSTANCE)
83+
.setDeploymentName("servletContext.war")
84+
.addServlet(
85+
new ServletInfo("servlet", CheckRolesServlet.class)
86+
.addMapping("/aa")
87+
)
88+
.addListener(new ListenerInfo(DeclareRolesServletContextListener.class))
89+
.setIdentityManager(identityManager)
90+
.setLoginConfig(loginConfig);
91+
92+
builder.addPrincipalVsRoleMappings("user1", DeclareRolesServletContextListener.ROLES);
93+
builder.addSecurityConstraint(new SecurityConstraint()
94+
.addWebResourceCollection(new WebResourceCollection()
95+
.addUrlPattern("/*"))
96+
.addRolesAllowed(DeclareRolesServletContextListener.ROLES)
97+
.setEmptyRoleSemantic(SecurityInfo.EmptyRoleSemantic.DENY));
98+
99+
manager = container.addDeployment(builder);
100+
manager.deploy();
101+
root.addPrefixPath(builder.getContextPath(), manager.start());
102+
103+
DefaultServer.setRootHandler(root);
104+
}
105+
106+
@Test
107+
public void testRoles() throws Exception {
108+
final StringBuilder sb = new StringBuilder(40);
109+
sb.append("dobby:true\n")
110+
.append("was:true\n")
111+
.append("here:true\n");
112+
testCall(sb.toString(), StandardCharsets.UTF_8, "Chrome", "user1", "password1", 200);
113+
}
114+
115+
public void testCall( final String expectedResponse, Charset charset, String userAgent, String user, String password, int expect) throws Exception {
116+
TestHttpClient client = new TestHttpClient();
117+
try {
118+
String url = DefaultServer.getDefaultServerURL() + "/servletContext/aa";
119+
HttpGet get = new HttpGet(url);
120+
get = new HttpGet(url);
121+
get.addHeader(Headers.USER_AGENT_STRING, userAgent);
122+
get.addHeader(AUTHORIZATION.toString(), BASIC + " " + FlexBase64.encodeString((user + ":" + password).getBytes(charset), false));
123+
HttpResponse result = client.execute(get);
124+
assertEquals(expect, result.getStatusLine().getStatusCode());
125+
126+
final String response = HttpClientUtils.readResponse(result);
127+
if(expect == 200) {
128+
assertEquals(expectedResponse, response);
129+
}
130+
} finally {
131+
client.getConnectionManager().shutdown();
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)