Commit e1fc24c2 authored by Matija Obreza's avatar Matija Obreza

Merge branch '27-ftp-and-spring-security' into 'master'

Resolve "FTP and Spring Security"

Closes #27

See merge request !24
parents a431f13d 08036e3f
Pipeline #6179 passed with stage
in 3 minutes and 13 seconds
......@@ -18,6 +18,8 @@ package org.genesys.filerepository.service.ftp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Date;
import java.util.List;
import org.apache.ftpserver.ftplet.FtpFile;
......@@ -28,20 +30,18 @@ import org.apache.ftpserver.ftplet.FtpFile;
public abstract class CanBeAnythingFile implements FtpFile {
/** The parent. */
private final String parent;
/** The name. */
private final String name;
private final Path path;
private long date = new Date().getTime();
protected boolean dir = false;
/**
* Instantiates a new can be anything file.
*
* @param parent the parent
* @param name the name
* @param path2 the name
*/
public CanBeAnythingFile(final String parent, final String name) {
this.parent = parent;
this.name = name;
public CanBeAnythingFile(final Path parent, final String name) {
this.path = parent.resolve(name);
}
/*
......@@ -50,7 +50,7 @@ public abstract class CanBeAnythingFile implements FtpFile {
*/
@Override
public String getAbsolutePath() {
return parent.length() > 1 ? parent.concat("/").concat(name) : parent.concat(name);
return path.toAbsolutePath().toString();
}
/*
......@@ -59,7 +59,7 @@ public abstract class CanBeAnythingFile implements FtpFile {
*/
@Override
public String getName() {
return name;
return path.getFileName().toString();
}
/*
......@@ -77,7 +77,7 @@ public abstract class CanBeAnythingFile implements FtpFile {
*/
@Override
public boolean isDirectory() {
return true;
return dir;
}
/*
......@@ -86,7 +86,7 @@ public abstract class CanBeAnythingFile implements FtpFile {
*/
@Override
public boolean isFile() {
return true;
return !dir;
}
/*
......@@ -158,7 +158,7 @@ public abstract class CanBeAnythingFile implements FtpFile {
*/
@Override
public long getLastModified() {
return 0;
return date;
}
/*
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* RunAs for FTP
*
* @author Matija Obreza
*/
public class FtpRunAs {
/** The Constant LOG. */
public static final Logger LOG = LoggerFactory.getLogger(FtpRunAs.class);
/**
* NoArgMethod.
*
* @param <R> the return type
* @param <T> the exception type
*/
public static interface NoArgMethod<R, T extends Throwable> {
R run() throws T;
}
/**
* Run method as the ftp user. Switches Spring security context to
* {@link FtpUser#user} and back to what it was.
*
* @param <R> method return type
* @param <T> exception type
* @param ftpUser the ftp user
* @param runnable the code to execute as FTP user
* @return the result of runnable
* @throws T the exception
*/
public static <R, T extends Throwable> R asFtpUser(FtpUser ftpUser, NoArgMethod<R, T> runnable) throws T {
final Authentication prevAuth = SecurityContextHolder.getContext().getAuthentication();
if (ftpUser != null && ftpUser.user != null) {
LOG.trace("Switching to {}", ftpUser.user.getUsername());
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(ftpUser.user, ftpUser.getPassword(), ftpUser.user.getAuthorities()));
}
try {
return runnable.run();
} finally {
if (ftpUser != null && ftpUser.user != null) {
LOG.trace("Switching back from {}", ftpUser.user.getUsername());
}
SecurityContextHolder.getContext().setAuthentication(prevAuth);
}
}
}
......@@ -21,6 +21,7 @@ import java.util.List;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.AuthorizationRequest;
import org.apache.ftpserver.ftplet.User;
import org.genesys.blocks.security.model.BasicUser;
/**
* The Class FtpUser.
......@@ -45,6 +46,8 @@ public class FtpUser implements User {
/** The home directory. */
private String homeDirectory;
BasicUser<?> user;
/**
* Instantiates a new ftp user.
*/
......@@ -60,6 +63,12 @@ public class FtpUser implements User {
name = username;
}
public FtpUser(BasicUser<?> user) {
this.user = user;
setName(user.getUsername());
setEnabled(user.isEnabled() && user.isAccountNonExpired() && user.isAccountNonLocked() && user.isCredentialsNonExpired());
}
/*
* (non-Javadoc)
* @see org.apache.ftpserver.ftplet.User#getName()
......
......@@ -63,12 +63,13 @@ public class TemporaryBytesManager {
/**
* Create a local file for writing. On stream close push the file to
* {@link BytesStorageService}
* @param user
*
* @param path the path
* @return the output stream
* @throws IOException Signals that an I/O exception has occurred.
*/
public OutputStream newFile(final Path path) throws IOException {
public OutputStream newFile(FtpUser user, final Path path) throws IOException {
final String parent = path.getParent().toString();
final String filename = path.getFileName().toString();
......@@ -85,14 +86,17 @@ public class TemporaryBytesManager {
@Override
protected void synchronizeWithRepository() throws IOException {
final byte[] bytes = readTempFileToBytes(tempFile);
try {
repositoryService.addFile(parent, filename, null, bytes, null);
} catch (InvalidRepositoryPathException | InvalidRepositoryFileDataException e) {
LOG.warn("Error synchronizing new file parent={} filename={} with repository: {}", parent, filename, e.getMessage());
throw new IOException(e);
}
LOG.info("Synchronized file={} with repository path={} originalFilename={}", tempFile.getAbsolutePath(), parent, filename);
FtpRunAs.asFtpUser(user, () -> {
final byte[] bytes = readTempFileToBytes(tempFile);
try {
repositoryService.addFile(parent, filename, null, bytes, null);
} catch (InvalidRepositoryPathException | InvalidRepositoryFileDataException e) {
LOG.warn("Error synchronizing new file parent={} filename={} with repository: {}", parent, filename, e.getMessage());
throw new IOException(e);
}
LOG.info("Synchronized file={} with repository path={} originalFilename={}", tempFile.getAbsolutePath(), parent, filename);
return null;
});
}
};
}
......@@ -176,7 +180,7 @@ public class TemporaryBytesManager {
* @return the output stream
* @throws IOException Signals that an I/O exception has occurred.
*/
public OutputStream createOutputStream(final RepositoryFile repositoryFile, final long offset) throws IOException {
public OutputStream createOutputStream(final FtpUser user, final RepositoryFile repositoryFile, final long offset) throws IOException {
final File tempFile = shadowRepositoryFile(repositoryFile);
LOG.info("Preparing local copy of rf={} offset={} temp={}", repositoryFile.getUuid(), offset, tempFile.getAbsolutePath());
......@@ -196,15 +200,18 @@ public class TemporaryBytesManager {
@Override
protected void synchronizeWithRepository() throws IOException {
final byte[] bytes = readTempFileToBytes(tempFile);
LOG.info("Stream to temporary upload buffer is closed, synchronizing bytes={}", bytes.length);
try {
repositoryService.updateBytes(repositoryFile, null, bytes);
} catch (final NoSuchRepositoryFileException e) {
LOG.warn("Error synchronizing new file with repository: {}", e.getMessage());
throw new IOException(e);
}
LOG.info("Synchronized file={} with repository path={} originalFilename={}", tempFile.getAbsolutePath(), repositoryFile.getPath(), repositoryFile.getOriginalFilename());
FtpRunAs.asFtpUser(user, () -> {
final byte[] bytes = readTempFileToBytes(tempFile);
LOG.info("Stream to temporary upload buffer is closed, synchronizing bytes={}", bytes.length);
try {
repositoryService.updateBytes(repositoryFile, null, bytes);
} catch (final NoSuchRepositoryFileException e) {
LOG.warn("Error synchronizing new file with repository: {}", e.getMessage());
throw new IOException(e);
}
LOG.info("Synchronized file={} with repository path={} originalFilename={}", tempFile.getAbsolutePath(), repositoryFile.getPath(), repositoryFile.getOriginalFilename());
return null;
});
}
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment