summaryrefslogtreecommitdiffstats
path: root/src/com/gitblit/LdapUserService.java
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2012-04-06 18:01:58 -0400
committerJames Moger <james.moger@gitblit.com>2012-04-06 18:01:58 -0400
commit6cca8699f98a606ff19e88d40a8a2535fdc340e7 (patch)
treeb4725b6edbed868e10e6edaae049a7423f4f3008 /src/com/gitblit/LdapUserService.java
parentd58f8efd44c73a649aa92e590cb138ee4507ac99 (diff)
downloadgitblit-6cca8699f98a606ff19e88d40a8a2535fdc340e7.tar.gz
gitblit-6cca8699f98a606ff19e88d40a8a2535fdc340e7.zip
Skeleton LdapUserService based on John Cryiger's implementation
Diffstat (limited to 'src/com/gitblit/LdapUserService.java')
-rw-r--r--src/com/gitblit/LdapUserService.java200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/com/gitblit/LdapUserService.java b/src/com/gitblit/LdapUserService.java
new file mode 100644
index 00000000..3ec45e17
--- /dev/null
+++ b/src/com/gitblit/LdapUserService.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 John Crygier
+ * Copyright 2012 gitblit.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit;
+
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ConnectionUtils.BlindSSLSocketFactory;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Implementation of an LDAP user service.
+ *
+ * @author John Crygier
+ */
+public class LdapUserService extends GitblitUserService {
+
+ public static final Logger logger = LoggerFactory.getLogger(LdapUserService.class);
+ private final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
+
+ private IStoredSettings settings;
+
+ public LdapUserService() {
+ super();
+ }
+
+ @Override
+ public void setup(IStoredSettings settings) {
+ this.settings = settings;
+ String file = settings.getString(Keys.realm.ldap_backingUserService, "users.conf");
+ File realmFile = GitBlit.getFileOrFolder(file);
+
+ serviceImpl = createUserService(realmFile);
+ logger.info("LDAP User Service backed by " + serviceImpl.toString());
+ }
+
+ /**
+ * Credentials are defined in the LDAP server and can not be manipulated
+ * from Gitblit.
+ *
+ * @return false
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ /**
+ * If the LDAP server will maintain team memberships then LdapUserService
+ * will not allow team membership changes. In this scenario all team
+ * changes must be made on the LDAP server by the LDAP administrator.
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ public boolean supportsTeamMembershipChanges() {
+ return !settings.getBoolean(Keys.realm.ldap_maintainTeams, false);
+ }
+
+ /**
+ * Does the user service support cookie authentication?
+ *
+ * @return true or false
+ */
+ @Override
+ public boolean supportsCookies() {
+ // TODO cookies need to be reviewed
+ return false;
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ String domainUser = getDomainUsername(username);
+ DirContext ctx = getDirContext(domainUser, new String(password));
+ // TODO do we need a bind here?
+ if (ctx != null) {
+ String simpleUsername = getSimpleUsername(username);
+ UserModel user = getUserModel(simpleUsername);
+ if (user == null) {
+ // create user object for new authenticated user
+ user = new UserModel(simpleUsername.toLowerCase());
+ }
+ user.password = new String(password);
+
+ if (!supportsTeamMembershipChanges()) {
+ // Teams are specified in LDAP server
+ // TODO search LDAP for team memberships
+ Set<String> foundTeams = new HashSet<String>();
+ for (String team : foundTeams) {
+ TeamModel model = getTeamModel(team);
+ if (model == null) {
+ // create the team
+ model = new TeamModel(team.toLowerCase());
+ updateTeamModel(model);
+ }
+ // add team to the user
+ user.teams.add(model);
+ }
+ }
+
+ try {
+ ctx.close();
+ } catch (NamingException e) {
+ logger.error("Can not close context", e);
+ }
+ return user;
+ }
+ return null;
+ }
+
+ protected DirContext getDirContext() {
+ String username = settings.getString(Keys.realm.ldap_username, "");
+ String password = settings.getString(Keys.realm.ldap_password, "");
+ return getDirContext(username, password);
+ }
+
+ protected DirContext getDirContext(String username, String password) {
+ try {
+ String server = settings.getRequiredString(Keys.realm.ldap_server);
+ Hashtable<String, String> env = new Hashtable<String, String>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
+ env.put(Context.PROVIDER_URL, server);
+ if (server.startsWith("ldaps:")) {
+ env.put("java.naming.ldap.factory.socket", BlindSSLSocketFactory.class.getName());
+ }
+ // TODO consider making this a setting
+ env.put("com.sun.jndi.ldap.read.timeout", "5000");
+
+ if (!StringUtils.isEmpty(username)) {
+ // authenticated login
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, getDomainUsername(username));
+ env.put(Context.SECURITY_CREDENTIALS, password == null ? "":password.trim());
+ }
+ return new InitialDirContext(env);
+ } catch (NamingException e) {
+ logger.warn(MessageFormat.format("Error connecting to LDAP with credentials. Please check {0}, {1}, and {2}",
+ Keys.realm.ldap_server, Keys.realm.ldap_username, Keys.realm.ldap_password), e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns a simple username without any domain prefixes.
+ *
+ * @param username
+ * @return a simple username
+ */
+ protected String getSimpleUsername(String username) {
+ int lastSlash = username.lastIndexOf('\\');
+ if (lastSlash > -1) {
+ username = username.substring(lastSlash + 1);
+ }
+ return username;
+ }
+
+ /**
+ * Returns a username with a domain prefix as long as the username does not
+ * already have a comain prefix.
+ *
+ * @param username
+ * @return a domain username
+ */
+ protected String getDomainUsername(String username) {
+ String domain = settings.getString(Keys.realm.ldap_domain, null);
+ String domainUsername = username;
+ if (!StringUtils.isEmpty(domain) && (domainUsername.indexOf('\\') == -1)) {
+ domainUsername = domain + "\\" + username;
+ }
+ return domainUsername.trim();
+ }
+}