From 092f0a62302e87f44403ba24fc519c65534dbfff Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Mon, 29 Oct 2012 23:22:54 -0400 Subject: [PATCH] Stabilizing and polishing permissions ui. Still in-progress. --- src/com/gitblit/models/RegistrantAccessPermission.java | 36 ++ src/com/gitblit/wicket/pages/EditRepositoryPage.java | 125 +++++++++- src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java | 69 ++++- src/com/gitblit/client/GitblitClient.java | 68 +++++ src/com/gitblit/client/EditTeamDialog.java | 10 src/com/gitblit/client/RegistrantPermissionsPanel.java | 56 +++- src/com/gitblit/wicket/pages/EditUserPage.java | 20 + src/com/gitblit/client/EditUserDialog.java | 18 + src/com/gitblit/models/UserModel.java | 18 + src/com/gitblit/GitBlit.java | 49 +++ src/com/gitblit/wicket/GitBlitWebApp.properties | 5 src/com/gitblit/wicket/pages/EditTeamPage.java | 8 src/com/gitblit/wicket/pages/BasePage.java | 16 + tests/com/gitblit/tests/RpcTests.java | 5 src/com/gitblit/client/UsersPanel.java | 17 + src/com/gitblit/client/EditRepositoryDialog.java | 63 +++++ src/com/gitblit/models/TeamModel.java | 10 src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html | 2 src/com/gitblit/wicket/pages/EditRepositoryPage.html | 7 resources/gitblit.css | 6 src/com/gitblit/client/RegistrantPermissionsTableModel.java | 4 src/com/gitblit/wicket/pages/RootSubPage.java | 19 + src/com/gitblit/Constants.java | 4 23 files changed, 520 insertions(+), 115 deletions(-) diff --git a/resources/gitblit.css b/resources/gitblit.css index 5c2d92a..e5363c8 100644 --- a/resources/gitblit.css +++ b/resources/gitblit.css @@ -188,6 +188,12 @@ vertical-align: middle; } +span.authorizationControl label { + display: inline; + color: #777; + padding:5px 0px 5px 10px; +} + div.page_footer { clear: both; height: 17px; diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index f74317e..e7812ee 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -386,6 +386,10 @@ REPOSITORY, USER, TEAM; } + public static enum PermissionType { + EXPLICIT, OWNER, REGEX; + } + public static enum GCStatus { READY, COLLECTING; diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 402f600..6e587ca 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -79,6 +79,7 @@ import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationStrategy; import com.gitblit.Constants.FederationToken; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; @@ -670,14 +671,35 @@ * @return a list of User-AccessPermission tuples */ public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) { - List<RegistrantAccessPermission> permissions = new ArrayList<RegistrantAccessPermission>(); + Set<RegistrantAccessPermission> permissions = new LinkedHashSet<RegistrantAccessPermission>(); + if (!StringUtils.isEmpty(repository.owner)) { + UserModel owner = userService.getUserModel(repository.owner); + if (owner != null) { + permissions.add(new RegistrantAccessPermission(owner.username, AccessPermission.REWIND, PermissionType.OWNER, RegistrantType.USER, false)); + } + } + if (repository.isPersonalRepository()) { + UserModel owner = userService.getUserModel(repository.projectPath.substring(1)); + if (owner != null) { + permissions.add(new RegistrantAccessPermission(owner.username, AccessPermission.REWIND, PermissionType.OWNER, RegistrantType.USER, false)); + } + } for (String user : userService.getUsernamesForRepositoryRole(repository.name)) { UserModel model = userService.getUserModel(user); AccessPermission ap = model.getRepositoryPermission(repository); - boolean isExplicit = model.hasExplicitRepositoryPermission(repository.name); - permissions.add(new RegistrantAccessPermission(user, ap, isExplicit, RegistrantType.USER)); + PermissionType pType = PermissionType.REGEX; + boolean editable = false; + if (repository.isOwner(model.username)) { + pType = PermissionType.OWNER; + } else if (repository.isUsersPersonalRepository(model.username)) { + pType = PermissionType.OWNER; + } else if (model.hasExplicitRepositoryPermission(repository.name)) { + pType = PermissionType.EXPLICIT; + editable = true; + } + permissions.add(new RegistrantAccessPermission(user, ap, pType, RegistrantType.USER, editable)); } - return permissions; + return new ArrayList<RegistrantAccessPermission>(permissions); } /** @@ -690,8 +712,8 @@ public boolean setUserAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) { List<UserModel> users = new ArrayList<UserModel>(); for (RegistrantAccessPermission up : permissions) { - if (up.isExplicit) { - // only set explicitly defined permissions + if (up.isEditable) { + // only set editable defined permissions UserModel user = userService.getUserModel(up.registrant); user.setRepositoryPermission(repository.name, up.permission); users.add(user); @@ -811,8 +833,13 @@ for (String team : userService.getTeamnamesForRepositoryRole(repository.name)) { TeamModel model = userService.getTeamModel(team); AccessPermission ap = model.getRepositoryPermission(repository); - boolean isExplicit = model.hasExplicitRepositoryPermission(repository.name); - permissions.add(new RegistrantAccessPermission(team, ap, isExplicit, RegistrantType.TEAM)); + PermissionType pType = PermissionType.REGEX; + boolean editable = false; + if (model.hasExplicitRepositoryPermission(repository.name)) { + pType = PermissionType.EXPLICIT; + editable = true; + } + permissions.add(new RegistrantAccessPermission(team, ap, pType, RegistrantType.TEAM, editable)); } return permissions; } @@ -827,7 +854,7 @@ public boolean setTeamAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) { List<TeamModel> teams = new ArrayList<TeamModel>(); for (RegistrantAccessPermission tp : permissions) { - if (tp.isExplicit) { + if (tp.isEditable) { // only set explicitly defined access permissions TeamModel team = userService.getTeamModel(tp.registrant); team.setRepositoryPermission(repository.name, tp.permission); @@ -1870,7 +1897,9 @@ config.setBoolean(Constants.CONFIG_GITBLIT, null, "isFederated", repository.isFederated); config.setString(Constants.CONFIG_GITBLIT, null, "gcThreshold", repository.gcThreshold); config.setString(Constants.CONFIG_GITBLIT, null, "gcPeriod", repository.gcPeriod); - config.setString(Constants.CONFIG_GITBLIT, null, "lastGC", new SimpleDateFormat(Constants.ISO8601).format(repository.lastGC)); + if (repository.lastGC != null) { + config.setString(Constants.CONFIG_GITBLIT, null, "lastGC", new SimpleDateFormat(Constants.ISO8601).format(repository.lastGC)); + } updateList(config, "federationSets", repository.federationSets); updateList(config, "preReceiveScript", repository.preReceiveScripts); diff --git a/src/com/gitblit/client/EditRepositoryDialog.java b/src/com/gitblit/client/EditRepositoryDialog.java index 0adf8a8..06621c2 100644 --- a/src/com/gitblit/client/EditRepositoryDialog.java +++ b/src/com/gitblit/client/EditRepositoryDialog.java @@ -24,6 +24,8 @@ import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.text.MessageFormat; import java.util.ArrayList; @@ -37,6 +39,7 @@ import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -59,6 +62,7 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationStrategy; +import com.gitblit.Constants.RegistrantType; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.RepositoryModel; import com.gitblit.utils.ArrayUtils; @@ -218,13 +222,41 @@ accessRestriction = new JComboBox(AccessRestrictionType.values()); accessRestriction.setRenderer(new AccessRestrictionRenderer()); accessRestriction.setSelectedItem(anRepository.accessRestriction); + accessRestriction.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + AccessRestrictionType art = (AccessRestrictionType) accessRestriction.getSelectedItem(); + EditRepositoryDialog.this.setupAccessPermissions(art); + } + } + }); boolean authenticated = anRepository.authorizationControl != null && AuthorizationControl.AUTHENTICATED.equals(anRepository.authorizationControl); allowAuthenticated = new JRadioButton(Translation.get("gb.allowAuthenticatedDescription")); allowAuthenticated.setSelected(authenticated); + allowAuthenticated.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + usersPalette.setEnabled(false); + teamsPalette.setEnabled(false); + } + } + }); + allowNamed = new JRadioButton(Translation.get("gb.allowNamedDescription")); allowNamed.setSelected(!authenticated); + allowNamed.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + usersPalette.setEnabled(true); + teamsPalette.setEnabled(true); + } + } + }); ButtonGroup group = new ButtonGroup(); group.add(allowAuthenticated); @@ -281,7 +313,7 @@ clonePushPanel .add(newFieldPanel(Translation.get("gb.verifyCommitter"), verifyCommitter)); - usersPalette = new RegistrantPermissionsPanel(); + usersPalette = new RegistrantPermissionsPanel(RegistrantType.USER); JPanel northAccessPanel = new JPanel(new BorderLayout(5, 5)); northAccessPanel.add(newFieldPanel(Translation.get("gb.accessRestriction"), accessRestriction), BorderLayout.NORTH); @@ -294,7 +326,7 @@ accessPanel.add(newFieldPanel(Translation.get("gb.userPermissions"), usersPalette), BorderLayout.CENTER); - teamsPalette = new RegistrantPermissionsPanel(); + teamsPalette = new RegistrantPermissionsPanel(RegistrantType.TEAM); JPanel teamsPanel = new JPanel(new BorderLayout(5, 5)); teamsPanel.add( newFieldPanel(Translation.get("gb.teamPermissions"), @@ -349,6 +381,8 @@ panel.addTab(Translation.get("gb.customFields"), customFieldsScrollPane); + setupAccessPermissions(anRepository.accessRestriction); + JButton createButton = new JButton(Translation.get("gb.save")); createButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { @@ -402,6 +436,25 @@ panel.add(fieldLabel); panel.add(comp); return panel; + } + + private void setupAccessPermissions(AccessRestrictionType art) { + if (AccessRestrictionType.NONE.equals(art)) { + usersPalette.setEnabled(false); + teamsPalette.setEnabled(false); + + allowAuthenticated.setEnabled(false); + allowNamed.setEnabled(false); + } else { + allowAuthenticated.setEnabled(true); + allowNamed.setEnabled(true); + + if (allowNamed.isSelected()) { + usersPalette.setEnabled(true); + teamsPalette.setEnabled(true); + } + } + } private boolean validateFields() { @@ -538,6 +591,7 @@ public void setAccessRestriction(AccessRestrictionType restriction) { this.accessRestriction.setSelectedItem(restriction); + setupAccessPermissions(restriction); } public void setAuthorizationControl(AuthorizationControl authorization) { @@ -659,14 +713,15 @@ * restriction. * */ - private class AccessRestrictionRenderer extends JLabel implements - ListCellRenderer { + private class AccessRestrictionRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = 1L; @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof AccessRestrictionType) { AccessRestrictionType restriction = (AccessRestrictionType) value; switch (restriction) { diff --git a/src/com/gitblit/client/EditTeamDialog.java b/src/com/gitblit/client/EditTeamDialog.java index 4350310..4d7af26 100644 --- a/src/com/gitblit/client/EditTeamDialog.java +++ b/src/com/gitblit/client/EditTeamDialog.java @@ -45,11 +45,12 @@ import javax.swing.KeyStroke; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.Constants.RegistrantType; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.RepositoryModel; import com.gitblit.models.ServerSettings; import com.gitblit.models.TeamModel; -import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.StringUtils; public class EditTeamDialog extends JDialog { @@ -140,7 +141,7 @@ fieldsPanel.add(newFieldPanel(Translation.get("gb.mailingLists"), mailingListsField)); final Insets _insets = new Insets(5, 5, 5, 5); - repositoryPalette = new RegistrantPermissionsPanel(); + repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY); userPalette = new JPalette<String>(); userPalette.setEnabled(settings.supportsTeamMembershipChanges); @@ -311,9 +312,10 @@ public void setRepositories(List<RepositoryModel> repositories, List<RegistrantAccessPermission> permissions) { List<String> restricted = new ArrayList<String>(); for (RepositoryModel repo : repositories) { - if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)) { + if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE) + && repo.authorizationControl.equals(AuthorizationControl.NAMED)) { restricted.add(repo.name); - } + } } StringUtils.sortRepositorynames(restricted); diff --git a/src/com/gitblit/client/EditUserDialog.java b/src/com/gitblit/client/EditUserDialog.java index e096693..070926d 100644 --- a/src/com/gitblit/client/EditUserDialog.java +++ b/src/com/gitblit/client/EditUserDialog.java @@ -46,6 +46,8 @@ import javax.swing.KeyStroke; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.Constants.RegistrantType; import com.gitblit.Keys; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.RepositoryModel; @@ -158,7 +160,7 @@ notFederatedCheckbox)); final Insets _insets = new Insets(5, 5, 5, 5); - repositoryPalette = new RegistrantPermissionsPanel(); + repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY); teamsPalette = new JPalette<TeamModel>(); teamsPalette.setEnabled(settings.supportsTeamMembershipChanges); @@ -343,8 +345,12 @@ public void setRepositories(List<RepositoryModel> repositories, List<RegistrantAccessPermission> permissions) { List<String> restricted = new ArrayList<String>(); for (RepositoryModel repo : repositories) { - if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)) { - restricted.add(repo.name); + // 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); + } } } StringUtils.sortRepositorynames(restricted); @@ -356,15 +362,15 @@ list.add("[^~].*"); String lastProject = null; for (String repo : restricted) { - String projectPath = StringUtils.getFirstPathElement(repo); + 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); } + list.add(repo); } // remove repositories for which user already has a permission @@ -372,7 +378,7 @@ permissions = new ArrayList<RegistrantAccessPermission>(); } else { for (RegistrantAccessPermission rp : permissions) { - list.remove(rp.registrant); + list.remove(rp.registrant.toLowerCase()); } } repositoryPalette.setObjects(list, permissions); diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java index 4620fef..b7047d7 100644 --- a/src/com/gitblit/client/GitblitClient.java +++ b/src/com/gitblit/client/GitblitClient.java @@ -31,6 +31,7 @@ import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.GitBlitException.ForbiddenException; import com.gitblit.GitBlitException.NotAllowedException; @@ -340,6 +341,7 @@ List<UserModel> users = RpcUtils.getUsers(url, account, password); allUsers.clear(); allUsers.addAll(users); + Collections.sort(users); return allUsers; } @@ -347,6 +349,7 @@ List<TeamModel> teams = RpcUtils.getTeams(url, account, password); allTeams.clear(); allTeams.addAll(teams); + Collections.sort(teams); return allTeams; } @@ -475,6 +478,15 @@ public List<UserModel> getUsers() { return allUsers; } + + public UserModel getUser(String username) { + for (UserModel user : getUsers()) { + if (user.username.equalsIgnoreCase(username)) { + return user; + } + } + return null; + } public List<String> getUsernames() { List<String> usernames = new ArrayList<String>(); @@ -496,15 +508,38 @@ } public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) { - List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(); - for (UserModel user : allUsers) { - if (user.hasRepositoryPermission(repository.name)) { - AccessPermission ap = user.getRepositoryPermission(repository); - boolean isExplicit = user.hasExplicitRepositoryPermission(repository.name); - list.add(new RegistrantAccessPermission(user.username, ap, isExplicit, RegistrantType.USER)); + Set<RegistrantAccessPermission> list = new LinkedHashSet<RegistrantAccessPermission>(); + if (!StringUtils.isEmpty(repository.owner)) { + UserModel owner = getUser(repository.owner); + if (owner != null) { + list.add(new RegistrantAccessPermission(owner.username, AccessPermission.REWIND, PermissionType.OWNER, RegistrantType.USER, false)); } } - return list; + if (repository.isPersonalRepository()) { + UserModel owner = getUser(repository.projectPath.substring(1)); + if (owner != null) { + list.add(new RegistrantAccessPermission(owner.username, AccessPermission.REWIND, PermissionType.OWNER, RegistrantType.USER, false)); + } + } + for (UserModel user : getUsers()) { + if (user.hasRepositoryPermission(repository.name)) { + AccessPermission ap = user.getRepositoryPermission(repository); + PermissionType pType = PermissionType.REGEX; + boolean editable = false; + if (repository.isOwner(user.username)) { + pType = PermissionType.OWNER; + } else if (repository.isUsersPersonalRepository(user.username)) { + pType = PermissionType.OWNER; + } else if (user.hasExplicitRepositoryPermission(repository.name)) { + pType = PermissionType.EXPLICIT; + editable = true; + } + list.add(new RegistrantAccessPermission(user.username, ap, pType, RegistrantType.USER, editable)); + } + } + List<RegistrantAccessPermission> raps = new ArrayList<RegistrantAccessPermission>(list); + Collections.sort(raps); + return raps; } public boolean setUserAccessPermissions(RepositoryModel repository, List<RegistrantAccessPermission> permissions) throws IOException { @@ -539,10 +574,16 @@ for (TeamModel team : allTeams) { if (team.hasRepositoryPermission(repository.name)) { AccessPermission ap = team.getRepositoryPermission(repository); - boolean isExplicit = team.hasExplicitRepositoryPermission(repository.name); - list.add(new RegistrantAccessPermission(team.name, ap, isExplicit, RegistrantType.TEAM)); + PermissionType pType = PermissionType.REGEX; + boolean editable = false; + if (team.hasExplicitRepositoryPermission(repository.name)) { + pType = PermissionType.EXPLICIT; + editable = true; + } + list.add(new RegistrantAccessPermission(team.name, ap, pType, RegistrantType.TEAM, editable)); } } + Collections.sort(list); return list; } @@ -566,6 +607,15 @@ public List<RepositoryModel> getRepositories() { return allRepositories; } + + public RepositoryModel getRepository(String name) { + for (RepositoryModel repository : allRepositories) { + if (repository.name.equalsIgnoreCase(name)) { + return repository; + } + } + return null; + } public boolean createRepository(RepositoryModel repository, List<RegistrantAccessPermission> userPermissions) throws IOException { diff --git a/src/com/gitblit/client/RegistrantPermissionsPanel.java b/src/com/gitblit/client/RegistrantPermissionsPanel.java index 4ea173f..b8ab939 100644 --- a/src/com/gitblit/client/RegistrantPermissionsPanel.java +++ b/src/com/gitblit/client/RegistrantPermissionsPanel.java @@ -33,7 +33,10 @@ import javax.swing.table.DefaultTableCellRenderer; import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.PermissionType; +import com.gitblit.Constants.RegistrantType; import com.gitblit.models.RegistrantAccessPermission; +import com.gitblit.utils.StringUtils; public class RegistrantPermissionsPanel extends JPanel { @@ -53,16 +56,19 @@ private JPanel addPanel; - public RegistrantPermissionsPanel() { + public RegistrantPermissionsPanel(final RegistrantType registrantType) { super(new BorderLayout(5, 5)); tableModel = new RegistrantPermissionsTableModel(); - permissionsTable = new JTable(tableModel); + permissionsTable = Utils.newTable(tableModel, Utils.DATE_FORMAT); + permissionsTable.setModel(tableModel); permissionsTable.setPreferredScrollableViewportSize(new Dimension(400, 150)); JScrollPane jsp = new JScrollPane(permissionsTable); add(jsp, BorderLayout.CENTER); + permissionsTable.getColumnModel().getColumn(RegistrantPermissionsTableModel.Columns.Registrant.ordinal()) + .setCellRenderer(new NameRenderer()); permissionsTable.getColumnModel().getColumn(RegistrantPermissionsTableModel.Columns.Type.ordinal()) - .setCellRenderer(new RegexRenderer()); + .setCellRenderer(new PermissionTypeRenderer()); permissionsTable.getColumnModel().getColumn(RegistrantPermissionsTableModel.Columns.Permission.ordinal()) .setCellEditor(new AccessPermissionEditor()); @@ -79,9 +85,15 @@ return; } - RegistrantAccessPermission rp = new RegistrantAccessPermission(); + RegistrantAccessPermission rp = new RegistrantAccessPermission(registrantType); rp.registrant = registrantSelector.getSelectedItem().toString(); rp.permission = (AccessPermission) permissionSelector.getSelectedItem(); + if (StringUtils.findInvalidCharacter(rp.registrant) != null) { + rp.permissionType = PermissionType.REGEX; + } else { + rp.permissionType = PermissionType.EXPLICIT; + } + tableModel.permissions.add(rp); registrantModel.removeElement(rp.registrant); @@ -103,7 +115,10 @@ @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); - permissionsTable.setEnabled(false); + permissionsTable.setEnabled(enabled); + registrantSelector.setEnabled(enabled); + permissionSelector.setEnabled(enabled); + addButton.setEnabled(enabled); } public void setObjects(List<String> registrants, List<RegistrantAccessPermission> permissions) { @@ -117,7 +132,11 @@ permissions = new ArrayList<RegistrantAccessPermission>(); } for (RegistrantAccessPermission rp : permissions) { - filtered.remove(rp.registrant); + if (rp.isEditable) { + // only remove editable duplicates + // this allows for specifying an explicit permission + filtered.remove(rp.registrant); + } } for (String registrant : filtered) { registrantModel.addElement(registrant); @@ -138,30 +157,35 @@ private static final long serialVersionUID = 1L; public AccessPermissionEditor() { - super(new JComboBox(AccessPermission.values())); + super(new JComboBox(AccessPermission.values())); } } - private class RegexRenderer extends DefaultTableCellRenderer { + private class PermissionTypeRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; - public RegexRenderer() { + public PermissionTypeRenderer() { super(); setHorizontalAlignment(SwingConstants.CENTER); } @Override protected void setValue(Object value) { - boolean isExplicit = (Boolean) value; - if (isExplicit) { - // explicit permission - setText(""); - setToolTipText(null); - } else { - // regex matched permission + PermissionType pType = (PermissionType) value; + switch (pType) { + case OWNER: + setText("owner"); + setToolTipText(Translation.get("gb.ownerPermission")); + break; + case REGEX: setText("regex"); setToolTipText(Translation.get("gb.regexPermission")); + break; + default: + setText(""); + setToolTipText(null); + break; } } } diff --git a/src/com/gitblit/client/RegistrantPermissionsTableModel.java b/src/com/gitblit/client/RegistrantPermissionsTableModel.java index fcd9c8b..9ed8db4 100644 --- a/src/com/gitblit/client/RegistrantPermissionsTableModel.java +++ b/src/com/gitblit/client/RegistrantPermissionsTableModel.java @@ -104,7 +104,7 @@ // and therefore can not be directly manipulated unless the current // object is the source of the regex (i.e. a user or team with explicit // regex definition) - return permissions.get(rowIndex).isExplicit; + return permissions.get(rowIndex).isEditable; } return false; } @@ -117,7 +117,7 @@ case Registrant: return rp.registrant; case Type: - return rp.isExplicit; + return rp.permissionType; case Permission: return rp.permission; } diff --git a/src/com/gitblit/client/UsersPanel.java b/src/com/gitblit/client/UsersPanel.java index 9fcad7b..cd571b2 100644 --- a/src/com/gitblit/client/UsersPanel.java +++ b/src/com/gitblit/client/UsersPanel.java @@ -40,7 +40,9 @@ import javax.swing.event.ListSelectionListener; import javax.swing.table.TableRowSorter; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RpcRequest; +import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; @@ -309,6 +311,21 @@ gitblit.getSettings()); dialog.setLocationRelativeTo(UsersPanel.this); dialog.setUsers(gitblit.getUsers()); + + List<RegistrantAccessPermission> permissions = user.getRepositoryPermissions(); + for (RegistrantAccessPermission permission : permissions) { + if (permission.isEditable && 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. + boolean isOwner = gitblit.getRepository(permission.registrant).isOwner(user.username); + if (isOwner) { + permission.permissionType = PermissionType.OWNER; + permission.isEditable = false; + } + } + } + dialog.setRepositories(gitblit.getRepositories(), user.getRepositoryPermissions()); dialog.setTeams(gitblit.getTeams(), user.teams == null ? null : new ArrayList<TeamModel>( user.teams)); diff --git a/src/com/gitblit/models/RegistrantAccessPermission.java b/src/com/gitblit/models/RegistrantAccessPermission.java index 4a560d4..7346d31 100644 --- a/src/com/gitblit/models/RegistrantAccessPermission.java +++ b/src/com/gitblit/models/RegistrantAccessPermission.java @@ -18,6 +18,7 @@ import java.io.Serializable; import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.utils.StringUtils; @@ -32,23 +33,27 @@ public String registrant; public AccessPermission permission; - public RegistrantType type; - public boolean isExplicit; + public RegistrantType registrantType; + public PermissionType permissionType; + public boolean isEditable; - public RegistrantAccessPermission() { - isExplicit = true; + public RegistrantAccessPermission(RegistrantType registrantType) { + this.registrantType = registrantType; + this.permissionType = PermissionType.EXPLICIT; + this.isEditable = true; } - public RegistrantAccessPermission(String registrant, AccessPermission permission, boolean isExplicit, RegistrantType type) { + public RegistrantAccessPermission(String registrant, AccessPermission permission, PermissionType permissionType, RegistrantType registrantType, boolean isEditable) { this.registrant = registrant; this.permission = permission; - this.isExplicit = isExplicit; - this.type = type; + this.permissionType = permissionType; + this.registrantType = registrantType; + this.isEditable = isEditable; } @Override public int compareTo(RegistrantAccessPermission p) { - switch (type) { + switch (registrantType) { case REPOSITORY: return StringUtils.compareRepositoryNames(registrant, p.registrant); default: @@ -57,6 +62,21 @@ } @Override + public int hashCode() { + return registrant.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof RegistrantAccessPermission) { + RegistrantAccessPermission p = (RegistrantAccessPermission) o; + return registrant.equals(p.registrant); + } + + return false; + } + + @Override public String toString() { return permission.asRole(registrant); } diff --git a/src/com/gitblit/models/TeamModel.java b/src/com/gitblit/models/TeamModel.java index 7d557db..e5e3b09 100644 --- a/src/com/gitblit/models/TeamModel.java +++ b/src/com/gitblit/models/TeamModel.java @@ -27,6 +27,7 @@ import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.Constants.Unused; import com.gitblit.utils.StringUtils; @@ -98,7 +99,14 @@ public List<RegistrantAccessPermission> getRepositoryPermissions() { List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(); for (Map.Entry<String, AccessPermission> entry : permissions.entrySet()) { - list.add(new RegistrantAccessPermission(entry.getKey(), entry.getValue(), true, RegistrantType.REPOSITORY)); + String registrant = entry.getKey(); + boolean editable = true; + PermissionType pType = PermissionType.EXPLICIT; + if (StringUtils.findInvalidCharacter(registrant) != null) { + // a regex will have at least 1 invalid character + pType = PermissionType.REGEX; + } + list.add(new RegistrantAccessPermission(registrant, entry.getValue(), pType, RegistrantType.REPOSITORY, editable)); } Collections.sort(list); return list; diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index d7bc293..22f250c 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -28,6 +28,7 @@ import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.Constants.Unused; import com.gitblit.utils.ArrayUtils; @@ -137,7 +138,17 @@ public List<RegistrantAccessPermission> getRepositoryPermissions() { List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(); for (Map.Entry<String, AccessPermission> entry : permissions.entrySet()) { - list.add(new RegistrantAccessPermission(entry.getKey(), entry.getValue(), true, RegistrantType.REPOSITORY)); + String registrant = entry.getKey(); + boolean editable = true; + PermissionType pType = PermissionType.EXPLICIT; + if (isMyPersonalRepository(registrant)) { + pType = PermissionType.OWNER; + editable = false; + } else if (StringUtils.findInvalidCharacter(registrant) != null) { + // a regex will have at least 1 invalid character + pType = PermissionType.REGEX; + } + list.add(new RegistrantAccessPermission(registrant, entry.getValue(), pType, RegistrantType.REPOSITORY, editable)); } Collections.sort(list); return list; @@ -494,4 +505,9 @@ // Default UserModel doesn't implement branch-level security. Other Realms (i.e. Gerrit) may override this method. return hasRepositoryPermission(repositoryName); } + + public boolean isMyPersonalRepository(String repository) { + String projectPath = StringUtils.getFirstPathElement(repository); + return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase("~" + username); + } } diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index 62e4817..c6ceb9f 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -311,8 +311,8 @@ gb.duration.oneYear = 1 year gb.duration.years = {0} years gb.authorizationControl = authorization control -gb.allowAuthenticatedDescription = grant restricted access to all authenticated users -gb.allowNamedDescription = grant restricted access to named users or teams +gb.allowAuthenticatedDescription = grant RW+ permission to all authenticated users +gb.allowNamedDescription = grant fine-grained permissions to named users or teams gb.markdownFailure = Failed to parse Markdown content! gb.clearCache = clear cache gb.projects = projects @@ -364,3 +364,4 @@ gb.gcPeriodDescription = duration between garbage collections gb.gcThreshold = GC threshold gb.gcThresholdDescription = minimum total size of loose objects to trigger early garbage collection +gb.ownerPermission = repository owner \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java index dcca361..ceeb912 100644 --- a/src/com/gitblit/wicket/pages/BasePage.java +++ b/src/com/gitblit/wicket/pages/BasePage.java @@ -55,6 +55,7 @@ import com.gitblit.Constants; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationStrategy; import com.gitblit.GitBlit; import com.gitblit.Keys; @@ -255,6 +256,21 @@ } return map; } + + protected Map<AuthorizationControl, String> getAuthorizationControls() { + Map<AuthorizationControl, String> map = new LinkedHashMap<AuthorizationControl, String>(); + for (AuthorizationControl type : AuthorizationControl.values()) { + switch (type) { + case AUTHENTICATED: + map.put(type, getString("gb.allowAuthenticatedDescription")); + break; + case NAMED: + map.put(type, getString("gb.allowNamedDescription")); + break; + } + } + return map; + } protected TimeZone getTimeZone() { return GitBlit.getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get() diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.html b/src/com/gitblit/wicket/pages/EditRepositoryPage.html index 7bd896c..638eae9 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.html +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.html @@ -49,12 +49,7 @@ <tbody class="settings"> <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="15" /></td></tr> <tr><th colspan="2"><hr/></th></tr> - <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"> - <wicket:container wicket:id="authorizationControl"> - <label class="radio"><input type="radio" wicket:id="allowAuthenticated" tabindex="16" /> <span class="help-inline"><wicket:message key="gb.allowAuthenticatedDescription"></wicket:message></span></label> - <label class="radio"><input type="radio" wicket:id="allowNamed" tabindex="17" /> <span class="help-inline"><wicket:message key="gb.allowNamedDescription"></wicket:message></span></label> - </wicket:container> - </td></tr> + <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"><span class="authorizationControl" wicket:id="authorizationControl"></span></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="18" /> <span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="19" /> <span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr> diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/com/gitblit/wicket/pages/EditRepositoryPage.java index 1a2e63c..58fdf66 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.java +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.java @@ -27,6 +27,9 @@ import java.util.Set; import org.apache.wicket.PageParameters; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.extensions.markup.html.form.palette.Palette; import org.apache.wicket.markup.html.WebMarkupContainer; @@ -36,8 +39,7 @@ import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.form.Radio; -import org.apache.wicket.markup.html.form.RadioGroup; +import org.apache.wicket.markup.html.form.RadioChoice; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; @@ -51,6 +53,7 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationStrategy; +import com.gitblit.Constants.RegistrantType; import com.gitblit.GitBlit; import com.gitblit.GitBlitException; import com.gitblit.Keys; @@ -70,6 +73,8 @@ private final boolean isCreate; private boolean isAdmin; + + RepositoryModel repositoryModel; private IModel<String> mailingLists; @@ -97,6 +102,7 @@ setupPage(model); setStatelessHint(false); + setOutputMarkupId(true); } public EditRepositoryPage(PageParameters params) { @@ -107,9 +113,12 @@ RepositoryModel model = GitBlit.self().getRepositoryModel(name); setupPage(model); setStatelessHint(false); + setOutputMarkupId(true); } - protected void setupPage(final RepositoryModel repositoryModel) { + protected void setupPage(RepositoryModel model) { + this.repositoryModel = model; + // ensure this user can create or edit this repository checkPermissions(repositoryModel); @@ -145,10 +154,10 @@ final String oldName = repositoryModel.name; - RegistrantPermissionsPanel usersPalette = new RegistrantPermissionsPanel("users", - GitBlit.self().getAllUsernames(), repositoryUsers, getAccessPermissions()); - RegistrantPermissionsPanel teamsPalette = new RegistrantPermissionsPanel("teams", - GitBlit.self().getAllTeamnames(), repositoryTeams, getAccessPermissions()); + final RegistrantPermissionsPanel usersPalette = new RegistrantPermissionsPanel("users", + RegistrantType.USER, GitBlit.self().getAllUsernames(), repositoryUsers, getAccessPermissions()); + final RegistrantPermissionsPanel teamsPalette = new RegistrantPermissionsPanel("teams", + RegistrantType.TEAM, GitBlit.self().getAllTeamnames(), repositoryTeams, getAccessPermissions()); // indexed local branches palette List<String> allLocalBranches = new ArrayList<String>(); @@ -206,9 +215,9 @@ }; customFieldsListView.setReuseItems(true); - CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>( + CompoundPropertyModel<RepositoryModel> rModel = new CompoundPropertyModel<RepositoryModel>( repositoryModel); - Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) { + Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", rModel) { private static final long serialVersionUID = 1L; @@ -366,8 +375,9 @@ form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames()) .setEnabled(GitBlitWebSession.get().canAdmin())); form.add(new CheckBox("allowForks")); - form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays - .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer())); + DropDownChoice<AccessRestrictionType> accessRestriction = new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays + .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()); + form.add(accessRestriction); form.add(new CheckBox("isFrozen")); // TODO enable origin definition form.add(new TextField<String>("origin").setEnabled(false/* isCreate */)); @@ -403,12 +413,10 @@ form.add(new TextField<String>("mailingLists", mailingLists)); form.add(indexedBranchesPalette); - RadioGroup<AuthorizationControl> group = new RadioGroup<AuthorizationControl>("authorizationControl"); - Radio<AuthorizationControl> allowAuthenticated = new Radio<AuthorizationControl>("allowAuthenticated", new Model<AuthorizationControl>(AuthorizationControl.AUTHENTICATED)); - Radio<AuthorizationControl> allowNamed = new Radio<AuthorizationControl>("allowNamed", new Model<AuthorizationControl>(AuthorizationControl.NAMED)); - group.add(allowAuthenticated); - group.add(allowNamed); - form.add(group); + List<AuthorizationControl> acList = Arrays.asList(AuthorizationControl.values()); + final RadioChoice<AuthorizationControl> authorizationControl = new RadioChoice<Constants.AuthorizationControl>( + "authorizationControl", acList, new AuthorizationControlRenderer()); + form.add(authorizationControl); form.add(new CheckBox("verifyCommitter")); @@ -425,7 +433,69 @@ WebMarkupContainer customFieldsSection = new WebMarkupContainer("customFieldsSection"); customFieldsSection.add(customFieldsListView); form.add(customFieldsSection.setVisible(!GitBlit.getString(Keys.groovy.customFields, "").isEmpty())); + + // initial enable/disable of permission controls + if (repositoryModel.accessRestriction.equals(AccessRestrictionType.NONE)) { + // anonymous everything, disable all controls + usersPalette.setEnabled(false); + teamsPalette.setEnabled(false); + authorizationControl.setEnabled(false); + } else { + // authenticated something + // enable authorization controls + authorizationControl.setEnabled(true); + + boolean allowFineGrainedControls = repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED); + usersPalette.setEnabled(allowFineGrainedControls); + teamsPalette.setEnabled(allowFineGrainedControls); + } + + accessRestriction.add(new AjaxFormComponentUpdatingBehavior("onchange") { + + private static final long serialVersionUID = 1L; + protected void onUpdate(AjaxRequestTarget target) { + // enable/disable permissions panel based on access restriction + boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE); + authorizationControl.setEnabled(allowAuthorizationControl); + + boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED); + usersPalette.setEnabled(allowFineGrainedControls); + teamsPalette.setEnabled(allowFineGrainedControls); + + if (allowFineGrainedControls) { + repositoryModel.authorizationControl = AuthorizationControl.NAMED; + } + + target.addComponent(authorizationControl); + target.addComponent(usersPalette); + target.addComponent(teamsPalette); + } + }); + + authorizationControl.add(new AjaxFormChoiceComponentUpdatingBehavior() { + + private static final long serialVersionUID = 1L; + + protected void onUpdate(AjaxRequestTarget target) { + // enable/disable permissions panel based on access restriction + boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE); + authorizationControl.setEnabled(allowAuthorizationControl); + + boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED); + usersPalette.setEnabled(allowFineGrainedControls); + teamsPalette.setEnabled(allowFineGrainedControls); + + if (allowFineGrainedControls) { + repositoryModel.authorizationControl = AuthorizationControl.NAMED; + } + + target.addComponent(authorizationControl); + target.addComponent(usersPalette); + target.addComponent(teamsPalette); + } + }); + form.add(new Button("save")); Button cancel = new Button("cancel") { private static final long serialVersionUID = 1L; @@ -528,4 +598,25 @@ return Integer.toString(index); } } + + private class AuthorizationControlRenderer implements IChoiceRenderer<AuthorizationControl> { + + private static final long serialVersionUID = 1L; + + private final Map<AuthorizationControl, String> map; + + public AuthorizationControlRenderer() { + map = getAuthorizationControls(); + } + + @Override + public String getDisplayValue(AuthorizationControl type) { + return map.get(type); + } + + @Override + public String getIdValue(AuthorizationControl type, int index) { + return Integer.toString(index); + } + } } diff --git a/src/com/gitblit/wicket/pages/EditTeamPage.java b/src/com/gitblit/wicket/pages/EditTeamPage.java index 8908676..8ced03c 100644 --- a/src/com/gitblit/wicket/pages/EditTeamPage.java +++ b/src/com/gitblit/wicket/pages/EditTeamPage.java @@ -38,6 +38,7 @@ import com.gitblit.GitBlit; import com.gitblit.GitBlitException; +import com.gitblit.Constants.RegistrantType; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.TeamModel; import com.gitblit.utils.StringUtils; @@ -60,6 +61,7 @@ isCreate = true; setupPage(new TeamModel("")); setStatelessHint(false); + setOutputMarkupId(true); } public EditTeamPage(PageParameters params) { @@ -70,6 +72,7 @@ TeamModel model = GitBlit.self().getTeamModel(name); setupPage(model); setStatelessHint(false); + setOutputMarkupId(true); } protected void setupPage(final TeamModel teamModel) { @@ -81,7 +84,7 @@ CompoundPropertyModel<TeamModel> model = new CompoundPropertyModel<TeamModel>(teamModel); - List<String> repos = getAccessRestrictedRepositoryList(true); + List<String> repos = getAccessRestrictedRepositoryList(true, null); List<String> teamUsers = new ArrayList<String>(teamModel.users); Collections.sort(teamUsers); @@ -215,7 +218,8 @@ : StringUtils.flattenStrings(teamModel.mailingLists, " ")); form.add(new TextField<String>("mailingLists", mailingLists)); - form.add(new RegistrantPermissionsPanel("repositories", repos, permissions, getAccessPermissions())); + form.add(new RegistrantPermissionsPanel("repositories", RegistrantType.REPOSITORY, + repos, permissions, getAccessPermissions())); form.add(preReceivePalette); form.add(new BulletListPanel("inheritedPreReceive", "inherited", GitBlit.self() .getPreReceiveScriptsInherited(null))); diff --git a/src/com/gitblit/wicket/pages/EditUserPage.java b/src/com/gitblit/wicket/pages/EditUserPage.java index a165305..19d297b 100644 --- a/src/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/com/gitblit/wicket/pages/EditUserPage.java @@ -37,6 +37,8 @@ import com.gitblit.GitBlit; import com.gitblit.GitBlitException; import com.gitblit.Keys; +import com.gitblit.Constants.PermissionType; +import com.gitblit.Constants.RegistrantType; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; @@ -61,6 +63,7 @@ isCreate = true; setupPage(new UserModel("")); setStatelessHint(false); + setOutputMarkupId(true); } public EditUserPage(PageParameters params) { @@ -71,6 +74,7 @@ UserModel model = GitBlit.self().getUserModel(name); setupPage(model); setStatelessHint(false); + setOutputMarkupId(true); } protected void setupPage(final UserModel userModel) { @@ -85,7 +89,7 @@ CompoundPropertyModel<UserModel> model = new CompoundPropertyModel<UserModel>(userModel); // build list of projects including all repositories wildcards - List<String> repos = getAccessRestrictedRepositoryList(true); + List<String> repos = getAccessRestrictedRepositoryList(true, userModel); List<String> userTeams = new ArrayList<String>(); for (TeamModel team : userModel.teams) { @@ -95,6 +99,18 @@ final String oldName = userModel.username; final List<RegistrantAccessPermission> permissions = userModel.getRepositoryPermissions(); + for (RegistrantAccessPermission permission : permissions) { + if (permission.isEditable && 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. + boolean isOwner = GitBlit.self().getRepositoryModel(permission.registrant).isOwner(oldName); + if (isOwner) { + permission.permissionType = PermissionType.OWNER; + permission.isEditable = false; + } + } + } final Palette<String> teams = new Palette<String>("teams", new ListModel<String>( new ArrayList<String>(userTeams)), new CollectionModel<String>(GitBlit.self() @@ -228,7 +244,7 @@ form.add(new CheckBox("canFork")); form.add(new CheckBox("canCreate")); form.add(new CheckBox("excludeFromFederation")); - form.add(new RegistrantPermissionsPanel("repositories", repos, permissions, getAccessPermissions())); + form.add(new RegistrantPermissionsPanel("repositories", RegistrantType.REPOSITORY, repos, permissions, getAccessPermissions())); form.add(teams.setEnabled(editTeams)); form.add(new Button("save")); diff --git a/src/com/gitblit/wicket/pages/RootSubPage.java b/src/com/gitblit/wicket/pages/RootSubPage.java index 30d296e..891c892 100644 --- a/src/com/gitblit/wicket/pages/RootSubPage.java +++ b/src/com/gitblit/wicket/pages/RootSubPage.java @@ -21,9 +21,11 @@ import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; -import com.gitblit.GitBlit; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.GitBlit; import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; /** @@ -52,7 +54,7 @@ super.setupPage("", pageName); } - protected List<String> getAccessRestrictedRepositoryList(boolean includeWildcards) { + protected List<String> getAccessRestrictedRepositoryList(boolean includeWildcards, UserModel user) { // build list of access-restricted projects String lastProject = null; List<String> repos = new ArrayList<String>(); @@ -62,19 +64,26 @@ // all repositories excluding personal repositories repos.add("[^~].*"); } + for (String repo : GitBlit.self().getRepositoryList()) { RepositoryModel repositoryModel = GitBlit.self().getRepositoryModel(repo); - if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) { + if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE) + && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED)) { + if (user != null && + (repositoryModel.isOwner(user.username) || repositoryModel.isUsersPersonalRepository(user.username))) { + // exclude Owner or personal repositories + continue; + } if (includeWildcards) { if (lastProject == null || !lastProject.equalsIgnoreCase(repositoryModel.projectPath)) { - lastProject = repositoryModel.projectPath; + lastProject = repositoryModel.projectPath.toLowerCase(); if (!StringUtils.isEmpty(repositoryModel.projectPath)) { // regex for all repositories within a project repos.add(repositoryModel.projectPath + "/.*"); } } } - repos.add(repo); + repos.add(repo.toLowerCase()); } } return repos; diff --git a/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html b/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html index 4c8c4ef..31f0b6b 100644 --- a/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html +++ b/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html @@ -9,7 +9,7 @@ <div wicket:id="permissionRow"> <div style="padding-top:10px;border-left:1px solid #ccc;border-right:1px solid #ccc;" class="row-fluid"> - <div style="padding-top:5px;padding-left:5px;" class="span6"><span wicket:id="registrant"></span></div><div style="padding-top:5px;padding-right:5px;text-align:right;" class="span2"><span class="label label-info" wicket:id="regex">[regex]</span></div> <select class="input-medium" wicket:id="permission"></select> + <div style="padding-top:5px;padding-left:5px" class="span6"><span wicket:id="registrant"></span></div><div style="padding-top:5px;padding-right:5px;text-align:right;" class="span2"><span class="label" wicket:id="pType">[permission type]</span></div> <select class="input-medium" wicket:id="permission"></select> </div> </div> diff --git a/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java b/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java index b6ed890..27e48fb 100644 --- a/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java +++ b/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java @@ -36,6 +36,7 @@ import org.apache.wicket.model.IModel; import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.utils.DeepCopier; @@ -52,8 +53,9 @@ private static final long serialVersionUID = 1L; - public RegistrantPermissionsPanel(String wicketId, List<String> allRegistrants, final List<RegistrantAccessPermission> permissions, final Map<AccessPermission, String> translations) { + public RegistrantPermissionsPanel(String wicketId, RegistrantType registrantType, List<String> allRegistrants, final List<RegistrantAccessPermission> permissions, final Map<AccessPermission, String> translations) { super(wicketId); + setOutputMarkupId(true); // update existing permissions repeater RefreshingView<RegistrantAccessPermission> dataView = new RefreshingView<RegistrantAccessPermission>("permissionRow") { @@ -80,22 +82,40 @@ public void populateItem(final Item<RegistrantAccessPermission> item) { final RegistrantAccessPermission entry = item.getModelObject(); - if (RegistrantType.REPOSITORY.equals(entry.type)) { - // repository, strip .git and show swatch + if (RegistrantType.REPOSITORY.equals(entry.registrantType)) { String repoName = StringUtils.stripDotGit(entry.registrant); - Label registrant = new Label("registrant", repoName); - WicketUtils.setCssClass(registrant, "repositorySwatch"); - WicketUtils.setCssBackground(registrant, repoName); - item.add(registrant); + if (StringUtils.findInvalidCharacter(repoName) == null) { + // repository, strip .git and show swatch + Label registrant = new Label("registrant", repoName); + WicketUtils.setCssClass(registrant, "repositorySwatch"); + WicketUtils.setCssBackground(registrant, repoName); + item.add(registrant); + } else { + // likely a regex + Label label = new Label("registrant", entry.registrant); + WicketUtils.setCssStyle(label, "font-weight: bold;"); + item.add(label); + } } else { - item.add(new Label("registrant", entry.registrant)); + // user or team + Label label = new Label("registrant", entry.registrant); + WicketUtils.setCssStyle(label, "font-weight: bold;"); + item.add(label); } - if (entry.isExplicit) { - item.add(new Label("regex", "").setVisible(false)); - } else { - Label regex = new Label("regex", "regex"); + switch (entry.permissionType) { + case OWNER: + Label owner = new Label("pType", "owner"); + WicketUtils.setHtmlTooltip(owner, getString("gb.ownerPermission")); + item.add(owner); + break; + case REGEX: + Label regex = new Label("pType", "regex"); WicketUtils.setHtmlTooltip(regex, getString("gb.regexPermission")); item.add(regex); + break; + default: + item.add(new Label("pType", "").setVisible(false)); + break; } // use ajax to get immediate update of permission level change @@ -106,8 +126,9 @@ // only allow changing an explicitly defined permission // this is designed to prevent changing a regex permission in // a repository - permissionChoice.setEnabled(entry.isExplicit); - if (entry.isExplicit) { + permissionChoice.setEnabled(entry.isEditable); + permissionChoice.setOutputMarkupId(true); + if (entry.isEditable) { permissionChoice.add(new AjaxFormComponentUpdatingBehavior("onchange") { private static final long serialVersionUID = 1L; @@ -127,11 +148,15 @@ // filter out registrants we already have permissions for final List<String> registrants = new ArrayList<String>(allRegistrants); for (RegistrantAccessPermission rp : permissions) { - registrants.remove(rp.registrant); + if (rp.isEditable) { + // only remove editable duplicates + // this allows for specifying an explicit permission + registrants.remove(rp.registrant); + } } // add new permission form - IModel<RegistrantAccessPermission> addPermissionModel = new CompoundPropertyModel<RegistrantAccessPermission>(new RegistrantAccessPermission()); + IModel<RegistrantAccessPermission> addPermissionModel = new CompoundPropertyModel<RegistrantAccessPermission>(new RegistrantAccessPermission(registrantType)); Form<RegistrantAccessPermission> addPermissionForm = new Form<RegistrantAccessPermission>("addPermissionForm", addPermissionModel); addPermissionForm.add(new DropDownChoice<String>("registrant", registrants)); addPermissionForm.add(new DropDownChoice<AccessPermission>("permission", Arrays @@ -144,7 +169,11 @@ protected void onSubmit(AjaxRequestTarget target, Form<?> form) { // add permission to our list RegistrantAccessPermission rp = (RegistrantAccessPermission) form.getModel().getObject(); - permissions.add(DeepCopier.copy(rp)); + RegistrantAccessPermission copy = DeepCopier.copy(rp); + if (StringUtils.findInvalidCharacter(copy.registrant) != null) { + copy.permissionType = PermissionType.REGEX; + } + permissions.add(copy); // remove registrant from available choices registrants.remove(rp.registrant); @@ -159,6 +188,12 @@ add(addPermissionForm.setVisible(registrants.size() > 0)); } + protected boolean getStatelessHint() + { + return false; + } + + private class AccessPermissionRenderer implements IChoiceRenderer<AccessPermission> { private static final long serialVersionUID = 1L; diff --git a/tests/com/gitblit/tests/RpcTests.java b/tests/com/gitblit/tests/RpcTests.java index 0be2e02..3df0ff8 100644 --- a/tests/com/gitblit/tests/RpcTests.java +++ b/tests/com/gitblit/tests/RpcTests.java @@ -35,6 +35,7 @@ import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.GitBlitException.UnauthorizedException; import com.gitblit.Keys; @@ -199,7 +200,7 @@ List<RegistrantAccessPermission> permissions = RpcUtils.getRepositoryMemberPermissions(retrievedRepository, url, account, password.toCharArray()); assertEquals("Membership permissions is not empty!", 0, permissions.size()); - permissions.add(new RegistrantAccessPermission(testMember.username, AccessPermission.PUSH, true, RegistrantType.USER)); + permissions.add(new RegistrantAccessPermission(testMember.username, AccessPermission.PUSH, PermissionType.EXPLICIT, RegistrantType.USER, true)); assertTrue( "Failed to set member permissions!", RpcUtils.setRepositoryMemberPermissions(retrievedRepository, permissions, url, account, @@ -288,7 +289,7 @@ // set no teams List<RegistrantAccessPermission> permissions = new ArrayList<RegistrantAccessPermission>(); for (String team : helloworldTeams) { - permissions.add(new RegistrantAccessPermission(team, AccessPermission.NONE, true, RegistrantType.TEAM)); + permissions.add(new RegistrantAccessPermission(team, AccessPermission.NONE, PermissionType.EXPLICIT, RegistrantType.TEAM, true)); } assertTrue(RpcUtils.setRepositoryTeamPermissions(helloworld, permissions, url, account, password.toCharArray())); -- Gitblit v1.9.1