package org.dcache.ftp.door;
import com.google.common.collect.Lists;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
import java.util.EnumSet;
import java.util.List;
import diskCacheV111.util.CacheException;
import diskCacheV111.util.FileExistsCacheException;
import diskCacheV111.util.FileNotFoundCacheException;
import diskCacheV111.util.FsPath;
import diskCacheV111.util.NotDirCacheException;
import diskCacheV111.util.NotFileCacheException;
import diskCacheV111.util.PermissionDeniedCacheException;
import diskCacheV111.util.PnfsHandler;
import diskCacheV111.util.PnfsId;
import diskCacheV111.util.TimeoutCacheException;
import org.dcache.auth.attributes.Restrictions;
import org.dcache.namespace.FileType;
import org.dcache.util.OptionParser;
import static com.google.common.net.InetAddresses.forString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@SuppressWarnings("unchecked")
public class AbstractFtpDoorV1Test {
private static final String NEW_DIR = "newdir";
private static final String OLD_DIR = "olddir";
private static final String SRC_FILE = "source";
private static final String DST_FILE = "target";
private static final String INVALID_FILE = "invalid";
@Mock
AbstractFtpDoorV1 door;
@Mock
PnfsHandler pnfs;
@Mock
Logger logger;
@Before
public void setUp()
{
MockitoAnnotations.initMocks(this);
door._settings = OptionParser.injectDefaults(new FtpDoorSettings());
door._userRootPath = FsPath.create("/pathRoot");
door._doorRootPath = FsPath.create("/pathRoot");
door._cwd = "/cwd";
door._pnfs = pnfs;
door._authz = Restrictions.none();
}
@After
public void tearDown() {
door = null;
}
private InterfaceAddress mockInterfaceAddress(String address)
{
InterfaceAddress mock = mock(InterfaceAddress.class);
Mockito.when(mock.getAddress()).thenReturn(forString(address));
return mock;
}
public static class ExpectedFtpCommandException implements TestRule {
private int _code;
private boolean checkCode;
private ExpectedFtpCommandException() {}
public static ExpectedFtpCommandException none() {
return new ExpectedFtpCommandException();
}
@Override
public Statement apply(final Statement stmnt, Description d) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
stmnt.evaluate();
if (checkCode)
fail("Expected FTPCommandException '"+_code+"' not thrown.");
} catch (FTPCommandException commandException) {
if (checkCode) {
assertEquals("Unexpected reply '"+commandException.getCode()+" "+commandException.getReply()+"'", _code, commandException.getCode());
} else {
fail("Caught unexpected exception FTPCommandException '" + _code + "':'"+commandException.getMessage()+"'.");
}
}
}
};
}
public void expectCode(int code) {
_code = code;
checkCode = true;
}
}
@Rule
public ExpectedFtpCommandException thrown = ExpectedFtpCommandException.none();
@Test
public void whenRnfrIsCalledWithEmptyFilenameReplyError500() throws FTPCommandException {
doCallRealMethod().when(door).ftp_rnfr(anyString());
thrown.expectCode(500);
door.ftp_rnfr("");
}
@Test
public void whenRnfrIsCalledForNonExistingFilenameReplyFileNotFound550()
throws FTPCommandException, CacheException {
doCallRealMethod().when(door).ftp_rnfr(anyString());
when(pnfs.getPnfsIdByPath("/pathRoot/cwd/"+INVALID_FILE)).thenThrow(FileNotFoundCacheException.class);
thrown.expectCode(550);
door.ftp_rnfr(INVALID_FILE);
}
@Test
public void whenRntoIsCalledWithEmptyFilenameReplyError500()
throws FTPCommandException, CacheException {
doCallRealMethod().when(door).ftp_rnfr(anyString());
doCallRealMethod().when(door).ftp_rnto(anyString());
when(pnfs.getPnfsIdByPath("/pathRoot/cwd/"+SRC_FILE)).thenReturn(new PnfsId("1"));
door.ftp_rnfr(SRC_FILE);
thrown.expectCode(500);
door.ftp_rnto("");
}
@Test
public void whenRntoIsCalledWithoutPreviousRnfrReplyError503()
throws FTPCommandException, CacheException {
doCallRealMethod().when(door).ftp_rnto(anyString());
when(pnfs.getPnfsIdByPath("/pathRoot/cwd/"+DST_FILE)).thenThrow(CacheException.class);
thrown.expectCode(503);
door.ftp_rnto(DST_FILE);
}
@Test
public void whenRenamingSuccessfulReply250() throws Exception {
doCallRealMethod().when(door).ftp_rnfr(anyString());
doCallRealMethod().when(door).ftp_rnto(anyString());
when(pnfs.getPnfsIdByPath("/pathRoot/cwd/"+SRC_FILE)).thenReturn(new PnfsId("1"));
door.ftp_rnfr(SRC_FILE);
door.ftp_rnto(DST_FILE);
InOrder orderedReplies = inOrder(door);
orderedReplies.verify(door).reply(startsWith("350"));
orderedReplies.verify(door).reply(startsWith("250"));
}
@Test
public void EPRTshouldReply200ForValidIP4Arg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
doCallRealMethod().when(door).ok(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door.ftp_eprt("|1|127.0.0.1|22|");
verify(door).reply(startsWith("200"));
}
@Test
public void EPRTshouldReply200ForValidIP6Arg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
doCallRealMethod().when(door).ok(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door.ftp_eprt("|2|::1|22|");
verify(door).reply(startsWith("200"));
}
@Test
public void EPRTshouldReply501ForInvalidIP4Arg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(501);
door.ftp_eprt("|1|999.0.0.0|22|");
}
@Test
public void EPRTshouldReply501ForInvalidIP6Arg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(501);
door.ftp_eprt("|2|:999999::1|22|");
}
@Test
public void EPRTshouldReply522ForMissingProtocolArg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(501);
door.ftp_eprt("|127.0.0.1|22|");
}
@Test
public void EPRTshouldReply522ForEmptyProtocolArg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(522);
door.ftp_eprt("||127.0.0.1|22|");
}
@Test
public void EPRTshouldReply501ForMissingArg()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(501);
door.ftp_eprt("");
}
@Test
public void EPRTshouldReply501ForTooManyArgs()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(501);
door.ftp_eprt("||||1|||127.0.0.1||22||||");
}
@Test
public void EPRTshouldReply522ForUnsupportedProtocol()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).getExtendedAddressOf(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
thrown.expectCode(522);
door.ftp_eprt("|3|127.0.0.1|22|");
}
@Test
public void EPSVshouldReply200WhenConnectionEstablished()
throws FTPCommandException, UnknownHostException {
doCallRealMethod().when(door).ftp_epsv(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
door.ftp_epsv("");
verify(door).reply(startsWith("229"));
}
@Test
public void EPSVshouldReply522WhenRequestingInvalidProtocol()
throws FTPCommandException, UnknownHostException {
doCallRealMethod().when(door).ftp_epsv(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
thrown.expectCode(522);
door.ftp_epsv("3");
}
@Test
public void EPSVshouldReply200WhenRequestingAll()
throws FTPCommandException, UnknownHostException
{
doCallRealMethod().when(door).ftp_epsv(anyString());
doCallRealMethod().when(door).ok(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
door.ftp_epsv("all");
verify(door).reply(startsWith("200"));
}
@Test
public void EPSVshouldReply229WhenRequestingAllFollowedByEPSVwithoutArgument()
throws FTPCommandException, UnknownHostException
{
doCallRealMethod().when(door).ftp_epsv(anyString());
doCallRealMethod().when(door).ok(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
door.ftp_epsv("all");
door.ftp_epsv("");
verify(door).reply(startsWith("229"));
}
@Test
public void PASVshouldBeRejectedAfterEPSVallCall()
throws FTPCommandException, UnknownHostException
{
doCallRealMethod().when(door).ftp_epsv(anyString());
doCallRealMethod().when(door).ftp_pasv(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
door.ftp_epsv("all");
thrown.expectCode(503);
door.ftp_pasv("192,168,1,1,6666");
}
@Test
public void PORTshouldBeRejectedAfterEPSVallCall()
throws FTPCommandException, UnknownHostException
{
doCallRealMethod().when(door).ftp_epsv(anyString());
doCallRealMethod().when(door).ftp_port(anyString());
doCallRealMethod().when(door).setActive((InetSocketAddress)any());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
door.ftp_epsv("all");
thrown.expectCode(503);
door.ftp_port("192,168,1,1,0,20");
}
@Test
public void EPRTshouldBeRejectedAfterEPSVallCall()
throws FTPCommandException, UnknownHostException
{
doCallRealMethod().when(door).ftp_epsv(anyString());
doCallRealMethod().when(door).ftp_eprt(anyString());
doCallRealMethod().when(door).setActive((InetSocketAddress)any());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
when(door.setPassive()).thenReturn(new InetSocketAddress(forString("::1"), 20));
door.ftp_epsv("all");
thrown.expectCode(503);
door.ftp_eprt("|3|127.0.0.1|22|");
}
@Test
public void EPSVshouldRebindIpWhenRequestedIPv4Protocol()
throws Exception {
doCallRealMethod().when(door).ftp_epsv(anyString());
doCallRealMethod().when(door).setPassive();
List<InterfaceAddress> addresses = Lists.newArrayList(
mockInterfaceAddress("::1"), mockInterfaceAddress("127.0.0.1"));
when(door.getLocalAddressInterfaces()).thenReturn(addresses);
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
door.ftp_epsv("1");
door.ftp_epsv("");
assertThat(door._preferredProtocol, is(AbstractFtpDoorV1.Protocol.IPV4));
assertEquals(Inet4Address.class, ((InetSocketAddress)door._passiveModeServerSocket.getLocalAddress()).getAddress().getClass());
}
@Test
public void EPSVshouldReply522WhenRequestedUnsupportedProtocol()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_epsv(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("::1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("::1"), 0);
thrown.expectCode(522);
door.ftp_epsv("3");
}
@Test
public void EPSVshouldReply500WhenRequestedOnIpV4()
throws FTPCommandException {
doCallRealMethod().when(door).ftp_epsv(anyString());
door._localSocketAddress = door._proxySocketAddress = new InetSocketAddress(forString("127.0.0.1"), 21);
door._remoteSocketAddress = new InetSocketAddress(forString("127.0.0.1"), 0);
thrown.expectCode(502);
door.ftp_epsv("1");
}
@Test
public void whenMkdSuccessfulReply257() throws Exception {
doCallRealMethod().when(door).ftp_mkd(anyString());
door.ftp_mkd(NEW_DIR);
verify(door).reply(startsWith("257 \"/cwd/"+NEW_DIR.replaceAll("\"","\"\"")+"\""));
}
@Test
public void whenMkdPermissionDeniedReply550() throws Exception {
doCallRealMethod().when(door).ftp_mkd(anyString());
when(pnfs.createPnfsDirectory("/pathRoot/cwd/"+NEW_DIR)).thenThrow(PermissionDeniedCacheException.class);
thrown.expectCode(550);
door.ftp_mkd(NEW_DIR);
}
@Test
public void whenMkdFileExistReply550() throws Exception {
doCallRealMethod().when(door).ftp_mkd(anyString());
when(pnfs.createPnfsDirectory("/pathRoot/cwd/"+NEW_DIR)).thenThrow(FileExistsCacheException.class);
thrown.expectCode(550);
door.ftp_mkd(NEW_DIR);
}
@Test
public void whenMkdTimeOutReply451() throws Exception {
doCallRealMethod().when(door).ftp_mkd(anyString());
when(pnfs.createPnfsDirectory("/pathRoot/cwd/"+NEW_DIR)).thenThrow(TimeoutCacheException.class);
thrown.expectCode(451);
door.ftp_mkd(NEW_DIR);
}
@Test
public void whenMkdCacheException550() throws Exception {
doCallRealMethod().when(door).ftp_mkd(anyString());
when(pnfs.createPnfsDirectory("/pathRoot/cwd/"+NEW_DIR)).thenThrow(CacheException.class);
thrown.expectCode(550);
door.ftp_mkd(NEW_DIR);
}
@Test
public void whenDelePermissionDeniedReply550() throws Exception {
doCallRealMethod().when(door).ftp_dele(anyString());
doThrow(new PermissionDeniedCacheException("Permission Denied")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+SRC_FILE,
EnumSet.of(FileType.REGULAR, FileType.LINK));
thrown.expectCode(550);
door.ftp_dele(SRC_FILE);
}
@Test
public void whenDeleNotFileReply550() throws Exception {
doCallRealMethod().when(door).ftp_dele(anyString());
doThrow(new NotFileCacheException("Not a File")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+SRC_FILE,
EnumSet.of(FileType.REGULAR, FileType.LINK));
thrown.expectCode(550);
door.ftp_dele(SRC_FILE);
}
@Test
public void whenDeleFileNotFoundReply550() throws Exception {
doCallRealMethod().when(door).ftp_dele(anyString());
doThrow(new FileNotFoundCacheException("File not found")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+SRC_FILE,
EnumSet.of(FileType.REGULAR, FileType.LINK));
thrown.expectCode(550);
door.ftp_dele(SRC_FILE);
}
@Test
public void whenDeleTimeOutReply451() throws Exception {
doCallRealMethod().when(door).ftp_dele(anyString());
doThrow(new TimeoutCacheException("Timeout")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+SRC_FILE,
EnumSet.of(FileType.REGULAR, FileType.LINK));
thrown.expectCode(451);
door.ftp_dele(SRC_FILE);
}
@Test
public void whenDeleCacheExceptionReply550() throws Exception {
doCallRealMethod().when(door).ftp_dele(anyString());
doThrow(new CacheException("Cache Exception")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/" + SRC_FILE,
EnumSet.of(FileType.REGULAR, FileType.LINK));
thrown.expectCode(550);
door.ftp_dele(SRC_FILE);
}
@Test
public void whenRmdSuccessfulReply250() throws Exception {
doCallRealMethod().when(door).ftp_rmd(anyString());
door.ftp_rmd(OLD_DIR);
verify(door).reply(startsWith("250"));
}
@Test
public void whenRmdPermissionDeniedReply550() throws Exception {
doCallRealMethod().when(door).ftp_rmd(anyString());
doThrow(new PermissionDeniedCacheException("Permission denied")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+OLD_DIR,EnumSet.of(FileType.DIR));
thrown.expectCode(550);
door.ftp_rmd(OLD_DIR);
}
@Test
public void whenRmdNotDirReply550() throws Exception {
doCallRealMethod().when(door).ftp_rmd(anyString());
doThrow(new NotDirCacheException("Not a directory")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+OLD_DIR,
EnumSet.of(FileType.DIR));
thrown.expectCode(550);
door.ftp_rmd(OLD_DIR);
}
@Test
public void whenRmdFileNotFoundReply550() throws Exception {
doCallRealMethod().when(door).ftp_rmd(anyString());
doThrow(new FileNotFoundCacheException("No such file or directory")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+OLD_DIR,
EnumSet.of(FileType.DIR));
thrown.expectCode(550);
door.ftp_rmd(OLD_DIR);
}
@Test
public void whenRmdTimeOutReply451() throws Exception {
doCallRealMethod().when(door).ftp_rmd(anyString());
doThrow(new TimeoutCacheException("Timeout")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/"+OLD_DIR,
EnumSet.of(FileType.DIR));
thrown.expectCode(451);
door.ftp_rmd(OLD_DIR);
}
@Test
public void whenRmdCacheExceptionReply550() throws Exception {
doCallRealMethod().when(door).ftp_rmd(anyString());
doThrow(new CacheException("Cache Exception")).
when(pnfs).deletePnfsEntry("/pathRoot/cwd/" + OLD_DIR,
EnumSet.of(FileType.DIR));
thrown.expectCode(550);
door.ftp_rmd(OLD_DIR);
}
}