blob: 42a4eb7aad419b9020871a826800176ac1059b57 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 5106721
27 * @summary Check the NotificationAccessController methods are properly called.
28 * @author Luis-Miguel Alventosa
29 * @run clean NotificationAccessControllerTest
30 * @run build NotificationAccessControllerTest
31 * @run main NotificationAccessControllerTest
32 */
33
34import com.sun.jmx.remote.security.NotificationAccessController;
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40import javax.management.MBeanServer;
41import javax.management.MBeanServerConnection;
42import javax.management.MBeanServerFactory;
43import javax.management.Notification;
44import javax.management.NotificationBroadcasterSupport;
45import javax.management.NotificationListener;
46import javax.management.ObjectName;
47import javax.management.remote.JMXAuthenticator;
48import javax.management.remote.JMXConnector;
49import javax.management.remote.JMXConnectorFactory;
50import javax.management.remote.JMXConnectorServer;
51import javax.management.remote.JMXConnectorServerFactory;
52import javax.management.remote.JMXPrincipal;
53import javax.management.remote.JMXServiceURL;
54import javax.security.auth.Subject;
55
56public class NotificationAccessControllerTest {
57
58 public class NAC implements NotificationAccessController {
59 private boolean throwException;
60 public NAC(boolean throwException) {
61 this.throwException = throwException;
62 }
63 public void addNotificationListener(
64 String connectionId,
65 ObjectName name,
66 Subject subject)
67 throws SecurityException {
68 echo("addNotificationListener:");
69 echo("\tconnectionId: " + connectionId);
70 echo("\tname: " + name);
71 echo("\tsubject: " +
72 (subject == null ? null : subject.getPrincipals()));
73 if (throwException)
74 if (name.getCanonicalName().equals("domain:name=1,type=NB")
75 &&
76 subject.getPrincipals().contains(new JMXPrincipal("role")))
77 throw new SecurityException();
78 }
79 public void removeNotificationListener(
80 String connectionId,
81 ObjectName name,
82 Subject subject)
83 throws SecurityException {
84 echo("removeNotificationListener:");
85 echo("\tconnectionId: " + connectionId);
86 echo("\tname: " + name);
87 echo("\tsubject: " +
88 (subject == null ? null : subject.getPrincipals()));
89 if (throwException)
90 if (name.getCanonicalName().equals("domain:name=2,type=NB")
91 &&
92 subject.getPrincipals().contains(new JMXPrincipal("role")))
93 throw new SecurityException();
94 }
95 public void fetchNotification(
96 String connectionId,
97 ObjectName name,
98 Notification notification,
99 Subject subject)
100 throws SecurityException {
101 echo("fetchNotification:");
102 echo("\tconnectionId: " + connectionId);
103 echo("\tname: " + name);
104 echo("\tnotification: " + notification);
105 echo("\tsubject: " +
106 (subject == null ? null : subject.getPrincipals()));
107 if (!throwException)
108 if (name.getCanonicalName().equals("domain:name=2,type=NB") &&
109 subject.getPrincipals().contains(new JMXPrincipal("role")))
110 throw new SecurityException();
111 }
112 }
113
114 public class CustomJMXAuthenticator implements JMXAuthenticator {
115 public Subject authenticate(Object credentials) {
116 String role = ((String[]) credentials)[0];
117 echo("\nCreate principal with name = " + role);
118 return new Subject(true,
119 Collections.singleton(new JMXPrincipal(role)),
120 Collections.EMPTY_SET,
121 Collections.EMPTY_SET);
122 }
123 }
124
125 public interface NBMBean {
126 public void emitNotification(int seqnum, ObjectName name);
127 }
128
129 public static class NB
130 extends NotificationBroadcasterSupport
131 implements NBMBean {
132 public void emitNotification(int seqnum, ObjectName name) {
133 if (name == null) {
134 sendNotification(new Notification("nb", this, seqnum));
135 } else {
136 sendNotification(new Notification("nb", name, seqnum));
137 }
138 }
139 }
140
141 public class Listener implements NotificationListener {
142 public List<Notification> notifs = new ArrayList<Notification>();
143 public void handleNotification(Notification n, Object h) {
144 echo("handleNotification:");
145 echo("\tNotification = " + n);
146 echo("\tNotification.SeqNum = " + n.getSequenceNumber());
147 echo("\tHandback = " + h);
148 notifs.add(n);
149 }
150 }
151
152 /**
153 * Check received notifications
154 */
155 public int checkNotifs(int size,
156 List<Notification> received,
157 List<ObjectName> expected) {
158 if (received.size() != size) {
159 echo("Error: expecting " + size + " notifications");
160 return 1;
161 } else {
162 for (Notification n : received) {
163 echo("Received notification: " + n);
164 if (!n.getType().equals("nb")) {
165 echo("Notification type must be \"nb\"");
166 return 1;
167 }
168 ObjectName o = (ObjectName) n.getSource();
169 int index = (int) n.getSequenceNumber();
170 ObjectName nb = expected.get(index);
171 if (!o.equals(nb)) {
172 echo("Notification source must be " + nb);
173 return 1;
174 }
175 }
176 }
177 return 0;
178 }
179
180 /**
181 * Run test
182 */
183 public int runTest(boolean enableChecks, boolean throwException)
184 throws Exception {
185
186 echo("\n=-=-= " + (enableChecks ? "Enable" : "Disable") +
187 " notification access control checks " +
188 (!enableChecks ? "" : (throwException ? ": add/remove " :
189 ": fetch ")) + "=-=-=");
190
191 JMXConnectorServer server = null;
192 JMXConnector client = null;
193
194 // Create a new MBeanServer
195 //
196 final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
197
198 try {
199 // Create server environment map
200 //
201 final Map<String,Object> env = new HashMap<String,Object>();
202 env.put("jmx.remote.authenticator", new CustomJMXAuthenticator());
203 if (enableChecks) {
204 env.put("com.sun.jmx.remote.notification.access.controller",
205 new NAC(throwException));
206 }
207
208 // Create the JMXServiceURL
209 //
210 final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
211
212 // Create a JMXConnectorServer
213 //
214 server = JMXConnectorServerFactory.newJMXConnectorServer(url,
215 env,
216 mbs);
217
218 // Start the JMXConnectorServer
219 //
220 server.start();
221
222 // Create server environment map
223 //
224 final Map<String,Object> cenv = new HashMap<String,Object>();
225 String[] credentials = new String[] { "role" , "password" };
226 cenv.put("jmx.remote.credentials", credentials);
227
228 // Create JMXConnector and connect to JMXConnectorServer
229 //
230 client = JMXConnectorFactory.connect(server.getAddress(), cenv);
231
232 // Get non-secure MBeanServerConnection
233 //
234 final MBeanServerConnection mbsc =
235 client.getMBeanServerConnection();
236
237 // Create NB MBean
238 //
239 ObjectName nb1 = ObjectName.getInstance("domain:type=NB,name=1");
240 ObjectName nb2 = ObjectName.getInstance("domain:type=NB,name=2");
241 ObjectName nb3 = ObjectName.getInstance("domain:type=NB,name=3");
242 mbsc.createMBean(NB.class.getName(), nb1);
243 mbsc.createMBean(NB.class.getName(), nb2);
244 mbsc.createMBean(NB.class.getName(), nb3);
245
246 // Add notification listener
247 //
248 Listener li = new Listener();
249 try {
250 mbsc.addNotificationListener(nb1, li, null, null);
251 if (enableChecks && throwException) {
252 echo("Didn't get expected exception");
253 return 1;
254 }
255 } catch (SecurityException e) {
256 if (enableChecks && throwException) {
257 echo("Got expected exception: " + e);
258 } else {
259 echo("Got unexpected exception: " + e);
260 return 1;
261 }
262 }
263 mbsc.addNotificationListener(nb2, li, null, null);
264
265 // Invoke the "sendNotification" method
266 //
267 mbsc.invoke(nb1, "emitNotification",
268 new Object[] {0, null},
269 new String[] {"int", "javax.management.ObjectName"});
270 mbsc.invoke(nb2, "emitNotification",
271 new Object[] {1, null},
272 new String[] {"int", "javax.management.ObjectName"});
273 mbsc.invoke(nb2, "emitNotification",
274 new Object[] {2, nb3},
275 new String[] {"int", "javax.management.ObjectName"});
276
277 // Wait for notifications to be emitted
278 //
279 Thread.sleep(2000);
280
281 // Remove notification listener
282 //
283 if (!throwException)
284 mbsc.removeNotificationListener(nb1, li);
285 try {
286 mbsc.removeNotificationListener(nb2, li);
287 if (enableChecks && throwException) {
288 echo("Didn't get expected exception");
289 return 1;
290 }
291 } catch (SecurityException e) {
292 if (enableChecks && throwException) {
293 echo("Got expected exception: " + e);
294 } else {
295 echo("Got unexpected exception: " + e);
296 return 1;
297 }
298 }
299
300 int result = 0;
301 List<ObjectName> sources = new ArrayList();
302 sources.add(nb1);
303 sources.add(nb2);
304 sources.add(nb3);
305 if (!enableChecks) {
306 // List must contain three notifs from sources nb1, nb2 and nb3
307 //
308 result = checkNotifs(3, li.notifs, sources);
309 }
310 if (enableChecks && !throwException) {
311 // List must contain one notif from source nb1
312 //
313 result = checkNotifs(1, li.notifs, sources);
314 }
315 if (enableChecks && throwException) {
316 // List must contain two notifs from sources nb2 and nb3
317 //
318 result = checkNotifs(2, li.notifs, sources);
319 }
320 if (result > 0) {
321 return result;
322 }
323 } catch (Exception e) {
324 echo("Failed to perform operation: " + e);
325 e.printStackTrace();
326 return 1;
327 } finally {
328 // Close the connection
329 //
330 if (client != null)
331 client.close();
332
333 // Stop the connector server
334 //
335 if (server != null)
336 server.stop();
337
338 // Release the MBeanServer
339 //
340 if (mbs != null)
341 MBeanServerFactory.releaseMBeanServer(mbs);
342 }
343
344 return 0;
345 }
346
347 /*
348 * Print message
349 */
350 private static void echo(String message) {
351 System.out.println(message);
352 }
353
354 public static void main(String[] args) throws Exception {
355
356 System.out.println("\nTest notification access control.");
357
358 NotificationAccessControllerTest nact =
359 new NotificationAccessControllerTest();
360
361 int error = 0;
362
363 error += nact.runTest(false, false);
364
365 error += nact.runTest(true, false);
366
367 error += nact.runTest(true, true);
368
369 if (error > 0) {
370 final String msg = "\nTest FAILED! Got " + error + " error(s)";
371 System.out.println(msg);
372 throw new IllegalArgumentException(msg);
373 } else {
374 System.out.println("\nTest PASSED!");
375 }
376 }
377}