Browse Source

Allow specifying accepted PUSH transports

tags/v1.5.0
James Moger 10 years ago
parent
commit
e9872c8ca4

+ 3
- 0
releases.moxie View File

@@ -23,10 +23,12 @@ r22: {
- Redirect to summary page on edit repository (issue-405)
- Option to allow LDAP users to directly authenticate without performing LDAP searches (pr-162)
- Replace JCommander with args4j to be consistent with other tools (ticket-28)
- Sort repository urls by descending permissions and by transport security within equal permissions
additions:
- Added an SSH daemon with public key authentication (issue-369, ticket-6)
- Added beginnings of a plugin framework for extending Gitblit (issue-381, ticket-23)
- Added a French translation (pr-163)
- Added a setting to control what transports may be used for pushes
dependencyChanges:
- args4j 2.0.26
- JGit 3.3.1
@@ -41,6 +43,7 @@ r22: {
settings:
- { name: 'realm.ldap.bindpattern', defaultValue: ' ' }
- { name: 'tickets.closeOnPushCommitMessageRegex', defaultValue: '(?:fixes|closes)[\\s-]+#?(\\d+)' }
- { name: 'git.acceptedPushTransports', defaultValue: ' ' }
- { name: 'git.sshPort', defaultValue: '29418' }
- { name: 'git.sshBindInterface', defaultValue: ' ' }
- { name: 'git.sshKeysManager', defaultValue: 'com.gitblit.transport.ssh.FileKeyManager' }

+ 10
- 0
src/main/distrib/data/gitblit.properties View File

@@ -173,6 +173,16 @@ git.certificateUsernameOIDs = CN
# SINCE 0.9.0
git.onlyAccessBareRepositories = false
# Specify the list of acceptable transports for pushes.
# If this setting is empty, all transports are acceptable.
#
# Valid choices are: GIT HTTP HTTPS SSH
#
# SINCE 1.5.0
# SPACE-DELIMITED
git.acceptedPushTransports = HTTP HTTPS SSH
# Allow an authenticated user to create a destination repository on a push if
# the repository does not already exist.
#

+ 19
- 0
src/main/java/com/gitblit/Constants.java View File

@@ -540,6 +540,25 @@ public class Constants {
}
}
public static enum Transport {
// ordered for url advertisements, assuming equal access permissions
SSH, HTTPS, HTTP, GIT;
public static Transport fromString(String value) {
for (Transport t : values()) {
if (t.name().equalsIgnoreCase(value)) {
return t;
}
}
return null;
}
public static Transport fromUrl(String url) {
String scheme = url.substring(0, url.indexOf("://"));
return fromString(scheme);
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Unused {

+ 76
- 0
src/main/java/com/gitblit/GitBlit.java View File

@@ -17,12 +17,17 @@ package com.gitblit;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;

import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.Transport;
import com.gitblit.manager.GitblitManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IFederationManager;
@@ -116,6 +121,32 @@ public class GitBlit extends GitblitManager {
return new Object [] { new GitBlitModule()};
}

protected boolean acceptPush(Transport byTransport) {
if (byTransport == null) {
logger.info("Unknown transport, push rejected!");
return false;
}

Set<Transport> transports = new HashSet<Transport>();
for (String value : getSettings().getStrings(Keys.git.acceptedPushTransports)) {
Transport transport = Transport.fromString(value);
if (transport == null) {
logger.info(String.format("Ignoring unknown registered transport %s", value));
continue;
}

transports.add(transport);
}

if (transports.isEmpty()) {
// no transports are explicitly specified, all are acceptable
return true;
}

// verify that the transport is permitted
return transports.contains(byTransport);
}

/**
* Returns a list of repository URLs and the user access permission.
*
@@ -137,6 +168,12 @@ public class GitBlit extends GitblitManager {
if (settings.getBoolean(Keys.git.enableGitServlet, true)) {
AccessPermission permission = user.getRepositoryPermission(repository).permission;
if (permission.exceeds(AccessPermission.NONE)) {
Transport transport = Transport.fromString(request.getScheme());
if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(transport)) {
// downgrade the repo permission for this transport
// because it is not an acceptable PUSH transport
permission = AccessPermission.CLONE;
}
list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission));
}
}
@@ -146,6 +183,12 @@ public class GitBlit extends GitblitManager {
if (!StringUtils.isEmpty(sshDaemonUrl)) {
AccessPermission permission = user.getRepositoryPermission(repository).permission;
if (permission.exceeds(AccessPermission.NONE)) {
if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.SSH)) {
// downgrade the repo permission for this transport
// because it is not an acceptable PUSH transport
permission = AccessPermission.CLONE;
}

list.add(new RepositoryUrl(sshDaemonUrl, permission));
}
}
@@ -155,6 +198,11 @@ public class GitBlit extends GitblitManager {
if (!StringUtils.isEmpty(gitDaemonUrl)) {
AccessPermission permission = servicesManager.getGitDaemonAccessPermission(user, repository);
if (permission.exceeds(AccessPermission.NONE)) {
if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.GIT)) {
// downgrade the repo permission for this transport
// because it is not an acceptable PUSH transport
permission = AccessPermission.CLONE;
}
list.add(new RepositoryUrl(gitDaemonUrl, permission));
}
}
@@ -173,6 +221,34 @@ public class GitBlit extends GitblitManager {
list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null));
}
}

// sort transports by highest permission and then by transport security
Collections.sort(list, new Comparator<RepositoryUrl>() {

@Override
public int compare(RepositoryUrl o1, RepositoryUrl o2) {
if (!o1.isExternal() && o2.isExternal()) {
// prefer Gitblit over external
return -1;
} else if (o1.isExternal() && !o2.isExternal()) {
// prefer Gitblit over external
return 1;
} else if (o1.isExternal() && o2.isExternal()) {
// sort by Transport ordinal
return o1.transport.compareTo(o2.transport);
} else if (o1.permission.exceeds(o2.permission)) {
// prefer highest permission
return -1;
} else if (o2.permission.exceeds(o1.permission)) {
// prefer highest permission
return 1;
}

// prefer more secure transports
return o1.transport.compareTo(o2.transport);
}
});

return list;
}


+ 46
- 0
src/main/java/com/gitblit/git/GitblitReceivePackFactory.java View File

@@ -15,6 +15,9 @@
*/
package com.gitblit.git;

import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.eclipse.jgit.lib.PersonIdent;
@@ -26,6 +29,7 @@ import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants.Transport;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.manager.IGitblit;
@@ -66,6 +70,7 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> {
String origin = "";
String gitblitUrl = "";
int timeout = 0;
Transport transport = null;

if (req instanceof HttpServletRequest) {
// http/https request may or may not be authenticated
@@ -82,6 +87,13 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> {
user = u;
}
}

// determine the transport
if ("http".equals(client.getScheme())) {
transport = Transport.HTTP;
} else if ("https".equals(client.getScheme())) {
transport = Transport.HTTPS;
}
} else if (req instanceof GitDaemonClient) {
// git daemon request is always anonymous
GitDaemonClient client = (GitDaemonClient) req;
@@ -90,12 +102,20 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> {

// set timeout from Git daemon
timeout = client.getDaemon().getTimeout();

transport = Transport.GIT;
} else if (req instanceof SshDaemonClient) {
// SSH request is always authenticated
SshDaemonClient client = (SshDaemonClient) req;
repositoryName = client.getRepositoryName();
origin = client.getRemoteAddress().toString();
user = client.getUser();

transport = Transport.SSH;
}

if (!acceptPush(transport)) {
throw new ServiceNotAuthorizedException();
}

boolean allowAnonymousPushes = settings.getBoolean(Keys.git.allowAnonymousPushes, false);
@@ -125,4 +145,30 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> {

return rp;
}

protected boolean acceptPush(Transport byTransport) {
if (byTransport == null) {
logger.info("Unknown transport, push rejected!");
return false;
}

Set<Transport> transports = new HashSet<Transport>();
for (String value : gitblit.getSettings().getStrings(Keys.git.acceptedPushTransports)) {
Transport transport = Transport.fromString(value);
if (transport == null) {
logger.info(String.format("Ignoring unknown registered transport %s", value));
continue;
}

transports.add(transport);
}

if (transports.isEmpty()) {
// no transports are explicitly specified, all are acceptable
return true;
}

// verify that the transport is permitted
return transports.contains(byTransport);
}
}

+ 3
- 0
src/main/java/com/gitblit/models/RepositoryUrl.java View File

@@ -18,6 +18,7 @@ package com.gitblit.models;
import java.io.Serializable;
import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.Transport;
/**
* Represents a git repository url and it's associated access permission for the
@@ -30,10 +31,12 @@ public class RepositoryUrl implements Serializable {
private static final long serialVersionUID = 1L;
public final Transport transport;
public final String url;
public final AccessPermission permission;
public RepositoryUrl(String url, AccessPermission permission) {
this.transport = Transport.fromUrl(url);
this.url = url;
this.permission = permission;
}

Loading…
Cancel
Save