From: James Moger Date: Tue, 23 Jul 2013 21:48:37 +0000 (-0400) Subject: Added PAMUserService for local Linux/Unix/MacOSX account authentication X-Git-Tag: v1.3.1~9 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2659e7430ff27adf324abad961bfe461599f1618;p=gitblit.git Added PAMUserService for local Linux/Unix/MacOSX account authentication --- diff --git a/.classpath b/.classpath index 179bfdf4..bfdaad28 100644 --- a/.classpath +++ b/.classpath @@ -45,6 +45,7 @@ + diff --git a/build.moxie b/build.moxie index d15d916a..f417d3c6 100644 --- a/build.moxie +++ b/build.moxie @@ -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 diff --git a/build.xml b/build.xml index 4939f7f9..d42750dc 100644 --- a/build.xml +++ b/build.xml @@ -302,6 +302,7 @@ + @@ -421,6 +422,7 @@ + diff --git a/gitblit.iml b/gitblit.iml index 85ba7791..c4ba270c 100644 --- a/gitblit.iml +++ b/gitblit.iml @@ -468,6 +468,17 @@ + + + + + + + + + + + diff --git a/releases.moxie b/releases.moxie index 17894a50..2f601eb5 100644 --- a/releases.moxie +++ b/releases.moxie @@ -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 diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties index 1ee6d800..7f2f8b48 100644 --- a/src/main/distrib/data/gitblit.properties +++ b/src/main/distrib/data/gitblit.properties @@ -501,6 +501,7 @@ web.projectsFile = ${baseFolder}/projects.conf # com.gitblit.RedmineUserService # com.gitblit.SalesforceUserService # com.gitblit.WindowsUserService +# com.gitblit.PAMUserService # # Any custom user service implementation must have a public default constructor. # @@ -1212,6 +1213,21 @@ realm.windows.allowGuests = false # SINCE 1.3.0 realm.windows.defaultDomain = +# The PAMUserService must be backed by another user service for standard user +# and team management. +# default: users.conf +# +# RESTART REQUIRED +# BASEFOLDER +# SINCE 1.3.1 +realm.pam.backingUserService = ${baseFolder}/users.conf + +# The PAM service name for authentication. +# default: system-auth +# +# SINCE 1.3.1 +realm.pam.serviceName = system-auth + # The SalesforceUserService must be backed by another user service for standard user # and team management. # default: users.conf diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java index 67f9d65d..c180bafb 100644 --- a/src/main/java/com/gitblit/Constants.java +++ b/src/main/java/com/gitblit/Constants.java @@ -480,7 +480,7 @@ public class Constants { } public static enum AccountType { - LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS; + LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM; public boolean isLocal() { return this == LOCAL; diff --git a/src/main/java/com/gitblit/PAMUserService.java b/src/main/java/com/gitblit/PAMUserService.java new file mode 100644 index 00000000..692b0f4a --- /dev/null +++ b/src/main/java/com/gitblit/PAMUserService.java @@ -0,0 +1,142 @@ +/* + * Copyright 2013 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 org.jvnet.libpam.PAM; +import org.jvnet.libpam.PAMException; +import org.jvnet.libpam.impl.CLibrary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.Constants.AccountType; +import com.gitblit.models.UserModel; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.StringUtils; + +/** + * Implementation of a PAM user service for Linux/Unix/MacOSX. + * + * @author James Moger + */ +public class PAMUserService extends GitblitUserService { + + private final Logger logger = LoggerFactory.getLogger(PAMUserService.class); + + private IStoredSettings settings; + + public PAMUserService() { + super(); + } + + @Override + public void setup(IStoredSettings settings) { + this.settings = settings; + + String file = settings.getString(Keys.realm.pam.backingUserService, "${baseFolder}/users.conf"); + File realmFile = GitBlit.getFileOrFolder(file); + + serviceImpl = createUserService(realmFile); + logger.info("PAM User Service backed by " + serviceImpl.toString()); + + // Try to identify the passwd database + String [] files = { "/etc/shadow", "/etc/master.passwd" }; + File passwdFile = null; + for (String name : files) { + File f = new File(name); + if (f.exists()) { + passwdFile = f; + break; + } + } + if (passwdFile == null) { + logger.error("PAM User Service could not find a passwd database!"); + } else if (!passwdFile.canRead()) { + logger.error("PAM User Service can not read passwd database {}! PAM authentications may fail!", passwdFile); + } + } + + @Override + public boolean supportsCredentialChanges() { + return false; + } + + @Override + public boolean supportsDisplayNameChanges() { + return true; + } + + @Override + public boolean supportsEmailAddressChanges() { + return true; + } + + @Override + public boolean supportsTeamMembershipChanges() { + return true; + } + + @Override + protected AccountType getAccountType() { + return AccountType.PAM; + } + + @Override + public UserModel authenticate(String username, char[] password) { + if (isLocalAccount(username)) { + // local account, bypass PAM authentication + return super.authenticate(username, password); + } + + if (CLibrary.libc.getpwnam(username) == null) { + logger.warn("Can not get PAM passwd for " + username); + return null; + } + + PAM pam = null; + try { + String serviceName = settings.getString(Keys.realm.pam.serviceName, "system-auth"); + pam = new PAM(serviceName); + pam.authenticate(username, new String(password)); + } catch (PAMException e) { + logger.error(e.getMessage()); + return null; + } finally { + pam.dispose(); + } + + UserModel user = getUserModel(username); + if (user == null) // create user object for new authenticated user + user = new UserModel(username.toLowerCase()); + + // create a user cookie + if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) { + user.cookie = StringUtils.getSHA1(user.username + new String(password)); + } + + // update user attributes from UnixUser + user.accountType = getAccountType(); + user.password = Constants.EXTERNAL_ACCOUNT; + + // TODO consider mapping PAM groups to teams + + // push the changes to the backing user service + super.updateUserModel(user); + + return user; + } +} diff --git a/src/site/features.mkd b/src/site/features.mkd index b9b44a52..d5bf4103 100644 --- a/src/site/features.mkd +++ b/src/site/features.mkd @@ -37,6 +37,7 @@ - Redmine authentication - Salesforce.com authentication - Windows authentication +- PAM authentication - Gravatar integration - Git-notes display support - Submodule support diff --git a/src/site/setup_authentication.mkd b/src/site/setup_authentication.mkd index 7f606180..0ec07fa5 100644 --- a/src/site/setup_authentication.mkd +++ b/src/site/setup_authentication.mkd @@ -6,6 +6,7 @@ Gitblit supports additional authentication mechanisms aside from it's internal o * LDAP authentication * Windows authentication +* PAM authentication * Redmine auhentication * Salesforce.com authentication * Servlet container authentication @@ -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 realm.windows.defaultDomain = +### PAM Authentication + +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. + + realm.userService = com.gitblit.PAMUserService + realm.pam.serviceName = system-auth + ### Redmine Authentication 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. diff --git a/src/site/siteindex.mkd b/src/site/siteindex.mkd index f72e9c17..1b6dcdf4 100644 --- a/src/site/siteindex.mkd +++ b/src/site/siteindex.mkd @@ -68,9 +68,10 @@ Administrators can create and manage all repositories, user accounts, and teams - Groovy push hook scripts - Pluggable user service mechanism - LDAP authentication with optional LDAP-controlled Team memberships - - Redmine authentication - - SalesForce.com authentication - - Windows authentication + - Redmine authentication + - SalesForce.com authentication + - Windows authentication + - PAM authentication - Custom authentication, authorization, and user management - Rich RSS feeds - JSON-based RPC mechanism