aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Web/Tango.MachineService/Controllers/FSEController.cs
blob: 38c75934a458120054ac0b9db6945a1719ed1ec2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
generated by cgit v1.3.1 (git 2.54.0) at 2026-07-21 05:18:10 +0000
 


'#n298'>298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
using SendGrid;
using SendGrid.Helpers.Mail;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Authentication;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using Tango.BL;
using Tango.BL.Builders;
using Tango.BL.Entities;
using Tango.Core;
using Tango.Core.Cryptography;
using Tango.Core.DB;
using Tango.FSE.Web.Messages;
using Tango.MachineService.Filters;
using Tango.Web.Controllers;
using Tango.Web.Helpers;
using Tango.Web.Security;
using Tango.Web.SMO;
using Tango.Web.SQLServer;
using Tango.Web.Storage;
using System.Data.Entity;
using static Tango.MachineService.Controllers.FSEController;
using Tango.MachineService.Models;
using Tango.BL.Enumerations;

namespace Tango.MachineService.Controllers
{
    public class FSEController : TangoController<TokenObject>
    {
        private static List<FSEPendingUpload> _pendingUploads;

        public class TokenObject
        {
            public String UserGuid { get; set; }
        }

        public class PasswordReset
        {
            public String ID { get; set; }
            public String UserGuid { get; set; }
            public String FullName { get; set; }
        }

        public static List<PasswordReset> PendingPasswordResets { get; set; }

        static FSEController()
        {
            _pendingUploads = new List<FSEPendingUpload>();
            PendingPasswordResets = new List<PasswordReset>();
        }

        [HttpPost]
        public LoginResponse Login(LoginRequest request)
        {
            User user = null;
            DataSource dataSource = null;
            IHashGenerator hash = new BasicHashGenerator();

            Version client_version;

            if (!Version.TryParse(request.Version, out client_version))
            {
                client_version = new Version("1.0.0.0");
            }

            var password = hash.Encrypt(request.Password);

            using (var db = ObservablesContextHelper.CreateContext())
            {
                user = new UserBuilder(db).Set(x => x.Email.ToLower() == request.Email.ToLower() && x.Password == password).WithRolesAndPermissions().WithDeleted().Build();

                if (user == null)
                {
                    throw new AuthenticationException("Invalid email or password.");
                }

                if (user.Deleted)
                {
                    throw new AuthenticationException("Your account has been disabled. Please contact your administrator.");
                }

                if (request.Build == BuildVariants.FSE && !user.HasPermission(Permissions.FSE_RunFSE))
                {
                    throw new AuthenticationException("You do not have permission to access Tango FSE. Please contact your administrator.");
                }

                if (request.Build == BuildVariants.TwineStudio && !user.HasPermission(Permissions.TwineStudioAccess))
                {
                    throw new AuthenticationException("You do not have permission to access Twine Studio. Please contact your administrator.");
                }

                user.LastLogin = DateTime.UtcNow;
                db.SaveChanges();
            }

            SQLServerManager sqlServer = new SQLServerManager();
            var accessToken = sqlServer.GetAccessToken();

            dataSource = new DataSource()
            {
                Address = MachineServiceConfig.DB_ADDRESS,
                Catalog = MachineServiceConfig.DB_CATALOG,
                Type = DataSourceType.AccessToken,
                IntegratedSecurity = false,
                AccessToken = accessToken.AccessToken,
                AccessTokenExpiration = accessToken.ExpiresOn.UtcDateTime
            };

            //Return data source
            return new LoginResponse()
            {
                DataSource = dataSource,
                AccessToken = WebToken<TokenObject>.CreateNew(MachineServiceConfig.JWT_TOKEN_SECRET, new TokenObject()
                {
                    UserGuid = user.Guid,
                }, DateTime.UtcNow.AddDays(1)).AccessToken,
                PasswordChangeRequired = user.PasswordChangeRequired
            };
        }

        [HttpPost]
        [JwtTokenFilter]
        public BugReportingInfoResponse GetBugReportInfo(BugReportingInfoRequest request)
        {
            return new BugReportingInfoResponse()
            {
                CollectionUrl = MachineServiceConfig.TFS_COLLECTION_URL,
                PersonalToken = MachineServiceConfig.TFS_PERSONAL_TOKEN,
                UserEmail = MachineServiceConfig.FSE_TFS_USER_EMAIL
            };
        }

        [HttpPost]
        [JwtTokenFilter]
        public DownloadTangoVersionResponse DownloadTangoVersion(DownloadTangoVersionRequest request)
        {
            DownloadTangoVersionResponse response = new DownloadTangoVersionResponse();

            using (ObservablesContext db = ObservablesContextHelper.CreateContext())
            {
                var tangoVersion = db.TangoVersions.SingleOrDefault(x => x.Guid == request.TangoVersionGuid);

                if (tangoVersion == null)
                {
                    throw new ArgumentException("Could not locate the specified Tango version.");
                }

                response.Version = tangoVersion.Version;

                var manager = new BlobStorageManager();
                var container = manager.GetContainer(MachineServiceConfig.TANGO_VERSIONS_CONTAINER);
                var blob = container.GetBlockBlobReference(tangoVersion.BlobName);

                response.BlobAddress = blob.GenerateReadSignature(TimeSpan.FromMinutes(60));

                if (!String.IsNullOrWhiteSpace(MachineServiceConfig.CDN_ENDPOINT))
                {
                    response.CdnAddress = MachineServiceConfig.CDN_ENDPOINT + blob.Uri.AbsolutePath;
                }

                DbCredentials credentials = new DbCredentials();

                using (SmoManager smo = new SmoManager())
                {
                    credentials = smo.CreateRandomLoginAndUser();

                    Task.Delay(TimeSpan.FromMinutes(PPCController.SQL_TEMP_CREDENTIALS_EXP_MINUTS)).ContinueWith((x) =>
                    {
                        using (SmoManager m = new SmoManager())
                        {
                            m.DeleteLoginAndUser(credentials.UserName);
                        }
                    });
                }

                response.DataSource = new DataSource()
                {
                    Address = MachineServiceConfig.DB_ADDRESS,
                    Catalog = MachineServiceConfig.DB_CATALOG,
                    UserName = credentials.UserName,
                    Password = credentials.Password,
                    IntegratedSecurity = false,
                    Type = DataSourceType.SQLServer,
                };
            }

            return response;
        }

        [HttpPost]
        [JwtTokenFilter]
        public CheckForUpdatesResponse CheckForUpdates(CheckForUpdatesRequest request)
        {
            CheckForUpdatesResponse response = new CheckForUpdatesResponse();

            using (ObservablesContext db = ObservablesContextHelper.CreateContext())
            {
                var versions = db.FseVersions.ToList().Where(x => x.BuildVariant == request.Build.ToInt32()).ToList();

                FseVersion latestVersion = null;

                latestVersion = versions.OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault();

                Version currentVersion = Version.Parse(request.Version);

                String comments = String.Join(Environment.NewLine, versions.OrderBy(x => Version.Parse(x.Version)).Where(x => Version.Parse(x.Version) > currentVersion).Select(x => x.Comments));

                if (latestVersion != null && Version.Parse(latestVersion.Version) > currentVersion)
                {
                    var manager = new BlobStorageManager();
                    var container = manager.GetContainer(MachineServiceConfig.FSE_VERSIONS_CONTAINER);
                    //var blob = container.GetBlockBlobReference(latestVersion.BlobName);
                    var installerBlob = container.GetBlockBlobReference(latestVersion.InstallerBlobName);

                    //response.BlobAddress = blob.GenerateReadSignature(TimeSpan.FromMinutes(60));

                    if (!String.IsNullOrWhiteSpace(MachineServiceConfig.CDN_ENDPOINT))
                    {
                        //response.CdnAddress = MachineServiceConfig.CDN_ENDPOINT + blob.Uri.AbsolutePath;
                        response.InstallerCdnAddress = MachineServiceConfig.CDN_ENDPOINT + installerBlob.Uri.AbsolutePath;
                    }

                    response.IsUpdateAvailable = true;
                    response.Version = latestVersion.Version;
                    response.Comments = latestVersion.Comments;
                }
            }

            return response;
        }

        [HttpPost]
        [JwtTokenFilter]
        public RefreshTokenResponse RefreshToken(RefreshTokenRequest request)
        {
            SQLServerManager sqlServer = new SQLServerManager();
            var accessToken = sqlServer.GetAccessToken();

            return new RefreshTokenResponse()
            {
                AccessToken = accessToken.AccessToken,
                Expiration = accessToken.ExpiresOn.UtcDateTime,
            };
        }

        [HttpPost]
        [JwtTokenFilter]
        public UserInvitationEmailResponse SendUserInvitationEmail(UserInvitationEmailRequest request)
        {
            User user;

            using (ObservablesContext db = ObservablesContextHelper.CreateContext())
            {
                user = db.Users.Include(x => x.Contact).SingleOrDefault(x => x.Guid == request.UserGuid);

                if (user == null)
                {
                    throw new InvalidOperationException("User not found.");
                }
            }

            var client = new SendGridClient(MachineServiceConfig.SEND_GRID_API_KEY);
            SendGridMessage msg = new SendGridMessage();
            msg.SetFrom("info@twine-s.com", "Twine Solutions LTD");
            msg.AddTo(user.Email);
            msg.Subject = "Welcome To Tango FSE";
            msg.SetTemplateId("d-2af42ed0ea3c44b3abaa61016223555a");

            var dynamicTemplateData = new
            {
                DownloadUrl = $"{request.MachineServiceAddress}/fse",
                FullName = user.Contact.FirstName,
                Password = request.Password,
            };

            msg.SetTemplateData(dynamicTemplateData);

            var result = client.SendEmailAsync(msg).GetAwaiter().GetResult();

            if (result.StatusCode != HttpStatusCode.Accepted)
            {
                throw new HttpException(result.StatusCode.ToString());
            }

            return new UserInvitationEmailResponse();
        }

        [HttpPost]
        public ForgotPasswordResponse SendForgotPasswordEmail(ForgotPasswordRequest request)
        {
            User user;

            using (ObservablesContext db = ObservablesContextHelper.CreateContext())
            {
                user = db.Users.Include(x => x.Contact).SingleOrDefault(x => x.Email.ToLower() == request.Email.ToLower());

                if (user == null)
                {
                    throw new InvalidOperationException("User not found.");
                }
            }

            String resetId = Guid.NewGuid().ToString();

            var client = new SendGridClient(MachineServiceConfig.SEND_GRID_API_KEY);
            SendGridMessage msg = new SendGridMessage();
            msg.SetFrom("info@twine-s.com", "Twine Solutions LTD");
            msg.AddTo(request.Email);
            msg.Subject = "Tango FSE Password Reset";
            msg.SetTemplateId("d-18065487dae4456b8684d4b47a91e4a6");

            var dynamicTemplateData = new
            {
                ResetPasswordUrl = $"{request.MachineServiceAddress}/FSEAccount/ResetPassword?id={resetId}",
                FullName = user.Contact.FirstName,
            };

            msg.SetTemplateData(dynamicTemplateData);

            var result = client.SendEmailAsync(msg).GetAwaiter().GetResult();

            if (result.StatusCode != HttpStatusCode.Accepted)
            {
                throw new HttpException(result.StatusCode.ToString());
            }

            PendingPasswordResets.Add(new PasswordReset()
            {
                ID = resetId,
                UserGuid = user.Guid,
                FullName = user.Contact.FirstName,
            });

            return new ForgotPasswordResponse();
        }

        #region Version Upload

        [HttpPost]
        public LatestVersionResponse GetLatestVersion(LatestVersionRequest request)
        {
            using (ObservablesContext db = ObservablesContextHelper.CreateContext())
            {
                var version = db.FseVersions.ToList().Where(x => x.BuildVariant == request.Build.ToInt32()).OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault();
                return new LatestVersionResponse() { Version = version != null ? version.Version : "0.0.0.0" };
            }
        }

        [HttpPost]
        [JwtTokenFilter]
        public UploadVersionResponse UploadVersion(UploadVersionRequest request)
        {
            UploadVersionResponse response = new UploadVersionResponse();

            using (ObservablesContext db = ObservablesContextHelper.CreateContext())
            {
                String userID = RequestToken.Object.UserGuid;

                var user = new UserBuilder(db).Set(userID).WithRolesAndPermissions().Build();

                if (user != null && user.HasPermission(Permissions.PublishMachineStudioVersions))
                
{
                    var latestVersion = db.FseVersions.ToList().Where(x => x.BuildVariant == request.Build.ToInt32()).ToList().OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault();
                    Version local_version = Version.Parse(request.Version);

                    if (latestVersion == null || local_version > Version.Parse(latestVersion.Version))
                    {
                        var manager = new BlobStorageManager();
                        var container = manager.GetContainer(MachineServiceConfig.FSE_VERSIONS_CONTAINER);
                        var installerBlob = container.CreateEmptyBlob(request.InstallerBlobName);

                        response.Token = Guid.NewGuid().ToString();
                        response.InstallerBlobAddress = installerBlob.GenerateWriteSignature(TimeSpan.FromMinutes(30));

                        FSEPendingUpload pending_upload = new FSEPendingUpload()
                        {
                            UserGuid = user.Guid,
                            Comments = request.Comments,
                            Token = response.Token,
                            Version = request.Version,
                            BlobName = "BLOB",
                            InstallerBlobName = installerBlob.Name,
                            BuildVariant = request.Build.ToInt32()
                        };

                        _pendingUploads.Add(pending_upload);
                    }
                    else
                    {
                        throw new ArgumentException("New version must be greater than latest version.");
                    }
                }
                else
                {
                    throw new AuthenticationException("Invalid user credentials.");
                }
            }

            return response;
        }

        [HttpPost]
        [JwtTokenFilter]
        public UploadCompletedResponse NotifyVersionUploadCompleted(UploadCompletedRequest request)
        {
            FSEPendingUpload upload = _pendingUploads.FirstOrDefault(x => x.Token == request.Token);

            if (upload != null)
            {
                _pendingUploads.RemoveAll(x => x.Token == upload.Token);

                using (ObservablesContext db = ObservablesContextHelper.CreateContext())
                {
                    db.FseVersions.Add(new FseVersion()
                    {
                        Comments = upload.Comments,
                        BlobName = upload.BlobName,
                        InstallerBlobName = upload.InstallerBlobName,
                        UserGuid = upload.UserGuid,
                        Version = upload.Version,
                        BuildVariant = upload.BuildVariant
                    });

                    db.SaveChanges();
                }

                return new UploadCompletedResponse();
            }
            else
            {
                throw new ArgumentException("Invalid Token.");
            }
        }

        #endregion
    }
}