Преглед на файлове

Switch to using SFTP instead of FTP for downloading vouchers.

Andrew Klopper преди 8 години
родител
ревизия
80c2d4b3fd

+ 1 - 1
BulkPrintingAPI/BulkPrintingAPI.csproj

@@ -9,7 +9,6 @@
9 9
     <Folder Include="wwwroot\" />
10 10
   </ItemGroup>
11 11
   <ItemGroup>
12
-    <PackageReference Include="CoreFtp" Version="1.3.5" />
13 12
     <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
14 13
     <PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
15 14
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
@@ -23,6 +22,7 @@
23 22
     <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
24 23
     <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.2" />
25 24
     <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.1" />
25
+    <PackageReference Include="SSH.NET" Version="2016.0.0" />
26 26
   </ItemGroup>
27 27
   <ItemGroup>
28 28
     <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" />

+ 6 - 2
BulkPrintingAPI/Configuration/FTPOptions.cs

@@ -2,17 +2,21 @@
2 2
 
3 3
 namespace BulkPrintingAPI.Configuration
4 4
 {
5
-    public class FTPOptions
5
+    public class SFTPOptions
6 6
     {
7
-        public FTPOptions(IConfiguration configuration)
7
+        public SFTPOptions(IConfiguration configuration)
8 8
         {
9 9
             configuration.Bind(this);
10 10
         }
11 11
 
12 12
         public string Host { get; set; }
13 13
 
14
+        public int Port { get; set; } = 22;
15
+
14 16
         public string Username { get; set; }
15 17
 
16 18
         public string Password { get; set; }
19
+
20
+        public int ConnectTimeout { get; set; } = 10;
17 21
     }
18 22
 }

+ 5 - 5
BulkPrintingAPI/Controllers/BatchesController.cs

@@ -35,18 +35,18 @@ namespace BulkPrintingAPI.Controllers
35 35
         private readonly ILogger _logger;
36 36
         private readonly IMemoryCache _cache;
37 37
         private readonly DataEncryptionOptions _dataEncryptionOptions;
38
-        private readonly FTPOptions _ftpOptions;
38
+        private readonly SFTPOptions _sftpOptions;
39 39
         private readonly MAX.ClientFactory _clientFactory;
40 40
         private readonly MAXContext _context;
41 41
 
42 42
         public BatchesController(ILoggerFactory loggerFactory, IMemoryCache cache,
43
-            DataEncryptionOptions dataEncryptionOptions, FTPOptions ftpOptions,
43
+            DataEncryptionOptions dataEncryptionOptions, SFTPOptions sftpOptions,
44 44
             MAX.ClientFactory clientFactory, MAXContext context)
45 45
         {
46 46
             _logger = loggerFactory.CreateLogger(GetType().FullName);
47 47
             _cache = cache;
48 48
             _dataEncryptionOptions = dataEncryptionOptions;
49
-            _ftpOptions = ftpOptions;
49
+            _sftpOptions = sftpOptions;
50 50
             _clientFactory = clientFactory;
51 51
             _context = context;
52 52
         }
@@ -86,7 +86,7 @@ namespace BulkPrintingAPI.Controllers
86 86
 
87 87
             if (!batch.ReadyForDownload)
88 88
             {
89
-                await Utils.DownloadVouchersAsync(_ftpOptions, _context, _logger, batch);
89
+                await Utils.DownloadVouchersAsync(_sftpOptions, _context, _logger, batch);
90 90
             }
91 91
 
92 92
             return Ok(batch);
@@ -130,7 +130,7 @@ namespace BulkPrintingAPI.Controllers
130 130
 
131 131
             try
132 132
             {
133
-                await Utils.DownloadVouchersAsync(_ftpOptions, _context, _logger, orderResponse.Batch);
133
+                await Utils.DownloadVouchersAsync(_sftpOptions, _context, _logger, orderResponse.Batch);
134 134
             }
135 135
             catch (Exception e)
136 136
             {

+ 48 - 40
BulkPrintingAPI/Controllers/Utils.cs

@@ -1,5 +1,4 @@
1 1
 using BulkPrintingAPI.Configuration;
2
-using CoreFtp;
3 2
 using MAX.Models;
4 3
 using Microsoft.AspNetCore.Http;
5 4
 using Microsoft.EntityFrameworkCore;
@@ -310,60 +309,69 @@ namespace BulkPrintingAPI.Controllers
310 309
             }
311 310
         }
312 311
 
313
-        public static async Task DownloadVouchersAsync(FTPOptions ftpOptions, MAXContext context,
312
+        public static async Task DownloadVouchersAsync(SFTPOptions sftpOptions, MAXContext context,
314 313
             ILogger logger, Batch batch)
315 314
         {
316 315
             var remoteFileName = string.Format("{0}_{1}.dat", batch.Account.Id, batch.Id);
317 316
             using (var voucherStream = new MemoryStream())
318 317
             {
319
-                using (var ftp = new FtpClient(new FtpClientConfiguration()
318
+                var connectionInfo = new Renci.SshNet.ConnectionInfo(
319
+                    sftpOptions.Host,
320
+                    sftpOptions.Port,
321
+                    sftpOptions.Username,
322
+                    new Renci.SshNet.PasswordAuthenticationMethod(sftpOptions.Username, sftpOptions.Password)
323
+                );
324
+                connectionInfo.Timeout = TimeSpan.FromSeconds(sftpOptions.ConnectTimeout);
325
+
326
+                await Task.Run(() =>
320 327
                 {
321
-                    Host = ftpOptions.Host,
322
-                    Username = ftpOptions.Username,
323
-                    Password = ftpOptions.Password
324
-                }))
325
-                {
326
-                    await ftp.LoginAsync().ConfigureAwait(false);
327
-                    using (var downloadStream = await ftp.OpenFileReadStreamAsync(remoteFileName)
328
-                        .ConfigureAwait(false))
328
+                    using (var sshClient = new Renci.SshNet.SftpClient(connectionInfo))
329 329
                     {
330
-                        await downloadStream.CopyToAsync(voucherStream).ConfigureAwait(false);
330
+                        sshClient.Connect();
331
+                        sshClient.DownloadFile(remoteFileName, voucherStream);
331 332
                     }
333
+                });
332 334
 
333
-                    voucherStream.Position = 0;
334
-                    using (var streamReader = new StreamReader(voucherStream))
335
+                voucherStream.Position = 0;
336
+                using (var streamReader = new StreamReader(voucherStream))
337
+                {
338
+                    while (streamReader.Peek() >= 0)
335 339
                     {
336
-                        while (streamReader.Peek() >= 0)
340
+                        var line = streamReader.ReadLine();
341
+                        var parts = line.Split('|');
342
+
343
+                        VoucherSanityCheck(batch, decimal.Parse(parts[4]), int.Parse(parts[5]),
344
+                            parts[7]);
345
+
346
+                        context.Add(new Voucher()
337 347
                         {
338
-                            var line = streamReader.ReadLine();
339
-                            var parts = line.Split('|');
340
-
341
-                            VoucherSanityCheck(batch, decimal.Parse(parts[4]), int.Parse(parts[5]),
342
-                                parts[7]);
343
-
344
-                            context.Add(new Voucher()
345
-                            {
346
-                                Id = int.Parse(parts[0]),
347
-                                ExpiryDate = DateTime.Parse(parts[1]),
348
-                                Serial = parts[2],
349
-                                EncryptedPIN = parts[3],
350
-                                SequenceNumber = int.Parse(parts[6]),
351
-                                Batch = batch
352
-                            });
353
-                        }
348
+                            Id = int.Parse(parts[0]),
349
+                            ExpiryDate = DateTime.Parse(parts[1]),
350
+                            Serial = parts[2],
351
+                            EncryptedPIN = parts[3],
352
+                            SequenceNumber = int.Parse(parts[6]),
353
+                            Batch = batch
354
+                        });
354 355
                     }
356
+                }
355 357
 
356
-                    batch.ReadyForDownload = true;
357
-                    await context.SaveChangesAsync().ConfigureAwait(false);
358
+                batch.ReadyForDownload = true;
359
+                await context.SaveChangesAsync().ConfigureAwait(false);
358 360
 
359
-                    try
360
-                    {
361
-                        await ftp.DeleteFileAsync(remoteFileName).ConfigureAwait(false);
362
-                    }
363
-                    catch (Exception e)
361
+                try
362
+                {
363
+                    await Task.Run(() =>
364 364
                     {
365
-                        logger.LogWarning("Failed to delete file on FTP server: {0}: {1}", remoteFileName, e.Message);
366
-                    }
365
+                        using (var sshClient = new Renci.SshNet.SftpClient(connectionInfo))
366
+                        {
367
+                            sshClient.Connect();
368
+                            sshClient.DeleteFile(remoteFileName);
369
+                        }
370
+                    });
371
+                }
372
+                catch (Exception e)
373
+                {
374
+                    logger.LogWarning("Failed to delete file on FTP server: {0}: {1}", remoteFileName, e.Message);
367 375
                 }
368 376
             }
369 377
         }

+ 2 - 2
BulkPrintingAPI/Startup.cs

@@ -42,8 +42,8 @@ namespace BulkPrintingAPI
42 42
                 Configuration.GetSection("DataEncryption")));
43 43
             services.AddSingleton(new MAX.ClientFactory(
44 44
                 Configuration.GetSection("MAX")));
45
-            services.AddSingleton(new FTPOptions(
46
-                Configuration.GetSection("FTP")));
45
+            services.AddSingleton(new SFTPOptions(
46
+                Configuration.GetSection("SFTP")));
47 47
             services.AddDbContext<MAX.Models.MAXContext>(
48 48
                 options => options.UseSqlServer(
49 49
                     Configuration["Database:ConnectionString"],