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