You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EditUserDialog.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  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. import java.awt.BorderLayout;
  18. import java.awt.Dimension;
  19. import java.awt.FlowLayout;
  20. import java.awt.Font;
  21. import java.awt.GridLayout;
  22. import java.awt.Insets;
  23. import java.awt.event.ActionEvent;
  24. import java.awt.event.ActionListener;
  25. import java.awt.event.KeyEvent;
  26. import java.text.MessageFormat;
  27. import java.util.ArrayList;
  28. import java.util.Collections;
  29. import java.util.HashMap;
  30. import java.util.HashSet;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.Set;
  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;
  43. import javax.swing.JRootPane;
  44. import javax.swing.JTabbedPane;
  45. import javax.swing.JTextField;
  46. import javax.swing.KeyStroke;
  47. import com.gitblit.Constants;
  48. import com.gitblit.Constants.AccessRestrictionType;
  49. import com.gitblit.Constants.AuthorizationControl;
  50. import com.gitblit.Constants.PermissionType;
  51. import com.gitblit.Constants.RegistrantType;
  52. import com.gitblit.Keys;
  53. import com.gitblit.models.RegistrantAccessPermission;
  54. import com.gitblit.models.RepositoryModel;
  55. import com.gitblit.models.ServerSettings;
  56. import com.gitblit.models.TeamModel;
  57. import com.gitblit.models.UserModel;
  58. import com.gitblit.utils.StringUtils;
  59. public class EditUserDialog extends JDialog {
  60. private static final long serialVersionUID = 1L;
  61. private final String username;
  62. private final UserModel user;
  63. private final ServerSettings settings;
  64. private boolean isCreate;
  65. private boolean canceled = true;
  66. private JTextField usernameField;
  67. private JPasswordField passwordField;
  68. private JPasswordField confirmPasswordField;
  69. private JTextField displayNameField;
  70. private JTextField emailAddressField;
  71. private JCheckBox canAdminCheckbox;
  72. private JCheckBox canForkCheckbox;
  73. private JCheckBox canCreateCheckbox;
  74. private JCheckBox notFederatedCheckbox;
  75. private JTextField organizationalUnitField;
  76. private JTextField organizationField;
  77. private JTextField localityField;
  78. private JTextField stateProvinceField;
  79. private JTextField countryCodeField;
  80. private RegistrantPermissionsPanel repositoryPalette;
  81. private JPalette<TeamModel> teamsPalette;
  82. private Set<String> usernames;
  83. public EditUserDialog(int protocolVersion, ServerSettings settings) {
  84. this(protocolVersion, new UserModel(""), settings);
  85. this.isCreate = true;
  86. setTitle(Translation.get("gb.newUser"));
  87. }
  88. public EditUserDialog(int protocolVersion, UserModel anUser, ServerSettings settings) {
  89. super();
  90. this.username = anUser.username;
  91. this.user = new UserModel("");
  92. this.settings = settings;
  93. this.usernames = new HashSet<String>();
  94. this.isCreate = false;
  95. initialize(protocolVersion, anUser);
  96. setModal(true);
  97. setTitle(Translation.get("gb.edit") + ": " + anUser.username);
  98. setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
  99. }
  100. @Override
  101. protected JRootPane createRootPane() {
  102. KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
  103. JRootPane rootPane = new JRootPane();
  104. rootPane.registerKeyboardAction(new ActionListener() {
  105. public void actionPerformed(ActionEvent actionEvent) {
  106. setVisible(false);
  107. }
  108. }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
  109. return rootPane;
  110. }
  111. private void initialize(int protocolVersion, UserModel anUser) {
  112. usernameField = new JTextField(anUser.username == null ? "" : anUser.username, 25);
  113. passwordField = new JPasswordField(anUser.password == null ? "" : anUser.password, 25);
  114. confirmPasswordField = new JPasswordField(anUser.password == null ? "" : anUser.password,
  115. 25);
  116. displayNameField = new JTextField(anUser.displayName == null ? "" : anUser.displayName, 25);
  117. emailAddressField = new JTextField(anUser.emailAddress == null ? "" : anUser.emailAddress, 25);
  118. canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), anUser.canAdmin);
  119. canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), anUser.canFork);
  120. canCreateCheckbox = new JCheckBox(Translation.get("gb.canCreateDescription"), anUser.canCreate);
  121. notFederatedCheckbox = new JCheckBox(
  122. Translation.get("gb.excludeFromFederationDescription"),
  123. anUser.excludeFromFederation);
  124. organizationalUnitField = new JTextField(anUser.organizationalUnit == null ? "" : anUser.organizationalUnit, 25);
  125. organizationField = new JTextField(anUser.organization == null ? "" : anUser.organization, 25);
  126. localityField = new JTextField(anUser.locality == null ? "" : anUser.locality, 25);
  127. stateProvinceField = new JTextField(anUser.stateProvince == null ? "" : anUser.stateProvince, 25);
  128. countryCodeField = new JTextField(anUser.countryCode == null ? "" : anUser.countryCode, 15);
  129. // credentials are optionally controlled by 3rd-party authentication
  130. usernameField.setEnabled(settings.supportsCredentialChanges);
  131. passwordField.setEnabled(settings.supportsCredentialChanges);
  132. confirmPasswordField.setEnabled(settings.supportsCredentialChanges);
  133. displayNameField.setEnabled(settings.supportsDisplayNameChanges);
  134. emailAddressField.setEnabled(settings.supportsEmailAddressChanges);
  135. organizationalUnitField.setEnabled(settings.supportsDisplayNameChanges);
  136. organizationField.setEnabled(settings.supportsDisplayNameChanges);
  137. localityField.setEnabled(settings.supportsDisplayNameChanges);
  138. stateProvinceField.setEnabled(settings.supportsDisplayNameChanges);
  139. countryCodeField.setEnabled(settings.supportsDisplayNameChanges);
  140. JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));
  141. fieldsPanel.add(newFieldPanel(Translation.get("gb.username"), usernameField));
  142. fieldsPanel.add(newFieldPanel(Translation.get("gb.password"), passwordField));
  143. fieldsPanel.add(newFieldPanel(Translation.get("gb.confirmPassword"), confirmPasswordField));
  144. fieldsPanel.add(newFieldPanel(Translation.get("gb.displayName"), displayNameField));
  145. fieldsPanel.add(newFieldPanel(Translation.get("gb.emailAddress"), emailAddressField));
  146. fieldsPanel.add(newFieldPanel(Translation.get("gb.canAdmin"), canAdminCheckbox));
  147. fieldsPanel.add(newFieldPanel(Translation.get("gb.canFork"), canForkCheckbox));
  148. fieldsPanel.add(newFieldPanel(Translation.get("gb.canCreate"), canCreateCheckbox));
  149. fieldsPanel.add(newFieldPanel(Translation.get("gb.excludeFromFederation"),
  150. notFederatedCheckbox));
  151. JPanel attributesPanel = new JPanel(new GridLayout(0, 1, 5, 2));
  152. attributesPanel.add(newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnitField));
  153. attributesPanel.add(newFieldPanel(Translation.get("gb.organization") + " (O)", organizationField));
  154. attributesPanel.add(newFieldPanel(Translation.get("gb.locality") + " (L)", localityField));
  155. attributesPanel.add(newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvinceField));
  156. attributesPanel.add(newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCodeField));
  157. final Insets _insets = new Insets(5, 5, 5, 5);
  158. repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);
  159. teamsPalette = new JPalette<TeamModel>();
  160. teamsPalette.setEnabled(settings.supportsTeamMembershipChanges);
  161. JPanel fieldsPanelTop = new JPanel(new BorderLayout());
  162. fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);
  163. JPanel attributesPanelTop = new JPanel(new BorderLayout());
  164. attributesPanelTop.add(attributesPanel, BorderLayout.NORTH);
  165. JPanel repositoriesPanel = new JPanel(new BorderLayout()) {
  166. private static final long serialVersionUID = 1L;
  167. public Insets getInsets() {
  168. return _insets;
  169. }
  170. };
  171. repositoriesPanel.add(repositoryPalette, BorderLayout.CENTER);
  172. JPanel teamsPanel = new JPanel(new BorderLayout()) {
  173. private static final long serialVersionUID = 1L;
  174. public Insets getInsets() {
  175. return _insets;
  176. }
  177. };
  178. teamsPanel.add(teamsPalette, BorderLayout.CENTER);
  179. JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
  180. panel.addTab(Translation.get("gb.general"), fieldsPanelTop);
  181. panel.addTab(Translation.get("gb.attributes"), attributesPanelTop);
  182. if (protocolVersion > 1) {
  183. panel.addTab(Translation.get("gb.teamMemberships"), teamsPanel);
  184. }
  185. panel.addTab(Translation.get("gb.restrictedRepositories"), repositoriesPanel);
  186. JButton createButton = new JButton(Translation.get("gb.save"));
  187. createButton.addActionListener(new ActionListener() {
  188. public void actionPerformed(ActionEvent event) {
  189. if (validateFields()) {
  190. canceled = false;
  191. setVisible(false);
  192. }
  193. }
  194. });
  195. JButton cancelButton = new JButton(Translation.get("gb.cancel"));
  196. cancelButton.addActionListener(new ActionListener() {
  197. public void actionPerformed(ActionEvent event) {
  198. canceled = true;
  199. setVisible(false);
  200. }
  201. });
  202. JPanel controls = new JPanel();
  203. controls.add(cancelButton);
  204. controls.add(createButton);
  205. JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) {
  206. private static final long serialVersionUID = 1L;
  207. @Override
  208. public Insets getInsets() {
  209. return _insets;
  210. }
  211. };
  212. centerPanel.add(panel, BorderLayout.CENTER);
  213. centerPanel.add(controls, BorderLayout.SOUTH);
  214. getContentPane().setLayout(new BorderLayout(5, 5));
  215. getContentPane().add(centerPanel, BorderLayout.CENTER);
  216. pack();
  217. }
  218. private JPanel newFieldPanel(String label, JComponent comp) {
  219. JLabel fieldLabel = new JLabel(label);
  220. fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD));
  221. fieldLabel.setPreferredSize(new Dimension(150, 20));
  222. JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
  223. panel.add(fieldLabel);
  224. panel.add(comp);
  225. return panel;
  226. }
  227. private boolean validateFields() {
  228. if (StringUtils.isEmpty(usernameField.getText())) {
  229. error("Please enter a username!");
  230. return false;
  231. }
  232. String uname = usernameField.getText().toLowerCase();
  233. boolean rename = false;
  234. // verify username uniqueness on create
  235. if (isCreate) {
  236. if (usernames.contains(uname)) {
  237. error(MessageFormat.format("Username ''{0}'' is unavailable.", uname));
  238. return false;
  239. }
  240. } else {
  241. // check rename collision
  242. rename = !StringUtils.isEmpty(username) && !username.equalsIgnoreCase(uname);
  243. if (rename) {
  244. if (usernames.contains(uname)) {
  245. error(MessageFormat.format(
  246. "Failed to rename ''{0}'' because ''{1}'' already exists.", username,
  247. uname));
  248. return false;
  249. }
  250. }
  251. }
  252. user.username = uname;
  253. int minLength = settings.get(Keys.realm.minPasswordLength).getInteger(5);
  254. if (minLength < 4) {
  255. minLength = 4;
  256. }
  257. String password = new String(passwordField.getPassword());
  258. if (StringUtils.isEmpty(password) || password.length() < minLength) {
  259. error(MessageFormat.format("Password is too short. Minimum length is {0} characters.",
  260. minLength));
  261. return false;
  262. }
  263. if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)
  264. && !password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {
  265. String cpw = new String(confirmPasswordField.getPassword());
  266. if (cpw == null || cpw.length() != password.length()) {
  267. error("Please confirm the password!");
  268. return false;
  269. }
  270. if (!password.equals(cpw)) {
  271. error("Passwords do not match!");
  272. return false;
  273. }
  274. String type = settings.get(Keys.realm.passwordStorage).getString("md5");
  275. if (type.equalsIgnoreCase("md5")) {
  276. // store MD5 digest of password
  277. user.password = StringUtils.MD5_TYPE + StringUtils.getMD5(password);
  278. } else if (type.equalsIgnoreCase("combined-md5")) {
  279. // store MD5 digest of username+password
  280. user.password = StringUtils.COMBINED_MD5_TYPE
  281. + StringUtils.getMD5(user.username + password);
  282. } else {
  283. // plain-text password
  284. user.password = password;
  285. }
  286. } else if (rename && password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {
  287. error("Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename.");
  288. return false;
  289. } else {
  290. // no change in password
  291. user.password = password;
  292. }
  293. user.displayName = displayNameField.getText().trim();
  294. user.emailAddress = emailAddressField.getText().trim();
  295. user.canAdmin = canAdminCheckbox.isSelected();
  296. user.canFork = canForkCheckbox.isSelected();
  297. user.canCreate = canCreateCheckbox.isSelected();
  298. user.excludeFromFederation = notFederatedCheckbox.isSelected();
  299. user.organizationalUnit = organizationalUnitField.getText().trim();
  300. user.organization = organizationField.getText().trim();
  301. user.locality = localityField.getText().trim();
  302. user.stateProvince = stateProvinceField.getText().trim();
  303. user.countryCode = countryCodeField.getText().trim();
  304. for (RegistrantAccessPermission rp : repositoryPalette.getPermissions()) {
  305. user.setRepositoryPermission(rp.registrant, rp.permission);
  306. }
  307. user.teams.clear();
  308. user.teams.addAll(teamsPalette.getSelections());
  309. return true;
  310. }
  311. private void error(String message) {
  312. JOptionPane.showMessageDialog(EditUserDialog.this, message, Translation.get("gb.error"),
  313. JOptionPane.ERROR_MESSAGE);
  314. }
  315. public void setUsers(List<UserModel> users) {
  316. usernames.clear();
  317. for (UserModel user : users) {
  318. usernames.add(user.username.toLowerCase());
  319. }
  320. }
  321. public void setRepositories(List<RepositoryModel> repositories, List<RegistrantAccessPermission> permissions) {
  322. Map<String, RepositoryModel> repoMap = new HashMap<String, RepositoryModel>();
  323. List<String> restricted = new ArrayList<String>();
  324. for (RepositoryModel repo : repositories) {
  325. // exclude Owner or personal repositories
  326. if (!repo.isOwner(username) && !repo.isUsersPersonalRepository(username)) {
  327. if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)
  328. && repo.authorizationControl.equals(AuthorizationControl.NAMED)) {
  329. restricted.add(repo.name);
  330. }
  331. }
  332. repoMap.put(repo.name.toLowerCase(), repo);
  333. }
  334. StringUtils.sortRepositorynames(restricted);
  335. List<String> list = new ArrayList<String>();
  336. // repositories
  337. list.add(".*");
  338. String prefix;
  339. if (settings.hasKey(Keys.git.userRepositoryPrefix)) {
  340. prefix = settings.get(Keys.git.userRepositoryPrefix).currentValue;
  341. if (StringUtils.isEmpty(prefix)) {
  342. prefix = Constants.DEFAULT_USER_REPOSITORY_PREFIX;
  343. }
  344. } else {
  345. prefix = Constants.DEFAULT_USER_REPOSITORY_PREFIX;
  346. }
  347. if (prefix.length() == 1) {
  348. // all repositories excluding personal repositories
  349. list.add("[^" + prefix + "].*");
  350. }
  351. String lastProject = null;
  352. for (String repo : restricted) {
  353. String projectPath = StringUtils.getFirstPathElement(repo).toLowerCase();
  354. if (lastProject == null || !lastProject.equalsIgnoreCase(projectPath)) {
  355. lastProject = projectPath;
  356. if (!StringUtils.isEmpty(projectPath)) {
  357. // regex for all repositories within a project
  358. list.add(projectPath + "/.*");
  359. }
  360. }
  361. list.add(repo);
  362. }
  363. // remove repositories for which user already has a permission
  364. if (permissions == null) {
  365. permissions = new ArrayList<RegistrantAccessPermission>();
  366. } else {
  367. for (RegistrantAccessPermission rp : permissions) {
  368. list.remove(rp.registrant.toLowerCase());
  369. }
  370. }
  371. // update owner and missing permissions for editing
  372. for (RegistrantAccessPermission permission : permissions) {
  373. if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
  374. // Ensure this is NOT an owner permission - which is non-editable
  375. // We don't know this from within the usermodel, ownership is a
  376. // property of a repository.
  377. RepositoryModel rm = repoMap.get(permission.registrant.toLowerCase());
  378. if (rm == null) {
  379. permission.permissionType = PermissionType.MISSING;
  380. permission.mutable = false;
  381. continue;
  382. }
  383. boolean isOwner = rm.isOwner(username);
  384. if (isOwner) {
  385. permission.permissionType = PermissionType.OWNER;
  386. permission.mutable = false;
  387. }
  388. }
  389. }
  390. repositoryPalette.setObjects(list, permissions);
  391. }
  392. public void setTeams(List<TeamModel> teams, List<TeamModel> selected) {
  393. Collections.sort(teams);
  394. if (selected != null) {
  395. Collections.sort(selected);
  396. }
  397. teamsPalette.setObjects(teams, selected);
  398. }
  399. public UserModel getUser() {
  400. if (canceled) {
  401. return null;
  402. }
  403. return user;
  404. }
  405. }