From 27ae9095639bb228a1b7ff86a3ebe4264abf05be Mon Sep 17 00:00:00 2001
From: mschaefers <mschaefers@scoop-gmbh.de>
Date: Thu, 29 Nov 2012 12:33:09 -0500
Subject: [PATCH] feature: when using LdapUserService one can configure Gitblit to fetch all users from ldap that can possibly login. This allows to see newly generated LDAP users instantly in Gitblit. By now an LDAP user had to log in once to appear in GitBlit.
---
src/com/gitblit/GitBlitServer.java | 194 +++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 173 insertions(+), 21 deletions(-)
diff --git a/src/com/gitblit/GitBlitServer.java b/src/com/gitblit/GitBlitServer.java
index 5de2265..d98f891 100644
--- a/src/com/gitblit/GitBlitServer.java
+++ b/src/com/gitblit/GitBlitServer.java
@@ -16,20 +16,26 @@
package com.gitblit;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
+import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.ProtectionDomain;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
+import java.util.Scanner;
+import org.eclipse.jetty.ajp.Ajp13SocketConnector;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
@@ -38,8 +44,11 @@
import org.eclipse.jetty.server.ssl.SslConnector;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSocketConnector;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,7 +57,17 @@
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
+import com.gitblit.authority.GitblitAuthority;
+import com.gitblit.authority.NewCertificateConfig;
import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+import com.gitblit.utils.X509Utils;
+import com.gitblit.utils.X509Utils.X509Log;
+import com.gitblit.utils.X509Utils.X509Metadata;
+import com.unboundid.ldap.listener.InMemoryDirectoryServer;
+import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
+import com.unboundid.ldap.listener.InMemoryListenerConfig;
+import com.unboundid.ldif.LDIFReader;
/**
* GitBlitServer is the embedded Jetty server for Gitblit GO. This class starts
@@ -130,6 +149,11 @@
*/
private static void start(Params params) {
FileSettings settings = Params.FILESETTINGS;
+ if (!StringUtils.isEmpty(params.settingsfile)) {
+ if (new File(params.settingsfile).exists()) {
+ settings = new FileSettings(params.settingsfile);
+ }
+ }
logger = LoggerFactory.getLogger(GitBlitServer.class);
logger.info(Constants.BORDER);
@@ -149,10 +173,12 @@
logger.info("");
logger.info(Constants.BORDER);
+ System.setProperty("java.awt.headless", "true");
+
String osname = System.getProperty("os.name");
String osversion = System.getProperty("os.version");
logger.info("Running on " + osname + " (" + osversion + ")");
-
+
List<Connector> connectors = new ArrayList<Connector>();
// conditionally configure the http connector
@@ -172,15 +198,52 @@
// conditionally configure the https connector
if (params.securePort > 0) {
- File keystore = new File("keystore");
- if (!keystore.exists()) {
- logger.info("Generating self-signed SSL certificate for localhost");
- MakeCertificate.generateSelfSignedCertificate("localhost", keystore,
- params.storePassword);
+ final File folder = new File(System.getProperty("user.dir"));
+ File certificatesConf = new File(folder, X509Utils.CA_CONFIG);
+ File serverKeyStore = new File(folder, X509Utils.SERVER_KEY_STORE);
+ File serverTrustStore = new File(folder, X509Utils.SERVER_TRUST_STORE);
+ File caRevocationList = new File(folder, X509Utils.CA_REVOCATION_LIST);
+
+ // generate CA & web certificates, create certificate stores
+ X509Metadata metadata = new X509Metadata("localhost", params.storePassword);
+ // set default certificate values from config file
+ if (certificatesConf.exists()) {
+ FileBasedConfig config = new FileBasedConfig(certificatesConf, FS.detect());
+ try {
+ config.load();
+ } catch (Exception e) {
+ logger.error("Error parsing " + certificatesConf, e);
+ }
+ NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config);
+ certificateConfig.update(metadata);
}
- if (keystore.exists()) {
- Connector secureConnector = createSSLConnector(keystore, params.storePassword,
- params.useNIO, params.securePort);
+
+ metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);
+ X509Utils.prepareX509Infrastructure(metadata, folder, new X509Log() {
+ @Override
+ public void log(String message) {
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(new File(folder, X509Utils.CERTS + File.separator + "log.txt"), true));
+ writer.write(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1}", new Date(), message));
+ writer.newLine();
+ writer.flush();
+ } catch (Exception e) {
+ LoggerFactory.getLogger(GitblitAuthority.class).error("Failed to append log entry!", e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ });
+
+ if (serverKeyStore.exists()) {
+ Connector secureConnector = createSSLConnector(serverKeyStore, serverTrustStore, params.storePassword,
+ caRevocationList, params.useNIO, params.securePort, params.requireClientCertificates);
String bindInterface = settings.getString(Keys.server.httpsBindInterface, null);
if (!StringUtils.isEmpty(bindInterface)) {
logger.warn(MessageFormat.format(
@@ -196,6 +259,21 @@
logger.warn("Failed to find or load Keystore?");
logger.warn("SSL connector DISABLED.");
}
+ }
+
+ // conditionally configure the ajp connector
+ if (params.ajpPort > 0) {
+ Connector ajpConnector = createAJPConnector(params.ajpPort);
+ String bindInterface = settings.getString(Keys.server.ajpBindInterface, null);
+ if (!StringUtils.isEmpty(bindInterface)) {
+ logger.warn(MessageFormat.format("Binding connector on port {0,number,0} to {1}",
+ params.ajpPort, bindInterface));
+ ajpConnector.setHost(bindInterface);
+ }
+ if (params.ajpPort < 1024 && !isWindows()) {
+ logger.warn("Gitblit needs to run with ROOT permissions for ports < 1024!");
+ }
+ connectors.add(ajpConnector);
}
// tempDir is where the embedded Gitblit web application is expanded and
@@ -245,6 +323,39 @@
// Override settings from the command-line
settings.overrideSetting(Keys.realm.userService, params.userService);
settings.overrideSetting(Keys.git.repositoriesFolder, params.repositoriesFolder);
+
+ // Start up an in-memory LDAP server, if configured
+ try {
+ if (StringUtils.isEmpty(params.ldapLdifFile) == false) {
+ File ldifFile = new File(params.ldapLdifFile);
+ if (ldifFile != null && ldifFile.exists()) {
+ URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server));
+ String firstLine = new Scanner(ldifFile).nextLine();
+ String rootDN = firstLine.substring(4);
+ String bindUserName = settings.getString(Keys.realm.ldap.username, "");
+ String bindPassword = settings.getString(Keys.realm.ldap.password, "");
+
+ // Get the port
+ int port = ldapUrl.getPort();
+ if (port == -1)
+ port = 389;
+
+ InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(rootDN);
+ config.addAdditionalBindCredentials(bindUserName, bindPassword);
+ config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", port));
+ config.setSchema(null);
+
+ InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
+ ds.importFromLDIF(true, new LDIFReader(ldifFile));
+ ds.startListening();
+
+ logger.info("LDAP Server started at ldap://localhost:" + port);
+ }
+ }
+ } catch (Exception e) {
+ // Completely optional, just show a warning
+ logger.warn("Unable to start LDAP server", e);
+ }
// Set the server's contexts
server.setHandler(rootContext);
@@ -293,9 +404,6 @@
connector.setPort(port);
connector.setMaxIdleTime(30000);
- if (port < 1024 && !isWindows()) {
- logger.warn("Gitblit needs to run with ROOT permissions for ports < 1024!");
- }
return connector;
}
@@ -305,24 +413,34 @@
* SSL renegotiation will be enabled if the JVM is 1.6.0_22 or later.
* oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html
*
- * @param keystore
- * @param password
+ * @param keyStore
+ * @param clientTrustStore
+ * @param storePassword
+ * @param caRevocationList
* @param useNIO
* @param port
+ * @param requireClientCertificates
* @return an https connector
*/
- private static Connector createSSLConnector(File keystore, String password, boolean useNIO,
- int port) {
+ private static Connector createSSLConnector(File keyStore, File clientTrustStore,
+ String storePassword, File caRevocationList, boolean useNIO, int port,
+ boolean requireClientCertificates) {
+ SslContextFactory sslContext = new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH);
SslConnector connector;
if (useNIO) {
logger.info("Setting up NIO SslSelectChannelConnector on port " + port);
- SslSelectChannelConnector ssl = new SslSelectChannelConnector();
+ SslSelectChannelConnector ssl = new SslSelectChannelConnector(sslContext);
ssl.setSoLingerTime(-1);
+ if (requireClientCertificates) {
+ sslContext.setNeedClientAuth(true);
+ } else {
+ sslContext.setWantClientAuth(true);
+ }
ssl.setThreadPool(new QueuedThreadPool(20));
connector = ssl;
} else {
logger.info("Setting up NIO SslSocketConnector on port " + port);
- SslSocketConnector ssl = new SslSocketConnector();
+ SslSocketConnector ssl = new SslSocketConnector(sslContext);
connector = ssl;
}
// disable renegotiation unless this is a patched JVM
@@ -341,13 +459,32 @@
}
if (allowRenegotiation) {
logger.info(" allowing SSL renegotiation on Java " + v);
- connector.setAllowRenegotiate(allowRenegotiation);
+ sslContext.setAllowRenegotiate(allowRenegotiation);
}
- connector.setKeystore(keystore.getAbsolutePath());
- connector.setPassword(password);
+ sslContext.setKeyStorePath(keyStore.getAbsolutePath());
+ sslContext.setKeyStorePassword(storePassword);
+ sslContext.setTrustStore(clientTrustStore.getAbsolutePath());
+ sslContext.setTrustStorePassword(storePassword);
+ sslContext.setCrlPath(caRevocationList.getAbsolutePath());
connector.setPort(port);
connector.setMaxIdleTime(30000);
return connector;
+ }
+
+ /**
+ * Creates an ajp connector.
+ *
+ * @param port
+ * @return an ajp connector
+ */
+ private static Connector createAJPConnector(int port) {
+ logger.info("Setting up AJP Connector on port " + port);
+ Ajp13SocketConnector ajp = new Ajp13SocketConnector();
+ ajp.setPort(port);
+ if (port < 1024 && !isWindows()) {
+ logger.warn("Gitblit needs to run with ROOT permissions for ports < 1024!");
+ }
+ return ajp;
}
/**
@@ -456,11 +593,26 @@
@Parameter(names = "--httpsPort", description = "HTTPS port to serve. (port <= 0 will disable this connector)")
public Integer securePort = FILESETTINGS.getInteger(Keys.server.httpsPort, 443);
+ @Parameter(names = "--ajpPort", description = "AJP port to serve. (port <= 0 will disable this connector)")
+ public Integer ajpPort = FILESETTINGS.getInteger(Keys.server.ajpPort, 0);
+
@Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.")
public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, "");
@Parameter(names = "--shutdownPort", description = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)")
public Integer shutdownPort = FILESETTINGS.getInteger(Keys.server.shutdownPort, 8081);
+ @Parameter(names = "--requireClientCertificates", description = "Require client X509 certificates for https connections.")
+ public Boolean requireClientCertificates = FILESETTINGS.getBoolean(Keys.server.requireClientCertificates, false);
+
+ /*
+ * Setting overrides
+ */
+ @Parameter(names = { "--settings" }, description = "Path to alternative settings")
+ public String settingsfile;
+
+ @Parameter(names = { "--ldapLdifFile" }, description = "Path to LDIF file. This will cause an in-memory LDAP server to be started according to gitblit settings")
+ public String ldapLdifFile;
+
}
}
\ No newline at end of file
--
Gitblit v1.9.1