aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Web/Tango.MachineService/Controllers/DataStoreController.cs
blob: 383a59850b279d4513c96b4897596759dff13830 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
generated by cgit v1.3.1 (git 2.54.0) at 2026-07-10 12:30:59 +0000
 


' href='#n257'>257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Authentication;
using System.Web.Http;
using Tango.BL.Builders;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core.Cryptography;
using Tango.DataStore;
using Tango.DataStore.EF;
using Tango.DataStore.Web;
using Tango.MachineService.Filters;
using Tango.Web.Controllers;
using Tango.Web.Helpers;
using Tango.Web.Security;
using static Tango.MachineService.Controllers.DataStoreController;

namespace Tango.MachineService.Controllers
{
    public class DataStoreController : TangoController<TokenObject>
    {
        public class TokenObject
        {
            public String UserGuid { get; set; }
            public List<Permissions> Permissions { get; set; }

            public TokenObject()
            {
                Permissions = new List<Permissions>();
            }
        }

        private IDataStoreManager _manager;

        public DataStoreController()
        {
            _manager = new EFDataStoreManager();
        }

        [HttpPost]
        public LoginResponse Login(LoginRequest request)
        {
            User user = null;

            IHashGenerator hash = new BasicHashGenerator();
            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 (!user.HasPermission(Permissions.DataStoreRead))
                {
                    throw new AuthenticationException("You are not authorized to access the data store.");
                }

                var token = WebToken<TokenObject>.CreateNew(MachineServiceConfig.JWT_TOKEN_SECRET, new TokenObject()
                {
                    UserGuid = user.Guid,
                    Permissions = user.Permissions.Select(x => (Permissions)x.Code).ToList()
                }, DateTime.UtcNow.AddDays(1));

                return new LoginResponse()
                {
                    Token = token.AccessToken,
                    ExpirationUTC = token.Expiration.Value,
                };
            }
        }

        [JwtWebApiTokenFilter]
        public List<DataStoreWebItem> Get(String sn = null, String collection = null, String key = null)
        {
            try
            {
                if (!RequestToken.Object.Permissions.Contains(Permissions.DataStoreRead))
                {
                    throw CreateHttpException(new AuthenticationException("The current user was not authorized to read from the data store."), HttpStatusCode.Unauthorized);
                }

                if (key != null && collection == null)
                {
                    throw CreateHttpException(new ArgumentException(), HttpStatusCode.BadRequest, "When specifying a key, collection must be specified.");
                }

                ValidateCollectionAndKey(collection, key);

                using (var db = ObservablesContextHelper.CreateContext())
                {
                    if (sn != null)
                    {
                        var machineGuid = db.Machines.Where(x => x.SerialNumber == sn).Select(x => x.Guid).FirstOrDefault();

                        if (machineGuid == null)
                        {
                            throw CreateHttpException(new KeyNotFoundException(), HttpStatusCode.NotFound, "The specified machine serial number could not be found.");
                        }

                        var localItems = db.DataStoreItems.Where(x => !x.IsDeleted).Where(x => x.MachineGuid == machineGuid && (collection == null || x.CollectionName == collection) && (key == null || x.Key == key)).ToList();
                        var globalItems = db.GlobalDataStoreItems.Where(x => (collection == null || x.CollectionName == collection) && (key == null || x.Key == key)).ToList();

                        if (localItems.Count == 0 && globalItems.Count == 0 && key != null)
                        {
                            throw CreateHttpException(new KeyNotFoundException(), HttpStatusCode.NotFound, "The specified key was not found on the data store.");
                        }

                        List<DataStoreWebItem> finalList = new List<DataStoreWebItem>();

                        foreach (var localItem in localItems)
                        {
                            var globalItem = globalItems.FirstOrDefault(x => x.CollectionName == localItem.CollectionName && x.Key == localItem.Key);
                            finalList.Add(localItem.ToWebItem(globalItem));
                            globalItems.Remove(globalItem);
                        }

                        finalList.AddRange(globalItems.Select(x => x.ToWebItem()));

                        return finalList;
                    }
                    else
                    {
                        var globalItems = db.GlobalDataStoreItems.Where(x => (collection == null || x.CollectionName == collection) && (key == null || x.Key == key)).ToList();

                        var finalList = globalItems.Select(x => x.ToWebItem()).ToList();

                        return finalList;
                    }
                }
            }
            catch (HttpResponseException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw CreateHttpException(ex, HttpStatusCode.InternalServerError);
            }
        }

        [JwtWebApiTokenFilter]
        public void Put([FromBody]DataStoreWebPutItem item)
        {
            try
            {
                if (!RequestToken.Object.Permissions.Contains(Permissions.DataStoreWrite))
                {
                    throw CreateHttpException(new AuthenticationException("The current user was not authorized to write to the data store."), HttpStatusCode.Unauthorized);
                }

                if (item.Collection == null || item.Key == null)
                {
                    throw CreateHttpException(new AuthenticationException("Collection and key must be specified."), HttpStatusCode.BadRequest);
                }

                ValidateCollectionAndKey(item.Collection, item.Key);

                using (var db = ObservablesContextHelper.CreateContext())
                {
                    if (item.MachineSerialNumber != null)
                    {
                        var machineGuid = db.Machines.Where(x => x.SerialNumber == item.MachineSerialNumber).Select(x => x.Guid).FirstOrDefault();

                        if (machineGuid == null)
                        {
                            throw CreateHttpException(new KeyNotFoundException("The specified machine serial number could not be found."), HttpStatusCode.NotFound);
                        }

                        DataStoreItem dbItem = db.DataStoreItems.FirstOrDefault(x => x.CollectionName == item.Collection && x.Key == item.Key);

                        if (dbItem == null)
                        {
                            if (!RequestToken.Object.Permissions.Contains(Permissions.DataStoreCreate))
                            {
                                throw CreateHttpException(new AuthenticationException("The current user was not authorized to create new items on the data store."), HttpStatusCode.Unauthorized);
                            }

                            dbItem = new DataStoreItem();
                            dbItem.Key = item.Key;
                            dbItem.CollectionName = item.Collection;
                            dbItem.MachineGuid = machineGuid;
                            db.DataStoreItems.Add(dbItem);
                        }

                        dbItem.DataType = (int)item.DataType;
                        dbItem.IsDeleted = false;
                        dbItem.IsSynchronized = false;
                        dbItem.LastUpdated = DateTime.UtcNow;
                        dbItem.Value = EFDataStoreHelper.CreateBytes(item.DataType, DataStoreHelper.ParseDataStoreValue(item.DataType, item.Value.ToStringSafe(), item.ProtoMessageType));
                    }
                    else
                    {
                        if (!RequestToken.Object.Permissions.Contains(Permissions.DataStoreCreateWriteGlobal))
                        {
                            throw CreateHttpException(new AuthenticationException("The current user was not authorized to write to the global data store."), HttpStatusCode.Unauthorized);
                        }

                        GlobalDataStoreItem dbItem = db.GlobalDataStoreItems.FirstOrDefault(x => x.CollectionName == item.Collection && x.Key == item.Key);

                        if (dbItem == null)
                        {
                            if (!RequestToken.Object.Permissions.Contains(Permissions.DataStoreCreate))
                            {
                                throw CreateHttpException(new AuthenticationException("The current user was not authorized to create new items on the data store."), HttpStatusCode.Unauthorized);
                            }

                            dbItem = new GlobalDataStoreItem();
                            dbItem.Key = item.Key;
                            dbItem.CollectionName = item.Collection;
                            db.GlobalDataStoreItems.Add(dbItem);
                        }

                        dbItem.DataType = (int)item.DataType;
                        dbItem.LastUpdated = DateTime.UtcNow;
                        dbItem.Value = EFDataStoreHelper.CreateBytes(item.DataType, DataStoreHelper.ParseDataStoreValue(item.DataType, item.Value.ToStringSafe(), item.ProtoMessageType));
                    }

                    db.SaveChanges();
                }
            }
            catch (HttpResponseException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw CreateHttpException(ex, HttpStatusCode.InternalServerError);
            }
        }

        private HttpResponseException CreateHttpException(Exception ex, HttpStatusCode code, String message = null)
        {
            return new HttpResponseException(new HttpResponseMessage(code)
            {
                Content = new StringContent(message != null ? message : ex.Message),
                ReasonPhrase = ex.FlattenMessage()
            });
        }

        private void ValidateCollectionAndKey(String collection = null, String key = null)
        {
            if (collection != null)
            {
                if (!DataStoreHelper.ValidateCollectionOrKeyName(collection))
                {
                    throw new ArgumentException("Collection name contains invalid characters.");
                }
            }

            if (key != null)
            {
                if (!DataStoreHelper.ValidateCollectionOrKeyName(key))
                {
                    throw new ArgumentException("Item key contains invalid characters.");
                }
            }
        }

    }

    #region Extension Methods

    public static class IDataStoreExtensions
    {
        public static DataStoreWebItem ToWebItem(this DataStoreItem item, GlobalDataStoreItem globalItem = null)
        {
            IDataStoreItem dsItem = item.ToDataStoreItem();
            DataStoreWebItem webItem = new DataStoreWebItem();
            webItem.Collection = item.CollectionName;
            webItem.Type = globalItem != null ? DataStoreWebItemType.Overrides : DataStoreWebItemType.Local;
            webItem.DataType = dsItem.Type;
            webItem.Date = dsItem.Date;
            webItem.Key = dsItem.Key;
            webItem.LocalValue = dsItem.Value;

            if (webItem.LocalValue is DataStoreProtoObject protoObject)
            {
                webItem.LocalValue = protoObject.Message;
                webItem.ProtoMessageType = protoObject.MessageType;
            }

            if (globalItem != null)
            {
                var dsGlobalItem = globalItem.ToDataStoreItem();

                webItem.GlobalValue = dsGlobalItem.Value;

                if (webItem.GlobalValue is DataStoreProtoObject protoObjectGlobal)
                {
                    webItem.GlobalValue = protoObjectGlobal.Message;
                    webItem.ProtoMessageType = protoObjectGlobal.MessageType;
                }
            }

            return webItem;
        }

        public static DataStoreWebItem ToWebItem(this GlobalDataStoreItem item)
        {
            IDataStoreItem dsItem = item.ToDataStoreItem();
            DataStoreWebItem webItem = new DataStoreWebItem();
            webItem.Collection = item.CollectionName;
            webItem.Type = DataStoreWebItemType.Global;
            webItem.DataType = dsItem.Type;
            webItem.Date = dsItem.Date;
            webItem.Key = dsItem.Key;
            webItem.GlobalValue = dsItem.Value;

            if (webItem.GlobalValue is DataStoreProtoObject protoObject)
            {
                webItem.GlobalValue = protoObject.Message;
                webItem.ProtoMessageType = protoObject.MessageType;
            }

            return webItem;
        }
    }
    #endregion
}