From e8b8eec715a41fc0b5e4c397f27c54997f505cb7 Mon Sep 17 00:00:00 2001
From: William Whittle <william.whittle@gmail.com>
Date: Fri, 07 Jun 2013 11:09:49 -0400
Subject: [PATCH] Added support for authenticating against a Salesforce org.

---
 src/main/java/com/gitblit/SalesforceUserService.java |  132 +++++++++++++++++++++++++++++++++
 .classpath                                           |    3 
 src/main/distrib/data/gitblit.properties             |   15 +++
 build.moxie                                          |    1 
 gitblit.iml                                          |   33 ++++++++
 5 files changed, 184 insertions(+), 0 deletions(-)

diff --git a/.classpath b/.classpath
index ffb673d..e25f68c 100644
--- a/.classpath
+++ b/.classpath
@@ -37,6 +37,9 @@
 	<classpathentry kind="lib" path="ext/jcalendar-1.3.2.jar" />
 	<classpathentry kind="lib" path="ext/commons-compress-1.4.1.jar" sourcepath="ext/src/commons-compress-1.4.1.jar" />
 	<classpathentry kind="lib" path="ext/xz-1.0.jar" sourcepath="ext/src/xz-1.0.jar" />
+	<classpathentry kind="lib" path="ext/force-partner-api-24.0.0.jar" sourcepath="ext/src/force-partner-api-24.0.0.jar" />
+	<classpathentry kind="lib" path="ext/force-wsc-24.0.0.jar" sourcepath="ext/src/force-wsc-24.0.0.jar" />
+	<classpathentry kind="lib" path="ext/js-1.7R2.jar" sourcepath="ext/src/js-1.7R2.jar" />
 	<classpathentry kind="lib" path="ext/junit-4.11.jar" sourcepath="ext/src/junit-4.11.jar" />
 	<classpathentry kind="lib" path="ext/hamcrest-core-1.3.jar" sourcepath="ext/src/hamcrest-core-1.3.jar" />
 	<classpathentry kind="lib" path="ext/selenium-java-2.28.0.jar" sourcepath="ext/src/selenium-java-2.28.0.jar" />
diff --git a/build.moxie b/build.moxie
index d27e08a..be9a21c 100644
--- a/build.moxie
+++ b/build.moxie
@@ -147,6 +147,7 @@
 - compile 'org.apache.ivy:ivy:2.2.0' :war
 - compile 'com.toedter:jcalendar:1.3.2' :authority
 - compile 'org.apache.commons:commons-compress:1.4.1' :war
+- compile 'com.force.api:force-partner-api:24.0.0' :war
 - test 'junit'
 # Dependencies for Selenium web page testing
 - test 'org.seleniumhq.selenium:selenium-java:${selenium.version}' @jar
diff --git a/gitblit.iml b/gitblit.iml
index fe245be..b90adbd 100644
--- a/gitblit.iml
+++ b/gitblit.iml
@@ -380,6 +380,39 @@
         </SOURCES>
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library name="force-partner-api-24.0.0.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/force-partner-api-24.0.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/force-partner-api-24.0.0.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="force-wsc-24.0.0.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/force-wsc-24.0.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/force-wsc-24.0.0.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="js-1.7R2.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/js-1.7R2.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/js-1.7R2.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="junit-4.11.jar">
         <CLASSES>
diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties
index 36d7803..82e77f3 100644
--- a/src/main/distrib/data/gitblit.properties
+++ b/src/main/distrib/data/gitblit.properties
@@ -499,6 +499,7 @@
 # Alternative user services:
 #    com.gitblit.LdapUserService
 #    com.gitblit.RedmineUserService
+#    com.gitblit.SalesforceUserService
 #
 # Any custom user service implementation must have a public default constructor.
 #
@@ -1096,6 +1097,20 @@
 # Advanced Realm Settings
 #
 
+# The SalesforceUserService must be backed by another user service for standard user
+# and team management.
+# default: users.conf
+#
+# RESTART REQUIRED
+# BASEFOLDER
+realm.salesforce.backingUserService = ${baseFolder}/users.conf
+
+# Restrict the Salesforce user to members of this org.
+# default: 0 (i.e. do not check the Org ID)
+#
+# RESTART REQUIRED
+realm.salesforce.orgId = 0
+
 # URL of the LDAP server.
 # To use encrypted transport, use either ldaps:// URL for SSL or ldap+tls:// to
 # send StartTLS command.
diff --git a/src/main/java/com/gitblit/SalesforceUserService.java b/src/main/java/com/gitblit/SalesforceUserService.java
new file mode 100644
index 0000000..4a6a1ba
--- /dev/null
+++ b/src/main/java/com/gitblit/SalesforceUserService.java
@@ -0,0 +1,132 @@
+package com.gitblit;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.sforce.soap.partner.Connector;
+import com.sforce.soap.partner.GetUserInfoResult;
+import com.sforce.soap.partner.PartnerConnection;
+import com.sforce.ws.ConnectionException;
+import com.sforce.ws.ConnectorConfig;
+
+public class SalesforceUserService extends GitblitUserService {
+	public static final Logger logger = LoggerFactory
+			.getLogger(SalesforceUserService.class);
+	private IStoredSettings settings;
+
+	@Override
+	public void setup(IStoredSettings settings) {
+		this.settings = settings;
+		String file = settings.getString(
+				Keys.realm.salesforce.backingUserService,
+				"${baseFolder}/users.conf");
+		File realmFile = GitBlit.getFileOrFolder(file);
+
+		serviceImpl = createUserService(realmFile);
+
+		logger.info("Salesforce User Service backed by "
+				+ serviceImpl.toString());
+	}
+
+	@Override
+	public UserModel authenticate(String username, char[] password) {
+		if (isLocalAccount(username)) {
+			// local account, bypass Salesforce authentication
+			return super.authenticate(username, password);
+		}
+
+		ConnectorConfig config = new ConnectorConfig();
+		config.setUsername(username);
+		config.setPassword(new String(password));
+
+		try {
+			PartnerConnection connection = Connector.newConnection(config);
+
+			GetUserInfoResult info = connection.getUserInfo();
+
+			String org = settings.getString(Keys.realm.salesforce.orgId, "0")
+					.trim();
+
+			if (!org.equals("0")) {
+				if (!org.equals(info.getOrganizationId())) {
+					logger.warn("Access attempted by user of an invalid org: "
+							+ info.getUserName() + ", org: "
+							+ info.getOrganizationName() + "("
+							+ info.getOrganizationId() + ")");
+
+					return null;
+				}
+			}
+
+			logger.info("Authenticated user " + info.getUserName()
+					+ " using org " + info.getOrganizationName() + "("
+					+ info.getOrganizationId() + ")");
+
+			String simpleUsername = getSimpleUsername(info);
+
+			UserModel user = null;
+			synchronized (this) {
+				user = getUserModel(simpleUsername);
+				if (user == null)
+					user = new UserModel(simpleUsername);
+
+				if (StringUtils.isEmpty(user.cookie)
+						&& !ArrayUtils.isEmpty(password)) {
+					user.cookie = StringUtils.getSHA1(user.username
+							+ new String(password));
+				}
+
+				setUserAttributes(user, info);
+
+				super.updateUserModel(user);
+			}
+
+			return user;
+		} catch (ConnectionException e) {
+			logger.error("Failed to authenticate", e);
+		}
+
+		return null;
+	}
+
+	private void setUserAttributes(UserModel user, GetUserInfoResult info) {
+		// Don't want visibility into the real password, make up a dummy
+		user.password = ExternalAccount;
+		user.accountType = getAccountType();
+
+		// Get full name Attribute
+		user.displayName = info.getUserFullName();
+
+		// Get email address Attribute
+		user.emailAddress = info.getUserEmail();
+	}
+
+	/**
+	 * Simple user name is the first part of the email address.
+	 */
+	private String getSimpleUsername(GetUserInfoResult info) {
+		String email = info.getUserEmail();
+
+		return email.split("@")[0];
+	}
+
+	@Override
+	public boolean supportsCredentialChanges() {
+		return false;
+	}
+
+	@Override
+	public boolean supportsDisplayNameChanges() {
+		return false;
+	}
+
+	@Override
+	public boolean supportsEmailAddressChanges() {
+		return false;
+	}
+}

--
Gitblit v1.9.1