001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 *
020 */
021 package org.apache.qpid.server.security.access.management;
022
023 import org.apache.qpid.server.management.MBeanDescription;
024 import org.apache.qpid.server.management.AMQManagedObject;
025 import org.apache.qpid.server.management.MBeanOperation;
026 import org.apache.qpid.server.management.MBeanInvocationHandlerImpl;
027 import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
028 import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
029 import org.apache.qpid.server.security.access.management.UserManagement;
030 import org.apache.log4j.Logger;
031 import org.apache.commons.configuration.ConfigurationException;
032
033 import javax.management.JMException;
034 import javax.management.remote.JMXPrincipal;
035 import javax.management.openmbean.TabularData;
036 import javax.management.openmbean.TabularDataSupport;
037 import javax.management.openmbean.TabularType;
038 import javax.management.openmbean.SimpleType;
039 import javax.management.openmbean.CompositeType;
040 import javax.management.openmbean.OpenType;
041 import javax.management.openmbean.OpenDataException;
042 import javax.management.openmbean.CompositeData;
043 import javax.management.openmbean.CompositeDataSupport;
044 import javax.security.auth.login.AccountNotFoundException;
045 import javax.security.auth.Subject;
046 import java.io.File;
047 import java.io.FileInputStream;
048 import java.io.IOException;
049 import java.io.FileOutputStream;
050 import java.util.Properties;
051 import java.util.List;
052 import java.util.Enumeration;
053 import java.util.Set;
054 import java.util.concurrent.locks.ReentrantLock;
055 import java.security.Principal;
056 import java.security.AccessControlContext;
057 import java.security.AccessController;
058
059 /** MBean class for AMQUserManagementMBean. It implements all the management features exposed for managing users. */
060 @MBeanDescription("User Management Interface")
061 public class AMQUserManagementMBean extends AMQManagedObject implements UserManagement
062 {
063
064 private static final Logger _logger = Logger.getLogger(AMQUserManagementMBean.class);
065
066 private PrincipalDatabase _principalDatabase;
067 private String _accessFileName;
068 private Properties _accessRights;
069 // private File _accessFile;
070 private ReentrantLock _accessRightsUpdate = new ReentrantLock();
071
072 // Setup for the TabularType
073 static TabularType _userlistDataType; // Datatype for representing User Lists
074
075 static CompositeType _userDataType; // Composite type for representing User
076 static String[] _userItemNames = {"Username", "read", "write", "admin"};
077
078 static
079 {
080 String[] userItemDesc = {"Broker Login username", "Management Console Read Permission",
081 "Management Console Write Permission", "Management Console Admin Permission"};
082
083 OpenType[] userItemTypes = new OpenType[4]; // User item types.
084 userItemTypes[0] = SimpleType.STRING; // For Username
085 userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read
086 userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write
087 userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin
088 String[] userDataIndex = {_userItemNames[0]};
089
090 try
091 {
092 _userDataType =
093 new CompositeType("User", "User Data", _userItemNames, userItemDesc, userItemTypes);
094
095 _userlistDataType = new TabularType("Users", "List of users", _userDataType, userDataIndex);
096 }
097 catch (OpenDataException e)
098 {
099 _logger.error("Tabular data setup for viewing users incorrect.");
100 _userlistDataType = null;
101 }
102 }
103
104
105 public AMQUserManagementMBean() throws JMException
106 {
107 super(UserManagement.class, UserManagement.TYPE);
108 }
109
110 public String getObjectInstanceName()
111 {
112 return UserManagement.TYPE;
113 }
114
115 public boolean setPassword(String username, char[] password)
116 {
117 try
118 {
119 //delegate password changes to the Principal Database
120 return _principalDatabase.updatePassword(new UsernamePrincipal(username), password);
121 }
122 catch (AccountNotFoundException e)
123 {
124 _logger.warn("Attempt to set password of non-existant user'" + username + "'");
125 return false;
126 }
127 }
128
129 public boolean setRights(String username, boolean read, boolean write, boolean admin)
130 {
131
132 if (_accessRights.get(username) == null)
133 {
134 // If the user doesn't exist in the user rights file check that they at least have an account.
135 if (_principalDatabase.getUser(username) == null)
136 {
137 return false;
138 }
139 }
140
141 try
142 {
143
144 _accessRightsUpdate.lock();
145
146 // Update the access rights
147 if (admin)
148 {
149 _accessRights.put(username, MBeanInvocationHandlerImpl.ADMIN);
150 }
151 else
152 {
153 if (read | write)
154 {
155 if (read)
156 {
157 _accessRights.put(username, MBeanInvocationHandlerImpl.READONLY);
158 }
159 if (write)
160 {
161 _accessRights.put(username, MBeanInvocationHandlerImpl.READWRITE);
162 }
163 }
164 else
165 {
166 _accessRights.remove(username);
167 }
168 }
169
170 saveAccessFile();
171 }
172 finally
173 {
174 if (_accessRightsUpdate.isHeldByCurrentThread())
175 {
176 _accessRightsUpdate.unlock();
177 }
178 }
179
180 return true;
181 }
182
183 public boolean createUser(String username, char[] password, boolean read, boolean write, boolean admin)
184 {
185 if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password))
186 {
187 _accessRights.put(username, "");
188
189 return setRights(username, read, write, admin);
190 }
191
192 return false;
193 }
194
195 public boolean deleteUser(String username)
196 {
197
198 try
199 {
200 if (_principalDatabase.deletePrincipal(new UsernamePrincipal(username)))
201 {
202 try
203 {
204 _accessRightsUpdate.lock();
205
206 _accessRights.remove(username);
207 saveAccessFile();
208 }
209 finally
210 {
211 if (_accessRightsUpdate.isHeldByCurrentThread())
212 {
213 _accessRightsUpdate.unlock();
214 }
215 }
216 return true;
217 }
218 }
219 catch (AccountNotFoundException e)
220 {
221 _logger.warn("Attempt to delete user (" + username + ") that doesn't exist");
222 }
223
224 return false;
225 }
226
227 public boolean reloadData()
228 {
229 try
230 {
231 loadAccessFile();
232 _principalDatabase.reload();
233 }
234 catch (ConfigurationException e)
235 {
236 _logger.info("Reload failed due to:" + e);
237 return false;
238 }
239 catch (IOException e)
240 {
241 _logger.info("Reload failed due to:" + e);
242 return false;
243 }
244 // Reload successful
245 return true;
246 }
247
248
249 @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.")
250 public TabularData viewUsers()
251 {
252 // Table of users
253 // Username(string), Access rights Read,Write,Admin(bool,bool,bool)
254
255 if (_userlistDataType == null)
256 {
257 _logger.warn("TabluarData not setup correctly");
258 return null;
259 }
260
261 List<Principal> users = _principalDatabase.getUsers();
262
263 TabularDataSupport userList = new TabularDataSupport(_userlistDataType);
264
265 try
266 {
267 // Create the tabular list of message header contents
268 for (Principal user : users)
269 {
270 // Create header attributes list
271
272 String rights = (String) _accessRights.get(user.getName());
273
274 Boolean read = false;
275 Boolean write = false;
276 Boolean admin = false;
277
278 if (rights != null)
279 {
280 read = rights.equals(MBeanInvocationHandlerImpl.READONLY)
281 || rights.equals(MBeanInvocationHandlerImpl.READWRITE);
282 write = rights.equals(MBeanInvocationHandlerImpl.READWRITE);
283 admin = rights.equals(MBeanInvocationHandlerImpl.ADMIN);
284 }
285
286 Object[] itemData = {user.getName(), read, write, admin};
287 CompositeData messageData = new CompositeDataSupport(_userDataType, _userItemNames, itemData);
288 userList.put(messageData);
289 }
290 }
291 catch (OpenDataException e)
292 {
293 _logger.warn("Unable to create user list due to :" + e);
294 return null;
295 }
296
297 return userList;
298 }
299
300 /*** Broker Methods **/
301
302 /**
303 * setPrincipalDatabase
304 *
305 * @param database set The Database to use for user lookup
306 */
307 public void setPrincipalDatabase(PrincipalDatabase database)
308 {
309 _principalDatabase = database;
310 }
311
312 /**
313 * setAccessFile
314 *
315 * @param accessFile the file to use for updating.
316 *
317 * @throws java.io.IOException If the file cannot be accessed
318 * @throws org.apache.commons.configuration.ConfigurationException
319 * if checks on the file fail.
320 */
321 public void setAccessFile(String accessFile) throws IOException, ConfigurationException
322 {
323 _accessFileName = accessFile;
324
325 if (_accessFileName != null)
326 {
327 loadAccessFile();
328 }
329 else
330 {
331 _logger.warn("Access rights file specified is null. Access rights not changed.");
332 }
333 }
334
335 private void loadAccessFile() throws IOException, ConfigurationException
336 {
337 try
338 {
339 _accessRightsUpdate.lock();
340
341 Properties accessRights = new Properties();
342
343 File accessFile = new File(_accessFileName);
344
345 if (!accessFile.exists())
346 {
347 throw new ConfigurationException("'" + _accessFileName + "' does not exist");
348 }
349
350 if (!accessFile.canRead())
351 {
352 throw new ConfigurationException("Cannot read '" + _accessFileName + "'.");
353 }
354
355 if (!accessFile.canWrite())
356 {
357 _logger.warn("Unable to write to access file '" + _accessFileName + "' changes will not be preserved.");
358 }
359
360 accessRights.load(new FileInputStream(accessFile));
361 checkAccessRights(accessRights);
362 setAccessRights(accessRights);
363 }
364 finally
365 {
366 if (_accessRightsUpdate.isHeldByCurrentThread())
367 {
368 _accessRightsUpdate.unlock();
369 }
370 }
371 }
372
373 private void checkAccessRights(Properties accessRights)
374 {
375 Enumeration values = accessRights.propertyNames();
376
377 while (values.hasMoreElements())
378 {
379 String user = (String) values.nextElement();
380
381 if (_principalDatabase.getUser(user) == null)
382 {
383 _logger.warn("Access rights contains user '" + user + "' but there is no authentication data for that user");
384 }
385 }
386 }
387
388 private void saveAccessFile()
389 {
390 try
391 {
392 _accessRightsUpdate.lock();
393 try
394 {
395 // Create temporary file
396 File tmp = File.createTempFile(_accessFileName, ".tmp");
397
398 // Rename current file
399 File rights = new File(_accessFileName);
400
401 FileOutputStream output = new FileOutputStream(tmp);
402 _accessRights.store(output, "Generated by AMQUserManagementMBean Console : Last edited by user:" + getCurrentJMXUser());
403 output.close();
404
405 // Rename new file to main file
406 tmp.renameTo(rights);
407
408 // delete tmp
409 tmp.delete();
410 }
411 catch (IOException e)
412 {
413 _logger.warn("Problem occured saving '" + _accessFileName + "' changes may not be preserved. :" + e);
414 }
415 }
416 finally
417 {
418 if (_accessRightsUpdate.isHeldByCurrentThread())
419 {
420 _accessRightsUpdate.unlock();
421 }
422 }
423 }
424
425 private String getCurrentJMXUser()
426 {
427 AccessControlContext acc = AccessController.getContext();
428
429 Subject subject = Subject.getSubject(acc);
430 if (subject == null)
431 {
432 return "Unknown user, authentication Subject was null";
433 }
434
435 // Retrieve JMXPrincipal from Subject
436 Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
437 if (principals == null || principals.isEmpty())
438 {
439 return "Unknown user principals were null";
440 }
441
442 Principal principal = principals.iterator().next();
443 return principal.getName();
444 }
445
446 /**
447 * user=read user=write user=readwrite user=admin
448 *
449 * @param accessRights The properties list of access rights to process
450 */
451 private void setAccessRights(Properties accessRights)
452 {
453 _logger.debug("Setting Access Rights:" + accessRights);
454 _accessRights = accessRights;
455 MBeanInvocationHandlerImpl.setAccessRights(_accessRights);
456 }
457 }
|