embulk-output-ftp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +3 -0
- data/README.md +98 -0
- data/build.gradle +80 -0
- data/classpath/bcpkix-jdk15on-1.52.jar +0 -0
- data/classpath/bcprov-jdk15on-1.52.jar +0 -0
- data/classpath/embulk-output-ftp-0.1.0.jar +0 -0
- data/classpath/ftp4j-1.7.2.jar +0 -0
- data/config/checkstyle/checkstyle.xml +128 -0
- data/config/checkstyle/default.xml +108 -0
- data/embulk-output-ftp.gemspec +18 -0
- data/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/gradle/wrapper/gradle-wrapper.properties +6 -0
- data/gradlew +160 -0
- data/gradlew.bat +90 -0
- data/lib/embulk/output/ftp.rb +3 -0
- data/libs/ftp4j-1.7.2.jar +0 -0
- data/src/main/java/org/embulk/output/ftp/BlockingTransfer.java +277 -0
- data/src/main/java/org/embulk/output/ftp/FtpFileOutputPlugin.java +456 -0
- data/src/main/java/org/embulk/output/ftp/SSLPlugins.java +260 -0
- data/src/main/java/org/embulk/output/ftp/TrustManagers.java +285 -0
- data/src/test/java/org/embulk/output/ftp/TestBlockingTransfer.java +5 -0
- data/src/test/java/org/embulk/output/ftp/TestFtpFileOutputPlugin.java +5 -0
- data/src/test/java/org/embulk/output/ftp/TestSSLPlugins.java +5 -0
- data/src/test/java/org/embulk/output/ftp/TrustedManagers.java +5 -0
- metadata +98 -0
@@ -0,0 +1,285 @@
|
|
1
|
+
package org.embulk.output.ftp;
|
2
|
+
|
3
|
+
import org.bouncycastle.cert.X509CertificateHolder;
|
4
|
+
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
5
|
+
import org.bouncycastle.openssl.PEMException;
|
6
|
+
import org.bouncycastle.openssl.PEMParser;
|
7
|
+
import sun.security.ssl.SSLSocketImpl;
|
8
|
+
|
9
|
+
import javax.net.ssl.KeyManager;
|
10
|
+
import javax.net.ssl.SSLContext;
|
11
|
+
import javax.net.ssl.SSLParameters;
|
12
|
+
import javax.net.ssl.SSLSocket;
|
13
|
+
import javax.net.ssl.SSLSocketFactory;
|
14
|
+
import javax.net.ssl.TrustManager;
|
15
|
+
import javax.net.ssl.TrustManagerFactory;
|
16
|
+
import javax.net.ssl.X509TrustManager;
|
17
|
+
|
18
|
+
import java.io.File;
|
19
|
+
import java.io.FileInputStream;
|
20
|
+
import java.io.IOException;
|
21
|
+
import java.io.Reader;
|
22
|
+
import java.net.InetAddress;
|
23
|
+
import java.net.Socket;
|
24
|
+
import java.net.UnknownHostException;
|
25
|
+
import java.security.InvalidAlgorithmParameterException;
|
26
|
+
import java.security.KeyManagementException;
|
27
|
+
import java.security.KeyStore;
|
28
|
+
import java.security.KeyStoreException;
|
29
|
+
import java.security.NoSuchAlgorithmException;
|
30
|
+
import java.security.SecureRandom;
|
31
|
+
import java.security.cert.CertificateException;
|
32
|
+
import java.security.cert.CertificateParsingException;
|
33
|
+
import java.security.cert.PKIXParameters;
|
34
|
+
import java.security.cert.TrustAnchor;
|
35
|
+
import java.security.cert.X509Certificate;
|
36
|
+
import java.util.ArrayList;
|
37
|
+
import java.util.List;
|
38
|
+
|
39
|
+
public class TrustManagers
|
40
|
+
{
|
41
|
+
private TrustManagers()
|
42
|
+
{
|
43
|
+
}
|
44
|
+
|
45
|
+
public static KeyStore readDefaultJavaKeyStore()
|
46
|
+
throws IOException, KeyStoreException, CertificateException
|
47
|
+
{
|
48
|
+
String path = (System.getProperty("java.home") + "/lib/security/cacerts").replace('/', File.separatorChar);
|
49
|
+
try {
|
50
|
+
KeyStore keyStore = KeyStore.getInstance("JKS");
|
51
|
+
try (FileInputStream in = new FileInputStream(path)) {
|
52
|
+
keyStore.load(in, null); // password=null because cacerts file is not encrypted
|
53
|
+
}
|
54
|
+
return keyStore;
|
55
|
+
}
|
56
|
+
catch (NoSuchAlgorithmException ex) {
|
57
|
+
throw new RuntimeException(ex); // TODO assertion exception?
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
public static List<X509Certificate> readDefaultJavaTrustedCertificates()
|
62
|
+
throws IOException, CertificateException, KeyStoreException, InvalidAlgorithmParameterException
|
63
|
+
{
|
64
|
+
KeyStore keyStore = readDefaultJavaKeyStore();
|
65
|
+
PKIXParameters params = new PKIXParameters(keyStore);
|
66
|
+
List<X509Certificate> certs = new ArrayList<>();
|
67
|
+
for (TrustAnchor trustAnchor : params.getTrustAnchors()) {
|
68
|
+
certs.add(trustAnchor.getTrustedCert());
|
69
|
+
}
|
70
|
+
return certs;
|
71
|
+
}
|
72
|
+
|
73
|
+
public static List<X509Certificate> readPemEncodedX509Certificates(Reader reader)
|
74
|
+
throws IOException, CertificateException
|
75
|
+
{
|
76
|
+
// this method abuses CertificateParsingException because its javadoc says
|
77
|
+
// CertificateParsingException is only for DER-encoded formats.
|
78
|
+
|
79
|
+
JcaX509CertificateConverter conv = new JcaX509CertificateConverter();
|
80
|
+
List<X509Certificate> certs = new ArrayList<>();
|
81
|
+
|
82
|
+
try {
|
83
|
+
PEMParser pemParser = new PEMParser(reader);
|
84
|
+
// PEMParser#close is unnecessary because it just closes underlying reader
|
85
|
+
|
86
|
+
while (true) {
|
87
|
+
Object pem = pemParser.readObject();
|
88
|
+
|
89
|
+
if (pem == null) {
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
|
93
|
+
if (pem instanceof X509CertificateHolder) {
|
94
|
+
X509Certificate cert = conv.getCertificate((X509CertificateHolder) pem);
|
95
|
+
certs.add(cert);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
catch (PEMException ex) {
|
100
|
+
// throw when parsing PemObject to Object fails
|
101
|
+
throw new CertificateParsingException(ex);
|
102
|
+
}
|
103
|
+
catch (IOException ex) {
|
104
|
+
if (ex.getClass().equals(IOException.class)) {
|
105
|
+
String message = ex.getMessage();
|
106
|
+
if (message.startsWith("unrecognised object: ")) {
|
107
|
+
// thrown at org.bouncycastle.openssl.PemParser.readObject when key type (header of a pem) is
|
108
|
+
// unknown.
|
109
|
+
throw new CertificateParsingException(ex);
|
110
|
+
}
|
111
|
+
else if (message.startsWith("-----END ") && message.endsWith(" not found")) {
|
112
|
+
// thrown at org.bouncycastle.util.io.pem.PemReader.loadObject when a pem file format is invalid
|
113
|
+
throw new CertificateParsingException(ex);
|
114
|
+
}
|
115
|
+
}
|
116
|
+
else {
|
117
|
+
throw ex;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
return certs;
|
122
|
+
}
|
123
|
+
|
124
|
+
public static KeyStore buildKeyStoreFromTrustedCertificates(List<X509Certificate> certificates)
|
125
|
+
throws KeyStoreException
|
126
|
+
{
|
127
|
+
KeyStore keyStore = KeyStore.getInstance("JKS");
|
128
|
+
try {
|
129
|
+
keyStore.load(null);
|
130
|
+
}
|
131
|
+
catch (IOException | CertificateException | NoSuchAlgorithmException ex) {
|
132
|
+
throw new RuntimeException(ex);
|
133
|
+
}
|
134
|
+
int i = 0;
|
135
|
+
for (X509Certificate cert : certificates) {
|
136
|
+
keyStore.setCertificateEntry("cert_" + i, cert);
|
137
|
+
i++;
|
138
|
+
}
|
139
|
+
return keyStore;
|
140
|
+
}
|
141
|
+
|
142
|
+
public static X509TrustManager[] newTrustManager(List<X509Certificate> trustedCertificates)
|
143
|
+
throws KeyStoreException
|
144
|
+
{
|
145
|
+
try {
|
146
|
+
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
147
|
+
KeyStore keyStore = buildKeyStoreFromTrustedCertificates(trustedCertificates);
|
148
|
+
factory.init(keyStore);
|
149
|
+
List<X509TrustManager> tms = new ArrayList<>();
|
150
|
+
for (TrustManager tm : factory.getTrustManagers()) {
|
151
|
+
if (tm instanceof X509TrustManager) {
|
152
|
+
tms.add((X509TrustManager) tm);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
return tms.toArray(new X509TrustManager[tms.size()]);
|
156
|
+
}
|
157
|
+
catch (NoSuchAlgorithmException ex) {
|
158
|
+
throw new RuntimeException(ex); // TODO assertion exception?
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
public static X509TrustManager[] newDefaultJavaTrustManager()
|
163
|
+
throws IOException, CertificateException, KeyStoreException, InvalidAlgorithmParameterException
|
164
|
+
{
|
165
|
+
return newTrustManager(readDefaultJavaTrustedCertificates());
|
166
|
+
}
|
167
|
+
|
168
|
+
public static SSLContext newSSLContext(KeyManager[] keyManager, X509TrustManager[] trustManager)
|
169
|
+
throws KeyManagementException
|
170
|
+
{
|
171
|
+
try {
|
172
|
+
SSLContext context = SSLContext.getInstance("TLS");
|
173
|
+
context.init(
|
174
|
+
keyManager,
|
175
|
+
trustManager,
|
176
|
+
new SecureRandom());
|
177
|
+
return context;
|
178
|
+
}
|
179
|
+
catch (NoSuchAlgorithmException ex) {
|
180
|
+
throw new RuntimeException(ex);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
public static SSLSocketFactory newSSLSocketFactory(KeyManager[] keyManager, X509TrustManager[] trustManager, String verifyHostname)
|
185
|
+
throws KeyManagementException
|
186
|
+
{
|
187
|
+
SSLContext context = newSSLContext(keyManager, trustManager);
|
188
|
+
SSLSocketFactory factory = context.getSocketFactory();
|
189
|
+
if (verifyHostname == null) {
|
190
|
+
return factory;
|
191
|
+
}
|
192
|
+
else {
|
193
|
+
return new VerifyHostNameSSLSocketFactory(factory, verifyHostname);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
private static class VerifyHostNameSSLSocketFactory
|
198
|
+
extends SSLSocketFactory
|
199
|
+
{
|
200
|
+
private final SSLSocketFactory next;
|
201
|
+
private final String hostname;
|
202
|
+
|
203
|
+
public VerifyHostNameSSLSocketFactory(SSLSocketFactory next, String hostname)
|
204
|
+
{
|
205
|
+
this.next = next;
|
206
|
+
this.hostname = hostname;
|
207
|
+
}
|
208
|
+
|
209
|
+
@Override
|
210
|
+
public String[] getDefaultCipherSuites()
|
211
|
+
{
|
212
|
+
return next.getDefaultCipherSuites();
|
213
|
+
}
|
214
|
+
|
215
|
+
@Override
|
216
|
+
public String[] getSupportedCipherSuites()
|
217
|
+
{
|
218
|
+
return next.getSupportedCipherSuites();
|
219
|
+
}
|
220
|
+
|
221
|
+
@Override
|
222
|
+
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
|
223
|
+
throws IOException
|
224
|
+
{
|
225
|
+
Socket sock = next.createSocket(s, host, port, autoClose);
|
226
|
+
setSSLParameters(sock, false);
|
227
|
+
return sock;
|
228
|
+
}
|
229
|
+
|
230
|
+
@Override
|
231
|
+
public Socket createSocket(String host, int port)
|
232
|
+
throws IOException, UnknownHostException
|
233
|
+
{
|
234
|
+
Socket sock = next.createSocket(host, port);
|
235
|
+
setSSLParameters(sock, false);
|
236
|
+
return sock;
|
237
|
+
}
|
238
|
+
|
239
|
+
@Override
|
240
|
+
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
|
241
|
+
throws IOException, UnknownHostException
|
242
|
+
{
|
243
|
+
Socket sock = next.createSocket(host, port, localHost, localPort);
|
244
|
+
setSSLParameters(sock, false);
|
245
|
+
return sock;
|
246
|
+
}
|
247
|
+
|
248
|
+
@Override
|
249
|
+
public Socket createSocket(InetAddress host, int port)
|
250
|
+
throws IOException
|
251
|
+
{
|
252
|
+
Socket sock = next.createSocket(host, port);
|
253
|
+
setSSLParameters(sock, true);
|
254
|
+
return sock;
|
255
|
+
}
|
256
|
+
|
257
|
+
@Override
|
258
|
+
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
|
259
|
+
throws IOException
|
260
|
+
{
|
261
|
+
Socket sock = next.createSocket(address, port, localAddress, localPort);
|
262
|
+
setSSLParameters(sock, true);
|
263
|
+
return sock;
|
264
|
+
}
|
265
|
+
|
266
|
+
private void setSSLParameters(Socket sock, boolean setHostname)
|
267
|
+
{
|
268
|
+
if (sock instanceof SSLSocket) {
|
269
|
+
SSLSocket s = (SSLSocket) sock;
|
270
|
+
String identAlgorithm = s.getSSLParameters().getEndpointIdentificationAlgorithm();
|
271
|
+
if (identAlgorithm != null && identAlgorithm.equalsIgnoreCase("HTTPS")) {
|
272
|
+
// hostname verification is already configured.
|
273
|
+
} else {
|
274
|
+
if (setHostname && s instanceof SSLSocketImpl) {
|
275
|
+
((SSLSocketImpl) s).setHost(hostname);
|
276
|
+
}
|
277
|
+
SSLParameters params = s.getSSLParameters();
|
278
|
+
params.setEndpointIdentificationAlgorithm("HTTPS");
|
279
|
+
s.setSSLParameters(params);
|
280
|
+
// s.startHandshake
|
281
|
+
}
|
282
|
+
}
|
283
|
+
}
|
284
|
+
}
|
285
|
+
}
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: embulk-output-ftp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Satoshi Akama
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ~>
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.0'
|
19
|
+
name: bundler
|
20
|
+
prerelease: false
|
21
|
+
type: :development
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '10.0'
|
33
|
+
name: rake
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Stores files on FTP.
|
42
|
+
email:
|
43
|
+
- satoshiakama@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .travis.yml
|
50
|
+
- CHANGELOG.md
|
51
|
+
- README.md
|
52
|
+
- build.gradle
|
53
|
+
- config/checkstyle/checkstyle.xml
|
54
|
+
- config/checkstyle/default.xml
|
55
|
+
- embulk-output-ftp.gemspec
|
56
|
+
- gradle/wrapper/gradle-wrapper.jar
|
57
|
+
- gradle/wrapper/gradle-wrapper.properties
|
58
|
+
- gradlew
|
59
|
+
- gradlew.bat
|
60
|
+
- lib/embulk/output/ftp.rb
|
61
|
+
- libs/ftp4j-1.7.2.jar
|
62
|
+
- src/main/java/org/embulk/output/ftp/BlockingTransfer.java
|
63
|
+
- src/main/java/org/embulk/output/ftp/FtpFileOutputPlugin.java
|
64
|
+
- src/main/java/org/embulk/output/ftp/SSLPlugins.java
|
65
|
+
- src/main/java/org/embulk/output/ftp/TrustManagers.java
|
66
|
+
- src/test/java/org/embulk/output/ftp/TestBlockingTransfer.java
|
67
|
+
- src/test/java/org/embulk/output/ftp/TestFtpFileOutputPlugin.java
|
68
|
+
- src/test/java/org/embulk/output/ftp/TestSSLPlugins.java
|
69
|
+
- src/test/java/org/embulk/output/ftp/TrustedManagers.java
|
70
|
+
- classpath/bcpkix-jdk15on-1.52.jar
|
71
|
+
- classpath/bcprov-jdk15on-1.52.jar
|
72
|
+
- classpath/embulk-output-ftp-0.1.0.jar
|
73
|
+
- classpath/ftp4j-1.7.2.jar
|
74
|
+
homepage: https://github.com/sakama/embulk-output-ftp
|
75
|
+
licenses:
|
76
|
+
- Apache 2.0
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.1.9
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: FTP file output plugin for Embulk
|
98
|
+
test_files: []
|