Skip to content

Commit

Permalink
GEODE-1647: Add Integrated Security to Peer Authentication
Browse files Browse the repository at this point in the history
* This closes apache#210
  • Loading branch information
gracemeilen authored and jinmeiliao committed Jul 20, 2016
1 parent 7c2e219 commit 16b7356
Show file tree
Hide file tree
Showing 37 changed files with 385 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,15 @@
import static com.gemstone.gemfire.distributed.ConfigurationProperties.*;
import static com.gemstone.gemfire.internal.i18n.LocalizedStrings.*;

import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Properties;
import java.util.Set;

import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.distributed.internal.membership.NetView;
import com.gemstone.gemfire.distributed.internal.membership.gms.Services;
import com.gemstone.gemfire.distributed.internal.membership.gms.interfaces.Authenticator;
import com.gemstone.gemfire.internal.ClassLoadUtil;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.logging.InternalLogWriter;
import com.gemstone.gemfire.internal.security.GeodeSecurityUtil;
Expand All @@ -44,15 +40,13 @@

public class GMSAuthenticator implements Authenticator {

private final static String secPrefix = DistributionConfig.GEMFIRE_PREFIX + "sys.security-";
private final static int gemfireSysPrefixLen = (DistributionConfig.GEMFIRE_PREFIX + "sys.").length();

private Services services;
private Properties securityProps = getSecurityProps();
private Properties securityProps;

@Override
public void init(Services s) {
this.services = s;
this.securityProps = this.services.getConfig().getDistributionConfig().getSecurityProps();
}

@Override
Expand Down Expand Up @@ -105,57 +99,59 @@ public void memberSuspected(InternalDistributedMember initiator, InternalDistrib
* this will be removed since return string is used for failure
*/
@Override
public String authenticate(InternalDistributedMember member, Object credentials) throws AuthenticationFailedException {
public String authenticate(InternalDistributedMember member, Properties credentials) throws AuthenticationFailedException {
return authenticate(member, credentials, this.securityProps, this.services.getJoinLeave().getMemberID());
}

/**
* Method is package protected to be used in testing.
*/
String authenticate(DistributedMember member, Object credentials, Properties secProps, DistributedMember localMember) throws AuthenticationFailedException {

String authMethod = secProps.getProperty(SECURITY_PEER_AUTHENTICATOR);
if (authMethod == null || authMethod.length() == 0) {
String authenticate(DistributedMember member, Properties credentials, Properties secProps, DistributedMember localMember) throws AuthenticationFailedException {
if(!GeodeSecurityUtil.isPeerSecurityRequired()){
return null;
}

InternalLogWriter securityLogWriter = this.services.getSecurityLogWriter();
String failMsg = null;
if (credentials != null) {
try {
invokeAuthenticator(authMethod, member, credentials);

} catch (Exception ex) {
securityLogWriter.warning(AUTH_PEER_AUTHENTICATION_FAILED_WITH_EXCEPTION, new Object[] {member, authMethod, ex.getLocalizedMessage()}, ex);
failMsg = AUTH_PEER_AUTHENTICATION_FAILED.toLocalizedString(localMember);
}

} else { // No credentials - need to send failure message
securityLogWriter.warning(AUTH_PEER_AUTHENTICATION_MISSING_CREDENTIALS, new Object[] {member, authMethod});
failMsg = AUTH_PEER_AUTHENTICATION_MISSING_CREDENTIALS.toLocalizedString(member, authMethod);
if(credentials == null){
securityLogWriter.warning(AUTH_PEER_AUTHENTICATION_MISSING_CREDENTIALS, member);
return AUTH_PEER_AUTHENTICATION_MISSING_CREDENTIALS.toLocalizedString(member);
}

String failMsg = null;
try {
if(GeodeSecurityUtil.isIntegratedSecurity()){
String username = credentials.getProperty("security-username");
String password = credentials.getProperty("security-password");
GeodeSecurityUtil.login(username, password);
}
else {
invokeAuthenticator(secProps, member, credentials);
}
} catch (Exception ex) {
securityLogWriter.warning(AUTH_PEER_AUTHENTICATION_FAILED_WITH_EXCEPTION, new Object[] {
member, ex.getLocalizedMessage()
}, ex);
failMsg = AUTH_PEER_AUTHENTICATION_FAILED.toLocalizedString(localMember);
}
return failMsg;
}


/**
* Method is package protected to be used in testing.
*/
Principal invokeAuthenticator(String authMethod, DistributedMember member, Object credentials) throws AuthenticationFailedException {
Principal invokeAuthenticator(Properties securityProps, DistributedMember member, Properties credentials) throws AuthenticationFailedException {
String authMethod = securityProps.getProperty(SECURITY_PEER_AUTHENTICATOR);
com.gemstone.gemfire.security.Authenticator auth = null;

try {
Method getter = ClassLoadUtil.methodFromName(authMethod);
auth = (com.gemstone.gemfire.security.Authenticator) getter.invoke(null, (Object[]) null);
if (auth == null) {
throw new AuthenticationFailedException(HandShake_AUTHENTICATOR_INSTANCE_COULD_NOT_BE_OBTAINED.toLocalizedString());
}
auth = GeodeSecurityUtil.getObjectOfTypeFromFactoryMethod(authMethod, com.gemstone.gemfire.security.Authenticator .class);

LogWriter logWriter = this.services.getLogWriter();
LogWriter securityLogWriter = this.services.getSecurityLogWriter();

auth.init(this.securityProps, logWriter, securityLogWriter); // this.securityProps contains security-ldap-basedn but security-ldap-baseDomainName is expected
return auth.authenticate((Properties) credentials, member);
return auth.authenticate(credentials, member);

} catch (GemFireSecurityException gse) {
throw gse;
Expand All @@ -173,10 +169,10 @@ Principal invokeAuthenticator(String authMethod, DistributedMember member, Objec
*
* @param member
* the target distributed member
* @return the credential object
* @return the credentials
*/
@Override
public Object getCredentials(InternalDistributedMember member) {
public Properties getCredentials(InternalDistributedMember member) {
try {
return getCredentials(member, securityProps);

Expand Down Expand Up @@ -217,16 +213,11 @@ Properties getCredentials(DistributedMember member, Properties secProps) {
return credentials;
}

/**
* For testing only.
*/
Properties getSecurityProps() {
Properties props = new Properties();
Set keys = System.getProperties().keySet();
for (Object key: keys) {
String propKey = (String) key;
if (propKey.startsWith(secPrefix)) {
props.setProperty(propKey.substring(gemfireSysPrefixLen), System.getProperty(propKey));
}
}
return props;
return this.securityProps;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
*/
package com.gemstone.gemfire.distributed.internal.membership.gms.interfaces;

import java.util.Properties;

import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.security.AuthenticationFailedException;

public interface Authenticator extends Service {

String authenticate(InternalDistributedMember m, Object credentials) throws AuthenticationFailedException;
String authenticate(InternalDistributedMember m, Properties credentials) throws AuthenticationFailedException;

Object getCredentials(InternalDistributedMember m);
Properties getCredentials(InternalDistributedMember m);
}
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ private void processJoinRequest(JoinRequestMessage incomingRequest) {
Object creds = incomingRequest.getCredentials();
String rejection = null;
try {
rejection = services.getAuthenticator().authenticate(incomingRequest.getMemberID(), creds);
rejection = services.getAuthenticator().authenticate(incomingRequest.getMemberID(), (Properties)creds);
} catch (Exception e) {
rejection = e.getMessage();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ public void rejectedExecution(Runnable r, ThreadPoolExecutor pool) {
this.hsPool = tmp_hsPool;
}

isAuthenticationRequired = GeodeSecurityUtil.isSecurityRequired();
isAuthenticationRequired = GeodeSecurityUtil.isClientSecurityRequired();

isIntegratedSecurity = GeodeSecurityUtil.isIntegratedSecurity();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ public Properties readCredential(DataInputStream dis, DataOutputStream dos, Dist
throws GemFireSecurityException, IOException {

Properties credentials = null;
boolean requireAuthentication = GeodeSecurityUtil.isSecurityRequired();
boolean requireAuthentication = GeodeSecurityUtil.isClientSecurityRequired();
try {
byte secureMode = dis.readByte();
if (secureMode == CREDENTIALS_NONE) {
Expand Down Expand Up @@ -1161,7 +1161,7 @@ public static void initDHKeys(DistributionConfig config) throws Exception {
// non-blank setting for DH symmetric algo, or this is a server
// that has authenticator defined.
if ((dhSKAlgo != null && dhSKAlgo.length() > 0)
|| GeodeSecurityUtil.isSecurityRequired()) {
|| GeodeSecurityUtil.isClientSecurityRequired()) {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
DHParameterSpec dhSpec = new DHParameterSpec(dhP, dhG, dhL);
keyGen.initialize(dhSpec);
Expand Down Expand Up @@ -1632,7 +1632,7 @@ public static Properties readCredentials(DataInputStream dis,
DataOutputStream dos, DistributedSystem system)
throws GemFireSecurityException, IOException {

boolean requireAuthentication = GeodeSecurityUtil.isSecurityRequired();
boolean requireAuthentication = GeodeSecurityUtil.isClientSecurityRequired();
Properties credentials = null;
try {
byte secureMode = dis.readByte();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3731,9 +3731,9 @@ public class LocalizedStrings {
public static final StringId Network_partition_detected = new StringId(6607, "Exiting due to possible network partition event due to loss of {0} cache processes: {1}");

// GMSAuthenticator
public static final StringId AUTH_PEER_AUTHENTICATION_FAILED_WITH_EXCEPTION = new StringId(6608, "Authentication failed for [{0}] using Authenticator [{1}]. {2}");
public static final StringId AUTH_PEER_AUTHENTICATION_FAILED_WITH_EXCEPTION = new StringId(6608, "Authentication failed for [{0}]. {1}");
public static final StringId AUTH_PEER_AUTHENTICATION_FAILED = new StringId(6609, "Authentication failed. See coordinator [{0}] logs for details.");
public static final StringId AUTH_PEER_AUTHENTICATION_MISSING_CREDENTIALS = new StringId(6610, "Failed to find credentials from [{0}] using Authenticator [{1}]");
public static final StringId AUTH_PEER_AUTHENTICATION_MISSING_CREDENTIALS = new StringId(6610, "Failed to find credentials from [{0}]");
public static final StringId AUTH_FAILED_TO_ACQUIRE_AUTHINITIALIZE_INSTANCE = new StringId(6611, "AuthInitialize instance could not be obtained");
public static final StringId AUTH_FAILED_TO_OBTAIN_CREDENTIALS_IN_0_USING_AUTHINITIALIZE_1_2 = new StringId(6612, "Failed to obtain credentials using AuthInitialize [{1}]. {2}");
public static final StringId DistributedSystem_BACKUP_ALREADY_IN_PROGRESS = new StringId(6613, "A backup is already in progress.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class GeodeSecurityUtil {
* @return the shiro subject, null if security is not enabled
*/
public static Subject getSubject() {
if (!isIntegratedSecure) {
if (!isIntegratedSecurity) {
return null;
}

Expand Down Expand Up @@ -102,7 +102,7 @@ public static Subject getSubject() {
* @return null if security is not enabled, otherwise return a shiro subject
*/
public static Subject login(String username, String password) {
if (!isIntegratedSecure) {
if (!isIntegratedSecurity) {
return null;
}

Expand Down Expand Up @@ -271,8 +271,9 @@ public static void authorize(GeodePermission context) {

private static PostProcessor postProcessor;
private static SecurityManager securityManager;
private static boolean isSecure;
private static boolean isIntegratedSecure;
private static boolean isIntegratedSecurity;
private static boolean isClientAuthenticator;
private static boolean isPeerAuthenticator;

/**
* initialize Shiro's Security Manager and Security Utilities
Expand All @@ -286,6 +287,7 @@ public static void initSecurity(Properties securityProps) {
String shiroConfig = securityProps.getProperty(SECURITY_SHIRO_INIT);
String securityConfig = securityProps.getProperty(SECURITY_MANAGER);
String clientAuthenticatorConfig = securityProps.getProperty(SECURITY_CLIENT_AUTHENTICATOR);
String peerAuthenticatorConfig = securityProps.getProperty(SECURITY_PEER_AUTHENTICATOR);

if (!StringUtils.isBlank(shiroConfig)) {
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:" + shiroConfig);
Expand All @@ -299,8 +301,7 @@ public static void initSecurity(Properties securityProps) {

org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
isSecure = true;
isIntegratedSecure = true;
isIntegratedSecurity = true;
}
// only set up shiro realm if user has implemented SecurityManager
else if (!StringUtils.isBlank(securityConfig)) {
Expand All @@ -309,17 +310,18 @@ else if (!StringUtils.isBlank(securityConfig)) {
Realm realm = new CustomAuthRealm(securityManager);
org.apache.shiro.mgt.SecurityManager shiroManager = new DefaultSecurityManager(realm);
SecurityUtils.setSecurityManager(shiroManager);
isSecure = true;
isIntegratedSecure = true;
isIntegratedSecurity = true;
}
else if( !StringUtils.isBlank(clientAuthenticatorConfig)) {
isSecure = true;
isIntegratedSecure = false;
isClientAuthenticator = true;
}
else if (!StringUtils.isBlank(peerAuthenticatorConfig)) {
isPeerAuthenticator = true;
}
else {
SecurityUtils.setSecurityManager(null);
isSecure = false;
isIntegratedSecure = false;
isIntegratedSecurity = false;
isClientAuthenticator = false;
isPeerAuthenticator = false;
}

// this initializes the post processor
Expand All @@ -344,16 +346,17 @@ public static void close() {
postProcessor = null;
}
ThreadContext.remove();
isSecure = false;
isIntegratedSecure = false;
isIntegratedSecurity = false;
isClientAuthenticator = false;
isPeerAuthenticator = false;
}

/**
* postProcess call already has this logic built in, you don't need to call this everytime you call postProcess.
* But if your postProcess is pretty involved with preparations and you need to bypass it entirely, call this first.
*/
public static boolean needPostProcess(){
return (isIntegratedSecure && postProcessor != null);
return (isIntegratedSecurity && postProcessor != null);
}

public static Object postProcess(String regionPath, Object key, Object result){
Expand Down Expand Up @@ -446,12 +449,16 @@ public static SecurityManager getSecurityManager(){
}


public static boolean isSecurityRequired(){
return isSecure;
public static boolean isClientSecurityRequired() {
return isClientAuthenticator || isIntegratedSecurity;
}

public static boolean isPeerSecurityRequired() {
return isPeerAuthenticator || isIntegratedSecurity;
}

public static boolean isIntegratedSecurity(){
return isIntegratedSecure;
return isIntegratedSecurity;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.StringWriter;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -154,6 +155,10 @@ private static void readUsers(Map<String, User> acl, JsonNode node, Map<String,
}

private static Map<String, Role> readRoles(JsonNode jsonNode) {
if (jsonNode.get("roles") == null) {
return Collections.EMPTY_MAP;
}

Map<String, Role> roleMap = new HashMap<>();
for (JsonNode r : jsonNode.get("roles")) {
Role role = new Role();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import com.gemstone.gemfire.InternalGemFireException;
import com.gemstone.gemfire.UnmodifiableException;
import com.gemstone.gemfire.internal.ConfigSource;
import com.gemstone.gemfire.management.internal.security.JSONAuthorization;
import com.gemstone.gemfire.security.JSONAuthorization;
import com.gemstone.gemfire.test.junit.categories.UnitTest;

@Category(UnitTest.class)
Expand Down
Loading

0 comments on commit 16b7356

Please sign in to comment.