Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ldap unboundid #12

Merged
merged 6 commits into from
Apr 24, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
<classpathentry kind="lib" path="ext/org.eclipse.jgit.http.server-1.3.0.201202151440-r.jar" sourcepath="ext/org.eclipse.jgit.http.server-1.3.0.201202151440-r-sources.jar"/>
<classpathentry kind="lib" path="ext/lucene-highlighter-3.5.0.jar" sourcepath="ext/lucene-highlighter-3.5.0-sources.jar"/>
<classpathentry kind="lib" path="ext/lucene-memory-3.5.0.jar" sourcepath="ext/lucene-memory-3.5.0-sources.jar"/>
<classpathentry kind="lib" path="ext/unboundid-ldapsdk-2.3.0.jar" sourcepath="ext/unboundid-ldapsdk-2.3.0-sources.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
10 changes: 9 additions & 1 deletion NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,12 @@ GLYHPICONS
Creative Commons CC-BY License.

http://glyphicons.com


---------------------------------------------------------------------------
UnboundID
---------------------------------------------------------------------------
UnboundID, released under the
GNU LESSER GENERAL PUBLIC LICENSE. (http://www.unboundid.com/products/ldap-sdk/docs/LICENSE-LGPLv2.1.txt)

http://www.unboundid.com

85 changes: 85 additions & 0 deletions distrib/gitblit.properties
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,91 @@ realm.passwordStorage = md5
# SINCE 0.5.0
realm.minPasswordLength = 5

# URL of the LDAP server.
#
# SINCE 1.0.0
realm.ldap.server = ldap://localhost

# Login username for LDAP searches.
# The domain prefix may be omitted if it matches the domain specified in
# *realm.ldap.domain*. If this value is unspecified, anonymous LDAP login will
# be used.
#
# e.g. mydomain\\username
#
# SINCE 1.0.0
realm.ldap.username = cn=Directory Manager

# Login password for LDAP searches.
#
# SINCE 1.0.0
realm.ldap.password = password

# The LdapUserService must be backed by another user service for standard user
# and team management.
# default: users.conf
#
# SINCE 1.0.0
# RESTART REQUIRED
realm.ldap.backingUserService = users.conf

# Delegate team membership control to LDAP.
#
# If true, team user memberships will be specified by LDAP groups. This will
# disable team selection in Edit User and user selection in Edit Team.
#
# If false, LDAP will only be used for authentication and Gitblit will maintain
# team memberships with the *realm.ldap.backingUserService*.
#
# SINCE 1.0.0
realm.ldap.maintainTeams = false

# Root node that all Users sit under in LDAP
#
# This is the root node that searches for user information will begin from in LDAP
# If blank, it will search ALL of ldap.
#
# SINCE 1.0.0
realm.ldap.accountBase = OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain

# Filter Criteria for Users in LDAP
#
# Query pattern to use when searching for a user account. This may be any valid
# LDAP query expression, including the standard (&) and (|) operators. Variables may
# be injected via the ${variableName} syntax. Recognized variables are:
# ${username} - The text entered as the user name
#
# SINCE 1.0.0
realm.ldap.accountPattern = (&(objectClass=person)(sAMAccountName=${username}))

# Root node that all Teams sit under in LDAP
#
# This is the node that searches for team information will begin from in LDAP
# If blank, it will search ALL of ldap.
#
# SINCE 1.0.0
realm.ldap.groupBase = OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain

# Filter Criteria for Teams in LDAP
#
# Query pattern to use when searching for a team. This may be any valid
# LDAP query expression, including the standard (&) and (|) operators. Variables may
# be injected via the ${variableName} syntax. Recognized variables are:
# ${username} - The text entered as the user name
# ${dn} - The Distinguished Name of the user logged in
# All attributes on the User's record are also passed in. For example, if a user has an
# attribute "fullName" set to "John", "(fn=${fullName})" will be translated to "(fn=John)".
#
# SINCE 1.0.0
realm.ldap.groupMemberPattern = (&(objectClass=group)(member=${dn}))

# Users and or teams that are Admins, read from LDAP
#
# This is a space delimited list. If it starts with @, it indicates a Team Name
#
# SINCE 1.0.0
realm.ldap.admins= @Git_Admins

#
# Gitblit Web Settings
#
Expand Down
61 changes: 60 additions & 1 deletion docs/01_setup.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -447,4 +447,63 @@ Nothing special to configure, EGit figures out everything.
<pre>https://yourserver/git/your/repository</pre>
- **Command-line Git**
My testing indicates that your username must be embedded in the url. YMMV.
<pre>https://username@yourserver/git/your/repository</pre>
<pre>https://username@yourserver/git/your/repository</pre>

## LDAP Support
*SINCE 1.0.0*

LDAP can be used with Gitblit to read Users and the Teams that they belong to. If configured, LDAP will be queried upon every login to the system, and synchronize that information with the traditional Gitblit backed file (.conf or .properties). This "lazy" reading approach provides for fast reaction times, but will force a user to log in before you can maintain them (or their teams).

### Example Diagram (with attributes)
![block diagram](ldapSample.png "LDAP Sample")

Please see <gitblit>/tests/com/gitblit/tests/resources/ldapUserServiceSampleData.ldif to see the data in LDAP that reflects the above picture.

### GitBlit Properties (See gitblit.properties for full description)
The following is are descriptions of the properties that would follow the sample layout of an LDAP (or Active Directory) setup above.

<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td>realm.ldap.server</td><td>ldap://localhost:389</td>
<td>Tells Gitblit to connect to the LDAP server on localhost, port 389. URL Must be of form ldap(s)://<server>:<port> with port being optional (389 for ldap, 636 for ldaps).</td>
</tr>
<tr>
<td>realm.ldap.username</td><td>cn=Directory Manager</td>
<td>The credentials that will log into this gitblit server</td>
</tr>
<tr>
<td>realm.ldap.password</td><td>password</td>
<td>The credentials that will log into this gitblit server</td>
</tr>
<tr>
<td>realm.ldap.backingUserService</td><td>users.conf</td>
<td>Where to store all information that is used by Gitblit. All information will be synced here upon user login.</td>
</tr>
<tr>
<td>realm.ldap.maintainTeams</td><td>true</td>
<td>Are users maintained in LDAP (true), or manually in Gitblit (false).</td>
</tr>
<tr>
<td>realm.ldap.accountBase</td><td>OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain</td>
<td>What is the root node for all users in this LDAP system. Searches will be subtree searches starting from this node.</td>
</tr>
<tr>
<td>realm.ldap.accountPattern</td><td>(&(objectClass=person)(sAMAccountName=${username}))</td><td>The LDAP Search filter that will match a particular user in LDAP. ${username} will be replaced with whatever the user types in as their user name.</td>
</tr>
<tr>
<td>realm.ldap.groupBase</td><td>OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain</td>
<td>What is the root node for all teams in this LDAP system. Searches will be subtree searches starting from this node.</td>
</tr>
<tr>
<td>realm.ldap.groupMemberPattern</td><td>(&(objectClass=group)(member=${dn}))</td><td>The LDAP Search filter that will match all teams for the logging in user in LDAP. ${username} will be replaced with whatever the user types in as their user name. Anything else in ${} will be replaced by Attributes on the User node.</td>
</tr>
<tr>
<td>realm.ldap.admins</td><td>@Git_Admins</td><td>A space delimited list of users and teams (if starting with @) that indicate admin status in Gitblit.</td>
</tr>
</table>

You may notice that there are no properties to find the password on the User record. This is intentional, and the service utilizes the LDAP login process to verify that the user credentials are correct.

You can also start Gitblit GO with an in-memory (backed by an LDIF file) LDAP server by using the --ldapLdifFile property. It will listen where ever gitblit.settings is pointed to. However, it only supports ldap...not ldaps, so be sure to set that in gitblit.settings. It reads the user / password in gitblit.settings to create the root user login.

Finally, writing back to LDAP is not implemented at this time, so do not worry about corrupting your corporate LDAP. Many orgnizations are likely to go through a different flow to update their LDAP, so it's unlikely that this will become a feature.
1 change: 1 addition & 0 deletions docs/04_design.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The following dependencies are automatically downloaded by Gitblit GO (or alread
- [javamail](http://kenai.com/projects/javamail) (CDDL-1.0, BSD, GPL-2.0, GNU-Classpath)
- [Groovy](http://groovy.codehaus.org) (Apache 2.0)
- [Lucene](http://lucene.apache.org) (Apache 2.0)
- [UnboundID](http://www.unboundid.com) (LGPL 2.1)

### Other Build Dependencies
- [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed)
Expand Down
Binary file added docs/ldapSample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 24 additions & 1 deletion src/com/gitblit/ConfigUserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ public ConfigUserService(File realmFile) {
public void setup(IStoredSettings settings) {
}

/**
* Does the user service support changes to credentials?
*
* @return true or false
* @since 1.0.0
*/
@Override
public boolean supportsCredentialChanges() {
return true;
}

/**
* Does the user service support changes to team memberships?
*
* @return true or false
* @since 1.0.0
*/
public boolean supportsTeamMembershipChanges() {
return true;
}

/**
* Does the user service support cookie authentication?
*
Expand Down Expand Up @@ -656,7 +677,9 @@ private synchronized void write() throws IOException {

// write users
for (UserModel model : users.values()) {
config.setString(USER, model.username, PASSWORD, model.password);
if (!StringUtils.isEmpty(model.password)) {
config.setString(USER, model.username, PASSWORD, model.password);
}

// user roles
List<String> roles = new ArrayList<String>();
Expand Down
27 changes: 26 additions & 1 deletion src/com/gitblit/FileUserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ public FileUserService(File realmFile) {
public void setup(IStoredSettings settings) {
}

/**
* Does the user service support changes to credentials?
*
* @return true or false
* @since 1.0.0
*/
@Override
public boolean supportsCredentialChanges() {
return true;
}

/**
* Does the user service support changes to team memberships?
*
* @return true or false
* @since 1.0.0
*/
public boolean supportsTeamMembershipChanges() {
return true;
}

/**
* Does the user service support cookie authentication?
*
Expand Down Expand Up @@ -233,7 +254,9 @@ public boolean updateUserModel(String username, UserModel model) {
}

StringBuilder sb = new StringBuilder();
sb.append(model.password);
if (!StringUtils.isEmpty(model.password)) {
sb.append(model.password);
}
sb.append(',');
for (String role : roles) {
sb.append(role);
Expand Down Expand Up @@ -658,6 +681,8 @@ protected synchronized Properties read() {
team.addRepositories(repositories);
team.addUsers(users);
team.addMailingLists(mailingLists);
team.preReceiveScripts.addAll(preReceive);
team.postReceiveScripts.addAll(postReceive);
teams.put(team.name.toLowerCase(), team);
} else {
// user definition
Expand Down
16 changes: 16 additions & 0 deletions src/com/gitblit/GitBlit.java
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,22 @@ public void setUserService(IUserService userService) {
this.userService = userService;
this.userService.setup(settings);
}

/**
*
* @return true if the user service supports credential changes
*/
public boolean supportsCredentialChanges() {
return userService.supportsCredentialChanges();
}

/**
*
* @return true if the user service supports team membership changes
*/
public boolean supportsTeamMembershipChanges() {
return userService.supportsTeamMembershipChanges();
}

/**
* Authenticate a user based on a username and password.
Expand Down
42 changes: 42 additions & 0 deletions src/com/gitblit/GitBlitServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
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.List;
import java.util.Scanner;

import org.eclipse.jetty.ajp.Ajp13SocketConnector;
import org.eclipse.jetty.server.Connector;
Expand All @@ -50,6 +52,10 @@
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.gitblit.utils.StringUtils;
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
Expand Down Expand Up @@ -266,6 +272,39 @@ private static void start(Params params) {
// 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);
Expand Down Expand Up @@ -504,6 +543,9 @@ private static class Params {
*/
@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;

}
}
Loading