aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Web/Tango.MachineService
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2020-11-17 15:11:40 +0200
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2020-11-17 15:11:40 +0200
commit69dbba322f6ef275134b7b214510e47b9fcdd0b0 (patch)
treefd7a96fcf96ce86b7fa80f38161cb23ea140973c /Software/Visual_Studio/Web/Tango.MachineService
parent6f0f2a7908884deab8aca33ec967d03c5e564060 (diff)
downloadTango-69dbba322f6ef275134b7b214510e47b9fcdd0b0.tar.gz
Tango-69dbba322f6ef275134b7b214510e47b9fcdd0b0.zip
IMplemented NSwag for DataStore WebAPI controller.
Implemented data store tool dsUtil.
Diffstat (limited to 'Software/Visual_Studio/Web/Tango.MachineService')
-rw-r--r--Software/Visual_Studio/Web/Tango.MachineService/Controllers/DataStoreController.cs114
-rw-r--r--Software/Visual_Studio/Web/Tango.MachineService/Filters/JwtWebApiTokenFilter.cs72
-rw-r--r--Software/Visual_Studio/Web/Tango.MachineService/Nswag/DataStoreClient.nswag140
-rw-r--r--Software/Visual_Studio/Web/Tango.MachineService/Tango.MachineService.csproj2
4 files changed, 321 insertions, 7 deletions
diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/DataStoreController.cs b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/DataStoreController.cs
index f0dc0f2ba..0d35bd776 100644
--- a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/DataStoreController.cs
+++ b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/DataStoreController.cs
@@ -3,17 +3,36 @@ 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 : ApiController
+ 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()
@@ -21,10 +40,57 @@ namespace Tango.MachineService.Controllers
_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.");
+ }
+
+ 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.FSE_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())
@@ -35,12 +101,17 @@ namespace Tango.MachineService.Controllers
if (machineGuid == null)
{
- return ThrowException<List<DataStoreWebItem>>(new KeyNotFoundException(), HttpStatusCode.NotFound, "The specified machine serial number could not be found.");
+ 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)
@@ -64,16 +135,31 @@ namespace Tango.MachineService.Controllers
}
}
}
+ catch (HttpResponseException ex)
+ {
+ throw ex;
+ }
catch (Exception ex)
{
- return ThrowException<List<DataStoreWebItem>>(ex, HttpStatusCode.InternalServerError, ex.FlattenMessage());
+ throw CreateHttpException(ex, HttpStatusCode.InternalServerError);
}
}
+ [JwtWebApiTokenFilter]
public void Put([FromBody]DataStoreWebPutItem item)
{
try
{
+ if (!RequestToken.Object.Permissions.Contains(Permissions.FSE_DataStoreWrite))
+ {
+ throw CreateHttpException(new AuthenticationException("The current user was not authorized to write to the data store."), HttpStatusCode.BadRequest);
+ }
+
+ 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())
@@ -84,13 +170,18 @@ namespace Tango.MachineService.Controllers
if (machineGuid == null)
{
- ThrowException<List<DataStoreWebItem>>(new KeyNotFoundException(), HttpStatusCode.NotFound, "The specified machine serial number could not be found.");
+ 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.FSE_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;
@@ -110,6 +201,11 @@ namespace Tango.MachineService.Controllers
if (dbItem == null)
{
+ if (!RequestToken.Object.Permissions.Contains(Permissions.FSE_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;
@@ -124,15 +220,19 @@ namespace Tango.MachineService.Controllers
db.SaveChanges();
}
}
+ catch (HttpResponseException ex)
+ {
+ throw ex;
+ }
catch (Exception ex)
{
- ThrowException<List<DataStoreWebItem>>(ex, HttpStatusCode.InternalServerError, ex.FlattenMessage());
+ throw CreateHttpException(ex, HttpStatusCode.InternalServerError);
}
}
- private T ThrowException<T>(Exception ex, HttpStatusCode code, String message = null)
+ private HttpResponseException CreateHttpException(Exception ex, HttpStatusCode code, String message = null)
{
- throw new HttpResponseException(new HttpResponseMessage(code)
+ return new HttpResponseException(new HttpResponseMessage(code)
{
Content = new StringContent(message != null ? message : ex.Message),
ReasonPhrase = ex.FlattenMessage()
diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Filters/JwtWebApiTokenFilter.cs b/Software/Visual_Studio/Web/Tango.MachineService/Filters/JwtWebApiTokenFilter.cs
new file mode 100644
index 000000000..89169fc48
--- /dev/null
+++ b/Software/Visual_Studio/Web/Tango.MachineService/Filters/JwtWebApiTokenFilter.cs
@@ -0,0 +1,72 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Security.Authentication;
+using System.Web;
+using System.Web.Http;
+using System.Web.Http.Controllers;
+using System.Web.Http.Filters;
+using Tango.Transport.Web;
+using Tango.Web.Security;
+
+namespace Tango.MachineService.Filters
+{
+ public class JwtWebApiTokenFilter : ActionFilterAttribute
+ {
+ public bool AllowExpired { get; private set; }
+
+ public JwtWebApiTokenFilter()
+ {
+
+ }
+
+ public JwtWebApiTokenFilter(bool allowExpired)
+ {
+ AllowExpired = allowExpired;
+ }
+
+ public override void OnActionExecuting(HttpActionContext actionContext)
+ {
+ try
+ {
+ var authorizationHeader = actionContext.Request.Headers.Authorization;
+
+ if (authorizationHeader != null && authorizationHeader.Parameter != null)
+ {
+ try
+ {
+ WebToken.Validate(MachineServiceConfig.JWT_TOKEN_SECRET, authorizationHeader.Parameter);
+ }
+ catch (JWT.TokenExpiredException)
+ {
+ if (!AllowExpired)
+ {
+ throw new TokenExpiredException("Token expired.");
+ }
+ }
+ catch (JWT.SignatureVerificationException)
+ {
+ throw new InvalidTokenException("Invalid token.");
+ }
+ }
+ else
+ {
+ throw new AuthenticationException("No token specified.");
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)
+ {
+ Content = new StringContent(ex.Message),
+ ReasonPhrase = ex.FlattenMessage()
+ });
+ }
+
+ base.OnActionExecuting(actionContext);
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Nswag/DataStoreClient.nswag b/Software/Visual_Studio/Web/Tango.MachineService/Nswag/DataStoreClient.nswag
new file mode 100644
index 000000000..c7aeb10a3
--- /dev/null
+++ b/Software/Visual_Studio/Web/Tango.MachineService/Nswag/DataStoreClient.nswag
@@ -0,0 +1,140 @@
+{
+ "runtime": "Default",
+ "defaultVariables": "",
+ "documentGenerator": {
+ "webApiToOpenApi": {
+ "controllerNames": [
+ "Tango.MachineService.Controllers.DataStoreController"
+ ],
+ "isAspNetCore": false,
+ "resolveJsonOptions": false,
+ "defaultUrlTemplate": "api/{controller}/{action}",
+ "addMissingPathParameters": false,
+ "includedVersions": null,
+ "defaultPropertyNameHandling": "Default",
+ "defaultReferenceTypeNullHandling": "Null",
+ "defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
+ "defaultResponseReferenceTypeNullHandling": "NotNull",
+ "defaultEnumHandling": "Integer",
+ "flattenInheritanceHierarchy": false,
+ "generateKnownTypes": true,
+ "generateEnumMappingDescription": false,
+ "generateXmlObjects": false,
+ "generateAbstractProperties": false,
+ "generateAbstractSchemas": true,
+ "ignoreObsoleteProperties": false,
+ "allowReferencesWithProperties": false,
+ "excludedTypeNames": [],
+ "serviceHost": null,
+ "serviceBasePath": null,
+ "serviceSchemes": [],
+ "infoTitle": "My Title",
+ "infoDescription": null,
+ "infoVersion": "1.0.0",
+ "documentTemplate": null,
+ "documentProcessorTypes": [],
+ "operationProcessorTypes": [],
+ "typeNameGeneratorType": null,
+ "schemaNameGeneratorType": null,
+ "contractResolverType": null,
+ "serializerSettingsType": null,
+ "useDocumentProvider": true,
+ "documentName": "v1",
+ "aspNetCoreEnvironment": null,
+ "createWebHostBuilderMethod": null,
+ "startupType": null,
+ "allowNullableBodyParameters": true,
+ "output": null,
+ "outputType": "Swagger2",
+ "assemblyPaths": [
+ "$(assembly)"
+ ],
+ "assemblyConfig": null,
+ "referencePaths": [],
+ "useNuGetCache": false
+ }
+ },
+ "codeGenerators": {
+ "openApiToCSharpClient": {
+ "clientBaseClass": null,
+ "configurationClass": null,
+ "generateClientClasses": true,
+ "generateClientInterfaces": false,
+ "injectHttpClient": true,
+ "disposeHttpClient": true,
+ "protectedMethods": [],
+ "generateExceptionClasses": true,
+ "exceptionClass": "ApiException",
+ "wrapDtoExceptions": true,
+ "useHttpClientCreationMethod": false,
+ "httpClientType": "System.Net.Http.HttpClient",
+ "useHttpRequestMessageCreationMethod": false,
+ "useBaseUrl": true,
+ "generateBaseUrlProperty": true,
+ "generateSyncMethods": true,
+ "exposeJsonSerializerSettings": false,
+ "clientClassAccessModifier": "public",
+ "typeAccessModifier": "public",
+ "generateContractsOutput": false,
+ "contractsNamespace": null,
+ "contractsOutputFilePath": null,
+ "parameterDateTimeFormat": "s",
+ "parameterDateFormat": "yyyy-MM-dd",
+ "generateUpdateJsonSerializerSettingsMethod": true,
+ "useRequestAndResponseSerializationSettings": false,
+ "serializeTypeInformation": false,
+ "queryNullValue": "",
+ "className": "DataStoreClient",
+ "operationGenerationMode": "MultipleClientsFromOperationId",
+ "additionalNamespaceUsages": [],
+ "additionalContractNamespaceUsages": [],
+ "generateOptionalParameters": false,
+ "generateJsonMethods": false,
+ "enforceFlagEnums": false,
+ "parameterArrayType": "System.Collections.Generic.IEnumerable",
+ "parameterDictionaryType": "System.Collections.Generic.IDictionary",
+ "responseArrayType": "System.Collections.Generic.ICollection",
+ "responseDictionaryType": "System.Collections.Generic.IDictionary",
+ "wrapResponses": false,
+ "wrapResponseMethods": [],
+ "generateResponseClasses": true,
+ "responseClass": "SwaggerResponse",
+ "namespace": "Tango.DataStore.Web",
+ "requiredPropertiesMustBeDefined": true,
+ "dateType": "System.DateTimeOffset",
+ "jsonConverters": null,
+ "anyType": "object",
+ "dateTimeType": "System.DateTimeOffset",
+ "timeType": "System.TimeSpan",
+ "timeSpanType": "System.TimeSpan",
+ "arrayType": "System.Collections.Generic.ICollection",
+ "arrayInstanceType": "System.Collections.ObjectModel.Collection",
+ "dictionaryType": "System.Collections.Generic.IDictionary",
+ "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
+ "arrayBaseType": "System.Collections.ObjectModel.Collection",
+ "dictionaryBaseType": "System.Collections.Generic.Dictionary",
+ "classStyle": "Poco",
+ "generateDefaultValues": true,
+ "generateDataAnnotations": true,
+ "excludedTypeNames": [],
+ "excludedParameterNames": [],
+ "handleReferences": false,
+ "generateImmutableArrayProperties": false,
+ "generateImmutableDictionaryProperties": false,
+ "jsonSerializerSettingsTransformationMethod": null,
+ "inlineNamedArrays": false,
+ "inlineNamedDictionaries": false,
+ "inlineNamedTuples": true,
+ "inlineNamedAny": false,
+ "generateDtoTypes": true,
+ "generateOptionalPropertiesAsNullable": false,
+ "templateDirectory": null,
+ "typeNameGeneratorType": null,
+ "propertyNameGeneratorType": null,
+ "enumNameGeneratorType": null,
+ "serviceHost": null,
+ "serviceSchemes": null,
+ "output": "$(output)"
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Tango.MachineService.csproj b/Software/Visual_Studio/Web/Tango.MachineService/Tango.MachineService.csproj
index df4e346f6..bccd84cad 100644
--- a/Software/Visual_Studio/Web/Tango.MachineService/Tango.MachineService.csproj
+++ b/Software/Visual_Studio/Web/Tango.MachineService/Tango.MachineService.csproj
@@ -322,6 +322,7 @@
<Compile Include="Controllers\DownloadsController.cs" />
<Compile Include="Controllers\AccountController.cs" />
<Compile Include="Controllers\FSEController.cs" />
+ <Compile Include="Filters\JwtWebApiTokenFilter.cs" />
<Compile Include="Filters\JwtTokenFilter.cs" />
<Compile Include="Hubs\ExternalBridgeHub.cs" />
<Compile Include="MachineServiceConfig.cs" />
@@ -342,6 +343,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Views\FSEAccount\ResetPasswordVM.cs" />
<Compile Include="Views\FSEDownloads\IndexViewModel.cs" />
+ <Content Include="Nswag\DataStoreClient.nswag" />
<None Include="Security\RefreshTokenEncoder.cs" />
<None Include="Security\RefreshTokenEntity.cs" />
<None Include="Security\RefreshTokensManager.cs" />