aboutsummaryrefslogtreecommitdiff
path: root/backend/api
diff options
context:
space:
mode:
authorTAMARA JERINIC <tamara.jerinic@gmail.com>2022-03-09 22:19:17 +0100
committerTAMARA JERINIC <tamara.jerinic@gmail.com>2022-03-09 22:19:17 +0100
commitcbdc861b07e82502664f1c849f141a51104cd3d0 (patch)
tree9959995ea265df2d0be6ff307838a957b813f88e /backend/api
parent8e7f2346cc4507b7e8056b57e01c0e85c11a4a2c (diff)
parenta95a921478435cc6400236d291281c3d0156a7e1 (diff)
Merge branch 'dev' of http://gitlab.pmf.kg.ac.rs/igrannonica/neuronstellar into microservice
Diffstat (limited to 'backend/api')
-rw-r--r--backend/api/api/.gitignore3
-rw-r--r--backend/api/api/Controllers/AuthController.cs36
-rw-r--r--backend/api/api/Controllers/FileUploadController.cs47
-rw-r--r--backend/api/api/Controllers/UserController.cs108
-rw-r--r--backend/api/api/Data/UserStoreDatabaseSettings.cs13
-rw-r--r--backend/api/api/Interfaces/IUserStoreDatabaseSettings.cs9
-rw-r--r--backend/api/api/Models/JwtToken.cs37
-rw-r--r--backend/api/api/Models/PasswordCrypt.cs27
-rw-r--r--backend/api/api/Models/User.cs28
-rw-r--r--backend/api/api/Models/Users/AuthRequest.cs13
-rw-r--r--backend/api/api/Models/Users/RegisterRequest.cs23
-rw-r--r--backend/api/api/Program.cs53
-rw-r--r--backend/api/api/Services/AuthService.cs49
-rw-r--r--backend/api/api/Services/IAuthService.cs10
-rw-r--r--backend/api/api/Services/IUserService.cs14
-rw-r--r--backend/api/api/Services/UserService.cs56
-rw-r--r--backend/api/api/api.csproj16
-rw-r--r--backend/api/api/appsettings.json17
18 files changed, 555 insertions, 4 deletions
diff --git a/backend/api/api/.gitignore b/backend/api/api/.gitignore
index 8afdcb63..242abea5 100644
--- a/backend/api/api/.gitignore
+++ b/backend/api/api/.gitignore
@@ -3,6 +3,9 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+##Ignore contents for UploadedFiles Folder
+UploadedFiles/*
+
# User-specific files
*.rsuser
*.suo
diff --git a/backend/api/api/Controllers/AuthController.cs b/backend/api/api/Controllers/AuthController.cs
new file mode 100644
index 00000000..01354f87
--- /dev/null
+++ b/backend/api/api/Controllers/AuthController.cs
@@ -0,0 +1,36 @@
+using api.Models.Users;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using api.Services;
+using Microsoft.AspNetCore.Authorization;
+
+namespace api.Controllers
+{
+ [Route("api/[controller]")]
+ [ApiController]
+ public class AuthController : ControllerBase
+ {
+ private IAuthService _auth;
+ public AuthController(IAuthService auth)
+ {
+ _auth = auth;
+ }
+
+ [HttpPost("register")]
+ public async Task<ActionResult<string>> Register(RegisterRequest user)
+ {
+
+ return Ok(_auth.Register(user));
+ }
+
+ [HttpPost("login")]
+ public async Task<ActionResult<string>> Login(AuthRequest user)
+ {
+
+ return Ok(_auth.Login(user));
+ }
+
+
+
+ }
+}
diff --git a/backend/api/api/Controllers/FileUploadController.cs b/backend/api/api/Controllers/FileUploadController.cs
new file mode 100644
index 00000000..46e7f4f9
--- /dev/null
+++ b/backend/api/api/Controllers/FileUploadController.cs
@@ -0,0 +1,47 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+namespace api.Controllers
+{
+ [Route("api/[controller]")]
+ [ApiController]
+ public class FileUploadController : ControllerBase
+ {
+ private string[] permittedExtensions = { ".csv" };
+
+
+ [HttpPost("Csv")]
+ [Authorize(Roles = "User")]
+ public async Task<ActionResult<string>> CsvUpload([FromForm]IFormFile file,[FromForm]string username)//???Umesto username poslati jwt odakle se moze preuzeti username radi sigurnosti
+ {
+ var filename=file.FileName;
+ var ext=Path.GetExtension(filename).ToLowerInvariant();
+ var name = Path.GetFileNameWithoutExtension(filename).ToLowerInvariant();
+ if (string.IsNullOrEmpty(ext) || ! permittedExtensions.Contains(ext)) {
+ return BadRequest("Wrong file type");
+ }
+ var folderPath=Path.Combine(Directory.GetCurrentDirectory(),"UploadedFiles",username);
+ if (!Directory.Exists(folderPath))
+ {
+ Directory.CreateDirectory(folderPath);
+ }
+
+ var fullPath = Path.Combine(folderPath, filename);
+ int i=0;
+
+ while (System.IO.File.Exists(fullPath)) {
+ i++;
+ fullPath = Path.Combine(folderPath,name+i.ToString()+ext);
+ }
+
+
+
+ using (var stream=new FileStream(fullPath, FileMode.Create))
+ {
+ await file.CopyToAsync(stream);
+ }
+
+ return Ok();
+ }
+ }
+}
diff --git a/backend/api/api/Controllers/UserController.cs b/backend/api/api/Controllers/UserController.cs
new file mode 100644
index 00000000..85f8218d
--- /dev/null
+++ b/backend/api/api/Controllers/UserController.cs
@@ -0,0 +1,108 @@
+using api.Models;
+using api.Services;
+using Microsoft.AspNetCore.Mvc;
+using System.Diagnostics;
+
+// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
+//dovrsi kontroler
+namespace api.Controllers
+{
+ [Route("api/[controller]")]
+ [ApiController]
+ public class UserController : ControllerBase
+ {
+ private readonly IUserService userService;
+
+ public UserController(IUserService userService)
+ {
+ this.userService = userService;
+ }
+
+ // GET: api/<UserController>
+ [HttpGet]
+ public ActionResult<List<User>> Get()
+ {
+ return userService.Get();
+ }
+
+ // GET api/<UserController>/5
+ //potrebno za profile page
+ [HttpGet("{id}")]
+ public ActionResult<User> Get(string id)
+ {
+ var user = userService.Get(id);
+
+ if (user == null)
+ return NotFound($"User with Id = {id} not found");
+
+ return user;
+ }
+ // GET api/<UserController>/5
+ //potrebno za profile page
+ [HttpGet("{id}")]
+ public ActionResult<User> GetUserUsername(string username)
+ {
+ var user = userService.GetUserUsername(username);
+
+ if (user == null)
+ return NotFound($"User with Id = {username} not found");
+
+ return user;
+ }
+ // POST api/<UserController>
+ [HttpPost]
+ public ActionResult<User> Post([FromBody] User user)
+ {
+
+ var existingUser = userService.GetUserUsername(user.Username);
+
+ if (existingUser != null)
+ return NotFound($"User with username = {user.Username} exisits");
+ else
+ {
+ userService.Create(user);
+
+ //Debug.WriteLine("\nTest.\n");
+
+ return CreatedAtAction(nameof(Get), new { id = user._id }, user);
+ }
+ }
+
+ // PUT api/<UserController>/5
+ [HttpPut("{id}")]
+ public ActionResult Put(string id, [FromBody] User user)
+ {
+ var existingUser = userService.Get(id);
+
+ //ne mora da se proverava
+ if(existingUser == null)
+ return NotFound($"User with Id = {id} not found");
+
+ userService.Update(id, user);
+ return NoContent();
+ }
+
+ // DELETE api/<UserController>/5
+ [HttpDelete("{id}")]
+ public ActionResult Delete(string id)
+ {
+ var user = userService.Get(id);
+
+ if (user == null)
+ return NotFound($"User with Id = {id} not found");
+
+ userService.Delete(user._id);
+ return Ok($"Student with Id = {id} deleted");
+ }
+ }
+}
+/*
+{
+ "_id": "",
+ "username" : "ivan996sk",
+ "email" : "ivan996sk@gmail.com",
+ "password" : "proba",
+ "firstName" : "Ivan",
+ "lastName" : "Ljubisavljevic"
+}
+*/ \ No newline at end of file
diff --git a/backend/api/api/Data/UserStoreDatabaseSettings.cs b/backend/api/api/Data/UserStoreDatabaseSettings.cs
new file mode 100644
index 00000000..0efd2895
--- /dev/null
+++ b/backend/api/api/Data/UserStoreDatabaseSettings.cs
@@ -0,0 +1,13 @@
+using Microsoft.EntityFrameworkCore;
+using MongoDB.Driver;
+using api.Interfaces;
+
+namespace api.Data
+{
+ public class UserStoreDatabaseSettings : IUserStoreDatabaseSettings
+ {
+ public string ConnectionString { get; set; } = String.Empty;
+ public string DatabaseName { get; set; } = String.Empty;
+ public string CollectionName { get; set; } = String.Empty;
+ }
+}
diff --git a/backend/api/api/Interfaces/IUserStoreDatabaseSettings.cs b/backend/api/api/Interfaces/IUserStoreDatabaseSettings.cs
new file mode 100644
index 00000000..43fe9b3a
--- /dev/null
+++ b/backend/api/api/Interfaces/IUserStoreDatabaseSettings.cs
@@ -0,0 +1,9 @@
+namespace api.Interfaces
+{
+ public interface IUserStoreDatabaseSettings
+ {
+ string ConnectionString { get; set; }
+ string DatabaseName { get; set; }
+ string CollectionName { get; set; }
+ }
+}
diff --git a/backend/api/api/Models/JwtToken.cs b/backend/api/api/Models/JwtToken.cs
new file mode 100644
index 00000000..23307185
--- /dev/null
+++ b/backend/api/api/Models/JwtToken.cs
@@ -0,0 +1,37 @@
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+using api.Models.Users;
+using Microsoft.IdentityModel.Tokens;
+
+namespace api.Models
+{
+ public class JwtToken
+ {
+ private readonly IConfiguration _configuration;
+
+ public JwtToken(IConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public string GenToken(AuthRequest user)
+ {
+ var tokenHandler = new JwtSecurityTokenHandler();
+ var key = Encoding.ASCII.GetBytes(_configuration.GetSection("AppSettings:JwtToken").Value);
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Subject = new ClaimsIdentity(new[] { new Claim("name", user.UserName),
+ new Claim("role", "User")}),
+ Expires = DateTime.UtcNow.AddDays(1),
+ SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
+ };
+ var token = tokenHandler.CreateToken(tokenDescriptor);
+ return tokenHandler.WriteToken(token);
+
+ }
+
+
+
+ }
+}
diff --git a/backend/api/api/Models/PasswordCrypt.cs b/backend/api/api/Models/PasswordCrypt.cs
new file mode 100644
index 00000000..016fde51
--- /dev/null
+++ b/backend/api/api/Models/PasswordCrypt.cs
@@ -0,0 +1,27 @@
+namespace api.Models
+{
+ public class PasswordCrypt
+ {
+ private static int difficulty = 10;
+
+ public static String hashPassword(String password)
+ {
+ String salt = BCrypt.Net.BCrypt.GenerateSalt(difficulty);
+ String passwordHash = BCrypt.Net.BCrypt.HashPassword(password, salt);
+
+ return passwordHash;
+ }
+ public static Boolean checkPassword(String plainText,String hash)
+ {
+ Boolean verified = false;
+
+ if (hash == null || !hash.StartsWith("$2a$"))
+ throw new ArgumentException("invalid hash");
+
+ verified=BCrypt.Net.BCrypt.Verify(plainText, hash);
+
+ return verified;
+
+ }
+ }
+}
diff --git a/backend/api/api/Models/User.cs b/backend/api/api/Models/User.cs
new file mode 100644
index 00000000..46db50ab
--- /dev/null
+++ b/backend/api/api/Models/User.cs
@@ -0,0 +1,28 @@
+using System.ComponentModel.DataAnnotations;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+
+namespace api.Models
+{
+ [BsonIgnoreExtraElements]//ignorise visak elemenata iz baze --moze da se obrise jer nemamo viska
+ public class User
+ {
+ [BsonId]
+ [BsonRepresentation(BsonType.ObjectId)]//mongo data type to .net
+ public string _id { get; set; }
+ [BsonElement("username")]
+ public string Username { get; set; }
+ [BsonElement("email")]
+ public string Email { get; set; }
+ [BsonElement("password")]
+ public string Password { get; set; }
+
+
+ [BsonElement("firstName")]
+ public string FirstName { get; set; }
+ [BsonElement("lastName")]
+ public string LastName { get; set; }
+
+ }
+}
diff --git a/backend/api/api/Models/Users/AuthRequest.cs b/backend/api/api/Models/Users/AuthRequest.cs
new file mode 100644
index 00000000..fbf2412d
--- /dev/null
+++ b/backend/api/api/Models/Users/AuthRequest.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace api.Models.Users
+{
+ public class AuthRequest
+ {
+ [Required]
+ public string UserName { get; set; }
+ [Required]
+ public string Password { get; set; }
+
+ }
+}
diff --git a/backend/api/api/Models/Users/RegisterRequest.cs b/backend/api/api/Models/Users/RegisterRequest.cs
new file mode 100644
index 00000000..675d571d
--- /dev/null
+++ b/backend/api/api/Models/Users/RegisterRequest.cs
@@ -0,0 +1,23 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace api.Models.Users
+{
+ public class RegisterRequest
+ {
+ [Required]
+ public string firstName { get; set; }
+
+ [Required]
+ public string lastName { get; set; }
+
+ [Required]
+ public string username { get; set; }
+
+ [Required]
+ public string email { get; set; }
+
+ [Required]
+ public string password { get; set; }
+
+ }
+}
diff --git a/backend/api/api/Program.cs b/backend/api/api/Program.cs
index 478b7052..550f6ce1 100644
--- a/backend/api/api/Program.cs
+++ b/backend/api/api/Program.cs
@@ -1,13 +1,64 @@
+using System.Text;
+using api.Data;
+using api.Interfaces;
+using api.Services;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.Tokens;
+using MongoDB.Driver;
+
var builder = WebApplication.CreateBuilder(args);
-// Add services to the container.
+//Add Cors
+builder.Services.AddCors();
+
+// Add services to the container
+//dodajemo dep inj
+
+builder.Services.Configure<UserStoreDatabaseSettings>(
+ builder.Configuration.GetSection(nameof(UserStoreDatabaseSettings)));
+
+builder.Services.AddSingleton<IUserStoreDatabaseSettings>(sp =>
+ sp.GetRequiredService<IOptions<UserStoreDatabaseSettings>>().Value);
+
+builder.Services.AddSingleton<IMongoClient>(s =>
+ new MongoClient(builder.Configuration.GetValue<string>("UserStoreDatabaseSettings:ConnectionString")));
+
+//Inject Dependencies
+builder.Services.AddScoped<IUserService, UserService>();
+builder.Services.AddScoped<IAuthService, AuthService>();
+
+//Add Authentication
+builder.Services.AddAuthentication(
+ JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration.GetSection("AppSettings:JwtToken").Value)),
+ ValidateIssuer=false,
+ ValidateAudience=false
+ };
+
+ });
+
builder.Services.AddControllers();
var app = builder.Build();
+
+//Add Cors
+app.UseCors(
+ x=>x.AllowAnyOrigin()
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ );
+
// Configure the HTTP request pipeline.
+//Add Authentication
+app.UseAuthentication();
+
app.UseAuthorization();
app.MapControllers();
diff --git a/backend/api/api/Services/AuthService.cs b/backend/api/api/Services/AuthService.cs
new file mode 100644
index 00000000..015fdac7
--- /dev/null
+++ b/backend/api/api/Services/AuthService.cs
@@ -0,0 +1,49 @@
+using api.Interfaces;
+using api.Models;
+using api.Models.Users;
+using MongoDB.Driver;
+
+namespace api.Services
+{
+ public class AuthService : IAuthService
+ {
+ private JwtToken _jwt;
+ private readonly IConfiguration _configuration;
+ private readonly IMongoCollection<User> _users;
+ public AuthService(IConfiguration configuration, IUserStoreDatabaseSettings settings, IMongoClient mongoClient)
+ {
+ _configuration = configuration;
+ _jwt = new JwtToken(_configuration);
+ var database = mongoClient.GetDatabase(settings.DatabaseName);
+ _users = database.GetCollection<User>(settings.CollectionName);
+ }
+ public string Login(AuthRequest user)
+ {
+ User u = _users.Find(x => x.Username == user.UserName).FirstOrDefault();
+ if (u == null)
+ return "Username doesn't exist";
+ if (!PasswordCrypt.checkPassword(user.Password, u.Password))
+ return "Wrong password";
+ return _jwt.GenToken(user);
+
+ }
+ public string Register(RegisterRequest user)
+ {
+ User u = new User();
+ u.Username = user.username;
+ u.Email = user.email;
+ u.Password = PasswordCrypt.hashPassword(user.password);
+ u.FirstName = user.firstName;
+ u.LastName = user.lastName;
+ if (_users.Find(user => user.Username == u.Username).FirstOrDefault() != null)
+ return "Username Already Exists";
+ if (_users.Find(user => user.Email == u.Email).FirstOrDefault() != null)
+ return "Email Already Exists";
+
+ _users.InsertOne(u);
+ return "User added";
+ }
+
+
+ }
+}
diff --git a/backend/api/api/Services/IAuthService.cs b/backend/api/api/Services/IAuthService.cs
new file mode 100644
index 00000000..79085f8c
--- /dev/null
+++ b/backend/api/api/Services/IAuthService.cs
@@ -0,0 +1,10 @@
+using api.Models.Users;
+
+namespace api.Services
+{
+ public interface IAuthService
+ {
+ string Login(AuthRequest user);
+ string Register(RegisterRequest user);
+ }
+} \ No newline at end of file
diff --git a/backend/api/api/Services/IUserService.cs b/backend/api/api/Services/IUserService.cs
new file mode 100644
index 00000000..b6725694
--- /dev/null
+++ b/backend/api/api/Services/IUserService.cs
@@ -0,0 +1,14 @@
+using api.Models;
+
+namespace api.Services
+{
+ public interface IUserService
+ {
+ List<User> Get();// daje sve korisnike
+ User Get(string id); //daje korisnika po id-u
+ User GetUserUsername(string username); //daje korisnika po korisnickom imenu
+ User Create(User user); // kreira korisnika
+ void Update(string id, User user); //apdejruje korisnika po idu
+ void Delete(string id);//brise korisnika
+ }
+}
diff --git a/backend/api/api/Services/UserService.cs b/backend/api/api/Services/UserService.cs
new file mode 100644
index 00000000..e1d1e8b7
--- /dev/null
+++ b/backend/api/api/Services/UserService.cs
@@ -0,0 +1,56 @@
+using api.Interfaces;
+using api.Models;
+using MongoDB.Driver;
+
+namespace api.Services
+{
+ public class UserService : IUserService
+ {
+ private readonly IMongoCollection<User> _users;
+
+ public UserService(IUserStoreDatabaseSettings settings, IMongoClient mongoClient)
+ {
+ var database = mongoClient.GetDatabase(settings.DatabaseName);
+ _users = database.GetCollection<User>(settings.CollectionName);
+ }
+ public User Create(User user)
+ {
+ _users.InsertOne(user);
+ return user;
+ }
+
+ public List<User> Get()
+ {
+ return _users.Find(user => true).ToList();
+ }
+ public User GetUserUsername(string username)
+ {
+ return _users.Find(user => user.Username == username).FirstOrDefault();
+ }
+
+ public User Get(string id)
+ {
+ return _users.Find(user => user._id == id).FirstOrDefault();
+ }
+
+ public void Delete(string id)
+ {
+ _users.DeleteOne(user => user._id == id);
+
+ }
+ public void Update(string id, User user)
+ {
+ _users.ReplaceOne(user => user._id == id, user);
+ }
+ }
+}
+/*
+ {
+ "_id": "",
+ "username" : "ivan996sk",
+ "email" : "ivan996sk@gmail.com",
+ "password" : "proba",
+ "firstName" : "Ivan",
+ "lastName" : "Ljubisavljevic"
+}
+ */ \ No newline at end of file
diff --git a/backend/api/api/api.csproj b/backend/api/api/api.csproj
index f278c90a..46842c3e 100644
--- a/backend/api/api/api.csproj
+++ b/backend/api/api/api.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
@@ -7,7 +7,19 @@
</PropertyGroup>
<ItemGroup>
- <Folder Include="Controllers\" />
+ <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
+ <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.3" />
+ <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.16.0" />
+ <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.16.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.2" />
+ <PackageReference Include="MongoDB.Driver" Version="2.14.1" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Folder Include="UploadedFiles\" />
</ItemGroup>
</Project>
diff --git a/backend/api/api/appsettings.json b/backend/api/api/appsettings.json
index 10f68b8c..204eba33 100644
--- a/backend/api/api/appsettings.json
+++ b/backend/api/api/appsettings.json
@@ -1,9 +1,24 @@
{
+ "AppSettings": {
+ "JwtToken": "2mnttqPtRb4GIWHFtagm"
+ },
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "UserStoreDatabaseSettings": {
+ /* LocalHost
+ "ConnectionString": "mongodb://127.0.0.1:27017/",
+ "DatabaseName": "si_project",
+ "CollectionName": "User"
+
+ */
+ "ConnectionString": "mongodb+srv://si_user:si_user@sidatabase.twtfm.mongodb.net/myFirstDatabase?retryWrites=true&w=majority",
+ "DatabaseName": "si_db",
+ "CollectionName": "users"
+
+ }
}