# SINCE 1.0.0\r
git.defaultAccessRestriction = NONE\r
\r
+# The default authorization control for new repositories.\r
+# Valid values are AUTHENTICATED and NAMED\r
+# AUTHENTICATED = any authenticated user is granted restricted access\r
+# NAMED = only named users/teams are granted restricted access\r
+#\r
+# SINCE 1.0.1\r
+git.defaultAuthorizationControl = NAMED\r
+\r
# Number of bytes of a pack file to load into memory in a single read operation.\r
# This is the "page size" of the JGit buffer cache, used for all pack access\r
# operations. All disk IO occurs as single window reads. Setting this too large\r
- Fixed Lucene charset encoding bug when reindexing a repository (issue 112)\r
- Fixed null pointer in LdapUserSerivce if account has a null email address (issue 110)\r
\r
-#### changes\r
+#### additions\r
\r
+- Added a repository setting to control authorization as AUTHENTICATED or NAMED. \r
+NAMED is the original behavior for authorizing against a list of permitted users or permitted teams.\r
+AUTHENTICATED allows restricted access for any authenticated user.\r
+- Added default authorization control setting (AUTHENTICATED or NAMED)\r
+ **New:** *git.defaultAuthorizationControl=NAMED* \r
- Added setting to control how deep Gitblit will recurse into *git.repositoriesFolder* looking for repositories (issue 103)\r
**New:** *git.searchRecursionDepth=-1* \r
- Added setting to specify regex exclusions for repositories (issue 103)\r
**New:** *git.searchExclusions=* \r
- Blob page now supports displaying images (issue 6)\r
- Non-image binary files can now be downloaded using the RAW link\r
+\r
+#### changes\r
+\r
- Updated Polish translation\r
\r
**1.0.0** *released 2012-07-14*\r
public AuthenticatedRequest(HttpServletRequest req) {\r
super(req);\r
user = new UserModel("anonymous");\r
+ user.isAuthenticated = false;\r
}\r
\r
UserModel getUser() {\r
return name();\r
}\r
}\r
+ \r
+ /**\r
+ * Enumeration representing the types of authorization control for an\r
+ * access restricted resource.\r
+ */\r
+ public static enum AuthorizationControl {\r
+ AUTHENTICATED, NAMED;\r
+ \r
+ public static AuthorizationControl fromName(String name) {\r
+ for (AuthorizationControl type : values()) {\r
+ if (type.name().equalsIgnoreCase(name)) {\r
+ return type;\r
+ }\r
+ }\r
+ return NAMED;\r
+ }\r
+ \r
+ public String toString() {\r
+ return name();\r
+ }\r
+ }\r
+\r
\r
/**\r
* Enumeration representing the types of federation tokens.\r
import org.slf4j.LoggerFactory;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.Constants.FederationRequest;\r
import com.gitblit.Constants.FederationStrategy;\r
import com.gitblit.Constants.FederationToken;\r
model.useDocs = getConfig(config, "useDocs", false);\r
model.accessRestriction = AccessRestrictionType.fromName(getConfig(config,\r
"accessRestriction", settings.getString(Keys.git.defaultAccessRestriction, null)));\r
+ model.authorizationControl = AuthorizationControl.fromName(getConfig(config,\r
+ "authorizationControl", settings.getString(Keys.git.defaultAuthorizationControl, null)));\r
model.showRemoteBranches = getConfig(config, "showRemoteBranches", false);\r
model.isFrozen = getConfig(config, "isFrozen", false);\r
model.showReadme = getConfig(config, "showReadme", false);\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "useTickets", repository.useTickets);\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "useDocs", repository.useDocs);\r
config.setString(Constants.CONFIG_GITBLIT, null, "accessRestriction", repository.accessRestriction.name());\r
+ config.setString(Constants.CONFIG_GITBLIT, null, "authorizationControl", repository.authorizationControl.name());\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "showRemoteBranches", repository.showRemoteBranches);\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "isFrozen", repository.isFrozen);\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "showReadme", repository.showReadme);\r
if (user == null) {\r
// anonymous push, create a temporary usermodel\r
user = new UserModel(person.getName());\r
+ user.isAuthenticated = false;\r
}\r
return user;\r
}\r
import java.util.Set;\r
\r
import javax.swing.BoxLayout;\r
+import javax.swing.ButtonGroup;\r
import javax.swing.DefaultComboBoxModel;\r
import javax.swing.ImageIcon;\r
import javax.swing.JButton;\r
import javax.swing.JList;\r
import javax.swing.JOptionPane;\r
import javax.swing.JPanel;\r
+import javax.swing.JRadioButton;\r
import javax.swing.JRootPane;\r
import javax.swing.JScrollPane;\r
import javax.swing.JTabbedPane;\r
import javax.swing.ScrollPaneConstants;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.Constants.FederationStrategy;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.utils.ArrayUtils;\r
private JTextField mailingListsField;\r
\r
private JComboBox accessRestriction;\r
+ \r
+ private JRadioButton allowAuthenticated;\r
+ \r
+ private JRadioButton allowNamed;\r
\r
private JComboBox federationStrategy;\r
\r
accessRestriction = new JComboBox(AccessRestrictionType.values());\r
accessRestriction.setRenderer(new AccessRestrictionRenderer());\r
accessRestriction.setSelectedItem(anRepository.accessRestriction);\r
+ \r
+ boolean authenticated = anRepository.authorizationControl != null \r
+ && AuthorizationControl.AUTHENTICATED.equals(anRepository.authorizationControl);\r
+ allowAuthenticated = new JRadioButton(Translation.get("gb.allowAuthenticatedDescription"));\r
+ allowAuthenticated.setSelected(authenticated);\r
+ allowNamed = new JRadioButton(Translation.get("gb.allowNamedDescription"));\r
+ allowNamed.setSelected(!authenticated);\r
+ \r
+ ButtonGroup group = new ButtonGroup();\r
+ group.add(allowAuthenticated);\r
+ group.add(allowNamed);\r
+ \r
+ JPanel authorizationPanel = new JPanel(new GridLayout(0, 1));\r
+ authorizationPanel.add(allowAuthenticated);\r
+ authorizationPanel.add(allowNamed);\r
\r
// federation strategies - remove ORIGIN choice if this repository has\r
// no origin.\r
mailingListsField));\r
\r
usersPalette = new JPalette<String>();\r
+ JPanel northAccessPanel = new JPanel(new BorderLayout(5, 5));\r
+ northAccessPanel.add(newFieldPanel(Translation.get("gb.accessRestriction"),\r
+ accessRestriction), BorderLayout.NORTH);\r
+ northAccessPanel.add(newFieldPanel(Translation.get("gb.authorizationControl"),\r
+ authorizationPanel), BorderLayout.CENTER);\r
+\r
JPanel accessPanel = new JPanel(new BorderLayout(5, 5));\r
- accessPanel.add(\r
- newFieldPanel(Translation.get("gb.accessRestriction"),\r
- accessRestriction), BorderLayout.NORTH);\r
- accessPanel.add(\r
- newFieldPanel(Translation.get("gb.permittedUsers"),\r
+ accessPanel.add(northAccessPanel, BorderLayout.NORTH);\r
+ accessPanel.add(newFieldPanel(Translation.get("gb.permittedUsers"),\r
usersPalette), BorderLayout.CENTER);\r
\r
teamsPalette = new JPalette<String>();\r
\r
repository.accessRestriction = (AccessRestrictionType) accessRestriction\r
.getSelectedItem();\r
+ repository.authorizationControl = allowAuthenticated.isSelected() ? \r
+ AuthorizationControl.AUTHENTICATED : AuthorizationControl.NAMED;\r
repository.federationStrategy = (FederationStrategy) federationStrategy\r
.getSelectedItem();\r
\r
this.accessRestriction.setSelectedItem(restriction);\r
}\r
\r
+ public void setAuthorizationControl(AuthorizationControl authorization) {\r
+ boolean authenticated = authorization != null && AuthorizationControl.AUTHENTICATED.equals(authorization);\r
+ this.allowAuthenticated.setSelected(authenticated);\r
+ this.allowNamed.setSelected(!authenticated);\r
+ }\r
+\r
public void setUsers(String owner, List<String> all, List<String> selected) {\r
ownerField.setModel(new DefaultComboBoxModel(all.toArray()));\r
if (!StringUtils.isEmpty(owner)) {\r
\r
import com.gitblit.Constants;\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.GitBlitException.ForbiddenException;\r
import com.gitblit.GitBlitException.NotAllowedException;\r
import com.gitblit.GitBlitException.UnauthorizedException;\r
return AccessRestrictionType.fromName(restriction);\r
}\r
\r
+ public AuthorizationControl getDefaultAuthorizationControl() {\r
+ String authorization = null;\r
+ if (settings.hasKey(Keys.git.defaultAuthorizationControl)) {\r
+ authorization = settings.get(Keys.git.defaultAuthorizationControl).currentValue;\r
+ }\r
+ return AuthorizationControl.fromName(authorization);\r
+ }\r
+\r
/**\r
* Returns the list of pre-receive scripts the repository inherited from the\r
* global settings and team affiliations.\r
EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion());\r
dialog.setLocationRelativeTo(RepositoriesPanel.this);\r
dialog.setAccessRestriction(gitblit.getDefaultAccessRestriction());\r
+ dialog.setAuthorizationControl(gitblit.getDefaultAuthorizationControl());\r
dialog.setUsers(null, gitblit.getUsernames(), null);\r
dialog.setTeams(gitblit.getTeamnames(), null);\r
dialog.setRepositories(gitblit.getRepositories());\r
import java.util.Map;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.Constants.FederationStrategy;\r
import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.StringUtils;\r
public boolean useTickets;\r
public boolean useDocs;\r
public AccessRestrictionType accessRestriction;\r
+ public AuthorizationControl authorizationControl;\r
+ public boolean allowAuthenticated;\r
public boolean isFrozen;\r
public boolean showReadme;\r
public FederationStrategy federationStrategy;\r
this.owner = owner;\r
this.lastChange = lastchange;\r
this.accessRestriction = AccessRestrictionType.NONE;\r
+ this.authorizationControl = AuthorizationControl.NAMED;\r
this.federationSets = new ArrayList<String>();\r
this.federationStrategy = FederationStrategy.FEDERATE_THIS; \r
}\r
import java.util.HashSet;\r
import java.util.Set;\r
\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.utils.StringUtils;\r
\r
/**\r
public final Set<String> repositories = new HashSet<String>();\r
public final Set<TeamModel> teams = new HashSet<TeamModel>();\r
\r
+ // non-persisted fields\r
+ public boolean isAuthenticated;\r
+ \r
public UserModel(String username) {\r
this.username = username;\r
+ this.isAuthenticated = true;\r
}\r
\r
/**\r
public boolean canAccessRepository(RepositoryModel repository) {\r
boolean isOwner = !StringUtils.isEmpty(repository.owner)\r
&& repository.owner.equals(username);\r
+ boolean allowAuthenticated = isAuthenticated && AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl);\r
return canAdmin || isOwner || repositories.contains(repository.name.toLowerCase())\r
- || hasTeamAccess(repository.name);\r
+ || hasTeamAccess(repository.name) || allowAuthenticated;\r
}\r
\r
public boolean hasTeamAccess(String repositoryName) {\r
gb.duration.months = {0} months\r
gb.duration.oneYear = 1 year\r
gb.duration.years = {0} years\r
+gb.authorizationControl = authorization control\r
+gb.allowAuthenticatedDescription = grant restricted access to all authenticated users\r
+gb.allowNamedDescription = grant restricted access to named users or teams
\ No newline at end of file
<tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="12" /> <span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr>\r
<tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="13" /></td></tr>\r
<tr><td colspan="2" style="padding-top:15px"><h3><wicket:message key="gb.accessPermissions"></wicket:message> <small><wicket:message key="gb.accessPermissionsDescription"></wicket:message></small></h3></td></tr> \r
- <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="14" /></td></tr> \r
+ <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="14" /></td></tr>\r
+ <tr><th colspan="2"><hr/></th></tr>\r
+ <tr><th style="vertical-align: top;"><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;">\r
+ <wicket:container wicket:id="authorizationControl">\r
+ <label class="radio"><input type="radio" wicket:id="allowAuthenticated" tabindex="15" /> <span class="help-inline"><wicket:message key="gb.allowAuthenticatedDescription"></wicket:message></span></label>\r
+ <label class="radio"><input type="radio" wicket:id="allowNamed" tabindex="16" /> <span class="help-inline"><wicket:message key="gb.allowNamedDescription"></wicket:message></span></label>\r
+ </wicket:container>\r
+ </td></tr>\r
+ <tr><th colspan="2"><hr/></th></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.permittedTeams"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
<tr><td colspan="2"><h3><wicket:message key="gb.federation"></wicket:message> <small><wicket:message key="gb.federationRepositoryDescription"></wicket:message></small></h3></td></tr> \r
- <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="15" /></td></tr>\r
+ <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="17" /></td></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>\r
<tr><td colspan="2"><h3><wicket:message key="gb.search"></wicket:message> <small><wicket:message key="gb.indexedBranchesDescription"></wicket:message></small></h3></td></tr> \r
<tr><th style="vertical-align: top;"><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr>\r
import org.apache.wicket.markup.html.form.DropDownChoice;\r
import org.apache.wicket.markup.html.form.Form;\r
import org.apache.wicket.markup.html.form.IChoiceRenderer;\r
+import org.apache.wicket.markup.html.form.Radio;\r
+import org.apache.wicket.markup.html.form.RadioGroup;\r
import org.apache.wicket.markup.html.form.TextField;\r
import org.apache.wicket.markup.html.list.ListItem;\r
import org.apache.wicket.markup.html.list.ListView;\r
\r
import com.gitblit.Constants;\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.Constants.FederationStrategy;\r
import com.gitblit.GitBlit;\r
import com.gitblit.GitBlitException;\r
RepositoryModel model = new RepositoryModel();\r
String restriction = GitBlit.getString(Keys.git.defaultAccessRestriction, null);\r
model.accessRestriction = AccessRestrictionType.fromName(restriction);\r
+ String authorization = GitBlit.getString(Keys.git.defaultAuthorizationControl, null);\r
+ model.authorizationControl = AuthorizationControl.fromName(authorization);\r
setupPage(model);\r
}\r
\r
: StringUtils.flattenStrings(repositoryModel.mailingLists, " "));\r
form.add(new TextField<String>("mailingLists", mailingLists));\r
form.add(indexedBranchesPalette);\r
+ \r
+ RadioGroup<AuthorizationControl> group = new RadioGroup<AuthorizationControl>("authorizationControl");\r
+ Radio<AuthorizationControl> allowAuthenticated = new Radio<AuthorizationControl>("allowAuthenticated", new Model<AuthorizationControl>(AuthorizationControl.AUTHENTICATED)); \r
+ Radio<AuthorizationControl> allowNamed = new Radio<AuthorizationControl>("allowNamed", new Model<AuthorizationControl>(AuthorizationControl.NAMED));\r
+ group.add(allowAuthenticated);\r
+ group.add(allowNamed);\r
+ form.add(group);\r
+ \r
form.add(usersPalette);\r
form.add(teamsPalette);\r
form.add(federationSetsPalette);\r
import org.junit.Test;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.GitBlit;\r
import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
\r
public class GitServletTest {\r
\r
\r
assertFalse("Bogus login cloned a repository?!", cloned);\r
}\r
+ \r
+ @Test\r
+ public void testUnauthorizedLoginClone() throws Exception {\r
+ // restrict repository access\r
+ RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");\r
+ model.accessRestriction = AccessRestrictionType.CLONE;\r
+ model.authorizationControl = AuthorizationControl.NAMED;\r
+ UserModel user = new UserModel("james");\r
+ user.password = "james";\r
+ GitBlit.self().updateUserModel(user.username, user, true);\r
+ GitBlit.self().updateRepositoryModel(model.name, model, false);\r
+\r
+ FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);\r
+ \r
+ // delete any existing working folder \r
+ boolean cloned = false;\r
+ try {\r
+ CloneCommand clone = Git.cloneRepository();\r
+ clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url));\r
+ clone.setDirectory(ticgit2Folder);\r
+ clone.setBare(false);\r
+ clone.setCloneAllBranches(true);\r
+ clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password));\r
+ close(clone.call());\r
+ cloned = true;\r
+ } catch (Exception e) {\r
+ // swallow the exception which we expect\r
+ }\r
+\r
+ assertFalse("Unauthorized login cloned a repository?!", cloned);\r
+\r
+ FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);\r
+ \r
+ // switch to authenticated\r
+ model.authorizationControl = AuthorizationControl.AUTHENTICATED;\r
+ GitBlit.self().updateRepositoryModel(model.name, model, false);\r
+ \r
+ // try clone again\r
+ cloned = false;\r
+ CloneCommand clone = Git.cloneRepository();\r
+ clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url));\r
+ clone.setDirectory(ticgit2Folder);\r
+ clone.setBare(false);\r
+ clone.setCloneAllBranches(true);\r
+ clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password));\r
+ close(clone.call());\r
+ cloned = true;\r
+\r
+ assertTrue("Authenticated login could not clone!", cloned);\r
+ \r
+ FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);\r
+ \r
+ // restore anonymous repository access\r
+ model.accessRestriction = AccessRestrictionType.NONE;\r
+ model.authorizationControl = AuthorizationControl.NAMED;\r
+ GitBlit.self().updateRepositoryModel(model.name, model, false);\r
+ GitBlit.self().deleteUser(user.username);\r
+ }\r
\r
@Test\r
public void testAnonymousPush() throws Exception {\r
import org.junit.Test;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.GitBlitException.UnauthorizedException;\r
import com.gitblit.Keys;\r
import com.gitblit.RpcServlet;\r
model.description = "created by RpcUtils";\r
model.owner = "garbage";\r
model.accessRestriction = AccessRestrictionType.VIEW;\r
+ model.authorizationControl = AuthorizationControl.AUTHENTICATED;\r
\r
// create\r
assertTrue("Failed to create repository!",\r
RepositoryModel retrievedRepository = findRepository(model.name);\r
assertNotNull("Failed to find " + model.name, retrievedRepository);\r
assertEquals(AccessRestrictionType.VIEW, retrievedRepository.accessRestriction);\r
+ assertEquals(AuthorizationControl.AUTHENTICATED, retrievedRepository.authorizationControl);\r
\r
// rename and change access restriciton\r
String originalName = model.name;\r