/*******************************************************************************
*
* Copyright (c) 2004-2012, Oracle Corporation
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Kohsuke Kawaguchi, Winston Prakash
*
*******************************************************************************/
package hudson.security;
import hudson.EnvVars;
import hudson.Functions;
import hudson.model.Descriptor;
import hudson.Util;
import hudson.Extension;
import hudson.util.FormValidation;
import java.util.ArrayList;
import org.eclipse.hudson.jna.NativeAccessException;
import org.eclipse.hudson.jna.NativeUtils;
import java.util.Arrays;
import java.util.List;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* {@link SecurityRealm} that uses Unix PAM authentication.
*
* @author Kohsuke Kawaguchi
* @since 1.282
*/
public class PAMSecurityRealm extends AbstractPasswordBasedSecurityRealm {
public final String serviceName;
@DataBoundConstructor
public PAMSecurityRealm(String serviceName) {
serviceName = Util.fixEmptyAndTrim(serviceName);
if (serviceName == null) {
serviceName = "sshd"; // use sshd as the default
}
this.serviceName = serviceName;
}
@Override
protected UserDetails authenticate(String username, String password) throws AuthenticationException {
try {
Set<String> grps = NativeUtils.getInstance().pamAuthenticate(serviceName, username, password);
List<GrantedAuthority> groups = new ArrayList<GrantedAuthority>();
for (String g : grps) {
groups.add(new GrantedAuthorityImpl(g));
}
groups.add(AUTHENTICATED_AUTHORITY);
EnvVars.setHudsonUserEnvVar(username);
return new User(username, "", true, true, true, true, groups);
} catch (NativeAccessException exc) {
throw new BadCredentialsException(exc.getMessage(), exc);
}
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
try {
if (!NativeUtils.getInstance().checkUnixUser(username)) {
throw new UsernameNotFoundException("No such Unix user: " + username);
}
} catch (NativeAccessException exc) {
throw new DataAccessException("Failed to find Unix User", exc) {
};
}
// return some dummy instance
return new User(username, "", true, true, true, true,
Arrays.asList(new GrantedAuthority[]{AUTHENTICATED_AUTHORITY}));
}
@Override
public GroupDetails loadGroupByGroupname(final String groupname) throws UsernameNotFoundException, DataAccessException {
try {
if (!NativeUtils.getInstance().checkUnixGroup(groupname)) {
throw new UsernameNotFoundException("No such Unix group: " + groupname);
}
} catch (NativeAccessException exc) {
throw new DataAccessException("Failed to find Unix Group", exc) {
};
}
return new GroupDetails() {
@Override
public String getName() {
return groupname;
}
};
}
public static final class DescriptorImpl extends Descriptor<SecurityRealm> {
@Override
public String getDisplayName() {
return Messages.PAMSecurityRealm_DisplayName();
}
public FormValidation doTest() {
try {
String message = NativeUtils.getInstance().checkPamAuthentication();
if (message.startsWith("Error:")) {
return FormValidation.error(message.replaceFirst("Error:", ""));
} else {
return FormValidation.ok(message);
}
} catch (NativeAccessException exc) {
return FormValidation.error("Native Support for PAM Authentication not available.");
}
}
}
@Extension
public static DescriptorImpl install() {
if (!Functions.isWindows()) {
return new DescriptorImpl();
}
return null;
}
}