James Moger
2012-10-22 eba89539a29deba954035056437279088c3e047b
commit | author | age
da0269 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.client;
17
18 import java.awt.BorderLayout;
19 import java.awt.Dimension;
20 import java.awt.FlowLayout;
21 import java.awt.Font;
22 import java.awt.GridLayout;
23 import java.awt.Insets;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
f14f76 26 import java.awt.event.KeyEvent;
da0269 27 import java.text.MessageFormat;
JM 28 import java.util.ArrayList;
f08aab 29 import java.util.Collections;
bcc616 30 import java.util.HashSet;
da0269 31 import java.util.List;
bcc616 32 import java.util.Set;
da0269 33
JM 34 import javax.swing.ImageIcon;
35 import javax.swing.JButton;
36 import javax.swing.JCheckBox;
37 import javax.swing.JComponent;
38 import javax.swing.JDialog;
39 import javax.swing.JLabel;
40 import javax.swing.JOptionPane;
41 import javax.swing.JPanel;
42 import javax.swing.JPasswordField;
f14f76 43 import javax.swing.JRootPane;
f08aab 44 import javax.swing.JTabbedPane;
da0269 45 import javax.swing.JTextField;
f14f76 46 import javax.swing.KeyStroke;
da0269 47
JM 48 import com.gitblit.Constants.AccessRestrictionType;
49 import com.gitblit.Keys;
822dfe 50 import com.gitblit.models.RegistrantAccessPermission;
da0269 51 import com.gitblit.models.RepositoryModel;
84c1d5 52 import com.gitblit.models.ServerSettings;
f08aab 53 import com.gitblit.models.TeamModel;
da0269 54 import com.gitblit.models.UserModel;
JM 55 import com.gitblit.utils.StringUtils;
56
57 public class EditUserDialog extends JDialog {
58
59     private static final long serialVersionUID = 1L;
60
16038c 61     private final String username;
JM 62
da0269 63     private final UserModel user;
JM 64
84c1d5 65     private final ServerSettings settings;
da0269 66
bcc616 67     private boolean isCreate;
84c1d5 68
da0269 69     private boolean canceled = true;
JM 70
71     private JTextField usernameField;
72
73     private JPasswordField passwordField;
74
75     private JPasswordField confirmPasswordField;
3980c1 76     
JM 77     private JTextField displayNameField;
78     
79     private JTextField emailAddressField;
da0269 80
JM 81     private JCheckBox canAdminCheckbox;
f22a06 82     
JM 83     private JCheckBox canForkCheckbox;
6662e3 84     
JM 85     private JCheckBox canCreateCheckbox;
da0269 86
JM 87     private JCheckBox notFederatedCheckbox;
88
822dfe 89     private RegistrantPermissionsPanel repositoryPalette;
ae0b13 90
f08aab 91     private JPalette<TeamModel> teamsPalette;
da0269 92
bcc616 93     private Set<String> usernames;
JM 94
f08aab 95     public EditUserDialog(int protocolVersion, ServerSettings settings) {
JM 96         this(protocolVersion, new UserModel(""), settings);
bcc616 97         this.isCreate = true;
84c1d5 98         setTitle(Translation.get("gb.newUser"));
da0269 99     }
JM 100
f08aab 101     public EditUserDialog(int protocolVersion, UserModel anUser, ServerSettings settings) {
da0269 102         super();
16038c 103         this.username = anUser.username;
da0269 104         this.user = new UserModel("");
JM 105         this.settings = settings;
bcc616 106         this.usernames = new HashSet<String>();
JM 107         this.isCreate = false;
f08aab 108         initialize(protocolVersion, anUser);
da0269 109         setModal(true);
b7f591 110         setTitle(Translation.get("gb.edit") + ": " + anUser.username);
da0269 111         setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
f14f76 112     }
84c1d5 113
f14f76 114     @Override
JM 115     protected JRootPane createRootPane() {
116         KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
117         JRootPane rootPane = new JRootPane();
118         rootPane.registerKeyboardAction(new ActionListener() {
119             public void actionPerformed(ActionEvent actionEvent) {
120                 setVisible(false);
121             }
122         }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
123         return rootPane;
da0269 124     }
JM 125
f08aab 126     private void initialize(int protocolVersion, UserModel anUser) {
da0269 127         usernameField = new JTextField(anUser.username == null ? "" : anUser.username, 25);
JM 128         passwordField = new JPasswordField(anUser.password == null ? "" : anUser.password, 25);
129         confirmPasswordField = new JPasswordField(anUser.password == null ? "" : anUser.password,
130                 25);
3980c1 131         displayNameField = new JTextField(anUser.displayName == null ? "" : anUser.displayName, 25);
JM 132         emailAddressField = new JTextField(anUser.emailAddress == null ? "" : anUser.emailAddress, 25);
133         canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), anUser.canAdmin);        
6662e3 134         canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), anUser.canFork);
JM 135         canCreateCheckbox = new JCheckBox(Translation.get("gb.canCreateDescription"), anUser.canCreate);
da0269 136         notFederatedCheckbox = new JCheckBox(
b7f591 137                 Translation.get("gb.excludeFromFederationDescription"),
da0269 138                 anUser.excludeFromFederation);
e19110 139         
JM 140         // credentials are optionally controlled by 3rd-party authentication
141         usernameField.setEnabled(settings.supportsCredentialChanges);
142         passwordField.setEnabled(settings.supportsCredentialChanges);
143         confirmPasswordField.setEnabled(settings.supportsCredentialChanges);
144
145         displayNameField.setEnabled(settings.supportsDisplayNameChanges);
146         emailAddressField.setEnabled(settings.supportsEmailAddressChanges);
da0269 147
JM 148         JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));
b7f591 149         fieldsPanel.add(newFieldPanel(Translation.get("gb.username"), usernameField));
JM 150         fieldsPanel.add(newFieldPanel(Translation.get("gb.password"), passwordField));
151         fieldsPanel.add(newFieldPanel(Translation.get("gb.confirmPassword"), confirmPasswordField));
3980c1 152         fieldsPanel.add(newFieldPanel(Translation.get("gb.displayName"), displayNameField));
JM 153         fieldsPanel.add(newFieldPanel(Translation.get("gb.emailAddress"), emailAddressField));
b7f591 154         fieldsPanel.add(newFieldPanel(Translation.get("gb.canAdmin"), canAdminCheckbox));
f22a06 155         fieldsPanel.add(newFieldPanel(Translation.get("gb.canFork"), canForkCheckbox));
6662e3 156         fieldsPanel.add(newFieldPanel(Translation.get("gb.canCreate"), canCreateCheckbox));
b7f591 157         fieldsPanel.add(newFieldPanel(Translation.get("gb.excludeFromFederation"),
JM 158                 notFederatedCheckbox));
da0269 159
f08aab 160         final Insets _insets = new Insets(5, 5, 5, 5);
822dfe 161         repositoryPalette = new RegistrantPermissionsPanel();
f08aab 162         teamsPalette = new JPalette<TeamModel>();
e19110 163         teamsPalette.setEnabled(settings.supportsTeamMembershipChanges);
ae0b13 164
f08aab 165         JPanel fieldsPanelTop = new JPanel(new BorderLayout());
JM 166         fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);
ae0b13 167
f08aab 168         JPanel repositoriesPanel = new JPanel(new BorderLayout()) {
JM 169
170             private static final long serialVersionUID = 1L;
171
172             public Insets getInsets() {
173                 return _insets;
174             }
175         };
176         repositoriesPanel.add(repositoryPalette, BorderLayout.CENTER);
177
178         JPanel teamsPanel = new JPanel(new BorderLayout()) {
179
180             private static final long serialVersionUID = 1L;
181
182             public Insets getInsets() {
183                 return _insets;
184             }
185         };
186         teamsPanel.add(teamsPalette, BorderLayout.CENTER);
187
188         JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
189         panel.addTab(Translation.get("gb.general"), fieldsPanelTop);
190         if (protocolVersion > 1) {
191             panel.addTab(Translation.get("gb.teamMemberships"), teamsPanel);
192         }
193         panel.addTab(Translation.get("gb.restrictedRepositories"), repositoriesPanel);
194
b7f591 195         JButton createButton = new JButton(Translation.get("gb.save"));
da0269 196         createButton.addActionListener(new ActionListener() {
JM 197             public void actionPerformed(ActionEvent event) {
198                 if (validateFields()) {
199                     canceled = false;
200                     setVisible(false);
201                 }
202             }
203         });
204
b7f591 205         JButton cancelButton = new JButton(Translation.get("gb.cancel"));
da0269 206         cancelButton.addActionListener(new ActionListener() {
JM 207             public void actionPerformed(ActionEvent event) {
208                 canceled = true;
209                 setVisible(false);
210             }
211         });
212
213         JPanel controls = new JPanel();
214         controls.add(cancelButton);
215         controls.add(createButton);
ae0b13 216
da0269 217         JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) {
JM 218
219             private static final long serialVersionUID = 1L;
220
221             @Override
222             public Insets getInsets() {
223                 return _insets;
224             }
225         };
226         centerPanel.add(panel, BorderLayout.CENTER);
227         centerPanel.add(controls, BorderLayout.SOUTH);
228
229         getContentPane().setLayout(new BorderLayout(5, 5));
230         getContentPane().add(centerPanel, BorderLayout.CENTER);
231         pack();
232     }
233
234     private JPanel newFieldPanel(String label, JComponent comp) {
235         JLabel fieldLabel = new JLabel(label);
236         fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD));
237         fieldLabel.setPreferredSize(new Dimension(150, 20));
238         JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
239         panel.add(fieldLabel);
240         panel.add(comp);
241         return panel;
242     }
243
244     private boolean validateFields() {
ae0b13 245         if (StringUtils.isEmpty(usernameField.getText())) {
bcc616 246             error("Please enter a username!");
da0269 247             return false;
JM 248         }
ae0b13 249         String uname = usernameField.getText().toLowerCase();
d5623a 250         boolean rename = false;
bcc616 251         // verify username uniqueness on create
JM 252         if (isCreate) {
ae0b13 253             if (usernames.contains(uname)) {
bcc616 254                 error(MessageFormat.format("Username ''{0}'' is unavailable.", uname));
JM 255                 return false;
256             }
16038c 257         } else {
JM 258             // check rename collision
d5623a 259             rename = !StringUtils.isEmpty(username) && !username.equalsIgnoreCase(uname);
JM 260             if (rename) {
ae0b13 261                 if (usernames.contains(uname)) {
16038c 262                     error(MessageFormat.format(
JM 263                             "Failed to rename ''{0}'' because ''{1}'' already exists.", username,
264                             uname));
265                     return false;
266                 }
267             }
bcc616 268         }
d5623a 269         user.username = uname;
da0269 270
b75734 271         int minLength = settings.get(Keys.realm.minPasswordLength).getInteger(5);
da0269 272         if (minLength < 4) {
JM 273             minLength = 4;
274         }
d5623a 275
JM 276         String password = new String(passwordField.getPassword());
277         if (StringUtils.isEmpty(password) || password.length() < minLength) {
84c1d5 278             error(MessageFormat.format("Password is too short. Minimum length is {0} characters.",
JM 279                     minLength));
da0269 280             return false;
JM 281         }
d5623a 282         if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)
JM 283                 && !password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {
284             String cpw = new String(confirmPasswordField.getPassword());
285             if (cpw == null || cpw.length() != password.length()) {
286                 error("Please confirm the password!");
287                 return false;
288             }
289             if (!password.equals(cpw)) {
290                 error("Passwords do not match!");
291                 return false;
292             }
293
294             String type = settings.get(Keys.realm.passwordStorage).getString("md5");
295             if (type.equalsIgnoreCase("md5")) {
296                 // store MD5 digest of password
297                 user.password = StringUtils.MD5_TYPE + StringUtils.getMD5(password);
298             } else if (type.equalsIgnoreCase("combined-md5")) {
299                 // store MD5 digest of username+password
300                 user.password = StringUtils.COMBINED_MD5_TYPE
ae0b13 301                         + StringUtils.getMD5(user.username + password);
d5623a 302             } else {
JM 303                 // plain-text password
304                 user.password = password;
305             }
306         } else if (rename && password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {
307             error("Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename.");
da0269 308             return false;
JM 309         } else {
d5623a 310             // no change in password
JM 311             user.password = password;
da0269 312         }
3980c1 313         
JM 314         user.displayName = displayNameField.getText().trim();
315         user.emailAddress = emailAddressField.getText().trim();
d5623a 316
da0269 317         user.canAdmin = canAdminCheckbox.isSelected();
f22a06 318         user.canFork = canForkCheckbox.isSelected();
6662e3 319         user.canCreate = canCreateCheckbox.isSelected();
da0269 320         user.excludeFromFederation = notFederatedCheckbox.isSelected();
JM 321
822dfe 322         for (RegistrantAccessPermission rp : repositoryPalette.getPermissions()) {
JM 323             user.setRepositoryPermission(rp.registrant, rp.permission);
324         }
ae0b13 325
f08aab 326         user.teams.clear();
JM 327         user.teams.addAll(teamsPalette.getSelections());
da0269 328         return true;
JM 329     }
330
bcc616 331     private void error(String message) {
b7f591 332         JOptionPane.showMessageDialog(EditUserDialog.this, message, Translation.get("gb.error"),
da0269 333                 JOptionPane.ERROR_MESSAGE);
JM 334     }
335
bcc616 336     public void setUsers(List<UserModel> users) {
JM 337         usernames.clear();
338         for (UserModel user : users) {
339             usernames.add(user.username.toLowerCase());
340         }
341     }
342
822dfe 343     public void setRepositories(List<RepositoryModel> repositories, List<RegistrantAccessPermission> permissions) {
da0269 344         List<String> restricted = new ArrayList<String>();
JM 345         for (RepositoryModel repo : repositories) {
346             if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
347                 restricted.add(repo.name);
348             }
349         }
ba5424 350
822dfe 351         // remove repositories for which user already has a permission
ba5424 352         if (permissions == null) {
JM 353             permissions = new ArrayList<RegistrantAccessPermission>();
354         } else {
355             for (RegistrantAccessPermission rp : permissions) {
356                 restricted.remove(rp.registrant);
357             }
da0269 358         }
822dfe 359         
JM 360         StringUtils.sortRepositorynames(restricted);
361         repositoryPalette.setObjects(restricted, permissions);
da0269 362     }
ae0b13 363
f08aab 364     public void setTeams(List<TeamModel> teams, List<TeamModel> selected) {
JM 365         Collections.sort(teams);
366         if (selected != null) {
367             Collections.sort(selected);
368         }
369         teamsPalette.setObjects(teams, selected);
370     }
e19110 371     
da0269 372     public UserModel getUser() {
JM 373         if (canceled) {
374             return null;
375         }
376         return user;
377     }
378 }