From 7b7efd5be88ecde74e9b09f34e75db4701855fd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Konvi=C4=8Dka?= <konvicka.g630@gmail.com>
Date: Sat, 6 Jan 2024 11:49:44 +0100
Subject: [PATCH] feat(#23): Add data validation

---
 ...compose.development.generated.override.yml | 21 ++++++++++
 CodeToXMI.sln.DotSettings.user                |  2 +-
 WebAPI/AuthenticateUserModel.cs               |  7 ----
 WebAPI/Controllers/ApiController.cs           | 41 ++++++++++++++-----
 WebAPI/DTO/Attribute/Base64Attribute.cs       | 25 +++++++++++
 WebAPI/DTO/AuthenticateUserModel.cs           | 14 +++++++
 WebAPI/DTO/External/SourceCodeExt.cs          | 11 +++++
 WebAPI/DTO/RegisterUserModel.cs               | 14 +++++++
 WebAPI/DTO/RemoveCodeModel.cs                 | 12 ++++++
 WebAPI/DTO/TokenDTO.cs                        | 12 ++++++
 WebAPI/DTO/UploadCodeModel.cs                 | 13 ++++++
 WebAPI/ListCodeModel.cs                       |  7 ----
 WebAPI/RegisterUserModel.cs                   |  8 ----
 WebAPI/RemoveCodeModel.cs                     |  7 ----
 WebAPI/SourceCodeExt.cs                       |  7 ----
 WebAPI/UploadCodeModel.cs                     |  7 ----
 docker-compose.development.yaml               | 12 ++++++
 17 files changed, 166 insertions(+), 54 deletions(-)
 create mode 100644 .idea/.idea.CodeToXMI/Docker/docker-compose.development.generated.override.yml
 delete mode 100644 WebAPI/AuthenticateUserModel.cs
 create mode 100644 WebAPI/DTO/Attribute/Base64Attribute.cs
 create mode 100644 WebAPI/DTO/AuthenticateUserModel.cs
 create mode 100644 WebAPI/DTO/External/SourceCodeExt.cs
 create mode 100644 WebAPI/DTO/RegisterUserModel.cs
 create mode 100644 WebAPI/DTO/RemoveCodeModel.cs
 create mode 100644 WebAPI/DTO/TokenDTO.cs
 create mode 100644 WebAPI/DTO/UploadCodeModel.cs
 delete mode 100644 WebAPI/ListCodeModel.cs
 delete mode 100644 WebAPI/RegisterUserModel.cs
 delete mode 100644 WebAPI/RemoveCodeModel.cs
 delete mode 100644 WebAPI/SourceCodeExt.cs
 delete mode 100644 WebAPI/UploadCodeModel.cs
 create mode 100644 docker-compose.development.yaml

diff --git a/.idea/.idea.CodeToXMI/Docker/docker-compose.development.generated.override.yml b/.idea/.idea.CodeToXMI/Docker/docker-compose.development.generated.override.yml
new file mode 100644
index 0000000..a1b94c4
--- /dev/null
+++ b/.idea/.idea.CodeToXMI/Docker/docker-compose.development.generated.override.yml
@@ -0,0 +1,21 @@
+# This is a generated file. Not intended for manual editing.
+version: "3"
+services:
+  codeToXMI_dev:
+    build:
+      context: "/Users/jakubkonvicka/RiderProjects/diplomathesis/WebAPI"
+      dockerfile: "Dockerfile"
+      target: "base"
+    command: []
+    entrypoint:
+    - "dotnet"
+    - "/app/bin/Debug/net7.0/WebAPI.dll"
+    environment:
+      DOTNET_USE_POLLING_FILE_WATCHER: "true"
+    image: "webapi:dev"
+    ports: []
+    volumes:
+    - "/Users/jakubkonvicka/.nuget/packages:/root/.nuget/packages"
+    - "/Users/jakubkonvicka/RiderProjects/diplomathesis/WebAPI:/app:rw"
+    - "/Users/jakubkonvicka/RiderProjects/diplomathesis:/src:rw"
+    working_dir: "/app"
diff --git a/CodeToXMI.sln.DotSettings.user b/CodeToXMI.sln.DotSettings.user
index 35ec7dd..b4ca132 100644
--- a/CodeToXMI.sln.DotSettings.user
+++ b/CodeToXMI.sln.DotSettings.user
@@ -1,7 +1,7 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
 	<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">/usr/local/share/dotnet/sdk/7.0.404/MSBuild.dll</s:String>
 	<s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsbuildVersion/@EntryValue">1114112</s:Int64>
-	<s:String x:Key="/Default/Environment/Highlighting/HighlightingSourceSnapshotLocation/@EntryValue">/Users/jakubkonvicka/Library/Caches/JetBrains/Rider2023.2/resharper-host/temp/Rider/vAny/CoverageData/_CodeToXMI.401400098/Snapshot/snapshot.utdcvr</s:String>
+	
 	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=7191ef50_002Dfb5e_002D4366_002D876f_002D7faeb1ecbf31/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="BuildEmptyDiagram" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;
   &lt;TestAncestor&gt;
     &lt;TestId&gt;NUnit3x::0F37435B-F6C5-40D0-8250-192C843D1465::net7.0::UnitTest.ClassDiagramTests&lt;/TestId&gt;
diff --git a/WebAPI/AuthenticateUserModel.cs b/WebAPI/AuthenticateUserModel.cs
deleted file mode 100644
index bd13ce3..0000000
--- a/WebAPI/AuthenticateUserModel.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace WebAPI;
-
-public class AuthenticateUserModel
-{
-    public string Username { get; set; }
-    public string Password { get; set; }
-}
\ No newline at end of file
diff --git a/WebAPI/Controllers/ApiController.cs b/WebAPI/Controllers/ApiController.cs
index 70406cc..7b7b67e 100644
--- a/WebAPI/Controllers/ApiController.cs
+++ b/WebAPI/Controllers/ApiController.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.ComponentModel.DataAnnotations;
 using System.Text;
 using Antlr4.Runtime;
 using Antlr4.Runtime.Tree;
@@ -8,10 +6,10 @@ using CodeParser.Cpp;
 using Database;
 using Database.Model;
 using DiagramBuilder.AbstractCreator;
-using DiagramBuilder.Activity.Interface;
 using DiagramBuilder.ConcreteCreator;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
+using WebAPI.DTO;
+using WebAPI.DTO.External;
 using WebAPI.Utils;
 
 namespace WebAPI.Controllers
@@ -36,7 +34,17 @@ namespace WebAPI.Controllers
         //convert-to-activity-diagram
         [HttpGet("convert-to-activity-diagram", Name = "ConvertToActivityDiagram")]
         [Produces("application/xml")]
-        public IActionResult ConvertToActivityDiagram(string token, int sourceCodeId, string functionIdentifier)
+        public IActionResult ConvertToActivityDiagram(
+            [Required(ErrorMessage = "Token is required")]
+            [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
+            string token,
+            
+            [Required(ErrorMessage = "SourceCodeId is required")]
+            [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
+            int sourceCodeId,
+            
+            [Required(ErrorMessage = "FunctionIdentifier is required")]
+            string functionIdentifier)
         {
             if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
             {
@@ -83,7 +91,13 @@ namespace WebAPI.Controllers
         //convert-to-class-diagram 
         [HttpGet("convert-to-class-diagram", Name = "ConvertToClassDiagram")]
         [Produces("application/xml")]
-        public IActionResult ConvertToClassDiagram(string token, int sourceCodeId)
+        public IActionResult ConvertToClassDiagram([Required(ErrorMessage = "Token is required")]
+            [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
+            string token,
+            
+            [Required(ErrorMessage = "SourceCodeId is required")]
+            [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
+            int sourceCodeId)
         {
             if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
             {
@@ -208,7 +222,13 @@ namespace WebAPI.Controllers
         }
 
         [HttpGet("code", Name = "ListCode")]
-        public IActionResult ListCode(string token, int id)
+        public IActionResult ListCode([Required(ErrorMessage = "Token is required")]
+            [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
+            string token,
+            
+            [Required(ErrorMessage = "Id is required")]
+            [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
+            int id)
         {
             if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
             {
@@ -243,7 +263,8 @@ namespace WebAPI.Controllers
         }
         
         [HttpGet("code-all", Name = "ListAllCode")]
-        public IActionResult ListAllCode(string token)
+        public IActionResult ListAllCode([RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
+            string token)
         {
             if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
             {
@@ -317,7 +338,7 @@ namespace WebAPI.Controllers
 
                 db.Users.Add(new User
                 {
-                    Name = model.UserName,
+                    Name = model.Username,
                     PasswordHash = passwordHash,
                     Token = "NOT LOGGED IN YET",
                     LastAccess = DateTime.MinValue
diff --git a/WebAPI/DTO/Attribute/Base64Attribute.cs b/WebAPI/DTO/Attribute/Base64Attribute.cs
new file mode 100644
index 0000000..78cbeee
--- /dev/null
+++ b/WebAPI/DTO/Attribute/Base64Attribute.cs
@@ -0,0 +1,25 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace WebAPI.DTO.Attribute;
+
+public class Base64Attribute : ValidationAttribute
+{
+    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
+    {
+        var errorMessage = $"{validationContext.DisplayName} must be a valid base64 string.";
+        if (value == null)
+        {
+            return ValidationResult.Success;
+        }
+        string stringValue = value.ToString();
+        try
+        {
+            var fromBase64String = Convert.FromBase64String(stringValue);
+            return ValidationResult.Success;
+        }
+        catch (FormatException)
+        {
+            return new ValidationResult(errorMessage);
+        }
+    }
+}
\ No newline at end of file
diff --git a/WebAPI/DTO/AuthenticateUserModel.cs b/WebAPI/DTO/AuthenticateUserModel.cs
new file mode 100644
index 0000000..7ea5c08
--- /dev/null
+++ b/WebAPI/DTO/AuthenticateUserModel.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace WebAPI.DTO;
+
+public class AuthenticateUserModel
+{
+    [DisplayName("Username")]
+    [Required(ErrorMessage = "Username is required")]
+    public string Username { get; set; }
+    [DisplayName("Password")]
+    [Required(ErrorMessage = "Password is required")]
+    public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/WebAPI/DTO/External/SourceCodeExt.cs b/WebAPI/DTO/External/SourceCodeExt.cs
new file mode 100644
index 0000000..e81f5cc
--- /dev/null
+++ b/WebAPI/DTO/External/SourceCodeExt.cs
@@ -0,0 +1,11 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using WebAPI.DTO.Attribute;
+
+namespace WebAPI.DTO.External;
+
+public class SourceCodeExt
+{
+    public int Id { get; set; }
+    public string Code {get; set;}
+}
\ No newline at end of file
diff --git a/WebAPI/DTO/RegisterUserModel.cs b/WebAPI/DTO/RegisterUserModel.cs
new file mode 100644
index 0000000..24e2944
--- /dev/null
+++ b/WebAPI/DTO/RegisterUserModel.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace WebAPI.DTO;
+
+public class RegisterUserModel : TokenDTO
+{
+    [DisplayName("Username")]
+    [Required(ErrorMessage = "Username is required")]
+    public string Username { get; set; }
+    [DisplayName("Password")]
+    [Required(ErrorMessage = "Password is required")]
+    public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/WebAPI/DTO/RemoveCodeModel.cs b/WebAPI/DTO/RemoveCodeModel.cs
new file mode 100644
index 0000000..41c88a2
--- /dev/null
+++ b/WebAPI/DTO/RemoveCodeModel.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace WebAPI.DTO;
+
+public class RemoveCodeModel : TokenDTO
+{
+    [DisplayName("Id")]
+    [Required(ErrorMessage = "Id is required")]
+    [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
+    public int Id { get; set; }
+}
\ No newline at end of file
diff --git a/WebAPI/DTO/TokenDTO.cs b/WebAPI/DTO/TokenDTO.cs
new file mode 100644
index 0000000..b210b2e
--- /dev/null
+++ b/WebAPI/DTO/TokenDTO.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace WebAPI.DTO;
+
+public abstract class TokenDTO
+{
+    [DisplayName("Token")]
+    [Required(ErrorMessage = "Token is required")]
+    [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
+    public string Token { get; set; }
+}
\ No newline at end of file
diff --git a/WebAPI/DTO/UploadCodeModel.cs b/WebAPI/DTO/UploadCodeModel.cs
new file mode 100644
index 0000000..a7982cc
--- /dev/null
+++ b/WebAPI/DTO/UploadCodeModel.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using WebAPI.DTO.Attribute;
+
+namespace WebAPI.DTO;
+
+public class UploadCodeModel : TokenDTO
+{
+    [DisplayName("Code")]
+    [Required(ErrorMessage = "Code is required")]
+    [Base64(ErrorMessage = "Code must be a valid base64 string.")]
+    public string Code { get; set; }
+}
\ No newline at end of file
diff --git a/WebAPI/ListCodeModel.cs b/WebAPI/ListCodeModel.cs
deleted file mode 100644
index 4c4a309..0000000
--- a/WebAPI/ListCodeModel.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace WebAPI;
-
-public class ListCodeModel
-{
-    public string Token { get; set; }
-    public int[] Ids { get; set; }
-}
\ No newline at end of file
diff --git a/WebAPI/RegisterUserModel.cs b/WebAPI/RegisterUserModel.cs
deleted file mode 100644
index 5f1a221..0000000
--- a/WebAPI/RegisterUserModel.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace WebAPI;
-
-public class RegisterUserModel
-{
-    public string Token { get; set; }
-    public string UserName { get; set; }
-    public string Password { get; set; }
-}
\ No newline at end of file
diff --git a/WebAPI/RemoveCodeModel.cs b/WebAPI/RemoveCodeModel.cs
deleted file mode 100644
index f899f18..0000000
--- a/WebAPI/RemoveCodeModel.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace WebAPI;
-
-public class RemoveCodeModel
-{
-    public string Token { get; set; }
-    public int Id { get; set; }
-}
\ No newline at end of file
diff --git a/WebAPI/SourceCodeExt.cs b/WebAPI/SourceCodeExt.cs
deleted file mode 100644
index e616a23..0000000
--- a/WebAPI/SourceCodeExt.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace WebAPI;
-
-public class SourceCodeExt
-{
-    public int Id { get; set; }
-    public string Code {get; set;}
-}
\ No newline at end of file
diff --git a/WebAPI/UploadCodeModel.cs b/WebAPI/UploadCodeModel.cs
deleted file mode 100644
index 5b73670..0000000
--- a/WebAPI/UploadCodeModel.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace WebAPI;
-
-public class UploadCodeModel
-{
-    public string Token { get; set; }
-    public string Code { get; set; }
-}
\ No newline at end of file
diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml
new file mode 100644
index 0000000..53e3428
--- /dev/null
+++ b/docker-compose.development.yaml
@@ -0,0 +1,12 @@
+version: '3'
+services:
+  codeToXMI_dev:
+    container_name: CodeToXMI_API_dev
+    image: webapi:dev
+    build:
+      context: ./WebAPI/
+      dockerfile: Dockerfile
+    ports:
+      - "7080:80"
+    environment:
+      - ASPNETCORE_ENVIRONMENT=Development
\ No newline at end of file
-- 
GitLab