]> source.dussan.org Git - gitblit.git/commitdiff
Added PAMUserService for local Linux/Unix/MacOSX account authentication
authorJames Moger <james.moger@gitblit.com>
Tue, 23 Jul 2013 21:48:37 +0000 (17:48 -0400)
committerJames Moger <james.moger@gitblit.com>
Tue, 23 Jul 2013 21:48:55 +0000 (17:48 -0400)
.classpath
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/PAMUserService.java [new file with mode: 0644]
src/site/features.mkd
src/site/setup_authentication.mkd
src/site/siteindex.mkd

index 179bfdf491e8663f93283c2c98b9a2a954cd3c1f..bfdaad2865d6c7f06032f43d28228cb8ca429c16 100644 (file)
@@ -45,6 +45,7 @@
        <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/libpam4j-1.7.jar" sourcepath="ext/src/libpam4j-1.7.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" />
index d15d916a8747fda81753b67ae7d9a75f74044a2a..f417d3c6b6b1f2087253760d869d65e32019bcd1 100644 (file)
@@ -150,6 +150,7 @@ dependencies:
 - 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
+- compile 'org.kohsuke:libpam4j:1.7' :war
 - test 'junit'
 # Dependencies for Selenium web page testing
 - test 'org.seleniumhq.selenium:selenium-java:${selenium.version}' @jar
index 4939f7f99183b75c9531aed7f5129103ccb261d5..d42750dc943b75a9924f8c27221d8556bf252c69 100644 (file)
--- a/build.xml
+++ b/build.xml
                        <class name="com.gitblit.RedmineUserService" />\r
                        <class name="com.gitblit.SalesforceUserService" />\r
                        <class name="com.gitblit.WindowsUserService" />\r
+                       <class name="com.gitblit.PAMUserService" />\r
                </mx:genjar>\r
 \r
                <!-- Build the WAR file -->\r
                        <class name="com.gitblit.RedmineUserService" />\r
                        <class name="com.gitblit.SalesforceUserService" />\r
                        <class name="com.gitblit.WindowsUserService" />\r
+                       <class name="com.gitblit.PAMUserService" />\r
                </mx:genjar>\r
 \r
                <!-- Build Express Zip file -->\r
index 85ba7791a8779d2caf8f8ef5cef77b2f913d4b83..c4ba270cee7607f18031310bac16133f36483604 100644 (file)
         </SOURCES>
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library name="libpam4j-1.7.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/libpam4j-1.7.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/libpam4j-1.7.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="junit-4.11.jar">
         <CLASSES>
index 17894a50ef429bdc14e9423b439e201d101f0cb4..2f601eb599e61d1a76be9ce3ab35833f7dff32fa 100644 (file)
@@ -32,9 +32,12 @@ r18: {
     additions:
        - Added optional browser-side page caching using Last-Modified and Cache-Control for the dashboard, activity, project, and several repository pages
        - Added a GET_USER request type for the RPC mechanism (issue-275)
+       - Added PAMUserService to authenticate against a local Linux/Unix/MacOSX server
     dependencyChanges: ~
        settings:
        - { name: 'web.pageCacheExpires', defaultValue: 0 }
+       - { name: 'realm.pam.backingUserService', defaultValue: 'users.conf' }
+       - { name: 'realm.pam.serviceName', defaultValue: 'system-auth' }
     contributors:
        - Rainer Alföldi 
        - Liyu Wang
index 1ee6d8007d38ed66a6c8072eaa376efb43a8b3c8..7f2f8b48ca5fa256a604f66f4e9bcade5d5bb95c 100644 (file)
@@ -501,6 +501,7 @@ web.projectsFile = ${baseFolder}/projects.conf
 #    com.gitblit.RedmineUserService\r
 #    com.gitblit.SalesforceUserService\r
 #    com.gitblit.WindowsUserService\r
+#    com.gitblit.PAMUserService\r
 #\r
 # Any custom user service implementation must have a public default constructor.\r
 #\r
@@ -1212,6 +1213,21 @@ realm.windows.allowGuests = false
 # SINCE 1.3.0\r
 realm.windows.defaultDomain =\r
 \r
+# The PAMUserService 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.1\r
+realm.pam.backingUserService = ${baseFolder}/users.conf\r
+\r
+# The PAM service name for authentication.\r
+# default: system-auth\r
+#\r
+# SINCE 1.3.1\r
+realm.pam.serviceName = system-auth\r
+\r
 # The SalesforceUserService must be backed by another user service for standard user\r
 # and team management.\r
 # default: users.conf\r
index 67f9d65d3c8e7ca14e08ff4ed44a353259903048..c180bafb7ee0d5b093a9c44d728bea1cc28f102c 100644 (file)
@@ -480,7 +480,7 @@ public class Constants {
        }\r
        \r
        public static enum AccountType {\r
-               LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS;\r
+               LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM;\r
                \r
                public boolean isLocal() {\r
                        return this == LOCAL;\r
diff --git a/src/main/java/com/gitblit/PAMUserService.java b/src/main/java/com/gitblit/PAMUserService.java
new file mode 100644 (file)
index 0000000..692b0f4
--- /dev/null
@@ -0,0 +1,142 @@
+/*\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
+\r
+import org.jvnet.libpam.PAM;\r
+import org.jvnet.libpam.PAMException;\r
+import org.jvnet.libpam.impl.CLibrary;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\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
+\r
+/**\r
+ * Implementation of a PAM user service for Linux/Unix/MacOSX.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class PAMUserService extends GitblitUserService {\r
+\r
+    private final Logger logger = LoggerFactory.getLogger(PAMUserService.class);\r
+\r
+    private IStoredSettings settings;\r
+    \r
+    public PAMUserService() {\r
+        super();\r
+    }\r
+\r
+    @Override\r
+    public void setup(IStoredSettings settings) {\r
+        this.settings = settings;\r
+\r
+        String file = settings.getString(Keys.realm.pam.backingUserService, "${baseFolder}/users.conf");\r
+        File realmFile = GitBlit.getFileOrFolder(file);\r
+\r
+        serviceImpl = createUserService(realmFile);\r
+        logger.info("PAM User Service backed by " + serviceImpl.toString());\r
+        \r
+        // Try to identify the passwd database\r
+        String [] files = { "/etc/shadow", "/etc/master.passwd" };\r
+               File passwdFile = null;\r
+               for (String name : files) {\r
+                       File f = new File(name);\r
+                       if (f.exists()) {\r
+                               passwdFile = f;\r
+                               break;\r
+                       }\r
+               }\r
+               if (passwdFile == null) {\r
+                       logger.error("PAM User Service could not find a passwd database!");\r
+               } else if (!passwdFile.canRead()) {\r
+                       logger.error("PAM User Service can not read passwd database {}! PAM authentications may fail!", passwdFile);\r
+               }\r
+    }\r
+    \r
+    @Override\r
+    public boolean supportsCredentialChanges() {\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public boolean supportsDisplayNameChanges() {\r
+        return true;\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.PAM;\r
+       }\r
+\r
+    @Override\r
+    public UserModel authenticate(String username, char[] password) {\r
+               if (isLocalAccount(username)) {\r
+                       // local account, bypass PAM authentication\r
+                       return super.authenticate(username, password);\r
+               }\r
+               \r
+               if (CLibrary.libc.getpwnam(username) == null) {\r
+                       logger.warn("Can not get PAM passwd for " + username);\r
+                       return null;\r
+               }\r
+\r
+               PAM pam = null;\r
+               try {\r
+                       String serviceName = settings.getString(Keys.realm.pam.serviceName, "system-auth");\r
+                       pam = new PAM(serviceName);\r
+                       pam.authenticate(username, new String(password));\r
+               } catch (PAMException e) {\r
+                       logger.error(e.getMessage());\r
+                       return null;\r
+               } finally {\r
+                       pam.dispose();\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 UnixUser\r
+        user.accountType = getAccountType();\r
+        user.password = Constants.EXTERNAL_ACCOUNT;\r
+\r
+        // TODO consider mapping PAM groups to teams\r
+\r
+        // push the changes to the backing user service\r
+        super.updateUserModel(user);\r
+        \r
+        return user;\r
+    }\r
+}\r
index b9b44a52579e1a530f9c3339f7aa10bc222c9476..d5bf4103724ed934706440336051e36432242705 100644 (file)
@@ -37,6 +37,7 @@
 - Redmine authentication\r
 - Salesforce.com authentication\r
 - Windows authentication\r
+- PAM authentication\r
 - Gravatar integration\r
 - Git-notes display support\r
 - Submodule support\r
index 7f606180cda5252371f493f714affb12ecd7b8b6..0ec07fa5d6c80d1ac740cf7dc7110a0488565380 100644 (file)
@@ -6,6 +6,7 @@ Gitblit supports additional authentication mechanisms aside from it's internal o
 \r
 * LDAP authentication\r
 * Windows authentication\r
+* PAM authentication\r
 * Redmine auhentication\r
 * Salesforce.com authentication\r
 * Servlet container authentication\r
@@ -83,6 +84,13 @@ Windows authentication is based on the use of Waffle and JNA.  It is known to wo
     realm.userService = com.gitblit.WindowsUserService\r
     realm.windows.defaultDomain =\r
 \r
+### PAM Authentication\r
+\r
+PAM authentication is based on the use of libpam4j and JNA.  To use this service, your Gitblit server must be installed on a Linux/Unix/MacOSX machine and the user that Gitblit runs-as must have root permissions.\r
+\r
+    realm.userService = com.gitblit.PAMUserService\r
+    realm.pam.serviceName = system-auth\r
+\r
 ### Redmine Authentication\r
 \r
 You may authenticate your users against a Redmine installation as long as your Redmine install has properly enabled [API authentication](http://www.redmine.org/projects/redmine/wiki/Rest_Api#Authentication).  This user service only supports user authentication; it does not support team creation based on Redmine groups.  Redmine administrators will also be Gitblit administrators.\r
index f72e9c17ee8e741d92df63a0456875a7ae6e3a19..1b6dcdf480da4954b5df304080a97925ac472f1c 100644 (file)
@@ -68,9 +68,10 @@ Administrators can create and manage all repositories, user accounts, and teams
 - Groovy push hook scripts\r
 - Pluggable user service mechanism\r
     - LDAP authentication with optional LDAP-controlled Team memberships\r
-       - Redmine authentication\r
-       - SalesForce.com authentication\r
-       - Windows authentication\r
+    - Redmine authentication\r
+    - SalesForce.com authentication\r
+    - Windows authentication\r
+    - PAM authentication\r
     - Custom authentication, authorization, and user management\r
 - Rich RSS feeds\r
 - JSON-based RPC mechanism\r