blob: 21295236c044e986b412d55c9d83f5e8081660d6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.airavata.mft.transport.scp;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.FileAttributes;
import net.schmizz.sshj.sftp.FileMode;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
import net.schmizz.sshj.userauth.method.AuthMethod;
import net.schmizz.sshj.userauth.method.AuthPublickey;
import net.schmizz.sshj.userauth.method.ChallengeResponseProvider;
import net.schmizz.sshj.userauth.password.Resource;
import org.apache.airavata.mft.agent.stub.*;
import org.apache.airavata.mft.core.api.MetadataCollector;
import org.apache.airavata.mft.credential.stubs.scp.SCPSecret;
import org.apache.airavata.mft.resource.stubs.scp.storage.SCPStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.file.Files;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class SCPMetadataCollector implements MetadataCollector {
private static final Logger logger = LoggerFactory.getLogger(SCPMetadataCollector.class);
boolean initialized = false;
private SCPStorage scpStorage;
private SCPSecret scpSecret;
@Override
public void init(StorageWrapper storage, SecretWrapper secret) {
this.scpStorage = storage.getScp();
this.scpSecret = secret.getScp();
this.initialized = true;
}
@Override
public ResourceMetadata getResourceMetadata(String resourcePath, boolean recursiveSearch) throws Exception {
ResourceMetadata.Builder resourceBuilder = ResourceMetadata.newBuilder();
try (SSHClient sshClient = getSSHClient(scpStorage, scpSecret)) {
try (SFTPClient sftpClient = sshClient.newSFTPClient()) {
if (sftpClient.statExistence(resourcePath) == null) {
resourceBuilder.setError(MetadataFetchError.NOT_FOUND);
return resourceBuilder.build();
}
FileAttributes lstat = sftpClient.lstat(resourcePath);
if (lstat.getType() == FileMode.Type.REGULAR) {
FileMetadata.Builder fileBuilder = FileMetadata.newBuilder();
fileBuilder.setResourceSize(lstat.getSize());
fileBuilder.setCreatedTime(lstat.getAtime());
fileBuilder.setUpdateTime(lstat.getMtime());
fileBuilder.setFriendlyName(new File(resourcePath).getName());
fileBuilder.setResourcePath(resourcePath);
resourceBuilder.setFile(fileBuilder);
} else if (lstat.getType() == FileMode.Type.DIRECTORY) {
DirectoryMetadata.Builder dirBuilder = DirectoryMetadata.newBuilder();
dirBuilder.setResourcePath(resourcePath);
dirBuilder.setFriendlyName(new File(resourcePath).getName());
dirBuilder.setUpdateTime(lstat.getMtime());
dirBuilder.setCreatedTime(lstat.getAtime());
List<RemoteResourceInfo> dirList = sftpClient.ls(resourcePath);
for (RemoteResourceInfo rr: dirList) {
if (rr.isDirectory()) {
DirectoryMetadata.Builder subDir = DirectoryMetadata.newBuilder();
subDir.setResourcePath(rr.getPath());
subDir.setFriendlyName(rr.getName());
subDir.setCreatedTime(rr.getAttributes().getAtime());
subDir.setUpdateTime(rr.getAttributes().getMtime());
dirBuilder.addDirectories(subDir);
} else {
FileMetadata.Builder fileBuilder = FileMetadata.newBuilder();
fileBuilder.setResourceSize(rr.getAttributes().getSize());
fileBuilder.setCreatedTime(rr.getAttributes().getAtime());
fileBuilder.setUpdateTime(rr.getAttributes().getMtime());
fileBuilder.setFriendlyName(rr.getName());
fileBuilder.setResourcePath(rr.getPath());
dirBuilder.addFiles(fileBuilder);
}
}
resourceBuilder.setDirectory(dirBuilder);
} else {
resourceBuilder.setError(MetadataFetchError.UNRECOGNIZED);
}
}
}
return resourceBuilder.build();
}
private void checkInitialized() {
if (!initialized) {
throw new IllegalStateException("SCP Metadata Collector is not initialized");
}
}
@Override
public Boolean isAvailable(String resourcePath) throws Exception {
checkInitialized();
try (SSHClient sshClient = getSSHClient(scpStorage, scpSecret)) {
logger.info("Checking the availability of file {}", resourcePath);
try (SFTPClient sftpClient = sshClient.newSFTPClient()) {
return sftpClient.statExistence(resourcePath) != null;
}
}
}
private SSHClient getSSHClient(SCPStorage scpStorage, SCPSecret scpSecret) throws IOException {
SSHClient sshClient = new SSHClient();
HostKeyVerifier hkv = new HostKeyVerifier() {
@Override
public boolean verify(String s, int i, PublicKey publicKey) {
return true;
}
@Override
public List<String> findExistingAlgorithms(String hostname, int port) {
return Collections.emptyList();
}
};
sshClient.addHostKeyVerifier(hkv);
File privateKeyFile = Files.createTempFile("id_rsa", "").toFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(privateKeyFile));
writer.write(scpSecret.getPrivateKey());
writer.close();
KeyProvider keyProvider = sshClient.loadKeys(privateKeyFile.getPath(), scpSecret.getPassphrase());
final List<AuthMethod> am = new LinkedList<>();
am.add(new AuthPublickey(keyProvider));
am.add(new AuthKeyboardInteractive(new ChallengeResponseProvider() {
@Override
public List<String> getSubmethods() {
return new ArrayList<>();
}
@Override
public void init(Resource resource, String name, String instruction) {}
@Override
public char[] getResponse(String prompt, boolean echo) {
return new char[0];
}
@Override
public boolean shouldRetry() {
return false;
}
}));
sshClient.connect(scpStorage.getHost(), scpStorage.getPort());
sshClient.auth(scpSecret.getUser(), am);
return sshClient;
}
}