aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Web/Tango.MachineService/Controllers/FSEController.cs
blob: b9dacfcf90c987b2ef5fb075a3986303a87f15e9 (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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

using Tango.Scripting.Editors.Document;

namespace Tango.Scripting.Editors.Xml
{
	/// <summary>
	/// Holds all objects that need to keep offsets up to date.
	/// </summary>
	class TrackedSegmentCollection
	{
		/// <summary>
		/// Holds all types of objects in one collection.
		/// </summary>
		TextSegmentCollection<TextSegment> segments = new TextSegmentCollection<TextSegment>();
		
		/// <summary>
		/// Is used to identify what memory range was touched by object
		/// The default is (StartOffset, EndOffset + 1) which is not stored
		/// </summary>
		class TouchedRange: TextSegment
		{
			public AXmlObject TouchedByObject { get; set; }
		}
		
		public void UpdateOffsetsAndInvalidate(IEnumerable<<
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.");
                }

                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();

                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().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().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
                        };

                        _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,
                    });

                    db.SaveChanges();
                }

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

        #endregion
    }
}