Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

273 support for ssh agent #600

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.operations.FileOperations;
import org.jetbrains.annotations.NotNull;

import java.net.URL;
import java.util.Iterator;
import java.util.List;

public class FileObjectWrapper implements FileObject {
Expand Down Expand Up @@ -62,6 +64,11 @@ public FileObject getParent() throws FileSystemException {
return parent.getParent();
}

@Override
public String getPublicURIString() {
return parent.getPublicURIString();
}

public FileSystem getFileSystem() {
return parent.getFileSystem();
}
Expand All @@ -75,16 +82,31 @@ public FileObject getChild(String name) throws FileSystemException {
}

public FileObject resolveFile(String name, NameScope scope)
throws FileSystemException {
throws FileSystemException {
return parent.resolveFile(name, scope);
}

@Override
public boolean setExecutable(boolean executable, boolean ownerOnly) throws FileSystemException {
return parent.setExecutable(executable, ownerOnly);
}

@Override
public boolean setReadable(boolean readable, boolean ownerOnly) throws FileSystemException {
return parent.setReadable(readable, ownerOnly);
}

@Override
public boolean setWritable(boolean writable, boolean ownerOnly) throws FileSystemException {
return parent.setWritable(writable, ownerOnly);
}

public FileObject resolveFile(String path) throws FileSystemException {
return parent.resolveFile(path);
}

public FileObject[] findFiles(FileSelector selector)
throws FileSystemException {
throws FileSystemException {
return parent.findFiles(selector);
}

Expand All @@ -101,6 +123,11 @@ public int delete(FileSelector selector) throws FileSystemException {
return parent.delete(selector);
}

@Override
public int deleteAll() throws FileSystemException {
return parent.deleteAll();
}

public void createFolder() throws FileSystemException {
parent.createFolder();
}
Expand All @@ -110,7 +137,7 @@ public void createFile() throws FileSystemException {
}

public void copyFrom(FileObject srcFile, FileSelector selector)
throws FileSystemException {
throws FileSystemException {
parent.copyFrom(srcFile, selector);
}

Expand Down Expand Up @@ -142,7 +169,33 @@ public boolean isContentOpen() {
return parent.isContentOpen();
}

@Override
public boolean isExecutable() throws FileSystemException {
return parent.isExecutable();
}

@Override
public boolean isFile() throws FileSystemException {
return parent.isFile();
}

@Override
public boolean isFolder() throws FileSystemException {
return parent.isFolder();
}

public FileOperations getFileOperations() throws FileSystemException {
return parent.getFileOperations();
}

@Override
public int compareTo(@NotNull FileObject o) {
return parent.compareTo(o);
}

@NotNull
@Override
public Iterator<FileObject> iterator() {
return parent.iterator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@

package pl.otros.vfs.browser.auth;

import pl.otros.vfs.browser.i18n.Messages;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.UserAuthenticationData;
import org.apache.commons.vfs2.provider.sftp.IdentityInfo;
import org.apache.commons.vfs2.provider.sftp.IdentityProvider;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
import pl.otros.vfs.browser.i18n.Messages;
import pl.otros.vfs.browser.util.PageantIdentityRepositoryFactory;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.OptionalInt;

public class SftpUserAuthenticator extends UserPassUserAuthenticator {

Expand All @@ -40,16 +43,22 @@ public SftpUserAuthenticator(AuthStore authStore, String url, FileSystemOptions

@Override
protected void getAuthenticationData(UserAuthenticationData authenticationData) {
super.getAuthenticationData(authenticationData);
authenticationData.setData(UserAuthenticationDataWrapper.SSH_KEY, sshKeyFileField.getText().trim().toCharArray());
authenticationData.setData(UserAuthenticationData.USERNAME, nameTf.getSelectedItem().toString().toCharArray());

if (StringUtils.isNotBlank(sshKeyFileField.getText())) {
try {
SftpFileSystemConfigBuilder.getInstance().setIdentities(getFileSystemOptions(), new File[]{new File(sshKeyFileField.getText())});
//TODO set user auth data file path
} catch (FileSystemException e) {
e.printStackTrace();
//use SSH KEY
authenticationData.setData(UserAuthenticationDataWrapper.SSH_KEY, sshKeyFileField.getText().trim().toCharArray());
IdentityProvider sshKeyAuth;
if (passTx.getPassword() != null && passTx.getPassword().length > 0) {
//SSH KEY secured with password
String stringPass = new String(passTx.getPassword());
sshKeyAuth = new IdentityInfo(new File(sshKeyFileField.getText()), stringPass.getBytes());
} else {
sshKeyAuth = new IdentityInfo(new File(sshKeyFileField.getText()));
}
SftpFileSystemConfigBuilder.getInstance().setIdentityProvider(getFileSystemOptions(), sshKeyAuth);
} else {
authenticationData.setData(UserAuthenticationData.PASSWORD, passTx.getPassword());
}

}
Expand Down Expand Up @@ -79,12 +88,25 @@ public void actionPerformed(ActionEvent e) {
}
}
});
panel.add(browseButton,"wrap");
panel.add(new JLabel(Messages.getMessage("authenticator.sshKeyFileDescription")),"span");
panel.add(browseButton, "wrap");

OptionalInt pageantActive = this.isPageantActive();
String pageantInfo;
if (pageantActive.isPresent()) {
pageantInfo = Messages.getMessage("authenticator.pageantActiveCount", pageantActive.getAsInt());
} else {
pageantInfo = Messages.getMessage("authenticator.pageantInactive");
}

panel.add(new JLabel(pageantInfo), "span");

return panel;
}

private OptionalInt isPageantActive() {
return PageantIdentityRepositoryFactory.getIdentitiesCount();
}


@Override
protected void userSelectedHook(UserAuthenticationData userAuthenticationData) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2022 Krzysztof Otrebski (otros.systems@gmail.com)
*
* 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 pl.otros.vfs.browser.util;

import com.jcraft.jsch.*;
import org.apache.commons.vfs2.provider.sftp.IdentityRepositoryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.OptionalInt;


public final class PageantIdentityRepositoryFactory implements IdentityRepositoryFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(PageantIdentityRepositoryFactory.class);
private static final byte SSH2_AGENTC_REQUEST_IDENTITIES = 11;
private static final byte SSH2_AGENT_IDENTITIES_ANSWER = 12;
private static final int MAX_AGENT_IDENTITIES = 2048;

@Override
public IdentityRepository create(JSch jsch) {
try {
AgentConnector con = new PageantConnector();
return new AgentIdentityRepository(con);
} catch (AgentProxyException | RuntimeException e) {
LOGGER.info("Unable to load PageantConnector", e);
return null;
}

}

public static OptionalInt getIdentitiesCount() {
try {
AgentConnector connector = new PageantConnector();

byte[] buf = new byte[1024];
Buffer buffer = new Buffer(buf);

int required_size = 1 + 4;
buffer.reset();
buffer.putInt(required_size - 4);
buffer.putByte(SSH2_AGENTC_REQUEST_IDENTITIES);

try {
connector.query(buffer);
} catch (AgentProxyException e) {
return OptionalInt.empty();
}

int rcode = buffer.getByte();
if (rcode != SSH2_AGENT_IDENTITIES_ANSWER) {
return OptionalInt.empty();
}

int count = buffer.getInt();
if (count <= 0 || count > MAX_AGENT_IDENTITIES) {
return OptionalInt.empty();
}
return OptionalInt.of(count);
} catch (AgentProxyException e) {
return OptionalInt.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
Expand Down Expand Up @@ -294,6 +295,7 @@ public static FileObject resolveFileObject(String filePath) throws FileSystemExc
builder.setStrictHostKeyChecking(opts, "no");
builder.setUserDirIsRoot(opts, false);
builder.setCompression(opts, "zlib,none");
builder.setIdentityRepositoryFactory(opts, new PageantIdentityRepositoryFactory());

} else if (filePath.startsWith("smb://")) {

Expand Down Expand Up @@ -333,7 +335,7 @@ public static FileObject resolveFileObject(String filePath, FileSystemOptions op
builder.setStrictHostKeyChecking(opts, "no");
builder.setUserDirIsRoot(opts, false);
builder.setCompression(opts, "zlib,none");
builder.setTimeout(opts,5000);
builder.setSessionTimeout(opts, Duration.ofSeconds(5));
}

DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(options, authenticator);
Expand Down
3 changes: 2 additions & 1 deletion OtrosVfsBrowser/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ authenticator.password=Password:
authenticator.savePassword=Save password
authenticator.selectSshKey=Select SSH key file
authenticator.sshKeyFile=SSH key file:
authenticator.sshKeyFileDescription=Your key has to be without paraphrase
authenticator.username=User name:
authenticator.pageantActiveCount=Pageant is active ({0} key(s) loaded)
authenticator.pageantInactive=Pageant is not active
browser.checkingSFtpLinksTask=Checking for symbolic links
browser.nameFilter.clearFilterText=Clear file name filter
browser.nameFilter.defaultText=Type name filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ authenticator.password=Password:
authenticator.savePassword=Password speichern
authenticator.selectSshKey=SSH Key selektieren
authenticator.sshKeyFile=SSH Key:
authenticator.sshKeyFileDescription=Das SSH Key darf nicht mit Password gesichert werden
authenticator.username=Benutzername:
authenticator.pageantActiveCount=Pageant ist aktiv ({0} Schl\u00FCssel geladen)
authenticator.pageantInactive=Pageant ist nicht aktiv
browser.checkingSFtpLinksTask=Suche nach symbolische Links
browser.nameFilter.clearFilterText=Filter l\u00F6schen
browser.nameFilter.defaultText=Filter eingeben (CTRL+F setzt den Fokus)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ authenticator.password=Password:
authenticator.savePassword=Salva la password
authenticator.selectSshKey=Selezionare il file chiave SSH
authenticator.sshKeyFile=File chiave SSH:
authenticator.sshKeyFileDescription=La vostra chiave deve essere senza password
authenticator.username=Nome utente:
browser.checkingSFtpLinksTask=Verifica di link simbolici
browser.nameFilter.clearFilterText=Eliminare filtro nome del file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ authenticator.password=Has\u0142o:
authenticator.savePassword=Zapisz has\u0142o
authenticator.selectSshKey=Wybierz plik z kluczem SSH
authenticator.sshKeyFile=Plik z kluczem SSH
authenticator.sshKeyFileDescription=Klucz nie moze zabezpieczony has?em
authenticator.username=Nazwa u\u017Cytkownika:
browser.checkingSFtpLinksTask=Sprawdzam wyst\u0119powanie link\u00F3w symbolicznych
browser.nameFilter.clearFilterText=Wyczy\u015B\u0107 filtr nazwy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ authenticator.password=\u041f\u0430\u0440\u043e\u043b\u044c:
authenticator.savePassword=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c
authenticator.selectSshKey=\u0412\u044b\u0431\u0440\u0430\u0442\u044c SSH \u0444\u0430\u0439\u043b-\u043a\u043b\u044e\u0447
authenticator.sshKeyFile=SSH \u0444\u0430\u0439\u043b-\u043a\u043b\u044e\u0447:
authenticator.sshKeyFileDescription=Your key has to be without paraphrase
authenticator.username=\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:
browser.checkingSFtpLinksTask=\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0441\u0441\u044b\u043b\u043a\u0438
browser.nameFilter.clearFilterText=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0444\u0438\u043B\u044C\u0442\u0440 \u043F\u043E \u0438\u043C\u0435\u043D\u0438
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ authenticator.password=\u041f\u0430\u0440\u043e\u043b\u044c:
authenticator.savePassword=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u043f\u0430\u0440\u043e\u043b\u044c
authenticator.selectSshKey=\u043e\u0431\u0440\u0430\u0442\u0438 SSH \u0444\u0430\u0439\u043b-\u043a\u043b\u044e\u0447
authenticator.sshKeyFile=SSH \u0444\u0430\u0439\u043b-\u043a\u043b\u044e\u0447:
authenticator.sshKeyFileDescription=Your key has to be without paraphrase
authenticator.username=\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430:
browser.checkingSFtpLinksTask=\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u043d\u0430 \u0437\u0441\u0438\u043b\u043a\u0438
browser.nameFilter.clearFilterText=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u0444\u0456\u043B\u044C\u0442\u0440 \u043F\u043E \u0456\u043C\u0435\u043D\u0456
Expand Down
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ project(':Common-libs') {
compile 'com.google.guava:guava:23.0'
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.github.mwiede:jsch:0.2.0'
compile 'net.java.dev.jna:jna-jpms:5.10.0'
compile 'net.java.dev.jna:jna-platform-jpms:5.10.0'
compile 'com.jcraft:jzlib:1.+'
compile 'com.miglayout:miglayout-swing:4.2'
compile 'com.fifesoft:rsyntaxtextarea:2.6.1'
Expand All @@ -70,7 +72,7 @@ project(':Common-libs') {
compile 'org.apache.logging.log4j:log4j-api:2.17.1'
compile 'org.apache.logging.log4j:log4j-core:2.17.1'
compile 'org.apache.commons:commons-compress:1.3'
compile 'org.apache.commons:commons-vfs2:2.0'
compile 'org.apache.commons:commons-vfs2:2.9.0'
compile 'org.slf4j:slf4j-api:1.7.33'
compile 'org.swinglabs.swingx:swingx-all:1.+'
compile 'org.swinglabs:jxlayer:3.0.4'
Expand Down