blob: f061240e1464a3843fcdb1629412564c8ea68c0b [file] [log] [blame]
package com.gemstone.gemfire.management.internal.security;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.JMX;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.junit.experimental.categories.Category;
import junit.framework.TestCase;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.operations.OperationContext;
import com.gemstone.gemfire.cache.operations.OperationContext.OperationCode;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.DistributedSystem;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.internal.AvailablePort;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.management.DistributedSystemMXBean;
import com.gemstone.gemfire.management.MemberMXBean;
import com.gemstone.gemfire.management.internal.MBeanJMXAdapter;
import com.gemstone.gemfire.management.internal.security.ResourceOperationContext.ResourceOperationCode;
import com.gemstone.gemfire.security.AccessControl;
import com.gemstone.gemfire.security.AuthenticationFailedException;
import com.gemstone.gemfire.security.Authenticator;
import com.gemstone.gemfire.security.NotAuthorizedException;
import com.gemstone.gemfire.test.junit.categories.UnitTest;
@Category(UnitTest.class)
public class ResourceOperationJUnit extends TestCase {
public static class TestUsernamePrincipal implements Principal,
Serializable {
private final String userName;
public TestUsernamePrincipal(String userName) {
this.userName = userName;
}
public String getName() {
return this.userName;
}
@Override
public String toString() {
return this.userName;
}
}
public static class TestAuthenticator implements Authenticator {
@Override
public void close() {
}
@Override
public void init(Properties securityProps, LogWriter systemLogger,
LogWriter securityLogger) throws AuthenticationFailedException {
}
@Override
public Principal authenticate(Properties props, DistributedMember member)
throws AuthenticationFailedException {
String user = props.getProperty(ManagementInterceptor.USER_NAME);
String pwd = props.getProperty(ManagementInterceptor.PASSWORD);
if (user!=null && !user.equals(pwd) && !"".equals(user))
throw new AuthenticationFailedException(
"Wrong username/password");
System.out.println("Authentication successful!! for " + user);
return new TestUsernamePrincipal(user);
}
}
public static class TestAccessControl implements AccessControl {
private Principal principal=null;
@Override
public void close() {
}
@Override
public void init(Principal principal, DistributedMember remoteMember,
Cache cache) throws NotAuthorizedException {
this.principal = principal;
}
@Override
public boolean authorizeOperation(String regionName,
OperationContext context) {
if(principal.getName().equals("tushark")) {
ResourceOperationCode authorizedOps[] = {
ResourceOperationCode.LIST_DS,
ResourceOperationCode.READ_DS,
ResourceOperationCode.CHANGE_ALERT_LEVEL_DS,
ResourceOperationCode.LOCATE_ENTRY_REGION
};
System.out.println("Context received " + context);
//if(context instanceof JMXOperationContext) {
ResourceOperationContext ctx = (ResourceOperationContext)context;
System.out.println("Checking for code " + ctx.getResourceOperationCode());
boolean found = false;
for(ResourceOperationCode code : authorizedOps) {
if(ctx.getResourceOperationCode().equals(code)){
found =true;
System.out.println("found code " + code.toString());
break;
}
}
if(found)
return true;
System.out.println("Did not find code " + ctx.getResourceOperationCode());
return false;
//}
}
return false;
}
}
public void testJMXOperationContext() {
System.setProperty("resource-auth-accessor", "com.gemstone.gemfire.management.internal.security.ResourceOperationJUnit$TestAccessControl");
System.setProperty("resource-authenticator", "com.gemstone.gemfire.management.internal.security.ResourceOperationJUnit$TestAuthenticator");
GemFireCacheImpl cache = null;
DistributedSystem ds = null;
Properties pr = new Properties();
pr.put("name", "testJMXOperationContext");
pr.put(DistributionConfig.JMX_MANAGER_NAME, "true");
pr.put(DistributionConfig.JMX_MANAGER_START_NAME, "true");
int port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
pr.put(DistributionConfig.JMX_MANAGER_PORT_NAME, String.valueOf(port));
pr.put(DistributionConfig.HTTP_SERVICE_PORT_NAME, "0");
ds = getSystem(pr);
cache = (GemFireCacheImpl) CacheFactory.create(ds);
ObjectName name = MBeanJMXAdapter.getDistributedSystemName();
String[] methods = {
"listCacheServerObjectNames",
"viewRemoteClusterStatus",
"getTotalHeapSize",
"setQueryCollectionsDepth",
"getQueryCollectionsDepth",
"changeAlertLevel",
"backupAllMembers",
"revokeMissingDiskStores",
"shutDownAllMembers",
"queryData",
"queryDataForCompressedResult",
"setQueryResultSetLimit",
};
ResourceOperationCode expectedCodes[] = {
ResourceOperationCode.LIST_DS,
ResourceOperationCode.LIST_DS,
ResourceOperationCode.READ_DS,
ResourceOperationCode.QUERYDATA_DS,
ResourceOperationCode.READ_DS,
ResourceOperationCode.CHANGE_ALERT_LEVEL_DS,
ResourceOperationCode.BACKUP_DS,
ResourceOperationCode.REMOVE_DISKSTORE_DS,
ResourceOperationCode.SHUTDOWN_DS,
ResourceOperationCode.QUERYDATA_DS,
ResourceOperationCode.QUERYDATA_DS,
ResourceOperationCode.QUERYDATA_DS
};
for(int i=0;i<methods.length;i++) {
String methodName = methods[i];
JMXOperationContext context = new JMXOperationContext(name, methodName);
assertEquals(expectedCodes[i],
context.getResourceOperationCode());
assertEquals(OperationCode.RESOURCE, context.getOperationCode());
}
JMXConnector cs = getGemfireMBeanServer(port, "tushark", "tushark");;
MBeanServerConnection mbeanServer =null;
try {
mbeanServer = cs.getMBeanServerConnection();
mbeanServer.invoke(MBeanJMXAdapter.getDistributedSystemName(), "listCacheServerObjectNames", null, null);
String oldLevel = (String)mbeanServer.getAttribute(MBeanJMXAdapter.getDistributedSystemName(), "AlertLevel");
System.out.println("Old Level = " + oldLevel);
mbeanServer.invoke(MBeanJMXAdapter.getDistributedSystemName(), "changeAlertLevel", new Object[]{"WARNING"},new String[]{
String.class.getCanonicalName()
});
String newLevel = (String)mbeanServer.getAttribute(MBeanJMXAdapter.getDistributedSystemName(), "AlertLevel");
System.out.println("New Level = " + newLevel);
//Checking accessControlMXBean
System.out.println("Checking access via AccessControlMbean");
ResourceOperationCode authorizedOps[] = {
ResourceOperationCode.LIST_DS,
ResourceOperationCode.READ_DS,
ResourceOperationCode.CHANGE_ALERT_LEVEL_DS,
ResourceOperationCode.LOCATE_ENTRY_REGION
};
ObjectName accControlON = new ObjectName(ManagementInterceptor.OBJECT_NAME_ACCESSCONTROL);
for(ResourceOperationCode c : authorizedOps) {
boolean result = (Boolean) mbeanServer.invoke(accControlON, "authorize"
, new Object[]{ResourceOperationCode.CHANGE_ALERT_LEVEL_DS.toString()}
, new String[]{String.class.getCanonicalName()});
assertTrue(result);
}
boolean result = (Boolean) mbeanServer.invoke(accControlON, "authorize"
, new Object[]{ResourceOperationCode.ADMIN_DS.toString()}
, new String[]{String.class.getCanonicalName()});
assertFalse(result);
} catch (InstanceNotFoundException e1) {
e1.printStackTrace();
fail("Error while invoking JMXRMI " + e1.getMessage());
} catch (MBeanException e1) {
e1.printStackTrace();
fail("Error while invoking JMXRMI " + e1.getMessage());
} catch (ReflectionException e1) {
fail("Error while invoking JMXRMI " + e1.getMessage());
} catch (IOException e1) {
fail("Error while invoking JMXRMI " + e1.getMessage());
} catch (AttributeNotFoundException e) {
fail("Error while invoking JMXRMI" + e.getMessage());
} catch (MalformedObjectNameException e) {
fail("Error while invoking JMXRMI" + e.getMessage());
}
try {
mbeanServer.invoke(MBeanJMXAdapter.getDistributedSystemName(),
"backupAllMembers",
new Object[]{"targetPath","baseLinePath"},
new String[]{String.class.getCanonicalName(), String.class.getCanonicalName()});
fail("Should not be authorized for backupAllMembers");
} catch (SecurityException e) {
//expected
} catch(Exception e){
e.printStackTrace();
fail("Unexpected exception : " + e.getMessage());
}
try {
mbeanServer.invoke(MBeanJMXAdapter.getDistributedSystemName(),
"shutDownAllMembers",null,null);
fail("Should not be authorized for shutDownAllMembers");
} catch (SecurityException e) {
//expected
} catch(Exception e){
fail("Unexpected exception : " + e.getMessage());
}
checkCLIContext(mbeanServer);
try {
cs.close();
} catch (IOException e) {
fail("Unexpected exception : " + e.getMessage());
}
cache.close();
ds.disconnect();
}
private void checkCLIContext(MBeanServerConnection mbeanServer) {
DistributedSystemMXBean proxy = JMX.newMXBeanProxy(mbeanServer, MBeanJMXAdapter.getDistributedSystemName(),
DistributedSystemMXBean.class);
ObjectName managerMemberObjectName = proxy.getMemberObjectName();
MemberMXBean memberMXBeanProxy = JMX.newMXBeanProxy(mbeanServer, managerMemberObjectName, MemberMXBean.class);
try {
Map<String,String> map = new HashMap<String,String>();
map.put("APP","GFSH");
String result = memberMXBeanProxy.processCommand("locate entry --key=k1 --region=/region1", map);
System.out.println("Result = " + result);
} catch (Exception e) {
System.out.println("Excpetion e " + e.getMessage());
e.printStackTrace();
}
}
public void testCLIOperationContext() {
System.setProperty("resource-auth-accessor", "com.gemstone.gemfire.management.internal.security.ResourceOperationJUnit$TestAccessControl");
System.setProperty("resource-authenticator", "com.gemstone.gemfire.management.internal.security.ResourceOperationJUnit$TestAuthenticator");
GemFireCacheImpl cache = null;
DistributedSystem ds = null;
Properties pr = new Properties();
pr.put("name", "testJMXOperationContext");
pr.put(DistributionConfig.JMX_MANAGER_NAME, "true");
pr.put(DistributionConfig.JMX_MANAGER_START_NAME, "true");
int port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
pr.put(DistributionConfig.JMX_MANAGER_PORT_NAME, String.valueOf(port));
pr.put(DistributionConfig.HTTP_SERVICE_PORT_NAME, "0");
ds = getSystem(pr);
cache = (GemFireCacheImpl) CacheFactory.create(ds);
String[] commands = {
"put --key=k1 --value=v1 --region=/region1",
"locate entry --key=k1 --region=/region1",
"query --query=\"select * from /region1\"",
"export data --region=value --file=value --member=value",
"import data --region=value --file=value --member=value",
"rebalance"
};
ResourceOperationCode expectedCodes[] = {
ResourceOperationCode.PUT_REGION,
ResourceOperationCode.LOCATE_ENTRY_REGION,
ResourceOperationCode.QUERYDATA_DS,
ResourceOperationCode.EXPORT_DATA_REGION,
ResourceOperationCode.IMPORT_DATA_REGION,
ResourceOperationCode.REBALANCE_DS
};
for(int i=0;i<commands.length;i++){
CLIOperationContext ctx = new CLIOperationContext(commands[i]);
System.out.println("Context " + ctx);
assertEquals(expectedCodes[i],ctx.getResourceOperationCode());
}
cache.close();
ds.disconnect();
}
private JMXConnector getGemfireMBeanServer(int port, String user, String pwd) {
JMXServiceURL url;
try {
url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:"+ port +"/jmxrmi");
if(user!=null){
Map env = new HashMap();
String[] creds = {user, pwd};
env.put(JMXConnector.CREDENTIALS, creds);
JMXConnector jmxc = JMXConnectorFactory.connect(url,env);
return jmxc;
} else {
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
return jmxc;
}
} catch (MalformedURLException e) {
fail("Error connecting to port=" + port + " " + e.getMessage());
} catch (IOException e) {
fail("Error connecting to port=" + port + " " + e.getMessage());
}
return null;
}
private static DistributedSystem getSystem(Properties properties) {
return DistributedSystem.connect(properties);
}
}