| /* | |
| * Copyright 2011 gitblit.com. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.gitblit.client; | |
| import java.awt.BorderLayout; | |
| import java.awt.Dimension; | |
| import java.awt.FlowLayout; | |
| import java.awt.Font; | |
| import java.awt.GridLayout; | |
| import java.awt.Insets; | |
| import java.awt.event.ActionEvent; | |
| import java.awt.event.ActionListener; | |
| import java.awt.event.KeyEvent; | |
| import java.text.MessageFormat; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.HashMap; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.Set; | |
| import javax.swing.ImageIcon; | |
| import javax.swing.JButton; | |
| import javax.swing.JCheckBox; | |
| import javax.swing.JComponent; | |
| import javax.swing.JDialog; | |
| import javax.swing.JLabel; | |
| import javax.swing.JOptionPane; | |
| import javax.swing.JPanel; | |
| import javax.swing.JPasswordField; | |
| import javax.swing.JRootPane; | |
| import javax.swing.JTabbedPane; | |
| import javax.swing.JTextField; | |
| import javax.swing.KeyStroke; | |
| import com.gitblit.Constants; | |
| import com.gitblit.Constants.AccessRestrictionType; | |
| import com.gitblit.Constants.AuthorizationControl; | |
| import com.gitblit.Constants.PermissionType; | |
| import com.gitblit.Constants.RegistrantType; | |
| import com.gitblit.Keys; | |
| import com.gitblit.models.RegistrantAccessPermission; | |
| import com.gitblit.models.RepositoryModel; | |
| import com.gitblit.models.ServerSettings; | |
| import com.gitblit.models.TeamModel; | |
| import com.gitblit.models.UserModel; | |
| import com.gitblit.utils.StringUtils; | |
| public class EditUserDialog extends JDialog { | |
| private static final long serialVersionUID = 1L; | |
| private final String username; | |
| private final UserModel user; | |
| private final ServerSettings settings; | |
| private boolean isCreate; | |
| private boolean canceled = true; | |
| private JTextField usernameField; | |
| private JPasswordField passwordField; | |
| private JPasswordField confirmPasswordField; | |
| private JTextField displayNameField; | |
| private JTextField emailAddressField; | |
| private JCheckBox canAdminCheckbox; | |
| private JCheckBox canForkCheckbox; | |
| private JCheckBox canCreateCheckbox; | |
| private JCheckBox notFederatedCheckbox; | |
| private JCheckBox disabledCheckbox; | |
| private JTextField organizationalUnitField; | |
| private JTextField organizationField; | |
| private JTextField localityField; | |
| private JTextField stateProvinceField; | |
| private JTextField countryCodeField; | |
| private RegistrantPermissionsPanel repositoryPalette; | |
| private JPalette<TeamModel> teamsPalette; | |
| private Set<String> usernames; | |
| public EditUserDialog(int protocolVersion, ServerSettings settings) { | |
| this(protocolVersion, new UserModel(""), settings); | |
| this.isCreate = true; | |
| setTitle(Translation.get("gb.newUser")); | |
| } | |
| public EditUserDialog(int protocolVersion, UserModel anUser, ServerSettings settings) { | |
| super(); | |
| this.username = anUser.username; | |
| this.user = new UserModel(""); | |
| this.settings = settings; | |
| this.usernames = new HashSet<String>(); | |
| this.isCreate = false; | |
| initialize(protocolVersion, anUser); | |
| setModal(true); | |
| setTitle(Translation.get("gb.edit") + ": " + anUser.username); | |
| setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage()); | |
| } | |
| @Override | |
| protected JRootPane createRootPane() { | |
| KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); | |
| JRootPane rootPane = new JRootPane(); | |
| rootPane.registerKeyboardAction(new ActionListener() { | |
| @Override | |
| public void actionPerformed(ActionEvent actionEvent) { | |
| setVisible(false); | |
| } | |
| }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); | |
| return rootPane; | |
| } | |
| private void initialize(int protocolVersion, UserModel anUser) { | |
| usernameField = new JTextField(anUser.username == null ? "" : anUser.username, 25); | |
| passwordField = new JPasswordField(anUser.password == null ? "" : anUser.password, 25); | |
| confirmPasswordField = new JPasswordField(anUser.password == null ? "" : anUser.password, | |
| 25); | |
| displayNameField = new JTextField(anUser.displayName == null ? "" : anUser.displayName, 25); | |
| emailAddressField = new JTextField(anUser.emailAddress == null ? "" : anUser.emailAddress, 25); | |
| canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), anUser.canAdmin); | |
| canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), anUser.canFork); | |
| canCreateCheckbox = new JCheckBox(Translation.get("gb.canCreateDescription"), anUser.canCreate); | |
| notFederatedCheckbox = new JCheckBox( | |
| Translation.get("gb.excludeFromFederationDescription"), | |
| anUser.excludeFromFederation); | |
| disabledCheckbox = new JCheckBox(Translation.get("gb.disableUserDescription"), anUser.disabled); | |
| organizationalUnitField = new JTextField(anUser.organizationalUnit == null ? "" : anUser.organizationalUnit, 25); | |
| organizationField = new JTextField(anUser.organization == null ? "" : anUser.organization, 25); | |
| localityField = new JTextField(anUser.locality == null ? "" : anUser.locality, 25); | |
| stateProvinceField = new JTextField(anUser.stateProvince == null ? "" : anUser.stateProvince, 25); | |
| countryCodeField = new JTextField(anUser.countryCode == null ? "" : anUser.countryCode, 15); | |
| // credentials are optionally controlled by 3rd-party authentication | |
| usernameField.setEnabled(anUser.isLocalAccount()); | |
| passwordField.setEnabled(anUser.isLocalAccount()); | |
| confirmPasswordField.setEnabled(anUser.isLocalAccount()); | |
| JPanel fieldsPanel = new JPanel(new GridLayout(0, 1)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.username"), usernameField)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.password"), passwordField)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.confirmPassword"), confirmPasswordField)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.displayName"), displayNameField)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.emailAddress"), emailAddressField)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.canAdmin"), canAdminCheckbox)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.canFork"), canForkCheckbox)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.canCreate"), canCreateCheckbox)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.excludeFromFederation"), | |
| notFederatedCheckbox)); | |
| fieldsPanel.add(newFieldPanel(Translation.get("gb.disableUser"), disabledCheckbox)); | |
| JPanel attributesPanel = new JPanel(new GridLayout(0, 1, 5, 2)); | |
| attributesPanel.add(newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnitField)); | |
| attributesPanel.add(newFieldPanel(Translation.get("gb.organization") + " (O)", organizationField)); | |
| attributesPanel.add(newFieldPanel(Translation.get("gb.locality") + " (L)", localityField)); | |
| attributesPanel.add(newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvinceField)); | |
| attributesPanel.add(newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCodeField)); | |
| final Insets _insets = new Insets(5, 5, 5, 5); | |
| repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY); | |
| teamsPalette = new JPalette<TeamModel>(); | |
| JPanel fieldsPanelTop = new JPanel(new BorderLayout()); | |
| fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH); | |
| JPanel attributesPanelTop = new JPanel(new BorderLayout()); | |
| attributesPanelTop.add(attributesPanel, BorderLayout.NORTH); | |
| JPanel repositoriesPanel = new JPanel(new BorderLayout()) { | |
| private static final long serialVersionUID = 1L; | |
| @Override | |
| public Insets getInsets() { | |
| return _insets; | |
| } | |
| }; | |
| repositoriesPanel.add(repositoryPalette, BorderLayout.CENTER); | |
| JPanel teamsPanel = new JPanel(new BorderLayout()) { | |
| private static final long serialVersionUID = 1L; | |
| @Override | |
| public Insets getInsets() { | |
| return _insets; | |
| } | |
| }; | |
| teamsPanel.add(teamsPalette, BorderLayout.CENTER); | |
| JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP); | |
| panel.addTab(Translation.get("gb.general"), fieldsPanelTop); | |
| panel.addTab(Translation.get("gb.attributes"), attributesPanelTop); | |
| if (protocolVersion > 1) { | |
| panel.addTab(Translation.get("gb.teamMemberships"), teamsPanel); | |
| } | |
| panel.addTab(Translation.get("gb.restrictedRepositories"), repositoriesPanel); | |
| JButton createButton = new JButton(Translation.get("gb.save")); | |
| createButton.addActionListener(new ActionListener() { | |
| @Override | |
| public void actionPerformed(ActionEvent event) { | |
| if (validateFields()) { | |
| canceled = false; | |
| setVisible(false); | |
| } | |
| } | |
| }); | |
| JButton cancelButton = new JButton(Translation.get("gb.cancel")); | |
| cancelButton.addActionListener(new ActionListener() { | |
| @Override | |
| public void actionPerformed(ActionEvent event) { | |
| canceled = true; | |
| setVisible(false); | |
| } | |
| }); | |
| JPanel controls = new JPanel(); | |
| controls.add(cancelButton); | |
| controls.add(createButton); | |
| JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) { | |
| private static final long serialVersionUID = 1L; | |
| @Override | |
| public Insets getInsets() { | |
| return _insets; | |
| } | |
| }; | |
| centerPanel.add(panel, BorderLayout.CENTER); | |
| centerPanel.add(controls, BorderLayout.SOUTH); | |
| getContentPane().setLayout(new BorderLayout(5, 5)); | |
| getContentPane().add(centerPanel, BorderLayout.CENTER); | |
| pack(); | |
| } | |
| private JPanel newFieldPanel(String label, JComponent comp) { | |
| JLabel fieldLabel = new JLabel(label); | |
| fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD)); | |
| fieldLabel.setPreferredSize(new Dimension(150, 20)); | |
| JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); | |
| panel.add(fieldLabel); | |
| panel.add(comp); | |
| return panel; | |
| } | |
| private boolean validateFields() { | |
| if (StringUtils.isEmpty(usernameField.getText())) { | |
| error("Please enter a username!"); | |
| return false; | |
| } | |
| String uname = usernameField.getText().toLowerCase(); | |
| boolean rename = false; | |
| // verify username uniqueness on create | |
| if (isCreate) { | |
| if (usernames.contains(uname)) { | |
| error(MessageFormat.format("Username ''{0}'' is unavailable.", uname)); | |
| return false; | |
| } | |
| } else { | |
| // check rename collision | |
| rename = !StringUtils.isEmpty(username) && !username.equalsIgnoreCase(uname); | |
| if (rename) { | |
| if (usernames.contains(uname)) { | |
| error(MessageFormat.format( | |
| "Failed to rename ''{0}'' because ''{1}'' already exists.", username, | |
| uname)); | |
| return false; | |
| } | |
| } | |
| } | |
| user.username = uname; | |
| int minLength = settings.get(Keys.realm.minPasswordLength).getInteger(5); | |
| if (minLength < 4) { | |
| minLength = 4; | |
| } | |
| String password = new String(passwordField.getPassword()); | |
| if (StringUtils.isEmpty(password) || password.length() < minLength) { | |
| error(MessageFormat.format("Password is too short. Minimum length is {0} characters.", | |
| minLength)); | |
| return false; | |
| } | |
| if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE) | |
| && !password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) { | |
| String cpw = new String(confirmPasswordField.getPassword()); | |
| if (cpw == null || cpw.length() != password.length()) { | |
| error("Please confirm the password!"); | |
| return false; | |
| } | |
| if (!password.equals(cpw)) { | |
| error("Passwords do not match!"); | |
| return false; | |
| } | |
| // change the cookie | |
| user.cookie = StringUtils.getSHA1(user.username + password); | |
| String type = settings.get(Keys.realm.passwordStorage).getString("md5"); | |
| if (type.equalsIgnoreCase("md5")) { | |
| // store MD5 digest of password | |
| user.password = StringUtils.MD5_TYPE + StringUtils.getMD5(password); | |
| } else if (type.equalsIgnoreCase("combined-md5")) { | |
| // store MD5 digest of username+password | |
| user.password = StringUtils.COMBINED_MD5_TYPE | |
| + StringUtils.getMD5(user.username + password); | |
| } else { | |
| // plain-text password | |
| user.password = password; | |
| } | |
| } else if (rename && password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) { | |
| error("Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename."); | |
| return false; | |
| } else { | |
| // no change in password | |
| user.password = password; | |
| } | |
| user.displayName = displayNameField.getText().trim(); | |
| user.emailAddress = emailAddressField.getText().trim(); | |
| user.canAdmin = canAdminCheckbox.isSelected(); | |
| user.canFork = canForkCheckbox.isSelected(); | |
| user.canCreate = canCreateCheckbox.isSelected(); | |
| user.excludeFromFederation = notFederatedCheckbox.isSelected(); | |
| user.disabled = disabledCheckbox.isSelected(); | |
| user.organizationalUnit = organizationalUnitField.getText().trim(); | |
| user.organization = organizationField.getText().trim(); | |
| user.locality = localityField.getText().trim(); | |
| user.stateProvince = stateProvinceField.getText().trim(); | |
| user.countryCode = countryCodeField.getText().trim(); | |
| for (RegistrantAccessPermission rp : repositoryPalette.getPermissions()) { | |
| user.setRepositoryPermission(rp.registrant, rp.permission); | |
| } | |
| user.teams.clear(); | |
| user.teams.addAll(teamsPalette.getSelections()); | |
| return true; | |
| } | |
| private void error(String message) { | |
| JOptionPane.showMessageDialog(EditUserDialog.this, message, Translation.get("gb.error"), | |
| JOptionPane.ERROR_MESSAGE); | |
| } | |
| public void setUsers(List<UserModel> users) { | |
| usernames.clear(); | |
| for (UserModel user : users) { | |
| usernames.add(user.username.toLowerCase()); | |
| } | |
| } | |
| public void setRepositories(List<RepositoryModel> repositories, List<RegistrantAccessPermission> permissions) { | |
| Map<String, RepositoryModel> repoMap = new HashMap<String, RepositoryModel>(); | |
| List<String> restricted = new ArrayList<String>(); | |
| for (RepositoryModel repo : repositories) { | |
| // exclude Owner or personal repositories | |
| if (!repo.isOwner(username) && !repo.isUsersPersonalRepository(username)) { | |
| if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE) | |
| && repo.authorizationControl.equals(AuthorizationControl.NAMED)) { | |
| restricted.add(repo.name); | |
| } | |
| } | |
| repoMap.put(repo.name.toLowerCase(), repo); | |
| } | |
| StringUtils.sortRepositorynames(restricted); | |
| List<String> list = new ArrayList<String>(); | |
| // repositories | |
| list.add(".*"); | |
| String prefix; | |
| if (settings.hasKey(Keys.git.userRepositoryPrefix)) { | |
| prefix = settings.get(Keys.git.userRepositoryPrefix).currentValue; | |
| if (StringUtils.isEmpty(prefix)) { | |
| prefix = Constants.DEFAULT_USER_REPOSITORY_PREFIX; | |
| } | |
| } else { | |
| prefix = Constants.DEFAULT_USER_REPOSITORY_PREFIX; | |
| } | |
| if (prefix.length() == 1) { | |
| // all repositories excluding personal repositories | |
| list.add("[^" + prefix + "].*"); | |
| } | |
| String lastProject = null; | |
| for (String repo : restricted) { | |
| String projectPath = StringUtils.getFirstPathElement(repo).toLowerCase(); | |
| if (lastProject == null || !lastProject.equalsIgnoreCase(projectPath)) { | |
| lastProject = projectPath; | |
| if (!StringUtils.isEmpty(projectPath)) { | |
| // regex for all repositories within a project | |
| list.add(projectPath + "/.*"); | |
| } | |
| } | |
| list.add(repo); | |
| } | |
| // remove repositories for which user already has a permission | |
| if (permissions == null) { | |
| permissions = new ArrayList<RegistrantAccessPermission>(); | |
| } else { | |
| for (RegistrantAccessPermission rp : permissions) { | |
| list.remove(rp.registrant.toLowerCase()); | |
| } | |
| } | |
| // update owner and missing permissions for editing | |
| for (RegistrantAccessPermission permission : permissions) { | |
| if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) { | |
| // Ensure this is NOT an owner permission - which is non-editable | |
| // We don't know this from within the usermodel, ownership is a | |
| // property of a repository. | |
| RepositoryModel rm = repoMap.get(permission.registrant.toLowerCase()); | |
| if (rm == null) { | |
| permission.permissionType = PermissionType.MISSING; | |
| permission.mutable = false; | |
| continue; | |
| } | |
| boolean isOwner = rm.isOwner(username); | |
| if (isOwner) { | |
| permission.permissionType = PermissionType.OWNER; | |
| permission.mutable = false; | |
| } | |
| } | |
| } | |
| repositoryPalette.setObjects(list, permissions); | |
| } | |
| public void setTeams(List<TeamModel> teams, List<TeamModel> selected) { | |
| Collections.sort(teams); | |
| if (selected != null) { | |
| Collections.sort(selected); | |
| } | |
| teamsPalette.setObjects(teams, selected); | |
| } | |
| public UserModel getUser() { | |
| if (canceled) { | |
| return null; | |
| } | |
| return user; | |
| } | |
| } |