Per-repository authorization control: AUTHENTICATED and NAMED (issue-117)
| | |
| | | # 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
|
| | |
| | | - 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*
|
| | |
| | | public AuthenticatedRequest(HttpServletRequest req) {
|
| | | super(req);
|
| | | user = new UserModel("anonymous");
|
| | | user.isAuthenticated = false;
|
| | | }
|
| | |
|
| | | UserModel getUser() {
|
| | |
| | | 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.
|
| | |
| | | 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;
|
| | |
| | | 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);
|
| | |
| | | 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);
|
| | |
| | | if (user == null) {
|
| | | // anonymous push, create a temporary usermodel
|
| | | user = new UserModel(person.getName());
|
| | | user.isAuthenticated = false;
|
| | | }
|
| | | return user;
|
| | | }
|
| | |
| | | import java.util.Set;
|
| | |
|
| | | import javax.swing.BoxLayout;
|
| | | import javax.swing.ButtonGroup;
|
| | | import javax.swing.DefaultComboBoxModel;
|
| | | import javax.swing.ImageIcon;
|
| | | import javax.swing.JButton;
|
| | |
| | | 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;
|
| | |
| | | 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;
|
| | |
| | | private JTextField mailingListsField;
|
| | |
|
| | | private JComboBox accessRestriction;
|
| | | |
| | | private JRadioButton allowAuthenticated;
|
| | | |
| | | private JRadioButton allowNamed;
|
| | |
|
| | | private JComboBox federationStrategy;
|
| | |
|
| | |
| | | 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.
|
| | | List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
|
| | |
| | | mailingListsField));
|
| | |
|
| | | usersPalette = new JPalette<String>();
|
| | | JPanel accessPanel = new JPanel(new BorderLayout(5, 5));
|
| | | accessPanel.add(
|
| | | newFieldPanel(Translation.get("gb.accessRestriction"),
|
| | | JPanel northAccessPanel = new JPanel(new BorderLayout(5, 5));
|
| | | northAccessPanel.add(newFieldPanel(Translation.get("gb.accessRestriction"),
|
| | | accessRestriction), BorderLayout.NORTH);
|
| | | accessPanel.add(
|
| | | newFieldPanel(Translation.get("gb.permittedUsers"),
|
| | | northAccessPanel.add(newFieldPanel(Translation.get("gb.authorizationControl"),
|
| | | authorizationPanel), BorderLayout.CENTER);
|
| | |
|
| | | JPanel accessPanel = new JPanel(new BorderLayout(5, 5));
|
| | | accessPanel.add(northAccessPanel, BorderLayout.NORTH);
|
| | | accessPanel.add(newFieldPanel(Translation.get("gb.permittedUsers"),
|
| | | usersPalette), BorderLayout.CENTER);
|
| | |
|
| | | teamsPalette = new JPalette<String>();
|
| | |
| | |
|
| | | repository.accessRestriction = (AccessRestrictionType) accessRestriction
|
| | | .getSelectedItem();
|
| | | repository.authorizationControl = allowAuthenticated.isSelected() ? |
| | | AuthorizationControl.AUTHENTICATED : AuthorizationControl.NAMED;
|
| | | repository.federationStrategy = (FederationStrategy) federationStrategy
|
| | | .getSelectedItem();
|
| | |
|
| | |
| | | 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<String> all, List<String> selected) {
|
| | | ownerField.setModel(new DefaultComboBoxModel(all.toArray()));
|
| | | if (!StringUtils.isEmpty(owner)) {
|
| | |
| | |
|
| | | 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;
|
| | |
| | | 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.
|
| | |
| | | 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());
|
| | |
| | | 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;
|
| | |
| | | public boolean useTickets;
|
| | | public boolean useDocs;
|
| | | public AccessRestrictionType accessRestriction;
|
| | | public AuthorizationControl authorizationControl;
|
| | | public boolean allowAuthenticated;
|
| | | public boolean isFrozen;
|
| | | public boolean showReadme;
|
| | | public FederationStrategy federationStrategy;
|
| | |
| | | this.owner = owner;
|
| | | this.lastChange = lastchange;
|
| | | this.accessRestriction = AccessRestrictionType.NONE;
|
| | | this.authorizationControl = AuthorizationControl.NAMED;
|
| | | this.federationSets = new ArrayList<String>();
|
| | | this.federationStrategy = FederationStrategy.FEDERATE_THIS;
|
| | | }
|
| | |
| | | import java.util.HashSet;
|
| | | import java.util.Set;
|
| | |
|
| | | import com.gitblit.Constants.AuthorizationControl;
|
| | | import com.gitblit.utils.StringUtils;
|
| | |
|
| | | /**
|
| | |
| | | public final Set<String> repositories = new HashSet<String>();
|
| | | public final Set<TeamModel> teams = new HashSet<TeamModel>();
|
| | |
|
| | | // non-persisted fields
|
| | | public boolean isAuthenticated;
|
| | | |
| | | public UserModel(String username) {
|
| | | this.username = username;
|
| | | this.isAuthenticated = true;
|
| | | }
|
| | |
|
| | | /**
|
| | |
| | | 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) {
|
| | |
| | | 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 |
| | |
| | | <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>
|
| | | <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>
|
| | | <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="14" /></td></tr>
|
| | | <tr><th colspan="2"><hr/></th></tr>
|
| | | <tr><th style="vertical-align: top;"><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="15" /> <span class="help-inline"><wicket:message key="gb.allowAuthenticatedDescription"></wicket:message></span></label>
|
| | | <label class="radio"><input type="radio" wicket:id="allowNamed" tabindex="16" /> <span class="help-inline"><wicket:message key="gb.allowNamedDescription"></wicket:message></span></label>
|
| | | </wicket:container>
|
| | | </td></tr>
|
| | | <tr><th colspan="2"><hr/></th></tr>
|
| | | <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>
|
| | | <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>
|
| | | <tr><td colspan="2"><h3><wicket:message key="gb.federation"></wicket:message> <small><wicket:message key="gb.federationRepositoryDescription"></wicket:message></small></h3></td></tr>
|
| | | <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="15" /></td></tr>
|
| | | <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="17" /></td></tr>
|
| | | <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>
|
| | | <tr><td colspan="2"><h3><wicket:message key="gb.search"></wicket:message> <small><wicket:message key="gb.indexedBranchesDescription"></wicket:message></small></h3></td></tr>
|
| | | <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>
|
| | |
| | | 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;
|
| | |
| | |
|
| | | 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;
|
| | |
| | | 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);
|
| | | }
|
| | |
|
| | |
| | | : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
|
| | | 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);
|
| | | |
| | | form.add(usersPalette);
|
| | | form.add(teamsPalette);
|
| | | form.add(federationSetsPalette);
|
| | |
| | | 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 {
|
| | |
|
| | |
| | | }
|
| | |
|
| | | @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 {
|
| | | Git git = Git.open(ticgitFolder);
|
| | | File file = new File(ticgitFolder, "TODO");
|
| | |
| | | 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;
|
| | |
| | | model.description = "created by RpcUtils";
|
| | | model.owner = "garbage";
|
| | | model.accessRestriction = AccessRestrictionType.VIEW;
|
| | | model.authorizationControl = AuthorizationControl.AUTHENTICATED;
|
| | |
|
| | | // create
|
| | | assertTrue("Failed to create repository!",
|
| | |
| | | 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;
|