001package jmri.jmrit.permission.swing;
002
003import java.awt.*;
004import java.util.*;
005import java.util.List;
006import java.util.function.BooleanSupplier;
007
008import javax.swing.*;
009import javax.swing.border.EmptyBorder;
010
011import jmri.*;
012import jmri.jmrit.permission.DefaultPermissionManager;
013import jmri.swing.PreferencesPanel;
014import jmri.util.swing.JmriJOptionPane;
015
016import org.openide.util.lookup.ServiceProvider;
017
018
019/**
020 * Preferences panel for Permission manager.
021 *
022 * @author Daniel Bergqvist Copyright 2024
023 */
024@ServiceProvider(service = PreferencesPanel.class)
025public class PermissionPreferencesPanel extends JPanel implements PreferencesPanel {
026
027    private final DefaultPermissionManager permissionManager;
028    private final Map<User, UserFields> _userFieldsMap = new HashMap<>();
029    private boolean _dirty = false;
030
031    public PermissionPreferencesPanel() {
032        PermissionManager mngr = InstanceManager.getDefault(PermissionManager.class);
033        if (!(mngr instanceof DefaultPermissionManager)) {
034            throw new RuntimeException("PermissionManager is not of type DefaultPermissionManager");
035        }
036        permissionManager = (DefaultPermissionManager)mngr;
037        initGUI();
038    }
039
040    private void initGUI() {
041
042        JTabbedPane rolesTabbedPane = new JTabbedPane();
043        JTabbedPane usersTabbedPane = new JTabbedPane();
044
045        List<Role> roleList = new ArrayList<>(permissionManager.getRoles());
046        roleList.sort((a,b) -> {
047            if (a.getPriority() != b.getPriority()) {
048                return Integer.compare(b.getPriority(), a.getPriority());
049            }
050            return a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
051        });
052
053        List<User> userList = new ArrayList<>(permissionManager.getUsers());
054        userList.sort((a,b) -> {
055            if (a.getPriority() != b.getPriority()) {
056                return Integer.compare(b.getPriority(), a.getPriority());
057            }
058            return a.getUserName().toLowerCase().compareTo(b.getUserName().toLowerCase());
059        });
060
061
062        JPanel outerPanel = new JPanel();
063
064        outerPanel.setLayout(new BoxLayout(outerPanel, BoxLayout.PAGE_AXIS));
065
066        JPanel settingsPanel = new JPanel();
067        settingsPanel.setLayout(new BoxLayout(settingsPanel, BoxLayout.PAGE_AXIS));
068        settingsPanel.setBorder(BorderFactory.createCompoundBorder(
069                BorderFactory.createLineBorder(Color.black, 1), new EmptyBorder(4,4,4,4)));
070
071        JCheckBox enablePermissionManagerCheckBox = new JCheckBox(Bundle.getMessage(
072                "PermissionPreferencesPanel_EnablePermissionManager"));
073        enablePermissionManagerCheckBox.setSelected(permissionManager.isEnabled());
074        enablePermissionManagerCheckBox.addActionListener((evt) -> {
075            if (enablePermissionManagerCheckBox.isSelected()) {
076                // Ask for confirmation before turning Permission Manager on
077                if (JmriJOptionPane.YES_OPTION == JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("PermissionPreferencesPanel_WarnStartPermissions"), Bundle.getMessage("WarningTitle"), JmriJOptionPane.YES_NO_OPTION)) {
078                    permissionManager.setEnabled(enablePermissionManagerCheckBox.isSelected());
079                    _dirty = true;
080                } else {
081                    enablePermissionManagerCheckBox.setSelected(false);
082                }
083            }
084        });
085        settingsPanel.add(enablePermissionManagerCheckBox);
086
087        JCheckBox allowEmptyPasswordsCheckBox = new JCheckBox(Bundle.getMessage(
088                "PermissionPreferencesPanel_AllowEmptyPasswords"));
089        allowEmptyPasswordsCheckBox.setSelected(permissionManager.isAllowEmptyPasswords());
090        allowEmptyPasswordsCheckBox.addActionListener((evt) -> {
091            permissionManager.setAllowEmptyPasswords(allowEmptyPasswordsCheckBox.isSelected());
092            _dirty = true;
093        });
094        settingsPanel.add(allowEmptyPasswordsCheckBox);
095
096        outerPanel.add(settingsPanel);
097
098        outerPanel.add(Box.createVerticalStrut(10));
099
100        JPanel rolesPanel = new JPanel();
101        rolesPanel.setLayout(new BoxLayout(rolesPanel, BoxLayout.PAGE_AXIS));
102
103        for (Role role : roleList) {
104            rolesTabbedPane.addTab(role.getName(), new JScrollPane(
105                    getRolePanel(role, rolesTabbedPane, usersTabbedPane,
106                            roleList, userList)));
107        }
108
109        rolesPanel.add(rolesTabbedPane);
110
111        JButton addRoleButton = new JButton(Bundle.getMessage("PermissionPreferencesPanel_AddRole"));
112        addRoleButton.addActionListener((evt) -> { createNewRole(rolesTabbedPane, usersTabbedPane, roleList, userList); });
113        rolesPanel.add(addRoleButton);
114
115
116        JPanel usersPanel = new JPanel();
117        usersPanel.setLayout(new BoxLayout(usersPanel, BoxLayout.PAGE_AXIS));
118
119        reloadUsersTabbedPane(usersTabbedPane, roleList, userList);
120
121        usersPanel.add(usersTabbedPane);
122
123        JButton addUserButton = new JButton(Bundle.getMessage("PermissionPreferencesPanel_AddUser"));
124        addUserButton.addActionListener((evt) -> {
125            new AddUserDialog(getFrame(), (user) -> {
126                // Find the index of the new user
127                userList.clear();
128                userList.addAll(permissionManager.getUsers());
129                userList.sort((a,b) -> {
130                    if (a.getPriority() != b.getPriority()) {
131                        return Integer.compare(b.getPriority(), a.getPriority());
132                    }
133                    return a.getUserName().toLowerCase().compareTo(b.getUserName().toLowerCase());
134                });
135                usersTabbedPane.insertTab(user.getUserName(), null,
136                        new JScrollPane(getUserPanel(user, usersTabbedPane, roleList, userList)),
137                        null, userList.indexOf(user));
138                getFrame().pack();
139                _dirty = true;
140            }).setVisible(true);
141        });
142        usersPanel.add(addUserButton);
143
144
145        JTabbedPane tabbedPane = new JTabbedPane();
146        tabbedPane.addTab(Bundle.getMessage("PermissionPreferencesPanel_Roles"),
147                new JScrollPane(rolesPanel));
148        tabbedPane.addTab(Bundle.getMessage("PermissionPreferencesPanel_Users"),
149                new JScrollPane(usersPanel));
150
151        JPanel outerTabbedPanel = new JPanel();
152        outerTabbedPanel.add(tabbedPane);
153        outerPanel.add(outerTabbedPanel);
154        add(outerPanel);
155    }
156
157    private Frame getFrame() {
158        Container c = this;
159        while (c != null && !(c instanceof Frame)) {
160            c = c.getParent();
161        }
162        // c is either a Frame or null
163        return (Frame)c;
164    }
165
166    private void createNewRole(JTabbedPane rolesTabbedPane,
167            JTabbedPane usersTabbedPane, List<Role> roleList, List<User> userList) {
168
169        String roleName = JOptionPane.showInputDialog(getFrame(),
170                Bundle.getMessage("PermissionPreferencesPanel_EnterRoleName"));
171
172        if (roleName == null) {
173            return;     // User selected "Cancel"
174        }
175
176        if (roleName.isBlank()) {
177            JmriJOptionPane.showMessageDialog(null,
178                    Bundle.getMessage("PermissionPreferencesPanel_NameEmpty"),
179                    jmri.Application.getApplicationName(),
180                    JmriJOptionPane.ERROR_MESSAGE);
181            return;
182        }
183
184        if (!roleName.equals(roleName.trim())) {
185            JmriJOptionPane.showMessageDialog(null,
186                    Bundle.getMessage("PermissionPreferencesPanel_SpaceNotAllowedInRoleName"),
187                    jmri.Application.getApplicationName(),
188                    JmriJOptionPane.ERROR_MESSAGE);
189            return;
190        }
191
192        try {
193            Role role = permissionManager.addRole(roleName);
194
195            // Find the index of the new role
196            roleList.clear();
197            roleList.addAll(permissionManager.getRoles());
198            roleList.sort((a,b) -> {
199                if (a.getPriority() != b.getPriority()) {
200                    return Integer.compare(b.getPriority(), a.getPriority());
201                }
202                return a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
203            });
204
205            rolesTabbedPane.insertTab(role.getName(), null,
206                    new JScrollPane(getRolePanel(role, rolesTabbedPane,
207                            usersTabbedPane, roleList, userList)),
208                    null, roleList.indexOf(role));
209
210            reloadUsersTabbedPane(usersTabbedPane, roleList, userList);
211            getFrame().pack();
212            _dirty = true;
213
214        } catch (PermissionManager.RoleAlreadyExistsException e) {
215            JmriJOptionPane.showMessageDialog(null,
216                    Bundle.getMessage("PermissionPreferencesPanel_RoleNameExists"),
217                    jmri.Application.getApplicationName(),
218                    JmriJOptionPane.ERROR_MESSAGE);
219        }
220    }
221
222    private JPanel getRolePanel(Role role, JTabbedPane rolesTabbedPane,
223            JTabbedPane usersTabbedPane, List<Role> roleList, List<User> userList) {
224        JPanel rolePanel = new JPanel();
225        rolePanel.setLayout(new BoxLayout(rolePanel, BoxLayout.PAGE_AXIS));
226
227        JLabel roleLabel = new JLabel("<html><font size=\"+1\"><b>"+role.getName()+"</b></font></html>");
228        roleLabel.setBorder(new EmptyBorder(4,4,0,4));
229        rolePanel.add(roleLabel);
230
231        for (PermissionOwner owner : permissionManager.getOwners()) {
232            JPanel ownerPanel = new JPanel();
233            ownerPanel.setLayout(new BoxLayout(ownerPanel, BoxLayout.PAGE_AXIS));
234
235            JLabel ownerLabel = new JLabel("<html><font size=\"0.5\"><b>"+owner.getName()+"</b></font></html>");
236            ownerLabel.setBorder(new EmptyBorder(15,4,4,4));
237            rolePanel.add(ownerLabel);
238
239            for (Permission permission : permissionManager.getPermissions(owner)) {
240                JCheckBox checkBox = new JCheckBox(permission.getName());
241                checkBox.setSelected(role.hasPermission(permission));
242                checkBox.addActionListener((evt) -> {
243                    role.setPermission(permission, checkBox.isSelected());
244                    _dirty = true;
245                });
246                ownerPanel.add(checkBox);
247            }
248            rolePanel.add(ownerPanel);
249        }
250
251        rolePanel.add(Box.createVerticalStrut(10));
252        JButton removeRoleButton = new JButton(Bundle.getMessage("PermissionPreferencesPanel_RemoveRole"));
253        removeRoleButton.addActionListener((evt) -> {
254            if (JmriJOptionPane.YES_OPTION == JmriJOptionPane.showConfirmDialog(
255                            null,
256                            Bundle.getMessage("PermissionPreferencesPanel_RemoveRoleConfirmation", role.getName()),
257                            Bundle.getMessage("PermissionPreferencesPanel_RemoveRoleTitle"),
258                            JmriJOptionPane.YES_NO_OPTION)) {
259                try {
260                    permissionManager.removeRole(role.getName());
261                    rolesTabbedPane.remove(roleList.indexOf(role));
262                    roleList.remove(role);
263                    reloadUsersTabbedPane(usersTabbedPane, roleList, userList);
264                    getFrame().pack();
265                    _dirty = true;
266                } catch (PermissionManager.RoleDoesNotExistException e) {
267                    log.error("Unexpected exception", e);
268                }
269            }
270        });
271        if (role.isSystemRole()) {
272            removeRoleButton.setEnabled(false);
273        }
274        rolePanel.add(removeRoleButton);
275
276        return rolePanel;
277    }
278
279    private void reloadUsersTabbedPane(JTabbedPane usersTabbedPane,
280            List<Role> roleList, List<User> userList) {
281
282        usersTabbedPane.removeAll();
283        for (User user : userList) {
284            usersTabbedPane.addTab(user.getUserName(), new JScrollPane(
285                    getUserPanel(user, usersTabbedPane, roleList, userList)));
286        }
287    }
288
289    private JPanel getUserPanel(User user, JTabbedPane usersTabbedPane,
290            List<Role> roleList, List<User> userList) {
291        JPanel userPanel = new JPanel();
292        userPanel.setLayout(new BoxLayout(userPanel, BoxLayout.PAGE_AXIS));
293
294        UserFields userFields = new UserFields();
295        _userFieldsMap.put(user, userFields);
296
297        JLabel usernameLabel = new JLabel("<html><font size=\"+1\"><b>"+user.getUserName()+"</b></font></html>");
298        usernameLabel.setBorder(new EmptyBorder(4,4,4,4));
299        userPanel.add(usernameLabel);
300        userPanel.add(new JLabel(Bundle.getMessage("PermissionPreferencesPanel_Name")));
301        userFields._nameTextField = new JTextField(20);
302        userFields._nameTextField.setText(user.getName());
303        userPanel.add(userFields._nameTextField);
304        userPanel.add(new JLabel(Bundle.getMessage("PermissionPreferencesPanel_Comment")));
305        userFields._commentTextField = new JTextField(40);
306        userFields._commentTextField.setText(user.getComment());
307        userPanel.add(userFields._commentTextField);
308
309        userPanel.add(Box.createVerticalStrut(10));
310
311        userPanel.add(new JLabel(Bundle.getMessage("PermissionPreferencesPanel_Roles")));
312        userPanel.add(Box.createVerticalStrut(5));
313
314        int lastPriority = 0;
315        for (Role role : roleList) {
316            if (role.getPriority() == 0 && lastPriority != 0) {
317                userPanel.add(Box.createVerticalStrut(10));
318            }
319            JCheckBox checkBox = new JCheckBox(role.getName());
320            checkBox.setSelected(user.getRoles().contains(role));
321            checkBox.addActionListener((evt) -> {
322                if (checkBox.isSelected()) {
323                    user.addRole(role);
324                } else {
325                    user.removeRole(role);
326                }
327                _dirty = true;
328            });
329            userPanel.add(checkBox);
330            lastPriority = role.getPriority();
331        }
332
333        userPanel.add(Box.createVerticalStrut(10));
334
335        JButton changePasswordButton = new JButton(Bundle.getMessage("PermissionPreferencesPanel_ChangePassword"));
336        changePasswordButton.setEnabled(!permissionManager.isGuestUser(user));
337        changePasswordButton.addActionListener((evt) -> {
338            new ChangeUserPasswordDialog(getFrame(), user, ()->{_dirty = true;})
339                    .setVisible(true);
340        });
341        userPanel.add(changePasswordButton);
342
343        JButton removeUserButton = new JButton(Bundle.getMessage("PermissionPreferencesPanel_RemoveUser"));
344        removeUserButton.addActionListener((evt) -> {
345            if (JmriJOptionPane.YES_OPTION == JmriJOptionPane.showConfirmDialog(
346                            null,
347                            Bundle.getMessage("PermissionPreferencesPanel_RemoveUserConfirmation", user.getUserName(), user.getName()),
348                            Bundle.getMessage("PermissionPreferencesPanel_RemoveUserTitle"),
349                            JmriJOptionPane.YES_NO_OPTION)) {
350                try {
351                    permissionManager.removeUser(user.getUserName());
352                    usersTabbedPane.remove(userList.indexOf(user));
353                    userList.remove(user);
354                    _dirty = true;
355                } catch (PermissionManager.UserDoesNotExistException e) {
356                    log.error("Unexpected exception", e);
357                }
358            }
359        });
360        if (user.isSystemUser()) {
361            removeUserButton.setEnabled(false);
362        }
363        userPanel.add(removeUserButton);
364
365        return userPanel;
366    }
367
368    @Override
369    public String getPreferencesItem() {
370        return "PREFERENCES"; // NOI18N
371    }
372
373    @Override
374    public String getPreferencesItemText() {
375        return Bundle.getMessage("MenuPermission"); // NOI18N
376    }
377
378    @Override
379    public String getTabbedPreferencesTitle() {
380        return getPreferencesItemText();
381    }
382
383    @Override
384    public String getLabelKey() {
385        return null;
386    }
387
388    @Override
389    public JComponent getPreferencesComponent() {
390        return this;
391    }
392
393    @Override
394    public boolean isPersistant() {
395        return false;
396    }
397
398    @Override
399    public String getPreferencesTooltip() {
400        return null;
401    }
402
403    @Override
404    public void savePreferences() {
405        for (var entry : _userFieldsMap.entrySet()) {
406            entry.getKey().setName(entry.getValue()._nameTextField.getText());
407            entry.getKey().setComment(entry.getValue()._commentTextField.getText());
408        }
409        permissionManager.storePermissionSettings();
410        _dirty = false;
411    }
412
413    @Override
414    public boolean isDirty() {
415        return _dirty;
416    }
417
418    @Override
419    public boolean isRestartRequired() {
420        return true;
421    }
422
423    @Override
424    public boolean isPreferencesValid() {
425        return true;
426    }
427
428//    @Override
429//    public int getSortOrder() {
430//        return PreferencesPanel.super.getSortOrder();
431//    }
432
433    @Override
434    public BooleanSupplier getIsEnabled() {
435        return () -> {
436            return InstanceManager.getDefault(PermissionManager.class)
437                    .checkPermission(PermissionsSystemAdmin.PERMISSION_EDIT_PERMISSIONS);
438        };
439    }
440
441
442    private static class UserFields {
443        JTextField _nameTextField;
444        JTextField _commentTextField;
445    }
446
447
448    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PermissionPreferencesPanel.class);
449}