001package jmri.jmrit.permission; 002 003import java.awt.GraphicsEnvironment; 004import java.io.*; 005import java.util.*; 006 007import jmri.*; 008import jmri.jmrit.XmlFile; 009import jmri.util.FileUtil; 010import jmri.util.ThreadingUtil; 011import jmri.util.swing.JmriJOptionPane; 012 013import org.jdom2.*; 014 015/* 016 TO DO 017 018 * Remove role 019 * Remove user 020*/ 021 022/** 023 * Default permission manager. 024 * 025 * @author Daniel Bergqvist (C) 2024 026 */ 027public class DefaultPermissionManager implements PermissionManager { 028 029 private static final DefaultUser USER_GUEST = 030 new DefaultUser(Bundle.getMessage("PermissionManager_User_Guest").toLowerCase(), 031 null, 50, "GUEST", new Role[]{DefaultRole.ROLE_GUEST}); 032 033 private static final DefaultUser USER_ADMIN = 034 new DefaultUser(Bundle.getMessage("PermissionManager_User_Admin").toLowerCase(), 035 "jmri", 100, "ADMIN", new Role[]{DefaultRole.ROLE_ADMIN, DefaultRole.ROLE_STANDARD_USER}); 036 037 private final Map<String, Role> _roles = new HashMap<>(); 038 private final Map<String, DefaultUser> _users = new HashMap<>(); 039 private final Set<PermissionOwner> _owners = new HashSet<>(); 040 private final Set<Permission> _permissions = new HashSet<>(); 041 private final Map<String, Permission> _permissionClassNames = new HashMap<>(); 042 private final List<LoginListener> _loginListeners = new ArrayList<>(); 043 044 private boolean _permissionsEnabled = false; 045 private boolean _allowEmptyPasswords = false; 046 private User _currentUser = USER_GUEST; 047 048 049 public DefaultPermissionManager init() { 050 _roles.put(DefaultRole.ROLE_GUEST.getName(), DefaultRole.ROLE_GUEST); 051 _roles.put(DefaultRole.ROLE_STANDARD_USER.getName(), DefaultRole.ROLE_STANDARD_USER); 052 _roles.put(DefaultRole.ROLE_ADMIN.getName(), DefaultRole.ROLE_ADMIN); 053 054 _users.put(USER_GUEST.getUserName(), USER_GUEST); 055 _users.put(USER_ADMIN.getUserName(), USER_ADMIN); 056 057 for (PermissionFactory factory : ServiceLoader.load(PermissionFactory.class)) { 058 factory.register(this); 059 } 060 loadPermissionSettings(); 061 ThreadingUtil.runOnGUIEventually(() -> { 062 checkThatAllRolesKnowsAllPermissions(); 063 }); 064 return this; 065 } 066 067 public Collection<Role> getRoles() { 068 return _roles.values(); 069 } 070 071 public Collection<DefaultUser> getUsers() { 072 return _users.values(); 073 } 074 075 public Set<PermissionOwner> getOwners() { 076 return _owners; 077 } 078 079 public Set<Permission> getPermissions(PermissionOwner owner) { 080 Set<Permission> set = new TreeSet<>((a,b) -> {return a.getName().compareTo(b.getName());}); 081 for (Permission p : _permissions) { 082 if (p.getOwner().equals(owner)) { 083 set.add(p); 084 } 085 } 086 return set; 087 } 088 089 private Role getSystemRole(String systemName) { 090 for (Role role : _roles.values()) { 091 if (role.isSystemRole() && role.getSystemName().equals(systemName)) { 092 return role; 093 } 094 } 095 return null; 096 } 097 098 private DefaultUser getSystemUser(String systemUsername) { 099 for (User u : _users.values()) { 100 DefaultUser du = (DefaultUser)u; 101 if (du.isSystemUser() && du.getSystemUsername().equals(systemUsername)) { 102 return du; 103 } 104 } 105 return null; 106 } 107 108 private void loadPermissionSettings() { 109 File file = new File(FileUtil.getPreferencesPath() + ".permissions.xml"); 110 111 log.info("Permission file: {}", file.getAbsolutePath()); 112 113 if (file.exists() && file.length() != 0) { 114 try { 115 Element root = new XmlFile().rootFromFile(file); 116 117 Element settings = root.getChild("Settings"); 118 _permissionsEnabled = "yes".equals(settings.getChild("Enabled").getValue()); 119 _allowEmptyPasswords = "yes".equals(settings.getChild("AllowEmptyPasswords").getValue()); 120 log.info("Permission system is enabled: {}", _permissionsEnabled ? "yes" : "no"); 121 122 List<Element> roleElementList = root.getChild("Roles").getChildren("Role"); 123 for (Element roleElement : roleElementList) { 124 Element systemNameElement = roleElement.getChild("SystemName"); 125 Role role; 126 if (systemNameElement != null) { 127 role = getSystemRole(systemNameElement.getValue()); 128 if (role == null) { 129 log.error("SystemRole {} is not found.", systemNameElement.getValue()); 130 continue; 131 } 132 } else { 133 role = new DefaultRole(roleElement.getChild("Name").getValue()); 134 _roles.put(role.getName(), role); 135 } 136 137 List<Element> permissionElementList = roleElement 138 .getChild("Permissions").getChildren("Permission"); 139 for (Element permissionElement : permissionElementList) { 140 String className = permissionElement.getChild("Class").getValue(); 141 boolean enabled = "yes".equals(permissionElement.getChild("Enabled").getValue()); 142 Permission permission = _permissionClassNames.get(className); 143 if (permission != null) { 144 ((DefaultRole)role).setPermissionWithoutCheck(permission, enabled); 145 } else { 146 String msg = String.format("Permission class %s does not exists", className); 147 if (!GraphicsEnvironment.isHeadless()) { 148 JmriJOptionPane.showMessageDialog(null, 149 msg, 150 jmri.Application.getApplicationName(), 151 JmriJOptionPane.ERROR_MESSAGE); 152 } 153 log.error(msg); 154 } 155 } 156 } 157 158 List<Element> userElementList = root.getChild("Users").getChildren("User"); 159 for (Element userElement : userElementList) { 160 161 Element systemNameElement = userElement.getChild("SystemUsername"); 162 DefaultUser user; 163 if (systemNameElement != null) { 164 user = getSystemUser(systemNameElement.getValue()); 165 if (user == null) { 166 log.error("SystemUser {} is not found.", systemNameElement.getValue()); 167 continue; 168 } 169 Element passwordElement = userElement.getChild("Password"); 170 if (passwordElement != null) { 171 user.setPasswordMD5(passwordElement.getValue()); 172 user.setSeed(userElement.getChild("Seed").getValue()); 173 } 174 } else { 175 user = new DefaultUser( 176 userElement.getChild("Username").getValue(), 177 userElement.getChild("Password").getValue(), 178 userElement.getChild("Seed").getValue()); 179 _users.put(user.getUserName(), user); 180 } 181 182 user.setName(userElement.getChild("Name").getValue()); 183 user.setComment(userElement.getChild("Comment").getValue()); 184 185 Set<Role> roles = new HashSet<>(); 186 187 List<Element> userRoleElementList = userElement.getChild("Roles").getChildren("Role"); 188 for (Element roleElement : userRoleElementList) { 189 Element roleSystemNameElement = roleElement.getChild("SystemName"); 190 Role role; 191 if (roleSystemNameElement != null) { 192 role = getSystemRole(roleSystemNameElement.getValue()); 193 if (role == null) { 194 log.error("SystemRole {} is not found.", roleSystemNameElement.getValue()); 195 continue; 196 } 197 } else { 198 role = _roles.get(roleElement.getChild("Name").getValue()); 199 if (role == null) { 200 log.error("UserRole {} is not found.", roleElement.getValue()); 201 continue; 202 } 203 } 204 roles.add(role); 205 } 206 user.setRoles(roles); 207 } 208 209 } catch (JDOMException | IOException ex) { 210 log.error("Exception during loading of permissions", ex); 211 } 212 } else { 213 log.info("Permission file not found or empty"); 214 } 215 } 216 217 @Override 218 public void storePermissionSettings() { 219 File file = new File(FileUtil.getPreferencesPath() + ".permissions.xml"); 220 221 try { 222 // Document doc = newDocument(root, dtdLocation+"layout-config-"+dtdVersion+".dtd"); 223 Element rootElement = new Element("Permissions"); 224 225 Element settings = new Element("Settings"); 226 settings.addContent(new Element("Enabled") 227 .addContent(this._permissionsEnabled ? "yes" : "no")); 228 settings.addContent(new Element("AllowEmptyPasswords") 229 .addContent(this._allowEmptyPasswords ? "yes" : "no")); 230 rootElement.addContent(settings); 231 232 checkThatAllRolesKnowsAllPermissions(); 233 234 Element rolesElement = new Element("Roles"); 235 for (Role role : _roles.values()) { 236 Element roleElement = new Element("Role"); 237 if (role.isSystemRole()) { 238 roleElement.addContent(new Element("SystemName").addContent(role.getSystemName())); 239 } 240 roleElement.addContent(new Element("Name").addContent(role.getName())); 241 242 Element rolePermissions = new Element("Permissions"); 243 for (var entry : role.getPermissions().entrySet()) { 244 Element userPermission = new Element("Permission"); 245 userPermission.addContent(new Element("Class").addContent(entry.getKey().getClass().getName())); 246 userPermission.addContent(new Element("Enabled").addContent(entry.getValue() ? "yes" : "no")); 247 rolePermissions.addContent(userPermission); 248 } 249 roleElement.addContent(rolePermissions); 250 rolesElement.addContent(roleElement); 251 } 252 rootElement.addContent(rolesElement); 253 254 255 Element usersElement = new Element("Users"); 256 for (DefaultUser user : _users.values()) { 257 Element userElement = new Element("User"); 258 if (user.isSystemUser()) { 259 userElement.addContent(new Element("SystemUsername").addContent(user.getSystemUsername())); 260 } 261 userElement.addContent(new Element("Username").addContent(user.getUserName())); 262 263 if (user.getPassword() != null) { // Guest user password is null 264 userElement.addContent(new Element("Password").addContent(user.getPassword())); 265 userElement.addContent(new Element("Seed").addContent(user.getSeed())); 266 } 267 268 userElement.addContent(new Element("Name").addContent(user.getName())); 269 userElement.addContent(new Element("Comment").addContent(user.getComment())); 270 271 Element userRolesElement = new Element("Roles"); 272 for (Role role : user.getRoles()) { 273 Element roleElement = new Element("Role"); 274 if (role.isSystemRole()) { 275 roleElement.addContent(new Element("SystemName") 276 .addContent(role.getSystemName())); 277 } 278 roleElement.addContent(new Element("Name").addContent(role.getName())); 279 userRolesElement.addContent(roleElement); 280 } 281 userElement.addContent(userRolesElement); 282 usersElement.addContent(userElement); 283 } 284 rootElement.addContent(usersElement); 285 286 Document doc = XmlFile.newDocument(rootElement); 287 new XmlFile().writeXML(file, doc); 288 289 } catch (java.io.FileNotFoundException ex3) { 290 log.error("FileNotFound error writing file: {}", file); 291 } catch (java.io.IOException ex2) { 292 log.error("IO error writing file: {}", file); 293 } 294 } 295 296 @Override 297 public Role addRole(String name) throws RoleAlreadyExistsException { 298 if (_users.containsKey(name)) { 299 throw new RoleAlreadyExistsException(); 300 } 301 Role role = new DefaultRole(name); 302 _roles.put(name, role); 303 return role; 304 } 305 306 @Override 307 public void removeRole(String name) throws RoleDoesNotExistException { 308 309 if (!_roles.containsKey(name)) { 310 throw new RoleDoesNotExistException(); 311 } 312 _roles.remove(name); 313 } 314 315 @Override 316 public User addUser(String username, String password) 317 throws UserAlreadyExistsException { 318 319 String u = username.toLowerCase(); 320 if (_users.containsKey(u)) { 321 throw new UserAlreadyExistsException(); 322 } 323 DefaultUser user = new DefaultUser(u, password); 324 _users.put(u, user); 325 return user; 326 } 327 328 @Override 329 public void removeUser(String username) 330 throws UserDoesNotExistException { 331 332 if (!_users.containsKey(username)) { 333 throw new UserDoesNotExistException(); 334 } 335 _users.remove(username); 336 } 337 338 @Override 339 public void changePassword(String newPassword, String oldPassword) { 340 if (_currentUser.changePassword(newPassword, oldPassword)) { 341 storePermissionSettings(); 342 } 343 } 344 345 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 346 justification="The text is from an exception") 347 @Override 348 public boolean login(String username, String password) { 349 DefaultUser newUser = _users.get(username); 350 if (newUser == null || !newUser.checkPassword(password)) { 351 String msg = new BadUserOrPasswordException().getMessage(); 352 353 if (!GraphicsEnvironment.isHeadless()) { 354 JmriJOptionPane.showMessageDialog(null, 355 msg, 356 jmri.Application.getApplicationName(), 357 JmriJOptionPane.ERROR_MESSAGE); 358 } else { 359 log.error(msg); 360 } 361 return false; 362 } else { 363 _currentUser = newUser; 364 notifyLoginListeners(true); 365 return true; 366 } 367 } 368 369 @Override 370 public void logout() { 371 _currentUser = USER_GUEST; 372 notifyLoginListeners(false); 373 } 374 375 private void notifyLoginListeners(boolean isLogin) { 376 for (LoginListener listener : _loginListeners) { 377 listener.loginLogout(isLogin); 378 } 379 } 380 381 @Override 382 public boolean isLoggedIn() { 383 return _currentUser != USER_GUEST; 384 } 385 386 @Override 387 public boolean isCurrentUser(String username) { 388 return _currentUser.getUserName().equals(username); 389 } 390 391 @Override 392 public boolean isCurrentUser(User user) { 393 return _currentUser == user; 394 } 395 396 @Override 397 public String getCurrentUserName() { 398 return _currentUser.getUserName(); 399 } 400 401 @Override 402 public boolean isGuestUser(User user) { 403 return user == USER_GUEST; 404 } 405 406 @Override 407 public void addLoginListener(LoginListener listener) { 408 _loginListeners.add(listener); 409 } 410 411 @Override 412 public boolean isEnabled() { 413 return _permissionsEnabled; 414 } 415 416 @Override 417 public void setEnabled(boolean enabled) { 418 if (! InstanceManager.getDefault(PermissionManager.class) 419 .checkPermission(PermissionsSystemAdmin.PERMISSION_EDIT_PREFERENCES)) { 420 return; 421 } 422 _permissionsEnabled = enabled; 423 } 424 425 @Override 426 public boolean isAllowEmptyPasswords() { 427 return _allowEmptyPasswords; 428 } 429 430 @Override 431 public void setAllowEmptyPasswords(boolean value) { 432 if (! InstanceManager.getDefault(PermissionManager.class) 433 .checkPermission(PermissionsSystemAdmin.PERMISSION_EDIT_PREFERENCES)) { 434 return; 435 } 436 _allowEmptyPasswords = value; 437 } 438 439 @Override 440 public boolean hasPermission(Permission permission) { 441 return !_permissionsEnabled || _currentUser.hasPermission(permission); 442 } 443 444 @Override 445 public boolean checkPermission(Permission permission) { 446 return !_permissionsEnabled || _currentUser.checkPermission(permission); 447 } 448 449 @Override 450 public void registerOwner(PermissionOwner owner) { 451 _owners.add(owner); 452 } 453 454 @Override 455 public void registerPermission(Permission permission) { 456 if (!_owners.contains(permission.getOwner())) { 457 throw new RuntimeException(String.format( 458 "Permission class %s has an owner that's not known: %s", 459 permission.getClass().getName(), permission.getOwner())); 460 } 461 _permissions.add(permission); 462 _permissionClassNames.put(permission.getClass().getName(), permission); 463 } 464 465 private void checkThatAllRolesKnowsAllPermissions() { 466 for (Role role : _roles.values()) { 467 for (Permission p : _permissions) { 468 if (!role.getPermissions().containsKey(p)) { 469 ((DefaultRole)role).setPermissionWithoutCheck( 470 p, p.getDefaultPermission(role)); 471 } 472 } 473 } 474 } 475 476 477 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultPermissionManager.class); 478}