Commit 0bd01d4b authored by Matija Obreza's avatar Matija Obreza
Browse files

FTP server started and login tested

parent ebf60222
......@@ -15,7 +15,9 @@
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.genesys-pgr</groupId>
......@@ -27,10 +29,43 @@
<description>FTP server for Genesys File Repository</description>
<properties>
<ftpserver.version>1.1.0</ftpserver.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.ftpserver</groupId>
<artifactId>ftpserver-core</artifactId>
<version>${ftpserver.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
<classifier>ftp</classifier>
<scope>test</scope>
</dependency>
</dependencies>
</project>
/*
* Copyright 2017 Global Crop Diversity Trust, www.croptrust.org
*
* 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 org.genesys.filerepository.service.ftp;
import java.util.Collections;
import java.util.List;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.AuthorizationRequest;
import org.apache.ftpserver.ftplet.User;
public class FtpUser implements User {
private String name;
private String password;
private List<? extends Authority> authorities;
private int maxIdleTime;
private boolean enabled;
private String homeDirectory;
public FtpUser() {
}
public FtpUser(String username) {
name = username;
}
@Override
public String getName() {
return name;
}
@Override
public String getPassword() {
return password;
}
@Override
public List<? extends Authority> getAuthorities() {
return Collections.unmodifiableList(authorities);
}
@Override
public List<? extends Authority> getAuthorities(Class<? extends Authority> clazz) {
return null;
}
@Override
public AuthorizationRequest authorize(AuthorizationRequest request) {
// check for no authorities at all
if (authorities == null) {
return null;
}
boolean someoneCouldAuthorize = false;
for (Authority authority : authorities) {
if (authority.canAuthorize(request)) {
someoneCouldAuthorize = true;
request = authority.authorize(request);
// authorization failed, return null
if (request == null) {
return null;
}
}
}
if (someoneCouldAuthorize) {
return request;
} else {
return null;
}
}
@Override
public int getMaxIdleTime() {
return maxIdleTime;
}
@Override
public boolean getEnabled() {
return enabled;
}
@Override
public String getHomeDirectory() {
return homeDirectory;
}
/**
* @param name the name to set
*/
public final void setName(String name) {
this.name = name;
}
/**
* @param password the password to set
*/
public final void setPassword(String password) {
this.password = password;
}
/**
* @param authorities the authorities to set
*/
public final void setAuthorities(List<? extends Authority> authorities) {
this.authorities = authorities;
}
/**
* @param maxIdleTime the maxIdleTime to set
*/
public final void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
/**
* @param enabled the enabled to set
*/
public final void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* @param homeDirectory the homeDirectory to set
*/
public final void setHomeDirectory(String homeDirectory) {
this.homeDirectory = homeDirectory;
}
}
/*
* Copyright 2017 Global Crop Diversity Trust, www.croptrust.org
*
* 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 org.genesys.filerepository.service.ftp;
import org.apache.ftpserver.ftplet.FileSystemFactory;
import org.apache.ftpserver.ftplet.FileSystemView;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.FtpFile;
import org.apache.ftpserver.ftplet.User;
public class RepositoryFileSystemFactory implements FileSystemFactory {
@Override
public FileSystemView createFileSystemView(User user) throws FtpException {
// TODO Auto-generated method stub
return new FileSystemView() {
@Override
public boolean isRandomAccessible() throws FtpException {
// TODO Auto-generated method stub
return false;
}
@Override
public FtpFile getWorkingDirectory() throws FtpException {
// TODO Auto-generated method stub
return null;
}
@Override
public FtpFile getHomeDirectory() throws FtpException {
// TODO Auto-generated method stub
return null;
}
@Override
public FtpFile getFile(String file) throws FtpException {
// TODO Auto-generated method stub
return null;
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
@Override
public boolean changeWorkingDirectory(String dir) throws FtpException {
// TODO Auto-generated method stub
return false;
}
};
}
}
/*
* Copyright 2017 Global Crop Diversity Trust, www.croptrust.org
*
* 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 org.genesys.filerepository.service.ftp;
import java.util.HashMap;
import java.util.Map;
import org.apache.ftpserver.ConnectionConfig;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.DefaultFtplet;
import org.apache.ftpserver.ftplet.FileSystemFactory;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.Ftplet;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.message.MessageResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class RepositoryFtpServer implements InitializingBean, DisposableBean {
private final static Logger LOG = LoggerFactory.getLogger(RepositoryFtpServer.class);
@Autowired
private UserManager userManager;
@Autowired(required = false)
private MessageResource messageResource;
private int ftpPort;
private int maxThreads;
// The maximum number of simultaneous users
private int maxLogins;
// Idle timeout
private int idleTimeout;
private FtpServer server = null;
public void setUserManager(UserManager userManager) {
this.userManager = userManager;
}
public void setMessageResource(MessageResource messageResource) {
this.messageResource = messageResource;
}
public void setFtpPort(int ftpPort) {
this.ftpPort = ftpPort;
}
public void afterPropertiesSet() throws FtpException {
FtpServerFactory serverFactory = new FtpServerFactory();
serverFactory.setUserManager(userManager);
if (messageResource != null) {
serverFactory.setMessageResource(messageResource);
}
{
ListenerFactory factory = new ListenerFactory();
// set the port of the listener
factory.setPort(ftpPort);
// set idle timeout
factory.setIdleTimeout(idleTimeout);
// replace the default listener
serverFactory.addListener("default", factory.createListener());
}
FileSystemFactory fileSystem = repositoryFileSystemFactory();
serverFactory.setFileSystem(fileSystem);
final ConnectionConfig ftpConnectionConfig = new ConnectionConfig() {
@Override
public boolean isAnonymousLoginEnabled() {
return true;
}
@Override
public int getMaxThreads() {
return maxThreads;
}
@Override
public int getMaxLogins() {
return maxLogins;
}
// The number of failed login attempts before the connection is closed
@Override
public int getMaxLoginFailures() {
return 3;
}
@Override
public int getMaxAnonymousLogins() {
return 3;
}
// The number of milliseconds that the connection is delayed after a failed login attempt.
@Override
public int getLoginFailureDelay() {
return 30;
}
};
serverFactory.setConnectionConfig(ftpConnectionConfig);
Map<String, Ftplet> ftplets = new HashMap<>();
ftplets.put("default", repositoryFtplet());
serverFactory.setFtplets(ftplets);
this.server = serverFactory.createServer();
LOG.info("Starting FTP server on port {}", this.ftpPort);
server.start();
}
@Override
public void destroy() throws Exception {
if (this.server != null) {
LOG.info("Shutting down FTP server on port {}", this.ftpPort);
this.server.stop();
}
}
@Bean
public FileSystemFactory repositoryFileSystemFactory() {
return new RepositoryFileSystemFactory();
}
@Bean
public Ftplet repositoryFtplet() {
return new DefaultFtplet() {
};
}
}
/*
* Copyright 2017 Global Crop Diversity Trust, www.croptrust.org
*
* 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 org.genesys.filerepository.service.ftp;
import java.util.ArrayList;
import java.util.List;
import org.apache.ftpserver.ftplet.Authentication;
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.usermanager.AnonymousAuthentication;
import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication;
import org.apache.ftpserver.usermanager.impl.AbstractUserManager;
import org.apache.ftpserver.usermanager.impl.ConcurrentLoginPermission;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
public RepositoryFtpServer ftpServer() {
RepositoryFtpServer ftpServer = new RepositoryFtpServer();
ftpServer.setFtpPort(8021);
ftpServer.setUserManager(userManager());
return ftpServer;
}
@Bean
public UserManager userManager() {
return new AbstractUserManager() {
@Override
public void save(User user) throws FtpException {
// Noop
}
@Override
public User getUserByName(String username) throws FtpException {
FtpUser user = new FtpUser(username);
user.setPassword(username + "1!");
user.setEnabled(true);
List<Authority> authorities = new ArrayList<>();
authorities.add(new ConcurrentLoginPermission(2, 0));
authorities.add(new WritePermission());
user.setAuthorities(authorities);
return user;
}
@Override
public String[] getAllUserNames() throws FtpException {
return new String[] {};
}
@Override
public boolean doesExist(String username) throws FtpException {
// TODO Auto-generated method stub
return true;
}
@Override
public void delete(String username) throws FtpException {
// Noop
}
@Override
public User authenticate(Authentication authentication) throws AuthenticationFailedException {
if (authentication instanceof UsernamePasswordAuthentication) {
UsernamePasswordAuthentication upauth = (UsernamePasswordAuthentication) authentication;
String user = upauth.getUsername();
String password = upauth.getPassword();
if (user == null) {
throw new AuthenticationFailedException("Authentication failed");
}
if (password == null) {
password = "";
}
String storedPassword = user.concat("1!");
if (!password.equals(storedPassword)) {
// user does not exist
throw new AuthenticationFailedException("Authentication failed");
}
// if (getPasswordEncryptor().matches(password, storedPassword)) {
if (password.equals(storedPassword)) {
try {
return getUserByName(user);
} catch (FtpException e) {
throw new AuthenticationFailedException("Authentication failed", e);
}
} else {
throw new AuthenticationFailedException("Authentication failed");
}
} else if (authentication instanceof AnonymousAuthentication) {
throw new AuthenticationFailedException("Authentication failed");
} else {
throw new IllegalArgumentException("Authentication not supported by this user manager");
}
}
};
}
}
/*
* Copyright 2017 Global Crop Diversity Trust, www.croptrust.org
*
* 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 org.genesys.filerepository.service.ftp;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.io.IOException;