]> source.dussan.org Git - gitblit.git/commitdiff
Added WindowsUserService using Waffle
authorJames Moger <james.moger@gitblit.com>
Thu, 20 Jun 2013 21:31:41 +0000 (17:31 -0400)
committerJames Moger <james.moger@gitblit.com>
Thu, 20 Jun 2013 21:31:41 +0000 (17:31 -0400)
.classpath
NOTICE
build.moxie
build.xml
gitblit.iml
releases.moxie
src/main/distrib/data/gitblit.properties
src/main/java/com/gitblit/Constants.java
src/main/java/com/gitblit/WindowsUserService.java [new file with mode: 0644]
src/site/design.mkd
src/site/features.mkd

index 8cd68de9ab6b30f879a8c4369d00d5f1fcce1c45..36fed74a4b7762bce46cc91b59c06ebec6cb3e11 100644 (file)
        <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/freemarker-2.3.19.jar" sourcepath="ext/src/freemarker-2.3.19.jar" />
+       <classpathentry kind="lib" path="ext/waffle-jna-1.5.jar" sourcepath="ext/src/waffle-jna-1.5.jar" />
+       <classpathentry kind="lib" path="ext/platform-3.5.0.jar" sourcepath="ext/src/platform-3.5.0.jar" />
+       <classpathentry kind="lib" path="ext/jna-3.5.0.jar" sourcepath="ext/src/jna-3.5.0.jar" />
+       <classpathentry kind="lib" path="ext/guava-13.0.1.jar" sourcepath="ext/src/guava-13.0.1.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" />
        <classpathentry kind="lib" path="ext/cglib-nodep-2.1_3.jar" sourcepath="ext/src/cglib-nodep-2.1_3.jar" />
        <classpathentry kind="lib" path="ext/json-20080701.jar" sourcepath="ext/src/json-20080701.jar" />
        <classpathentry kind="lib" path="ext/selenium-api-2.28.0.jar" sourcepath="ext/src/selenium-api-2.28.0.jar" />
-       <classpathentry kind="lib" path="ext/guava-13.0.1.jar" sourcepath="ext/src/guava-13.0.1.jar" />
        <classpathentry kind="lib" path="ext/httpclient-4.2.1.jar" sourcepath="ext/src/httpclient-4.2.1.jar" />
        <classpathentry kind="lib" path="ext/httpcore-4.2.1.jar" sourcepath="ext/src/httpcore-4.2.1.jar" />
        <classpathentry kind="lib" path="ext/commons-logging-1.1.1.jar" sourcepath="ext/src/commons-logging-1.1.1.jar" />
        <classpathentry kind="lib" path="ext/commons-codec-1.6.jar" sourcepath="ext/src/commons-codec-1.6.jar" />
        <classpathentry kind="lib" path="ext/commons-exec-1.1.jar" sourcepath="ext/src/commons-exec-1.1.jar" />
-       <classpathentry kind="lib" path="ext/jna-3.4.0.jar" sourcepath="ext/src/jna-3.4.0.jar" />
-       <classpathentry kind="lib" path="ext/platform-3.4.0.jar" sourcepath="ext/src/platform-3.4.0.jar" />
        <classpathentry kind="lib" path="ext/commons-io-2.2.jar" sourcepath="ext/src/commons-io-2.2.jar" />
        <classpathentry kind="output" path="bin/classes" />
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" />
diff --git a/NOTICE b/NOTICE
index ab0a0868fb36535a9573e553eaee5f5e8f20495f..9bd356a831112dbc3e6089068f71f3195aaba13f 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -277,4 +277,29 @@ FreeMarker
    FreeMarker, release under a\r
    modified BSD License. (http://www.freemarker.org/docs/app_license.html)\r
    \r
-   http://www.freemarker.org/
\ No newline at end of file
+   http://www.freemarker.org/\r
+\r
+---------------------------------------------------------------------------\r
+Waffle\r
+---------------------------------------------------------------------------\r
+   Waffle, release under the\r
+   Eclipse Public License, version 1.0\r
+   \r
+   http://dblock.github.io/waffle\r
+\r
+---------------------------------------------------------------------------\r
+JNA\r
+---------------------------------------------------------------------------\r
+   JNA, release under the\r
+   Lesser GNU Public License, version 2.1\r
+   \r
+   https://github.com/twall/jna\r
+   \r
+---------------------------------------------------------------------------\r
+Guava\r
+---------------------------------------------------------------------------\r
+   Guava, release under the\r
+   Apache License 2.0.\r
+   \r
+   https://code.google.com/p/guava-libraries\r
+  
\ No newline at end of file
index 9fc08dc6cf86806bde4fe6fecd75a198caab563f..16e06b21f3ce35cdcd852c4e57537bef2c03c856 100644 (file)
@@ -149,6 +149,7 @@ dependencies:
 - compile 'org.apache.commons:commons-compress:1.4.1' :war
 - compile 'com.force.api:force-partner-api:24.0.0' :war
 - compile 'org.freemarker:freemarker:2.3.19' :war
+- compile 'com.github.dblock.waffle:waffle-jna:1.5' :war
 - test 'junit'
 # Dependencies for Selenium web page testing
 - test 'org.seleniumhq.selenium:selenium-java:${selenium.version}' @jar
index 894e51a4b6fbab7f7a363ca4b98a1312f76155e1..308ec43b23f36a9d3cf6f0df8cf0962d1d9b7c04 100644 (file)
--- a/build.xml
+++ b/build.xml
                        <class name="com.gitblit.LdapUserService" />\r
                        <class name="com.gitblit.RedmineUserService" />\r
                        <class name="com.gitblit.SalesforceUserService" />\r
+                       <class name="com.gitblit.WindowsUserService" />\r
                </mx:genjar>\r
 \r
                <!-- Build the WAR file -->\r
                        <class name="com.gitblit.LdapUserService" />\r
                        <class name="com.gitblit.RedmineUserService" />\r
                        <class name="com.gitblit.SalesforceUserService" />\r
+                       <class name="com.gitblit.WindowsUserService" />\r
                </mx:genjar>\r
 \r
                <!-- Build Express Zip file -->\r
index 38a014a6dcdcfb376d06812069678e6889a0c15c..2ee6242ec7001d7b0b69df96a3baf51a85e622e3 100644 (file)
         </SOURCES>
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library name="waffle-jna-1.5.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/waffle-jna-1.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/waffle-jna-1.5.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="platform-3.5.0.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/platform-3.5.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/platform-3.5.0.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="jna-3.5.0.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/jna-3.5.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/jna-3.5.0.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="guava-13.0.1.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/guava-13.0.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/guava-13.0.1.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="junit-4.11.jar">
         <CLASSES>
         </SOURCES>
       </library>
     </orderEntry>
-    <orderEntry type="module-library" scope="TEST">
-      <library name="guava-13.0.1.jar">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/ext/guava-13.0.1.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="jar://$MODULE_DIR$/ext/src/guava-13.0.1.jar!/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="httpclient-4.2.1.jar">
         <CLASSES>
         </SOURCES>
       </library>
     </orderEntry>
-    <orderEntry type="module-library" scope="TEST">
-      <library name="jna-3.4.0.jar">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/ext/jna-3.4.0.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="jar://$MODULE_DIR$/ext/src/jna-3.4.0.jar!/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library" scope="TEST">
-      <library name="platform-3.4.0.jar">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/ext/platform-3.4.0.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="jar://$MODULE_DIR$/ext/src/platform-3.4.0.jar!/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="commons-io-2.2.jar">
         <CLASSES>
index 74e7d14d4a355d7bff62e6962ea17b5e9ad4777c..1b8b2b0558275754b6cc796af624c6634a71b0a6 100644 (file)
@@ -46,7 +46,8 @@ r17: {
         - Updated Polish translation\r
         - Updated Japanese translation\r
         \r
-    additions: \r
+    additions:\r
+        - Added WindowsUserService to authenticate users against Windows accounts (issue-250)\r
         - Global and per-repository setting to exclude authors from metrics (issue-251)\r
         - Added SalesForce.com user service\r
      - Added simple star/unstar function to flag or bookmark interesting repositories\r
@@ -74,6 +75,7 @@ r17: {
        - Bandarupalli Satyanarayana\r
        - Chad Horohoe\r
        - Christian Aistleitner\r
+       - Colin Bowern\r
        - David Ostrovsky\r
        - Egbert Teeselink\r
        - Hige Maniya\r
@@ -108,15 +110,20 @@ r17: {
        - Iconic font\r
        - AngularJS 1.0.7\r
        - FreeMarker 2.3.19\r
+       - Waffle 1.5\r
+       - JNA 3.5.0\r
+       - Guava 13.0.1\r
        \r
        settings:\r
        - { name: 'git.daemonBindInterface', defaultValue: 'localhost' }\r
        - { name: 'git.daemonPort', defaultValue: 0 }\r
-        - { name: 'git.defaultIncrementalPushTagPrefix', defaultValue: 'r' }\r
+       - { name: 'git.defaultIncrementalPushTagPrefix', defaultValue: 'r' }\r
        - { name: 'mail.smtps', defaultValue: 'false' }\r
        - { name: 'realm.container.autoCreateAccounts', defaultValue: 'false' }\r
        - { name: 'realm.salesforce.backingUserService', defaultValue: 'users.conf' }\r
        - { name: 'realm.salesforce.orgId', defaultValue: 0 }\r
+       - { name: 'realm.windows.defaultDomain', defaultValue: ' ' }\r
+       - { name: 'realm.windows.backingUserService', defaultValue: 'users.conf' }\r
        - { name: 'web.activityDurationChoices', defaultValue: '7 14 28 60 90 180' }\r
        - { name: 'web.allowAppCloneLinks', defaultValue: 'true' }\r
        - { name: 'web.forceDefaultLocale', defaultValue: ' ' }\r
index 7936bc2f67e66383cf20f91af897552a4e505503..f38afefcaa38afb6495fbfca3b71aa98e3045700 100644 (file)
@@ -500,6 +500,7 @@ web.projectsFile = ${baseFolder}/projects.conf
 #    com.gitblit.LdapUserService\r
 #    com.gitblit.RedmineUserService\r
 #    com.gitblit.SalesforceUserService\r
+#    com.gitblit.WindowsUserService\r
 #\r
 # Any custom user service implementation must have a public default constructor.\r
 #\r
@@ -1117,12 +1118,39 @@ federation.sets =
 # SINCE 1.3.0\r
 realm.container.autoCreateAccounts = false\r
 \r
+# The WindowsUserService must be backed by another user service for standard user\r
+# and team management.\r
+# default: users.conf\r
+#\r
+# RESTART REQUIRED\r
+# BASEFOLDER\r
+# SINCE 1.3.0\r
+realm.windows.backingUserService = ${baseFolder}/users.conf\r
+\r
+# Allow or prohibit Windows guest account logins\r
+#\r
+# SINCE 1.3.0\r
+realm.windows.allowGuests = false\r
+\r
+# The default domain for authentication.\r
+#\r
+# If specified, this domain will be used for authentication UNLESS the supplied\r
+# login name manually specifies a domain (.e.g. mydomain\james or james@mydomain)\r
+#\r
+# If unspecified, the username must be specified in UPN format (name@domain).\r
+#\r
+# if "." (dot) is specified, ONLY the local account database will be used.\r
+#\r
+# SINCE 1.3.0\r
+realm.windows.defaultDomain =\r
+\r
 # The SalesforceUserService must be backed by another user service for standard user\r
 # and team management.\r
 # default: users.conf\r
 #\r
 # RESTART REQUIRED\r
 # BASEFOLDER\r
+# SINCE 1.3.0\r
 realm.salesforce.backingUserService = ${baseFolder}/users.conf\r
 \r
 # Restrict the Salesforce user to members of this org.\r
index 2c64570d4fbada85231ffe9b3cc6d9c6688e6d97..f3ad5facbc4420e53b6a46e63cbd9a60ecebc1a6 100644 (file)
@@ -480,7 +480,7 @@ public class Constants {
        }\r
        \r
        public static enum AccountType {\r
-               LOCAL, LDAP, REDMINE, SALESFORCE;\r
+               LOCAL, LDAP, REDMINE, SALESFORCE, WINDOWS;\r
                \r
                public boolean isLocal() {\r
                        return this == LOCAL;\r
diff --git a/src/main/java/com/gitblit/WindowsUserService.java b/src/main/java/com/gitblit/WindowsUserService.java
new file mode 100644 (file)
index 0000000..4830297
--- /dev/null
@@ -0,0 +1,194 @@
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.File;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import waffle.windows.auth.IWindowsAccount;\r
+import waffle.windows.auth.IWindowsAuthProvider;\r
+import waffle.windows.auth.IWindowsComputer;\r
+import waffle.windows.auth.IWindowsIdentity;\r
+import waffle.windows.auth.impl.WindowsAuthProviderImpl;\r
+\r
+import com.gitblit.Constants.AccountType;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.sun.jna.platform.win32.Win32Exception;\r
+\r
+/**\r
+ * Implementation of a Windows user service.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class WindowsUserService extends GitblitUserService {\r
+\r
+    private final Logger logger = LoggerFactory.getLogger(WindowsUserService.class);\r
+\r
+    private IStoredSettings settings;\r
+    \r
+    private IWindowsAuthProvider waffle;\r
+\r
+    public WindowsUserService() {\r
+        super();\r
+    }\r
+\r
+    @Override\r
+    public void setup(IStoredSettings settings) {\r
+        this.settings = settings;\r
+\r
+        String file = settings.getString(Keys.realm.windows.backingUserService, "${baseFolder}/users.conf");\r
+        File realmFile = GitBlit.getFileOrFolder(file);\r
+\r
+        serviceImpl = createUserService(realmFile);\r
+        logger.info("Windows User Service backed by " + serviceImpl.toString());\r
+        \r
+        waffle = new WindowsAuthProviderImpl();\r
+        IWindowsComputer computer = waffle.getCurrentComputer();\r
+        logger.info("      name = " + computer.getComputerName());\r
+        logger.info("    status = " + describeJoinStatus(computer.getJoinStatus()));\r
+        logger.info("  memberOf = " + computer.getMemberOf());\r
+        //logger.info("  groups     = " + Arrays.asList(computer.getGroups()));\r
+    }\r
+    \r
+    protected String describeJoinStatus(String value) {\r
+       if ("NetSetupUnknownStatus".equals(value)) {\r
+               return "unknown";\r
+       } else if ("NetSetupUnjoined".equals(value)) {\r
+               return "not joined";\r
+       } else if ("NetSetupWorkgroupName".equals(value)) {\r
+               return "joined to a workgroup";\r
+       } else if ("NetSetupDomainName".equals(value)) {\r
+               return "joined to a domain";\r
+       }\r
+       return value;\r
+    }\r
+\r
+    @Override\r
+    public boolean supportsCredentialChanges() {\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public boolean supportsDisplayNameChanges() {\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public boolean supportsEmailAddressChanges() {\r
+        return true;\r
+    }\r
+\r
+    @Override\r
+    public boolean supportsTeamMembershipChanges() {\r
+        return true;\r
+    }\r
+    \r
+        @Override\r
+       protected AccountType getAccountType() {\r
+               return AccountType.WINDOWS;\r
+       }\r
+\r
+    @Override\r
+    public UserModel authenticate(String username, char[] password) {\r
+               if (isLocalAccount(username)) {\r
+                       // local account, bypass Windows authentication\r
+                       return super.authenticate(username, password);\r
+               }\r
+\r
+               String defaultDomain = settings.getString(Keys.realm.windows.defaultDomain, null);\r
+               if (StringUtils.isEmpty(defaultDomain)) {\r
+                       // ensure that default domain is null\r
+                       defaultDomain = null;\r
+               }\r
+\r
+               if (defaultDomain != null) {\r
+                       // sanitize username\r
+                       if (username.startsWith(defaultDomain + "\\")) {\r
+                               // strip default domain from domain\ username\r
+                               username = username.substring(defaultDomain.length() + 1);\r
+                       } else if (username.endsWith("@" + defaultDomain)) {\r
+                               // strip default domain from username@domain\r
+                               username = username.substring(0, username.lastIndexOf('@'));\r
+                       }\r
+               }\r
+\r
+               IWindowsIdentity identity = null;\r
+               try {\r
+                       if (username.indexOf('@') > -1 || username.indexOf('\\') > -1) {\r
+                               // manually specified domain\r
+                               identity = waffle.logonUser(username, new String(password));\r
+                       } else {\r
+                               // no domain specified, use default domain\r
+                               identity = waffle.logonDomainUser(username, defaultDomain, new String(password));\r
+                       }\r
+               } catch (Win32Exception e) {\r
+                       logger.error(e.getMessage());\r
+                       return null;\r
+               }\r
+\r
+               if (identity.isGuest() && !settings.getBoolean(Keys.realm.windows.allowGuests, false)) {\r
+                       logger.warn("Guest account access is disabled");\r
+                       identity.dispose();\r
+                       return null;\r
+               }\r
+               \r
+        UserModel user = getUserModel(username);\r
+        if (user == null)      // create user object for new authenticated user\r
+               user = new UserModel(username.toLowerCase());\r
+\r
+        // create a user cookie\r
+        if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {\r
+               user.cookie = StringUtils.getSHA1(user.username + new String(password));\r
+        }\r
+\r
+        // update user attributes from Windows identity\r
+        user.accountType = getAccountType();\r
+        String fqn = identity.getFqn();\r
+        if (fqn.indexOf('\\') > -1) {\r
+               user.displayName = fqn.substring(fqn.lastIndexOf('\\') + 1);\r
+        } else {\r
+               user.displayName = fqn;\r
+        }\r
+        user.password = Constants.EXTERNAL_ACCOUNT;\r
+\r
+        Set<String> groupNames = new TreeSet<String>();\r
+               for (IWindowsAccount group : identity.getGroups()) {\r
+                       groupNames.add(group.getFqn());\r
+        }\r
+        \r
+        if (groupNames.contains("BUILTIN\\Administrators")) {\r
+               // local administrator\r
+               user.canAdmin = true;\r
+        }\r
+        \r
+        // TODO consider mapping Windows groups to teams\r
+\r
+        // push the changes to the backing user service\r
+        super.updateUserModel(user);\r
+\r
+\r
+        // cleanup resources\r
+        identity.dispose();\r
+        \r
+        return user;\r
+    }\r
+}\r
index 7171197cc92551dec255da5a7b86869bf062e516..85c3fe2d3366d4aa5477bc3742c5852dce8d78d4 100644 (file)
@@ -48,6 +48,9 @@ The following dependencies are automatically downloaded by Gitblit GO (or alread
 - [Commons-Compress](http://commons.apache.org/compress) (Apache 2.0)\r
 - [XZ for Java](http://tukaani.org/xz/java.html) (Public Domain)\r
 - [FreeMarker](http://www.freemarker.org) (modified BSD)\r
+- [Waffle](http://dblock.github.io/waffle) (EPL 1.0)\r
+- [JNA](https://github.com/twall/jna) (LGPL 2.1)\r
+- [Guava](https://code.google.com/p/guava-libraries) (Apache 2.0)\r
 \r
 ### Other Build Dependencies\r
 - [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed)\r
index fd89ad8728732d45ba5f3d4d3886b4117832a7db..b9b44a52579e1a530f9c3339f7aa10bc222c9476 100644 (file)
@@ -36,6 +36,7 @@
 - LDAP authentication and optional LDAP-controlled Team memberships\r
 - Redmine authentication\r
 - Salesforce.com authentication\r
+- Windows authentication\r
 - Gravatar integration\r
 - Git-notes display support\r
 - Submodule support\r