From: James Moger Date: Thu, 2 Aug 2012 04:27:02 +0000 (-0400) Subject: Per-repository authorization control: AUTHENTICATED and NAMED (issue 117) X-Git-Tag: v1.1.0~42 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6adf56bb13227afac2c37871b3443fb5354d132c;p=gitblit.git Per-repository authorization control: AUTHENTICATED and NAMED (issue 117) --- diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index 0923c41b..70718b67 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -63,6 +63,14 @@ git.onlyAccessBareRepositories = false # SINCE 1.0.0 git.defaultAccessRestriction = NONE +# The default authorization control for new repositories. +# Valid values are AUTHENTICATED and NAMED +# AUTHENTICATED = any authenticated user is granted restricted access +# NAMED = only named users/teams are granted restricted access +# +# SINCE 1.0.1 +git.defaultAuthorizationControl = NAMED + # Number of bytes of a pack file to load into memory in a single read operation. # This is the "page size" of the JGit buffer cache, used for all pack access # operations. All disk IO occurs as single window reads. Setting this too large diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index e1972109..4e4ee99f 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -16,14 +16,22 @@ If you are updating from an 0.9.x release AND you have indexed branches with the - Fixed Lucene charset encoding bug when reindexing a repository (issue 112) - Fixed null pointer in LdapUserSerivce if account has a null email address (issue 110) -#### changes +#### additions +- Added a repository setting to control authorization as AUTHENTICATED or NAMED. +NAMED is the original behavior for authorizing against a list of permitted users or permitted teams. +AUTHENTICATED allows restricted access for any authenticated user. +- Added default authorization control setting (AUTHENTICATED or NAMED) + **New:** *git.defaultAuthorizationControl=NAMED* - Added setting to control how deep Gitblit will recurse into *git.repositoriesFolder* looking for repositories (issue 103) **New:** *git.searchRecursionDepth=-1* - Added setting to specify regex exclusions for repositories (issue 103) **New:** *git.searchExclusions=* - Blob page now supports displaying images (issue 6) - Non-image binary files can now be downloaded using the RAW link + +#### changes + - Updated Polish translation **1.0.0** *released 2012-07-14* diff --git a/src/com/gitblit/AuthenticationFilter.java b/src/com/gitblit/AuthenticationFilter.java index 50a67a08..259991c9 100644 --- a/src/com/gitblit/AuthenticationFilter.java +++ b/src/com/gitblit/AuthenticationFilter.java @@ -170,6 +170,7 @@ public abstract class AuthenticationFilter implements Filter { public AuthenticatedRequest(HttpServletRequest req) { super(req); user = new UserModel("anonymous"); + user.isAuthenticated = false; } UserModel getUser() { diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index b80c968c..181fb8f0 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -109,6 +109,28 @@ public class Constants { return name(); } } + + /** + * Enumeration representing the types of authorization control for an + * access restricted resource. + */ + public static enum AuthorizationControl { + AUTHENTICATED, NAMED; + + public static AuthorizationControl fromName(String name) { + for (AuthorizationControl type : values()) { + if (type.name().equalsIgnoreCase(name)) { + return type; + } + } + return NAMED; + } + + public String toString() { + return name(); + } + } + /** * Enumeration representing the types of federation tokens. diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 8f51069e..26f30f91 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -69,6 +69,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationStrategy; import com.gitblit.Constants.FederationToken; @@ -876,6 +877,8 @@ public class GitBlit implements ServletContextListener { model.useDocs = getConfig(config, "useDocs", false); model.accessRestriction = AccessRestrictionType.fromName(getConfig(config, "accessRestriction", settings.getString(Keys.git.defaultAccessRestriction, null))); + model.authorizationControl = AuthorizationControl.fromName(getConfig(config, + "authorizationControl", settings.getString(Keys.git.defaultAuthorizationControl, null))); model.showRemoteBranches = getConfig(config, "showRemoteBranches", false); model.isFrozen = getConfig(config, "isFrozen", false); model.showReadme = getConfig(config, "showReadme", false); @@ -1135,6 +1138,7 @@ public class GitBlit implements ServletContextListener { config.setBoolean(Constants.CONFIG_GITBLIT, null, "useTickets", repository.useTickets); config.setBoolean(Constants.CONFIG_GITBLIT, null, "useDocs", repository.useDocs); config.setString(Constants.CONFIG_GITBLIT, null, "accessRestriction", repository.accessRestriction.name()); + config.setString(Constants.CONFIG_GITBLIT, null, "authorizationControl", repository.authorizationControl.name()); config.setBoolean(Constants.CONFIG_GITBLIT, null, "showRemoteBranches", repository.showRemoteBranches); config.setBoolean(Constants.CONFIG_GITBLIT, null, "isFrozen", repository.isFrozen); config.setBoolean(Constants.CONFIG_GITBLIT, null, "showReadme", repository.showReadme); diff --git a/src/com/gitblit/GitServlet.java b/src/com/gitblit/GitServlet.java index 68097cb8..0b5575bc 100644 --- a/src/com/gitblit/GitServlet.java +++ b/src/com/gitblit/GitServlet.java @@ -231,6 +231,7 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { if (user == null) { // anonymous push, create a temporary usermodel user = new UserModel(person.getName()); + user.isAuthenticated = false; } return user; } diff --git a/src/com/gitblit/client/EditRepositoryDialog.java b/src/com/gitblit/client/EditRepositoryDialog.java index 77878cbb..8ce076ec 100644 --- a/src/com/gitblit/client/EditRepositoryDialog.java +++ b/src/com/gitblit/client/EditRepositoryDialog.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -46,6 +47,7 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.JRootPane; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; @@ -55,6 +57,7 @@ import javax.swing.ListCellRenderer; import javax.swing.ScrollPaneConstants; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationStrategy; import com.gitblit.models.RepositoryModel; import com.gitblit.utils.ArrayUtils; @@ -98,6 +101,10 @@ public class EditRepositoryDialog extends JDialog { private JTextField mailingListsField; private JComboBox accessRestriction; + + private JRadioButton allowAuthenticated; + + private JRadioButton allowNamed; private JComboBox federationStrategy; @@ -206,6 +213,21 @@ public class EditRepositoryDialog extends JDialog { accessRestriction = new JComboBox(AccessRestrictionType.values()); accessRestriction.setRenderer(new AccessRestrictionRenderer()); accessRestriction.setSelectedItem(anRepository.accessRestriction); + + boolean authenticated = anRepository.authorizationControl != null + && AuthorizationControl.AUTHENTICATED.equals(anRepository.authorizationControl); + allowAuthenticated = new JRadioButton(Translation.get("gb.allowAuthenticatedDescription")); + allowAuthenticated.setSelected(authenticated); + allowNamed = new JRadioButton(Translation.get("gb.allowNamedDescription")); + allowNamed.setSelected(!authenticated); + + ButtonGroup group = new ButtonGroup(); + group.add(allowAuthenticated); + group.add(allowNamed); + + JPanel authorizationPanel = new JPanel(new GridLayout(0, 1)); + authorizationPanel.add(allowAuthenticated); + authorizationPanel.add(allowNamed); // federation strategies - remove ORIGIN choice if this repository has // no origin. @@ -246,12 +268,15 @@ public class EditRepositoryDialog extends JDialog { mailingListsField)); usersPalette = new JPalette(); + JPanel northAccessPanel = new JPanel(new BorderLayout(5, 5)); + northAccessPanel.add(newFieldPanel(Translation.get("gb.accessRestriction"), + accessRestriction), BorderLayout.NORTH); + northAccessPanel.add(newFieldPanel(Translation.get("gb.authorizationControl"), + authorizationPanel), BorderLayout.CENTER); + JPanel accessPanel = new JPanel(new BorderLayout(5, 5)); - accessPanel.add( - newFieldPanel(Translation.get("gb.accessRestriction"), - accessRestriction), BorderLayout.NORTH); - accessPanel.add( - newFieldPanel(Translation.get("gb.permittedUsers"), + accessPanel.add(northAccessPanel, BorderLayout.NORTH); + accessPanel.add(newFieldPanel(Translation.get("gb.permittedUsers"), usersPalette), BorderLayout.CENTER); teamsPalette = new JPalette(); @@ -463,6 +488,8 @@ public class EditRepositoryDialog extends JDialog { repository.accessRestriction = (AccessRestrictionType) accessRestriction .getSelectedItem(); + repository.authorizationControl = allowAuthenticated.isSelected() ? + AuthorizationControl.AUTHENTICATED : AuthorizationControl.NAMED; repository.federationStrategy = (FederationStrategy) federationStrategy .getSelectedItem(); @@ -495,6 +522,12 @@ public class EditRepositoryDialog extends JDialog { this.accessRestriction.setSelectedItem(restriction); } + public void setAuthorizationControl(AuthorizationControl authorization) { + boolean authenticated = authorization != null && AuthorizationControl.AUTHENTICATED.equals(authorization); + this.allowAuthenticated.setSelected(authenticated); + this.allowNamed.setSelected(!authenticated); + } + public void setUsers(String owner, List all, List selected) { ownerField.setModel(new DefaultComboBoxModel(all.toArray())); if (!StringUtils.isEmpty(owner)) { diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java index ed5a1337..5e05fa49 100644 --- a/src/com/gitblit/client/GitblitClient.java +++ b/src/com/gitblit/client/GitblitClient.java @@ -29,6 +29,7 @@ import java.util.TreeSet; import com.gitblit.Constants; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.GitBlitException.ForbiddenException; import com.gitblit.GitBlitException.NotAllowedException; import com.gitblit.GitBlitException.UnauthorizedException; @@ -195,6 +196,14 @@ public class GitblitClient implements Serializable { return AccessRestrictionType.fromName(restriction); } + public AuthorizationControl getDefaultAuthorizationControl() { + String authorization = null; + if (settings.hasKey(Keys.git.defaultAuthorizationControl)) { + authorization = settings.get(Keys.git.defaultAuthorizationControl).currentValue; + } + return AuthorizationControl.fromName(authorization); + } + /** * Returns the list of pre-receive scripts the repository inherited from the * global settings and team affiliations. diff --git a/src/com/gitblit/client/RepositoriesPanel.java b/src/com/gitblit/client/RepositoriesPanel.java index 70b87c6c..cbe18743 100644 --- a/src/com/gitblit/client/RepositoriesPanel.java +++ b/src/com/gitblit/client/RepositoriesPanel.java @@ -358,6 +358,7 @@ public abstract class RepositoriesPanel extends JPanel { EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion()); dialog.setLocationRelativeTo(RepositoriesPanel.this); dialog.setAccessRestriction(gitblit.getDefaultAccessRestriction()); + dialog.setAuthorizationControl(gitblit.getDefaultAuthorizationControl()); dialog.setUsers(null, gitblit.getUsernames(), null); dialog.setTeams(gitblit.getTeamnames(), null); dialog.setRepositories(gitblit.getRepositories()); diff --git a/src/com/gitblit/models/RepositoryModel.java b/src/com/gitblit/models/RepositoryModel.java index 0e0c2df1..27196635 100644 --- a/src/com/gitblit/models/RepositoryModel.java +++ b/src/com/gitblit/models/RepositoryModel.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationStrategy; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.StringUtils; @@ -47,6 +48,8 @@ public class RepositoryModel implements Serializable, Comparable(); this.federationStrategy = FederationStrategy.FEDERATE_THIS; } diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index 6632c611..8349bab6 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -20,6 +20,7 @@ import java.security.Principal; import java.util.HashSet; import java.util.Set; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.utils.StringUtils; /** @@ -45,8 +46,12 @@ public class UserModel implements Principal, Serializable, Comparable public final Set repositories = new HashSet(); public final Set teams = new HashSet(); + // non-persisted fields + public boolean isAuthenticated; + public UserModel(String username) { this.username = username; + this.isAuthenticated = true; } /** @@ -65,8 +70,9 @@ public class UserModel implements Principal, Serializable, Comparable public boolean canAccessRepository(RepositoryModel repository) { boolean isOwner = !StringUtils.isEmpty(repository.owner) && repository.owner.equals(username); + boolean allowAuthenticated = isAuthenticated && AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl); return canAdmin || isOwner || repositories.contains(repository.name.toLowerCase()) - || hasTeamAccess(repository.name); + || hasTeamAccess(repository.name) || allowAuthenticated; } public boolean hasTeamAccess(String repositoryName) { diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index f8a936de..bcd63370 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -310,3 +310,6 @@ gb.duration.oneMonth = 1 month gb.duration.months = {0} months 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 \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.html b/src/com/gitblit/wicket/pages/EditRepositoryPage.html index a419698d..2bb5776c 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.html +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.html @@ -25,11 +25,19 @@

 

- +
+ + + + + + +

 

-

 

diff --git a/src/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/com/gitblit/wicket/pages/EditRepositoryPage.java index 0176249b..505cb548 100644 --- a/src/com/gitblit/wicket/pages/EditRepositoryPage.java +++ b/src/com/gitblit/wicket/pages/EditRepositoryPage.java @@ -36,6 +36,8 @@ import org.apache.wicket.markup.html.form.CheckBox; 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.TextField; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; @@ -47,6 +49,7 @@ import org.apache.wicket.model.util.ListModel; import com.gitblit.Constants; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.FederationStrategy; import com.gitblit.GitBlit; import com.gitblit.GitBlitException; @@ -75,6 +78,8 @@ public class EditRepositoryPage extends RootSubPage { RepositoryModel model = new RepositoryModel(); String restriction = GitBlit.getString(Keys.git.defaultAccessRestriction, null); model.accessRestriction = AccessRestrictionType.fromName(restriction); + String authorization = GitBlit.getString(Keys.git.defaultAuthorizationControl, null); + model.authorizationControl = AuthorizationControl.fromName(authorization); setupPage(model); } @@ -370,6 +375,14 @@ public class EditRepositoryPage extends RootSubPage { : StringUtils.flattenStrings(repositoryModel.mailingLists, " ")); form.add(new TextField("mailingLists", mailingLists)); form.add(indexedBranchesPalette); + + RadioGroup group = new RadioGroup("authorizationControl"); + Radio allowAuthenticated = new Radio("allowAuthenticated", new Model(AuthorizationControl.AUTHENTICATED)); + Radio allowNamed = new Radio("allowNamed", new Model(AuthorizationControl.NAMED)); + group.add(allowAuthenticated); + group.add(allowNamed); + form.add(group); + form.add(usersPalette); form.add(teamsPalette); form.add(federationSetsPalette); diff --git a/tests/com/gitblit/tests/GitServletTest.java b/tests/com/gitblit/tests/GitServletTest.java index 848a1d05..bdbb2a5a 100644 --- a/tests/com/gitblit/tests/GitServletTest.java +++ b/tests/com/gitblit/tests/GitServletTest.java @@ -21,8 +21,10 @@ import org.junit.BeforeClass; import org.junit.Test; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.GitBlit; import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; public class GitServletTest { @@ -108,6 +110,64 @@ public class GitServletTest { assertFalse("Bogus login cloned a repository?!", cloned); } + + @Test + public void testUnauthorizedLoginClone() throws Exception { + // restrict repository access + RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); + model.accessRestriction = AccessRestrictionType.CLONE; + model.authorizationControl = AuthorizationControl.NAMED; + UserModel user = new UserModel("james"); + user.password = "james"; + GitBlit.self().updateUserModel(user.username, user, true); + GitBlit.self().updateRepositoryModel(model.name, model, false); + + FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); + + // delete any existing working folder + boolean cloned = false; + try { + CloneCommand clone = Git.cloneRepository(); + clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); + clone.setDirectory(ticgit2Folder); + clone.setBare(false); + clone.setCloneAllBranches(true); + clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password)); + close(clone.call()); + cloned = true; + } catch (Exception e) { + // swallow the exception which we expect + } + + assertFalse("Unauthorized login cloned a repository?!", cloned); + + FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); + + // switch to authenticated + model.authorizationControl = AuthorizationControl.AUTHENTICATED; + GitBlit.self().updateRepositoryModel(model.name, model, false); + + // try clone again + cloned = false; + CloneCommand clone = Git.cloneRepository(); + clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); + clone.setDirectory(ticgit2Folder); + clone.setBare(false); + clone.setCloneAllBranches(true); + clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password)); + close(clone.call()); + cloned = true; + + assertTrue("Authenticated login could not clone!", cloned); + + FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); + + // restore anonymous repository access + model.accessRestriction = AccessRestrictionType.NONE; + model.authorizationControl = AuthorizationControl.NAMED; + GitBlit.self().updateRepositoryModel(model.name, model, false); + GitBlit.self().deleteUser(user.username); + } @Test public void testAnonymousPush() throws Exception { diff --git a/tests/com/gitblit/tests/RpcTests.java b/tests/com/gitblit/tests/RpcTests.java index f85dd79a..1080849c 100644 --- a/tests/com/gitblit/tests/RpcTests.java +++ b/tests/com/gitblit/tests/RpcTests.java @@ -33,6 +33,7 @@ import org.junit.BeforeClass; import org.junit.Test; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AuthorizationControl; import com.gitblit.GitBlitException.UnauthorizedException; import com.gitblit.Keys; import com.gitblit.RpcServlet; @@ -164,6 +165,7 @@ public class RpcTests { model.description = "created by RpcUtils"; model.owner = "garbage"; model.accessRestriction = AccessRestrictionType.VIEW; + model.authorizationControl = AuthorizationControl.AUTHENTICATED; // create assertTrue("Failed to create repository!", @@ -172,6 +174,7 @@ public class RpcTests { RepositoryModel retrievedRepository = findRepository(model.name); assertNotNull("Failed to find " + model.name, retrievedRepository); assertEquals(AccessRestrictionType.VIEW, retrievedRepository.accessRestriction); + assertEquals(AuthorizationControl.AUTHENTICATED, retrievedRepository.authorizationControl); // rename and change access restriciton String originalName = model.name;