diff --git a/README.md b/README.md index df28940..a4a586e 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,35 @@ to start the container run: ``` docker run --name= -p:5000:80 -v /home/michele/sledgemapper/:/app/db -d privateregistry.michelescandura.com/michele/sledgemapper: +``` + +Curl + +Register user +``` + curl -i -X POST -H "Content-Type: application/json" http://localhost:5000/users/register -d '{\"FirstName\":\"Michele\",\"LastName\":\"Scandura\", \"Username\":\"michele.scandura@outlook.com\", \"Password\":\"password1\", \"Initials\":\"MS\"}' +``` + +Login +``` + curl -i -X POST -H "Content-Type: application/json" http://localhost:5000/users/authenticate -d '{\"Username\":\"michele.scandura@outlook.com\", \"Password\":\"password1\"}' +``` + +New Campaign +``` +curl -i -X POST -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE2MTM3MjgzMjksImV4cCI6MTYxNDMzMzEyOCwiaWF0IjoxNjEzNzI4MzI5fQ.VEsDbYUTnnvpgWnB5buE88JyGx_cg3TJb5ybC3eLvmc" http://localhost:5000/campaign/highfell -d '""' +``` + +Get Campaigns +``` +curl -i -X GET -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE2MTM3MjgzMjksImV4cCI6MTYxNDMzMzEyOCwiaWF0IjoxNjEzNzI4MzI5fQ.VEsDbYUTnnvpgWnB5buE88JyGx_cg3TJb5ybC3eLvmc" http://localhost:5000/campaign -d '""' +``` + + +``` +curl -i -X POST -H "Content-Type: application/json" http://localhost:5000/AuthManagement/register -d '{\"FirstName\":\"Michele\",\"LastName\":\"Scandura\", \"UserName\":\"michele\", \"Password\":\"Password1!\", \"Initials\":\"MS\", \"Email\":\"michele.scandura@outlook.com\"}' +``` + +``` +curl -i -X POST -H "Content-Type: application/json" http://localhost:5000/AuthManagement/login -d '{ \"Password\":\"Password1!\", \"Email\":\"michele.scandura@outlook.com\"}' ``` \ No newline at end of file diff --git a/Sledgemapper.Api/Commands/NewSessionCommand.cs b/Sledgemapper.Api/Commands/NewSessionCommand.cs index 4b184cb..e85c6ab 100644 --- a/Sledgemapper.Api/Commands/NewSessionCommand.cs +++ b/Sledgemapper.Api/Commands/NewSessionCommand.cs @@ -1,9 +1,36 @@ +using System; +using System.Collections.Generic; +using MediatR; + namespace Sledgemapper.Api.Commands { public class NewSessionCommand : BaseCommand { - public NewSessionCommand(string sessionName, int userId):base(sessionName, userId) + public NewSessionCommand(string sessionName, int userId) : base(sessionName, userId) { } } + + public class NewCampaignCommand : IRequest + { + public double Timestamp { get; private set; } + public string CampaignName { get; private set; } + public string UserId { get; private set; } + public NewCampaignCommand(string campaingName, string userId) + { + Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + CampaignName = campaingName; + UserId = userId; + } + } + + public class GetCampaignsCommand : IRequest> + { + public string UserId { get; private set; } + public GetCampaignsCommand(string userId) + { + UserId = userId; + } + } + } \ No newline at end of file diff --git a/Sledgemapper.Api/Controllers/AuthManagementController.cs b/Sledgemapper.Api/Controllers/AuthManagementController.cs new file mode 100644 index 0000000..53acc5a --- /dev/null +++ b/Sledgemapper.Api/Controllers/AuthManagementController.cs @@ -0,0 +1,184 @@ +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using Sledgemapper.Api.Commands; +using Sledgemapper.Api.Notifications; +using Sledgemapper.Entities; +using Sledgemapper.Models.Users; +using Sledgemapper.Shared.Entities; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; + +namespace Sledgemapper.Api.Controllers +{ + + [Route("[controller]")] // api/authmanagement + [ApiController] + public class AuthManagementController : ControllerBase + { + private readonly UserManager _userManager; + private readonly JwtConfig _jwtConfig; + + public AuthManagementController(UserManager userManager, + IOptionsMonitor optionsMonitor) + { + _userManager = userManager; + _jwtConfig = optionsMonitor.CurrentValue; + } + + [HttpPost] + [Route("Register")] + public async Task Register([FromBody] Sledgemapper.Models.Users.RegisterModel user) + { + // Check if the incoming request is valid + if (ModelState.IsValid) + { + // check i the user with the same email exist + var existingUser = await _userManager.FindByEmailAsync(user.Email); + + if (existingUser != null) + { + return BadRequest(new Sledgemapper.Models.Users.RegistrationResponse() + { + Result = false, + Errors = new List(){ + "Email already exist" + } + }); + } + + var newUser = new User() { Email = user.Email, UserName = user.UserName }; + var isCreated = await _userManager.CreateAsync(newUser, user.Password); + if (isCreated.Succeeded) + { + var jwtToken = GenerateJwtToken(newUser); + + return Ok(new Sledgemapper.Models.Users.RegistrationResponse() + { + Result = true, + Token = jwtToken + }); + } + + return new JsonResult(new Sledgemapper.Models.Users.RegistrationResponse() + { + Result = false, + Errors = isCreated.Errors.Select(x => x.Description).ToList() + } + ) + { StatusCode = 500 }; + } + + return BadRequest(new Sledgemapper.Models.Users.RegistrationResponse() + { + Result = false, + Errors = new List(){ + "Invalid payload" + } + }); + } + + private string GenerateJwtToken(User user) + { + // Now its ime to define the jwt token which will be responsible of creating our tokens + var jwtTokenHandler = new JwtSecurityTokenHandler(); + + // We get our secret from the appsettings + var key = Encoding.ASCII.GetBytes(_jwtConfig.Secret); + + // we define our token descriptor + // We need to utilise claims which are properties in our token which gives information about the token + // which belong to the specific user who it belongs to + // so it could contain their id, name, email the good part is that these information + // are generated by our server and identity framework which is valid and trusted + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new[] + { + new Claim("Id", user.Id.ToString()), + new Claim(JwtRegisteredClaimNames.Sub, user.Email), + new Claim(JwtRegisteredClaimNames.Email, user.Email), + // the JTI is used for our refresh token which we will be convering in the next video + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }), + // the life span of the token needs to be shorter and utilise refresh token to keep the user signedin + // but since this is a demo app we can extend it to fit our current need + Expires = DateTime.UtcNow.AddHours(6), + // here we are adding the encryption alogorithim information which will be used to decrypt our token + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature) + }; + + var token = jwtTokenHandler.CreateToken(tokenDescriptor); + + var jwtToken = jwtTokenHandler.WriteToken(token); + + return jwtToken; + } + + [HttpPost] + [Route("Login")] + public async Task Login([FromBody] UserLoginRequest user) + { + if (ModelState.IsValid) + { + // check if the user with the same email exist + var existingUser = await _userManager.FindByEmailAsync(user.Email); + + if (existingUser == null) + { + // We dont want to give to much information on why the request has failed for security reasons + return BadRequest(new RegistrationResponse() + { + Result = false, + Errors = new List(){ + "Invalid authentication request" + } + }); + } + + // Now we need to check if the user has inputed the right password + var isCorrect = await _userManager.CheckPasswordAsync(existingUser, user.Password); + + if (isCorrect) + { + var jwtToken = GenerateJwtToken(existingUser); + + return Ok(new RegistrationResponse() + { + Result = true, + Token = jwtToken + }); + } + else + { + // We dont want to give to much information on why the request has failed for security reasons + return BadRequest(new RegistrationResponse() + { + Result = false, + Errors = new List(){ + "Invalid authentication request" + } + }); + } + } + + return BadRequest(new RegistrationResponse() + { + Result = false, + Errors = new List(){ + "Invalid payload" + } + }); + } + } + + +} diff --git a/Sledgemapper.Api/Controllers/CampaignController.cs b/Sledgemapper.Api/Controllers/CampaignController.cs new file mode 100644 index 0000000..8bd6c49 --- /dev/null +++ b/Sledgemapper.Api/Controllers/CampaignController.cs @@ -0,0 +1,39 @@ +using MediatR; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Sledgemapper.Api.Commands; +using Sledgemapper.Api.Notifications; +using Sledgemapper.Shared.Entities; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Sledgemapper.Api.Controllers +{ + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [Route("[controller]")] + public class CampaignController : ControllerBase + { + private readonly IMediator _mediator; + private string UserId => HttpContext.User.Claims.FirstOrDefault(m => m.Type == "Id").Value; + + public CampaignController(IMediator mediator) => _mediator = mediator; + + [HttpPost] + [Route("{campaignName}")] + public async Task Post(string campaignName) + { + var result = await _mediator.Send(new NewCampaignCommand(campaignName, UserId.ToString())); + return result; + } + + [HttpGet] + public async Task> Get() + { + var result = await _mediator.Send(new GetCampaignsCommand(UserId)); + return result; + } + + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Controllers/SessionController.cs b/Sledgemapper.Api/Controllers/SessionController.cs index 4926ba2..e13c9d1 100644 --- a/Sledgemapper.Api/Controllers/SessionController.cs +++ b/Sledgemapper.Api/Controllers/SessionController.cs @@ -9,98 +9,82 @@ using System.Threading.Tasks; namespace Sledgemapper.Api.Controllers { [Authorize] - [Route("[controller]/{sessionName}")] - public class SessionController : ControllerBase + [Route("[controller]/{mapName}")] + public class MapController : ControllerBase { private readonly IMediator _mediator; private int UserId => int.Parse(HttpContext.User.Identity.Name); - public SessionController(IMediator mediator) => _mediator = mediator; + public MapController(IMediator mediator) => _mediator = mediator; + + [HttpPost] - public async Task Post(string sessionName) + public async Task Post(string mapName) { - var result = await _mediator.Send(new NewSessionCommand(sessionName, UserId)); + var result = await _mediator.Send(new NewSessionCommand(mapName, UserId)); return result; } [HttpGet] - public async Task Get(string sessionName) + public async Task Get(string mapName) { - var result = await _mediator.Send(new GetMapSnapshotCommand(sessionName)); + var result = await _mediator.Send(new GetMapSnapshotCommand(mapName)); return result; } - // [HttpPost("ping")] - // public async Task Post(string sessionName, [FromBody] Ping pingLocation) - // { - // await _mediator.Send(new PingCommand(sessionName, pingLocation, UserId)); - // } - [HttpPost("snapshot")] - public async Task Post(string sessionName, [FromBody] Session session) + public async Task Post(string mapName, [FromBody] Session session) { - await _mediator.Send(new NewSnapshotCommand(sessionName, session, UserId)); - } - - [HttpPost("tile")] - public async Task Post(string sessionName, [FromBody] Tile tile) - { - await _mediator.Send(new NewTileCommand(sessionName, tile, UserId)); + await _mediator.Send(new NewSnapshotCommand(mapName, session, UserId)); } [HttpPost("overlay")] - public async Task Post(string sessionName, [FromBody] Overlay overlay) + public async Task Post(string mapName, [FromBody] Overlay overlay) { - await _mediator.Send(new NewOverlayCommand(sessionName, overlay, UserId)); + await _mediator.Send(new NewOverlayCommand(mapName, overlay, UserId)); } [HttpPost("wall")] - public async Task Post(string sessionName, [FromBody] Wall wall) + public async Task Post(string mapName, [FromBody] Wall wall) { - await _mediator.Send(new NewWallCommand(sessionName, wall, UserId)); + await _mediator.Send(new NewWallCommand(mapName, wall, UserId)); } [HttpPost("note")] - public async Task Post(string sessionName, [FromBody] Note note) + public async Task Post(string mapName, [FromBody] Note note) { - await _mediator.Send(new NewNoteCommand(sessionName, note, UserId)); + await _mediator.Send(new NewNoteCommand(mapName, note, UserId)); } [HttpPost("room")] - public async Task Post(string sessionName, [FromBody] Room room) + public async Task Post(string mapName, [FromBody] Room room) { - await _mediator.Send(new NewRoomCommand(sessionName, room, UserId)); + await _mediator.Send(new NewRoomCommand(mapName, room, UserId)); } [HttpPost("line")] - public async Task Post(string sessionName, [FromBody] Line line) + public async Task Post(string mapName, [FromBody] Line line) { - await _mediator.Send(new NewLineCommand(sessionName, line, UserId)); - } - - [HttpDelete("tile")] - public async Task Delete(string sessionName, [FromBody] Tile tile) - { - await _mediator.Send(new DeleteTileCommand(sessionName, tile, UserId)); + await _mediator.Send(new NewLineCommand(mapName, line, UserId)); } [HttpDelete("overlay")] - public async Task Delete(string sessionName, [FromBody] Overlay overlay) + public async Task Delete(string mapName, [FromBody] Overlay overlay) { - await _mediator.Send(new DeleteOverlayCommand(sessionName, overlay, UserId)); + await _mediator.Send(new DeleteOverlayCommand(mapName, overlay, UserId)); } [HttpDelete("wall")] - public async Task Delete(string sessionName, [FromBody] Wall wall) + public async Task Delete(string mapName, [FromBody] Wall wall) { - await _mediator.Send(new DeleteWallCommand(sessionName, wall, UserId)); + await _mediator.Send(new DeleteWallCommand(mapName, wall, UserId)); } [HttpDelete("note")] - public async Task Delete(string sessionName, [FromBody] Note note) + public async Task Delete(string mapName, [FromBody] Note note) { - await _mediator.Send(new DeleteNoteCommand(sessionName, note, UserId)); + await _mediator.Send(new DeleteNoteCommand(mapName, note, UserId)); } } } \ No newline at end of file diff --git a/Sledgemapper.Api/Controllers/UsersController.cs b/Sledgemapper.Api/Controllers/UsersController.cs deleted file mode 100644 index faf3f3a..0000000 --- a/Sledgemapper.Api/Controllers/UsersController.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc; -using AutoMapper; -using System.IdentityModel.Tokens.Jwt; - -using Microsoft.Extensions.Options; -using System.Text; -using Microsoft.IdentityModel.Tokens; -using System.Security.Claims; -using Microsoft.AspNetCore.Authorization; -using Sledgemapper.Entities; -using Sledgemapper.Models.Users; -using Sledgemapper.Helpers; -using Sledgemapper.Api.Infrastructure.Services; - -namespace Sledgemapper.Controllers -{ - [Authorize] - [ApiController] - [Route("[controller]")] - public class UsersController : ControllerBase - { - private IUserService _userService; - private IMapper _mapper; - private readonly AppSettings _appSettings; - - public UsersController( - IUserService userService, - IMapper mapper, - IOptions appSettings) - { - _userService = userService; - _mapper = mapper; - _appSettings = appSettings.Value; - } - - [AllowAnonymous] - [HttpPost("authenticate")] - public IActionResult Authenticate([FromBody]AuthenticateModel model) - { - var user = _userService.Authenticate(model.Username, model.Password); - - if (user == null) - return BadRequest(new { message = "Username or password is incorrect" }); - - var tokenHandler = new JwtSecurityTokenHandler(); - var key = Encoding.ASCII.GetBytes(_appSettings.Secret); - var tokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(new Claim[] - { - new Claim(ClaimTypes.Name, user.Id.ToString()) - }), - Expires = DateTime.UtcNow.AddDays(7), - SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) - }; - var token = tokenHandler.CreateToken(tokenDescriptor); - var tokenString = tokenHandler.WriteToken(token); - - // return basic user info and authentication token - return Ok(new - { - Id = user.Id, - Username = user.Username, - FirstName = user.FirstName, - LastName = user.LastName, - Initials = user.Initials, - Token = tokenString - }); - } - - [AllowAnonymous] - [HttpPost("register")] - public IActionResult Register([FromBody]RegisterModel model) - { - // map model to entity - var user = _mapper.Map(model); - - try - { - // create user - _userService.Create(user, model.Password); - return Ok(); - } - catch (AppException ex) - { - // return error message if there was an exception - return BadRequest(new { message = ex.Message }); - } - } - - [HttpGet] - public IActionResult GetAll() - { - var users = _userService.GetAll(); - var model = _mapper.Map>(users); - return Ok(model); - } - - [HttpGet("{id}")] - public IActionResult GetById(int id) - { - var user = _userService.GetById(id); - var model = _mapper.Map(user); - return Ok(model); - } - - [HttpPut("{id}")] - public IActionResult Update(int id, [FromBody]UpdateModel model) - { - // map model to entity and set id - var user = _mapper.Map(model); - user.Id = id; - - try - { - // update user - _userService.Update(user, model.Password); - return Ok(); - } - catch (AppException ex) - { - // return error message if there was an exception - return BadRequest(new { message = ex.Message }); - } - } - - [HttpDelete("{id}")] - public IActionResult Delete(int id) - { - _userService.Delete(id); - return Ok(); - } - } -} diff --git a/Sledgemapper.Api/Core/Entities/BaseEntity.cs b/Sledgemapper.Api/Core/Entities/BaseEntity.cs new file mode 100644 index 0000000..5196c35 --- /dev/null +++ b/Sledgemapper.Api/Core/Entities/BaseEntity.cs @@ -0,0 +1,7 @@ +using System.ComponentModel.DataAnnotations; + +public abstract class BaseEntity +{ + [Key] + public int Id { get; set; } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Core/Entities/Campaign.cs b/Sledgemapper.Api/Core/Entities/Campaign.cs new file mode 100644 index 0000000..2dc13b5 --- /dev/null +++ b/Sledgemapper.Api/Core/Entities/Campaign.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Sledgemapper.Entities; + +namespace Sledgemapper.Api.Core.Entities +{ + [Index(nameof(CampaignName), nameof(OwnerId), IsUnique = true)] + public class Campaign + { + public int CampaignId { get; set; } + public string CampaignName { get; set; } + + public string OwnerId { get; set; } + + public User Owner { get; set; } + + public ICollection InvitedUsers { get; set; } + + public ICollection Maps { get; set; } + } +} diff --git a/Sledgemapper.Api/Core/Entities/CampaignAggregate/Campaign.cs b/Sledgemapper.Api/Core/Entities/CampaignAggregate/Campaign.cs deleted file mode 100644 index 7d9b6ab..0000000 --- a/Sledgemapper.Api/Core/Entities/CampaignAggregate/Campaign.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Sledgemapper.Entities; - -namespace Sledgemapper.Api.Core.Entities.CampaignAggregate -{ - public class Campaign - { - [Key] - public int CampaignId { get; set; } - - [Required] - public string CampaignName { get; set; } - - [Required] - public int OwnerUserId { get; set; } - - public List InvitedUsers { get; set; } - } -} diff --git a/Sledgemapper.Api/Core/Entities/CampaignAggregate/Map.cs b/Sledgemapper.Api/Core/Entities/Map.cs similarity index 81% rename from Sledgemapper.Api/Core/Entities/CampaignAggregate/Map.cs rename to Sledgemapper.Api/Core/Entities/Map.cs index 44ed87b..b46a373 100644 --- a/Sledgemapper.Api/Core/Entities/CampaignAggregate/Map.cs +++ b/Sledgemapper.Api/Core/Entities/Map.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Sledgemapper.Api.Core.Entities.CampaignAggregate +namespace Sledgemapper.Api.Core.Entities { public class Map { diff --git a/Sledgemapper.Api/Models/MapLog.cs b/Sledgemapper.Api/Core/Entities/MapLog.cs similarity index 100% rename from Sledgemapper.Api/Models/MapLog.cs rename to Sledgemapper.Api/Core/Entities/MapLog.cs diff --git a/Sledgemapper.Api/Models/Session.cs b/Sledgemapper.Api/Core/Entities/Session.cs similarity index 100% rename from Sledgemapper.Api/Models/Session.cs rename to Sledgemapper.Api/Core/Entities/Session.cs diff --git a/Sledgemapper.Api/Models/SessionUser.cs b/Sledgemapper.Api/Core/Entities/SessionUser.cs similarity index 100% rename from Sledgemapper.Api/Models/SessionUser.cs rename to Sledgemapper.Api/Core/Entities/SessionUser.cs diff --git a/Sledgemapper.Api/Models/Snapshot.cs b/Sledgemapper.Api/Core/Entities/Snapshot.cs similarity index 100% rename from Sledgemapper.Api/Models/Snapshot.cs rename to Sledgemapper.Api/Core/Entities/Snapshot.cs diff --git a/Sledgemapper.Api/Core/Entities/User.cs b/Sledgemapper.Api/Core/Entities/User.cs new file mode 100644 index 0000000..a4af244 --- /dev/null +++ b/Sledgemapper.Api/Core/Entities/User.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Identity; + +namespace Sledgemapper.Entities +{ + public class User:IdentityUser + { + // public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + // public string Email { get; set; } + // public string Username { get; set; } + public string Initials { get; set; } + // public byte[] PasswordHash { get; set; } + public byte[] PasswordSalt { get; set; } + public ICollection Campaigns {get;set;} + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Models/UserConnection.cs b/Sledgemapper.Api/Core/Entities/UserConnection.cs similarity index 100% rename from Sledgemapper.Api/Models/UserConnection.cs rename to Sledgemapper.Api/Core/Entities/UserConnection.cs diff --git a/Sledgemapper.Api/Handlers/NewCampaignCommandHandler.cs b/Sledgemapper.Api/Handlers/NewCampaignCommandHandler.cs new file mode 100644 index 0000000..7f464fb --- /dev/null +++ b/Sledgemapper.Api/Handlers/NewCampaignCommandHandler.cs @@ -0,0 +1,78 @@ +using MediatR; +using Microsoft.EntityFrameworkCore; +using Sledgemapper.Api.Commands; +using Sledgemapper.Api.Infrastructure.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Sledgemapper.Api.Handlers +{ + public class NewCampaignCommandHandler : IRequestHandler + { + private readonly IMediator _mediator; + private readonly SledgemapperDbContext _dbcontext; + + public NewCampaignCommandHandler(IMediator mediator, SledgemapperDbContext dbcontext) + { + _mediator = mediator; + _dbcontext = dbcontext; + } + + public async Task Handle(NewCampaignCommand notification, CancellationToken cancellationToken) + { + try + { + var user = await _dbcontext.Users.FindAsync(notification.UserId); + _dbcontext.Attach(user); + var campaign = new Core.Entities.Campaign + { + CampaignName = notification.CampaignName, + OwnerId = user.Id, + InvitedUsers = new System.Collections.Generic.List { user } + }; + + _dbcontext.Campaigns.Add(campaign); + + await _dbcontext.SaveChangesAsync(); + return true; + } + catch (Exception ex) + { + + + } + return false; + } + } + + public class GetCampaignsCommandHandler : IRequestHandler> + { + private readonly IMediator _mediator; + private readonly SledgemapperDbContext _dbcontext; + + public GetCampaignsCommandHandler(IMediator mediator, SledgemapperDbContext dbcontext) + { + _mediator = mediator; + _dbcontext = dbcontext; + } + + public async Task> Handle(GetCampaignsCommand command, CancellationToken cancellationToken) + { + try + { + var user = await _dbcontext.Users.FindAsync(command.UserId); + _dbcontext.Attach(user); + var campaigns = _dbcontext.Campaigns.Where(campaign => campaign.OwnerId == command.UserId || campaign.InvitedUsers.Contains(user)); + + return campaigns.ToList(); + } + catch (Exception ex) + { + } + return new List(); + } + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Helpers/AppSettings.cs b/Sledgemapper.Api/Helpers/AppSettings.cs deleted file mode 100644 index bcf36f3..0000000 --- a/Sledgemapper.Api/Helpers/AppSettings.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Sledgemapper.Helpers -{ - public class AppSettings - { - public string Secret { get; set; } - } -} \ No newline at end of file diff --git a/Sledgemapper.Api/Helpers/AutoMapperProfile.cs b/Sledgemapper.Api/Helpers/AutoMapperProfile.cs index fe86397..e9fd4aa 100644 --- a/Sledgemapper.Api/Helpers/AutoMapperProfile.cs +++ b/Sledgemapper.Api/Helpers/AutoMapperProfile.cs @@ -8,9 +8,7 @@ namespace Sledgemapper.Helpers { public AutoMapperProfile() { - CreateMap(); CreateMap(); - CreateMap(); } } } \ No newline at end of file diff --git a/Sledgemapper.Api/Helpers/DataContext.cs b/Sledgemapper.Api/Helpers/DataContext.cs deleted file mode 100644 index 33a3b85..0000000 --- a/Sledgemapper.Api/Helpers/DataContext.cs +++ /dev/null @@ -1,24 +0,0 @@ -// using Microsoft.EntityFrameworkCore; -// using Microsoft.Extensions.Configuration; -// using Sledgemapper.Entities; - -// namespace Sledgemapper.Helpers -// { -// public class DataContext : DbContext -// { -// protected readonly IConfiguration Configuration; - -// public DataContext(IConfiguration configuration) -// { -// Configuration = configuration; -// } - -// protected override void OnConfiguring(DbContextOptionsBuilder options) -// { -// // connect to sql server database -// options.UseSqlServer(Configuration.GetConnectionString("WebApiDatabase")); -// } - -// public DbSet Users { get; set; } -// } -// } \ No newline at end of file diff --git a/Sledgemapper.Api/Helpers/SqliteDataContext.cs b/Sledgemapper.Api/Helpers/SqliteDataContext.cs deleted file mode 100644 index 67a7ca9..0000000 --- a/Sledgemapper.Api/Helpers/SqliteDataContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -// using Microsoft.EntityFrameworkCore; -// using Microsoft.Extensions.Configuration; - -// namespace Sledgemapper.Helpers -// { -// public class SqliteDataContext : DataContext -// { -// public SqliteDataContext(IConfiguration configuration) : base(configuration) { } - -// protected override void OnConfiguring(DbContextOptionsBuilder options) -// { -// // connect to sqlite database -// options.UseSqlite(Configuration.GetConnectionString("WebApiDatabase")); -// } -// } -// } \ No newline at end of file diff --git a/Sledgemapper.Api/Hubs/SledgemapperHub.cs b/Sledgemapper.Api/Hubs/SledgemapperHub.cs index 03bce2b..2fcb6b4 100644 --- a/Sledgemapper.Api/Hubs/SledgemapperHub.cs +++ b/Sledgemapper.Api/Hubs/SledgemapperHub.cs @@ -89,7 +89,7 @@ namespace Sledgemapper.Api.Hubs public async Task Ping(string sessionName, Tile location) { var userId = int.Parse(Context.User.Identity.Name); - var user = _dbContext.Users.First(u => u.Id == userId); + var user = _dbContext.Users.First(u => u.Id == Context.User.Identity.Name); var player = new Player { UserId = userId, Initials = user.Initials, Position = new Tile { X = 0, Y = 0 }, Color = UserColors[userId] }; await Clients.Group(sessionName).Ping(new Ping{X=location.X, Y=location.Y, Player=player}); @@ -106,7 +106,7 @@ namespace Sledgemapper.Api.Hubs _dbContext.SessionUsers.Add(userSession); await _dbContext.SaveChangesAsync(); await Groups.AddToGroupAsync(Context.ConnectionId, session.SessionName); - var user = _dbContext.Users.First(u => u.Id == userId); + var user = _dbContext.Users.First(u => u.Id == Context.User.Identity.Name); var player = new Player { UserId = userId, Initials = user.Initials, Position = new Tile { X = 0, Y = 0 }, Color = UserColors[userId] }; @@ -131,7 +131,7 @@ namespace Sledgemapper.Api.Hubs { var userId = int.Parse(Context.User.Identity.Name); var SessionUsers = _dbContext.SessionUsers.Where(m => m.SessionId == sessionId).OrderBy(m => m.UserId).ToList(); - var user = _dbContext.Users.First(u => u.Id == userId); + var user = _dbContext.Users.First(u => u.Id == Context.User.Identity.Name); var player = new Player { UserId = userId, Initials = user.Initials, Position = tile, Color = UserColors[userId] }; await Clients.Group(sessionName).PlayerUpdate(player); } diff --git a/Sledgemapper.Api/Infrastructure/Data/MyDbContext.cs b/Sledgemapper.Api/Infrastructure/Data/MyDbContext.cs deleted file mode 100644 index 26b39bf..0000000 --- a/Sledgemapper.Api/Infrastructure/Data/MyDbContext.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Sledgemapper.Api.Core.Entities.CampaignAggregate; -using Sledgemapper.Api.Models; -using Sledgemapper.Entities; - -namespace Sledgemapper.Api.Infrastructure.Data -{ - // public static class DbInitializer - // { - // public static void Initialize(SledgemapperDbContext context) - // { - // context.Database.EnsureCreated(); - // } - // } - - public class SledgemapperDbContext : DbContext - { - public DbSet Campaigns { get; set; } - public DbSet MapLogs { get; set; } - public DbSet Maps { get; set; } - public DbSet Sessions { get; set; } - public DbSet SessionUsers { get; set; } - public DbSet Snapshots { get; set; } - public DbSet Users { get; set; } - public DbSet UserConnections { get; set; } - - public SledgemapperDbContext(DbContextOptions options) : base(options) - { - - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - // optionsBuilder. - // options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); - // optionsBuilder.UseSqlite("Filename=SledgemapperDatabase.db", options => - // { - // options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); - // }); - - optionsBuilder.UseSqlite("Filename=Sledgemapper.db").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - - base.OnConfiguring(optionsBuilder); - } - - // protected override void OnModelCreating(ModelBuilder modelBuilder) - // { - // // Map table names - // modelBuilder.Entity().ToTable("MapLog", "dbo"); - // modelBuilder.Entity(entity => - // { - // entity.HasKey(e => e.MapLogId); - // }); - - // modelBuilder.Entity().ToTable("Session", "dbo"); - // modelBuilder.Entity(entity => - // { - // entity.HasKey(e => e.SessionId); - // entity.HasIndex(e => e.SessionName).IsUnique(); - // }); - - - // modelBuilder.Entity().ToTable("UserConnection", "dbo"); - // modelBuilder.Entity(entity => - // { - // entity.HasKey(e => e.UserConnectionId); - // entity.HasIndex(e => e.UserId); - - // }); - - // modelBuilder.Entity().ToTable("SessionUser", "dbo"); - // modelBuilder.Entity(entity => - // { - // entity.HasKey(e => e.SessionUserId); - // entity.HasIndex(e => e.SessionId); - - // }); - - // modelBuilder.Entity().ToTable("Snapshot", "dbo"); - // modelBuilder.Entity(entity => - // { - // entity.HasKey(e => e.SnapshotId); - // }); - - // base.OnModelCreating(modelBuilder); - // } - } -} \ No newline at end of file diff --git a/Sledgemapper.Api/Infrastructure/Data/SledgemapperDbContext.cs b/Sledgemapper.Api/Infrastructure/Data/SledgemapperDbContext.cs new file mode 100644 index 0000000..15fbf27 --- /dev/null +++ b/Sledgemapper.Api/Infrastructure/Data/SledgemapperDbContext.cs @@ -0,0 +1,96 @@ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Sledgemapper.Api.Core.Entities; +using Sledgemapper.Api.Models; +using Sledgemapper.Entities; + +namespace Sledgemapper.Api.Infrastructure.Data +{ + // public static class DbInitializer + // { + // public static void Initialize(SledgemapperDbContext context) + // { + // context.Database.EnsureCreated(); + // } + // } + + public class SledgemapperDbContext : IdentityDbContext + { + public DbSet Campaigns { get; set; } + public DbSet MapLogs { get; set; } + public DbSet Maps { get; set; } + public DbSet Sessions { get; set; } + public DbSet SessionUsers { get; set; } + public DbSet Snapshots { get; set; } + public DbSet Users { get; set; } + public DbSet UserConnections { get; set; } + + public SledgemapperDbContext(DbContextOptions options) : base(options) + { + + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // optionsBuilder. + // options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); + // optionsBuilder.UseSqlite("Filename=SledgemapperDatabase.db", options => + // { + // options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); + // }); + + optionsBuilder.UseSqlite("Filename=Sledgemapper.db").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasOne(e=>e.Owner); + +modelBuilder.Entity().HasMany(e=>e.InvitedUsers).WithMany(e=>e.Campaigns); + + modelBuilder.Entity() //Use your application user class here + .ToTable( "Users" ); //Set the table name here + + // // Map table names + // modelBuilder.Entity().ToTable("MapLog", "dbo"); + // modelBuilder.Entity(entity => + // { + // entity.HasKey(e => e.MapLogId); + // }); + + // modelBuilder.Entity().ToTable("Session", "dbo"); + // modelBuilder.Entity(entity => + // { + // entity.HasKey(e => e.SessionId); + // entity.HasIndex(e => e.SessionName).IsUnique(); + // }); + + + // modelBuilder.Entity().ToTable("UserConnection", "dbo"); + // modelBuilder.Entity(entity => + // { + // entity.HasKey(e => e.UserConnectionId); + // entity.HasIndex(e => e.UserId); + + // }); + + // modelBuilder.Entity().ToTable("SessionUser", "dbo"); + // modelBuilder.Entity(entity => + // { + // entity.HasKey(e => e.SessionUserId); + // entity.HasIndex(e => e.SessionId); + + // }); + + // modelBuilder.Entity().ToTable("Snapshot", "dbo"); + // modelBuilder.Entity(entity => + // { + // entity.HasKey(e => e.SnapshotId); + // }); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Infrastructure/Services/UserService.cs b/Sledgemapper.Api/Infrastructure/Services/UserService.cs deleted file mode 100644 index 8e1eed9..0000000 --- a/Sledgemapper.Api/Infrastructure/Services/UserService.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Sledgemapper.Api.Infrastructure.Data; -using Sledgemapper.Entities; -using Sledgemapper.Helpers; - -namespace Sledgemapper.Api.Infrastructure.Services -{ - public interface IUserService - { - User Authenticate(string username, string password); - IEnumerable GetAll(); - User GetById(int id); - User Create(User user, string password); - void Update(User user, string password = null); - void Delete(int id); - } - - public class UserService : IUserService - { - private SledgemapperDbContext _context; - - public UserService(SledgemapperDbContext context) - { - _context = context; - } - - public User Authenticate(string username, string password) - { - if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) - return null; - - var user = _context.Users.SingleOrDefault(x => x.Username == username); - - // check if username exists - if (user == null) - return null; - - // check if password is correct - if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt)) - return null; - - // authentication successful - return user; - } - - public IEnumerable GetAll() - { - return _context.Users; - } - - public User GetById(int id) - { - return _context.Users.Find(id); - } - - public User Create(User user, string password) - { - // validation - if (string.IsNullOrWhiteSpace(password)) - throw new AppException("Password is required"); - - if (_context.Users.Any(x => x.Username == user.Username)) - throw new AppException("Username \"" + user.Username + "\" is already taken"); - - byte[] passwordHash, passwordSalt; - CreatePasswordHash(password, out passwordHash, out passwordSalt); - - user.PasswordHash = passwordHash; - user.PasswordSalt = passwordSalt; - - _context.Users.Add(user); - _context.SaveChanges(); - - return user; - } - - public void Update(User userParam, string password = null) - { - var user = _context.Users.Find(userParam.Id); - - if (user == null) - throw new AppException("User not found"); - - // update username if it has changed - if (!string.IsNullOrWhiteSpace(userParam.Username) && userParam.Username != user.Username) - { - // throw error if the new username is already taken - if (_context.Users.Any(x => x.Username == userParam.Username)) - throw new AppException("Username " + userParam.Username + " is already taken"); - - user.Username = userParam.Username; - } - - // update user properties if provided - if (!string.IsNullOrWhiteSpace(userParam.FirstName)) - user.FirstName = userParam.FirstName; - - if (!string.IsNullOrWhiteSpace(userParam.LastName)) - user.LastName = userParam.LastName; - - // update password if provided - if (!string.IsNullOrWhiteSpace(password)) - { - byte[] passwordHash, passwordSalt; - CreatePasswordHash(password, out passwordHash, out passwordSalt); - - user.PasswordHash = passwordHash; - user.PasswordSalt = passwordSalt; - } - - _context.Users.Update(user); - _context.SaveChanges(); - } - - public void Delete(int id) - { - var user = _context.Users.Find(id); - if (user != null) - { - _context.Users.Remove(user); - _context.SaveChanges(); - } - } - - // private helper methods - - private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt) - { - if (password == null) throw new ArgumentNullException("password"); - if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password"); - - using (var hmac = new System.Security.Cryptography.HMACSHA512()) - { - passwordSalt = hmac.Key; - passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password)); - } - } - - private static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt) - { - if (password == null) throw new ArgumentNullException("password"); - if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password"); - if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", "passwordHash"); - if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", "passwordHash"); - - using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt)) - { - var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password)); - for (int i = 0; i < computedHash.Length; i++) - { - if (computedHash[i] != storedHash[i]) return false; - } - } - - return true; - } - } -} \ No newline at end of file diff --git a/Sledgemapper.Api/JwtConfig.cs b/Sledgemapper.Api/JwtConfig.cs new file mode 100644 index 0000000..7c5c5c4 --- /dev/null +++ b/Sledgemapper.Api/JwtConfig.cs @@ -0,0 +1,7 @@ +namespace Sledgemapper.Api +{ + public class JwtConfig + { + public string Secret { get; set; } + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Infrastructure/Data/Migrations/20210214232542_InitialCreate.Designer.cs b/Sledgemapper.Api/Migrations/20210219223114_InitialCreate.Designer.cs similarity index 73% rename from Sledgemapper.Api/Infrastructure/Data/Migrations/20210214232542_InitialCreate.Designer.cs rename to Sledgemapper.Api/Migrations/20210219223114_InitialCreate.Designer.cs index 9ad309b..fc87031 100644 --- a/Sledgemapper.Api/Infrastructure/Data/Migrations/20210214232542_InitialCreate.Designer.cs +++ b/Sledgemapper.Api/Migrations/20210219223114_InitialCreate.Designer.cs @@ -9,7 +9,7 @@ using Sledgemapper.Api.Infrastructure.Data; namespace Sledgemapper.Api.Migrations { [DbContext(typeof(SledgemapperDbContext))] - [Migration("20210214232542_InitialCreate")] + [Migration("20210219223114_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -18,25 +18,44 @@ namespace Sledgemapper.Api.Migrations modelBuilder .HasAnnotation("ProductVersion", "5.0.3"); - modelBuilder.Entity("Sledgemapper.Api.Models.Campaign", b => + modelBuilder.Entity("CampaignUser", b => + { + b.Property("CampaignsCampaignId") + .HasColumnType("INTEGER"); + + b.Property("InvitedUsersId") + .HasColumnType("INTEGER"); + + b.HasKey("CampaignsCampaignId", "InvitedUsersId"); + + b.HasIndex("InvitedUsersId"); + + b.ToTable("CampaignUser"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => { b.Property("CampaignId") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); b.Property("CampaignName") - .IsRequired() .HasColumnType("TEXT"); - b.Property("OwnerUserId") + b.Property("OwnerId") .HasColumnType("INTEGER"); b.HasKey("CampaignId"); + b.HasIndex("OwnerId"); + + b.HasIndex("CampaignName", "OwnerId") + .IsUnique(); + b.ToTable("Campaigns"); }); - modelBuilder.Entity("Sledgemapper.Api.Models.Map", b => + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => { b.Property("MapId") .ValueGeneratedOnAdd() @@ -51,6 +70,8 @@ namespace Sledgemapper.Api.Migrations b.HasKey("MapId"); + b.HasIndex("CampaignId"); + b.ToTable("Maps"); }); @@ -169,9 +190,6 @@ namespace Sledgemapper.Api.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("CampaignId") - .HasColumnType("INTEGER"); - b.Property("FirstName") .HasColumnType("TEXT"); @@ -192,21 +210,47 @@ namespace Sledgemapper.Api.Migrations b.HasKey("Id"); - b.HasIndex("CampaignId"); - b.ToTable("Users"); }); - modelBuilder.Entity("Sledgemapper.Entities.User", b => + modelBuilder.Entity("CampaignUser", b => { - b.HasOne("Sledgemapper.Api.Models.Campaign", null) - .WithMany("InvitedUsers") - .HasForeignKey("CampaignId"); + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany() + .HasForeignKey("CampaignsCampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Sledgemapper.Entities.User", null) + .WithMany() + .HasForeignKey("InvitedUsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); - modelBuilder.Entity("Sledgemapper.Api.Models.Campaign", b => + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => { - b.Navigation("InvitedUsers"); + b.HasOne("Sledgemapper.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => + { + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany("Maps") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.Navigation("Maps"); }); #pragma warning restore 612, 618 } diff --git a/Sledgemapper.Api/Infrastructure/Data/Migrations/20210214232542_InitialCreate.cs b/Sledgemapper.Api/Migrations/20210219223114_InitialCreate.cs similarity index 73% rename from Sledgemapper.Api/Infrastructure/Data/Migrations/20210214232542_InitialCreate.cs rename to Sledgemapper.Api/Migrations/20210219223114_InitialCreate.cs index e9d609a..06c8d02 100644 --- a/Sledgemapper.Api/Infrastructure/Data/Migrations/20210214232542_InitialCreate.cs +++ b/Sledgemapper.Api/Migrations/20210219223114_InitialCreate.cs @@ -7,20 +7,6 @@ namespace Sledgemapper.Api.Migrations { protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.CreateTable( - name: "Campaigns", - columns: table => new - { - CampaignId = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - CampaignName = table.Column(type: "TEXT", nullable: false), - OwnerUserId = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Campaigns", x => x.CampaignId); - }); - migrationBuilder.CreateTable( name: "MapLogs", columns: table => new @@ -39,20 +25,6 @@ namespace Sledgemapper.Api.Migrations table.PrimaryKey("PK_MapLogs", x => x.MapLogId); }); - migrationBuilder.CreateTable( - name: "Maps", - columns: table => new - { - MapId = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - MapName = table.Column(type: "TEXT", nullable: false), - CampaignId = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Maps", x => x.MapId); - }); - migrationBuilder.CreateTable( name: "Sessions", columns: table => new @@ -121,28 +93,104 @@ namespace Sledgemapper.Api.Migrations Username = table.Column(type: "TEXT", nullable: true), Initials = table.Column(type: "TEXT", nullable: true), PasswordHash = table.Column(type: "BLOB", nullable: true), - PasswordSalt = table.Column(type: "BLOB", nullable: true), - CampaignId = table.Column(type: "INTEGER", nullable: true) + PasswordSalt = table.Column(type: "BLOB", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Campaigns", + columns: table => new + { + CampaignId = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CampaignName = table.Column(type: "TEXT", nullable: true), + OwnerId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Campaigns", x => x.CampaignId); table.ForeignKey( - name: "FK_Users_Campaigns_CampaignId", + name: "FK_Campaigns_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "CampaignUser", + columns: table => new + { + CampaignsCampaignId = table.Column(type: "INTEGER", nullable: false), + InvitedUsersId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CampaignUser", x => new { x.CampaignsCampaignId, x.InvitedUsersId }); + table.ForeignKey( + name: "FK_CampaignUser_Campaigns_CampaignsCampaignId", + column: x => x.CampaignsCampaignId, + principalTable: "Campaigns", + principalColumn: "CampaignId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_CampaignUser_Users_InvitedUsersId", + column: x => x.InvitedUsersId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Maps", + columns: table => new + { + MapId = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + MapName = table.Column(type: "TEXT", nullable: false), + CampaignId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Maps", x => x.MapId); + table.ForeignKey( + name: "FK_Maps_Campaigns_CampaignId", column: x => x.CampaignId, principalTable: "Campaigns", principalColumn: "CampaignId", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( - name: "IX_Users_CampaignId", - table: "Users", + name: "IX_Campaigns_CampaignName_OwnerId", + table: "Campaigns", + columns: new[] { "CampaignName", "OwnerId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Campaigns_OwnerId", + table: "Campaigns", + column: "OwnerId"); + + migrationBuilder.CreateIndex( + name: "IX_CampaignUser_InvitedUsersId", + table: "CampaignUser", + column: "InvitedUsersId"); + + migrationBuilder.CreateIndex( + name: "IX_Maps_CampaignId", + table: "Maps", column: "CampaignId"); } protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "CampaignUser"); + migrationBuilder.DropTable( name: "MapLogs"); @@ -162,10 +210,10 @@ namespace Sledgemapper.Api.Migrations name: "UserConnections"); migrationBuilder.DropTable( - name: "Users"); + name: "Campaigns"); migrationBuilder.DropTable( - name: "Campaigns"); + name: "Users"); } } } diff --git a/Sledgemapper.Api/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/Sledgemapper.Api/Migrations/20210219223251_Adding authentication to our Api.Designer.cs similarity index 71% rename from Sledgemapper.Api/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs rename to Sledgemapper.Api/Migrations/20210219223251_Adding authentication to our Api.Designer.cs index 409062f..faeeb8c 100644 --- a/Sledgemapper.Api/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs +++ b/Sledgemapper.Api/Migrations/20210219223251_Adding authentication to our Api.Designer.cs @@ -2,39 +2,60 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Sledgemapper.Api.Infrastructure.Data; namespace Sledgemapper.Api.Migrations { [DbContext(typeof(SledgemapperDbContext))] - partial class SledgemapperDbContextModelSnapshot : ModelSnapshot + [Migration("20210219223251_Adding authentication to our Api")] + partial class AddingauthenticationtoourApi { - protected override void BuildModel(ModelBuilder modelBuilder) + protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "5.0.3"); - modelBuilder.Entity("Sledgemapper.Api.Models.Campaign", b => + modelBuilder.Entity("CampaignUser", b => + { + b.Property("CampaignsCampaignId") + .HasColumnType("INTEGER"); + + b.Property("InvitedUsersId") + .HasColumnType("INTEGER"); + + b.HasKey("CampaignsCampaignId", "InvitedUsersId"); + + b.HasIndex("InvitedUsersId"); + + b.ToTable("CampaignUser"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => { b.Property("CampaignId") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); b.Property("CampaignName") - .IsRequired() .HasColumnType("TEXT"); - b.Property("OwnerUserId") + b.Property("OwnerId") .HasColumnType("INTEGER"); b.HasKey("CampaignId"); + b.HasIndex("OwnerId"); + + b.HasIndex("CampaignName", "OwnerId") + .IsUnique(); + b.ToTable("Campaigns"); }); - modelBuilder.Entity("Sledgemapper.Api.Models.Map", b => + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => { b.Property("MapId") .ValueGeneratedOnAdd() @@ -49,6 +70,8 @@ namespace Sledgemapper.Api.Migrations b.HasKey("MapId"); + b.HasIndex("CampaignId"); + b.ToTable("Maps"); }); @@ -167,9 +190,6 @@ namespace Sledgemapper.Api.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("CampaignId") - .HasColumnType("INTEGER"); - b.Property("FirstName") .HasColumnType("TEXT"); @@ -190,21 +210,47 @@ namespace Sledgemapper.Api.Migrations b.HasKey("Id"); - b.HasIndex("CampaignId"); - b.ToTable("Users"); }); - modelBuilder.Entity("Sledgemapper.Entities.User", b => + modelBuilder.Entity("CampaignUser", b => { - b.HasOne("Sledgemapper.Api.Models.Campaign", null) - .WithMany("InvitedUsers") - .HasForeignKey("CampaignId"); + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany() + .HasForeignKey("CampaignsCampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Sledgemapper.Entities.User", null) + .WithMany() + .HasForeignKey("InvitedUsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); - modelBuilder.Entity("Sledgemapper.Api.Models.Campaign", b => + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => { - b.Navigation("InvitedUsers"); + b.HasOne("Sledgemapper.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => + { + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany("Maps") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.Navigation("Maps"); }); #pragma warning restore 612, 618 } diff --git a/Sledgemapper.Api/Migrations/20210219223251_Adding authentication to our Api.cs b/Sledgemapper.Api/Migrations/20210219223251_Adding authentication to our Api.cs new file mode 100644 index 0000000..79e5c82 --- /dev/null +++ b/Sledgemapper.Api/Migrations/20210219223251_Adding authentication to our Api.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Sledgemapper.Api.Migrations +{ + public partial class AddingauthenticationtoourApi : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Sledgemapper.Api/Migrations/20210219230948_rename table.Designer.cs b/Sledgemapper.Api/Migrations/20210219230948_rename table.Designer.cs new file mode 100644 index 0000000..d16b294 --- /dev/null +++ b/Sledgemapper.Api/Migrations/20210219230948_rename table.Designer.cs @@ -0,0 +1,505 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Sledgemapper.Api.Infrastructure.Data; + +namespace Sledgemapper.Api.Migrations +{ + [DbContext(typeof(SledgemapperDbContext))] + [Migration("20210219230948_rename table")] + partial class renametable + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("CampaignUser", b => + { + b.Property("CampaignsCampaignId") + .HasColumnType("INTEGER"); + + b.Property("InvitedUsersId") + .HasColumnType("TEXT"); + + b.HasKey("CampaignsCampaignId", "InvitedUsersId"); + + b.HasIndex("InvitedUsersId"); + + b.ToTable("CampaignUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.Property("CampaignId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampaignName") + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .HasColumnType("TEXT"); + + b.HasKey("CampaignId"); + + b.HasIndex("OwnerId"); + + b.HasIndex("CampaignName", "OwnerId") + .IsUnique(); + + b.ToTable("Campaigns"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampaignId") + .HasColumnType("INTEGER"); + + b.Property("MapName") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("MapId"); + + b.HasIndex("CampaignId"); + + b.ToTable("Maps"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.MapLog", b => + { + b.Property("MapLogId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Object") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Operation") + .IsRequired() + .HasMaxLength(1) + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("REAL"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("MapLogId"); + + b.ToTable("MapLogs"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.Session", b => + { + b.Property("SessionId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("OwnerUserId") + .HasColumnType("INTEGER"); + + b.Property("SessionName") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("SessionId"); + + b.ToTable("Sessions"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.SessionUser", b => + { + b.Property("SessionUserId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("SessionId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("SessionUserId"); + + b.ToTable("SessionUsers"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.Snapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Object") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("REAL"); + + b.HasKey("SnapshotId"); + + b.ToTable("Snapshots"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.UserConnection", b => + { + b.Property("UserConnectionId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("UserConnectionId"); + + b.ToTable("UserConnections"); + }); + + modelBuilder.Entity("Sledgemapper.Entities.User", b => + { + b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); + + b.Property("FirstName") + .HasColumnType("TEXT"); + + b.Property("Initials") + .HasColumnType("TEXT"); + + b.Property("LastName") + .HasColumnType("TEXT"); + + b.Property("PasswordSalt") + .HasColumnType("BLOB"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("CampaignUser", b => + { + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany() + .HasForeignKey("CampaignsCampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Sledgemapper.Entities.User", null) + .WithMany() + .HasForeignKey("InvitedUsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.HasOne("Sledgemapper.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => + { + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany("Maps") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Entities.User", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithOne() + .HasForeignKey("Sledgemapper.Entities.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.Navigation("Maps"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Sledgemapper.Api/Migrations/20210219230948_rename table.cs b/Sledgemapper.Api/Migrations/20210219230948_rename table.cs new file mode 100644 index 0000000..ac5f815 --- /dev/null +++ b/Sledgemapper.Api/Migrations/20210219230948_rename table.cs @@ -0,0 +1,315 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Sledgemapper.Api.Migrations +{ + public partial class renametable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Campaigns_Users_OwnerId", + table: "Campaigns"); + + migrationBuilder.DropColumn( + name: "PasswordHash", + table: "Users"); + + migrationBuilder.AlterColumn( + name: "Id", + table: "Users", + type: "TEXT", + nullable: false, + oldClrType: typeof(int), + oldType: "INTEGER") + .OldAnnotation("Sqlite:Autoincrement", true); + + migrationBuilder.AlterColumn( + name: "InvitedUsersId", + table: "CampaignUser", + type: "TEXT", + nullable: false, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.AlterColumn( + name: "OwnerId", + table: "Campaigns", + type: "TEXT", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Name = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Value = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Campaigns_Users_OwnerId", + table: "Campaigns", + column: "OwnerId", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Users_AspNetUsers_Id", + table: "Users", + column: "Id", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Campaigns_Users_OwnerId", + table: "Campaigns"); + + migrationBuilder.DropForeignKey( + name: "FK_Users_AspNetUsers_Id", + table: "Users"); + + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.AlterColumn( + name: "Id", + table: "Users", + type: "INTEGER", + nullable: false, + oldClrType: typeof(string), + oldType: "TEXT") + .Annotation("Sqlite:Autoincrement", true); + + migrationBuilder.AddColumn( + name: "PasswordHash", + table: "Users", + type: "BLOB", + nullable: true); + + migrationBuilder.AlterColumn( + name: "InvitedUsersId", + table: "CampaignUser", + type: "INTEGER", + nullable: false, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "OwnerId", + table: "Campaigns", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Campaigns_Users_OwnerId", + table: "Campaigns", + column: "OwnerId", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/Sledgemapper.Api/Migrations/SledgemapperDbContextModelSnapshot.cs b/Sledgemapper.Api/Migrations/SledgemapperDbContextModelSnapshot.cs new file mode 100644 index 0000000..152de98 --- /dev/null +++ b/Sledgemapper.Api/Migrations/SledgemapperDbContextModelSnapshot.cs @@ -0,0 +1,503 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Sledgemapper.Api.Infrastructure.Data; + +namespace Sledgemapper.Api.Migrations +{ + [DbContext(typeof(SledgemapperDbContext))] + partial class SledgemapperDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("CampaignUser", b => + { + b.Property("CampaignsCampaignId") + .HasColumnType("INTEGER"); + + b.Property("InvitedUsersId") + .HasColumnType("TEXT"); + + b.HasKey("CampaignsCampaignId", "InvitedUsersId"); + + b.HasIndex("InvitedUsersId"); + + b.ToTable("CampaignUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.Property("CampaignId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampaignName") + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .HasColumnType("TEXT"); + + b.HasKey("CampaignId"); + + b.HasIndex("OwnerId"); + + b.HasIndex("CampaignName", "OwnerId") + .IsUnique(); + + b.ToTable("Campaigns"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => + { + b.Property("MapId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CampaignId") + .HasColumnType("INTEGER"); + + b.Property("MapName") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("MapId"); + + b.HasIndex("CampaignId"); + + b.ToTable("Maps"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.MapLog", b => + { + b.Property("MapLogId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Object") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Operation") + .IsRequired() + .HasMaxLength(1) + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("REAL"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("MapLogId"); + + b.ToTable("MapLogs"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.Session", b => + { + b.Property("SessionId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("OwnerUserId") + .HasColumnType("INTEGER"); + + b.Property("SessionName") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("SessionId"); + + b.ToTable("Sessions"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.SessionUser", b => + { + b.Property("SessionUserId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("SessionId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("SessionUserId"); + + b.ToTable("SessionUsers"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.Snapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Object") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("REAL"); + + b.HasKey("SnapshotId"); + + b.ToTable("Snapshots"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Models.UserConnection", b => + { + b.Property("UserConnectionId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("UserConnectionId"); + + b.ToTable("UserConnections"); + }); + + modelBuilder.Entity("Sledgemapper.Entities.User", b => + { + b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); + + b.Property("FirstName") + .HasColumnType("TEXT"); + + b.Property("Initials") + .HasColumnType("TEXT"); + + b.Property("LastName") + .HasColumnType("TEXT"); + + b.Property("PasswordSalt") + .HasColumnType("BLOB"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("CampaignUser", b => + { + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany() + .HasForeignKey("CampaignsCampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Sledgemapper.Entities.User", null) + .WithMany() + .HasForeignKey("InvitedUsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.HasOne("Sledgemapper.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Map", b => + { + b.HasOne("Sledgemapper.Api.Core.Entities.Campaign", null) + .WithMany("Maps") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Entities.User", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithOne() + .HasForeignKey("Sledgemapper.Entities.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Sledgemapper.Api.Core.Entities.Campaign", b => + { + b.Navigation("Maps"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Sledgemapper.Api/Models/AuthResult.cs b/Sledgemapper.Api/Models/AuthResult.cs new file mode 100644 index 0000000..ec22a2c --- /dev/null +++ b/Sledgemapper.Api/Models/AuthResult.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Sledgemapper.Models.Users +{ + public class AuthResult + { + public string Token { get; set; } + public bool Result { get; set; } + public List Errors { get; set; } + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Models/RegisterModel.cs b/Sledgemapper.Api/Models/RegisterModel.cs index 98e3af3..de274f0 100644 --- a/Sledgemapper.Api/Models/RegisterModel.cs +++ b/Sledgemapper.Api/Models/RegisterModel.cs @@ -6,16 +6,18 @@ namespace Sledgemapper.Models.Users { [Required] public string FirstName { get; set; } + [Required] + public string UserName { get; set; } [Required] public string LastName { get; set; } [Required] - public string Username { get; set; } + public string Email { get; set; } [Required] public string Password { get; set; } - + [Required] public string Initials { get; set; } } diff --git a/Sledgemapper.Api/Models/RegistrationResponse.cs b/Sledgemapper.Api/Models/RegistrationResponse.cs new file mode 100644 index 0000000..59789c6 --- /dev/null +++ b/Sledgemapper.Api/Models/RegistrationResponse.cs @@ -0,0 +1,7 @@ +namespace Sledgemapper.Models.Users +{ + public class RegistrationResponse : AuthResult + { + + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Models/UpdateModel.cs b/Sledgemapper.Api/Models/UpdateModel.cs deleted file mode 100644 index fb421f7..0000000 --- a/Sledgemapper.Api/Models/UpdateModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Sledgemapper.Models.Users -{ - public class UpdateModel - { - public string FirstName { get; set; } - public string LastName { get; set; } - public string Username { get; set; } - public string Password { get; set; } - } -} \ No newline at end of file diff --git a/Sledgemapper.Api/Models/User.cs b/Sledgemapper.Api/Models/User.cs deleted file mode 100644 index 00eb6d7..0000000 --- a/Sledgemapper.Api/Models/User.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Sledgemapper.Entities -{ - public class User - { - public int Id { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public string Username { get; set; } - public string Initials { get; set; } - public byte[] PasswordHash { get; set; } - public byte[] PasswordSalt { get; set; } - } -} \ No newline at end of file diff --git a/Sledgemapper.Api/Models/UserLoginRequest.cs b/Sledgemapper.Api/Models/UserLoginRequest.cs new file mode 100644 index 0000000..4e5fa99 --- /dev/null +++ b/Sledgemapper.Api/Models/UserLoginRequest.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace Sledgemapper.Models.Users +{ + public class UserLoginRequest + { + [Required] + public string Email { get; set; } + [Required] + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/Sledgemapper.Api/Models/UserModel.cs b/Sledgemapper.Api/Models/UserModel.cs deleted file mode 100644 index c1bb502..0000000 --- a/Sledgemapper.Api/Models/UserModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Sledgemapper.Models.Users -{ - public class UserModel - { - public int Id { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public string Username { get; set; } - public string Initials { get; set; } - } -} \ No newline at end of file diff --git a/Sledgemapper.Api/Sledgemapper.Api.csproj b/Sledgemapper.Api/Sledgemapper.Api.csproj index 6456570..842ad1b 100644 --- a/Sledgemapper.Api/Sledgemapper.Api.csproj +++ b/Sledgemapper.Api/Sledgemapper.Api.csproj @@ -10,11 +10,15 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + diff --git a/Sledgemapper.Api/Startup.cs b/Sledgemapper.Api/Startup.cs index 137a667..2ceb194 100644 --- a/Sledgemapper.Api/Startup.cs +++ b/Sledgemapper.Api/Startup.cs @@ -1,11 +1,8 @@ using System; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Sledgemapper.Api.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using MediatR; @@ -13,10 +10,7 @@ using Microsoft.IdentityModel.Tokens; using Sledgemapper.Helpers; using Microsoft.AspNetCore.Authentication.JwtBearer; using System.Text; -using AutoMapper; -using System.Security.Claims; using Sledgemapper.Api.Hubs; -using Sledgemapper.Api.Infrastructure.Services; namespace Sledgemapper.Api { @@ -31,93 +25,67 @@ namespace Sledgemapper.Api } - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - // if (_env.IsProduction()) - // services.AddDbContext(); - // else - // services.AddDbContext(); - - // services.AddRazorPages(); - services.AddCors(); + services.AddCors(); services.AddControllers(); services.AddSignalR(); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddMediatR(typeof(Startup)); - services.AddDbContext(options => {options.UseSqlite("Data Source=db/sledgemapper.db"); options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);}); + services.AddDbContext(options => { options.UseSqlite("Data Source=db/sledgemapper.db"); options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); }); - // services.AddEntityFrameworkSqlite().AddDbContext(); - // configure strongly typed settings objects - var c = Configuration.GetSection("AppSettings"); - var appSettingsSection = Configuration.GetSection("AppSettings"); - services.Configure(appSettingsSection); + services.Configure(Configuration.GetSection("JwtConfig")); - // configure jwt authentication - var appSettings = appSettingsSection.Get(); - var key = Encoding.ASCII.GetBytes(appSettings.Secret); - services.AddAuthentication(x => + // configure DI for application services + + // within this section we are configuring the authentication and setting the default scheme + services.AddAuthentication(options => { - x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) - .AddJwtBearer(x => + .AddJwtBearer(jwt => { - x.Events = new JwtBearerEvents + var key = Encoding.ASCII.GetBytes(Configuration["JwtConfig:Secret"]); + + jwt.SaveToken = true; + jwt.TokenValidationParameters = new TokenValidationParameters { - OnTokenValidated = context => - { - var userService = context.HttpContext.RequestServices.GetRequiredService(); - var userId = int.Parse(context.Principal.Identity.Name); - var user = userService.GetById(userId); - if (user == null) - { - // return unauthorized if user no longer exists - context.Fail("Unauthorized"); - } - context.HttpContext.User.Claims.Append(new Claim(ClaimTypes.Name, user.Username)); - context.HttpContext.User.Claims.Append(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); - return Task.CompletedTask; - } - }; - x.RequireHttpsMetadata = false; - x.SaveToken = true; - x.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateIssuerSigningKey = true, // this will validate the 3rd part of the jwt token using the secret that we added in the appsettings and verify we have generated the jwt token + IssuerSigningKey = new SymmetricSecurityKey(key), // Add the secret key to our Jwt encryption ValidateIssuer = false, - ValidateAudience = false + ValidateAudience = false, + RequireExpirationTime = false, + ValidateLifetime = true }; }); - // configure DI for application services - services.AddScoped(); + services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = false) + .AddEntityFrameworkStores(); + + services.AddSwaggerGen(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, SledgemapperDbContext dataContext) { - dataContext.Database.Migrate(); + // Enable middleware to serve generated Swagger as a JSON endpoint. + app.UseSwagger(); - // if (env.IsDevelopment()) - // { - // app.UseDeveloperExceptionPage(); - // } - // else - // { - // app.UseExceptionHandler("/Error"); - // // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - // app.UseHsts(); - // } + // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), + // specifying the Swagger JSON endpoint. + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + }); - // //app.UseHttpsRedirection(); - // app.UseStaticFiles(); + dataContext.Database.Migrate(); app.UseRouting(); - // global cors policy + app.UseCors(x => x .AllowAnyOrigin() .AllowAnyMethod() @@ -132,9 +100,7 @@ namespace Sledgemapper.Api app.UseEndpoints(endpoints => { - // endpoints.MapRazorPages(); endpoints.MapHub("/sledgemapperhub"); - }); } } diff --git a/Sledgemapper.Api/appsettings.json b/Sledgemapper.Api/appsettings.json index 8df0888..3471d80 100644 --- a/Sledgemapper.Api/appsettings.json +++ b/Sledgemapper.Api/appsettings.json @@ -1,16 +1,16 @@ { - "AppSettings": { - "Secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING" - }, - "ConnectionStrings": { - "WebApiDatabase": "ENTER PRODUCTION SQL SERVER CONNECTION STRING HERE" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} + "JwtConfig": { + "Secret": "ALy9zGFVZBza9cju458dVC2rBdwPqfb7" + }, + "ConnectionStrings": { + "WebApiDatabase": "ENTER PRODUCTION SQL SERVER CONNECTION STRING HERE" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/Sledgemapper/UI/MainWidget.Generated.cs b/Sledgemapper/UI/MainWidget.Generated.cs index 5cdeb3a..3be2355 100644 --- a/Sledgemapper/UI/MainWidget.Generated.cs +++ b/Sledgemapper/UI/MainWidget.Generated.cs @@ -1,4 +1,4 @@ -/* Generated by MyraPad at 11/02/2021 23:16:18 */ +/* Generated by MyraPad at 19/02/2021 14:56:38 */ using Myra; using Myra.Graphics2D; using Myra.Graphics2D.TextureAtlases; @@ -103,17 +103,17 @@ namespace Sledgemapper.UI var menuSeparator4 = new MenuSeparator(); - MenuCampaignInvite = new MenuItem(); - MenuCampaignInvite.Text = "&Invite"; - MenuCampaignInvite.ShortcutText = "Ctrl+I"; - MenuCampaignInvite.Id = "MenuCampaignInvite"; + MenuCampaignPlayers = new MenuItem(); + MenuCampaignPlayers.Text = "&Players"; + MenuCampaignPlayers.ShortcutText = "CtrI+P"; + MenuCampaignPlayers.Id = "MenuCampaignPlayers"; var menuItem2 = new MenuItem(); menuItem2.Text = "C&aign"; menuItem2.Items.Add(MenuCampaingNew); menuItem2.Items.Add(MenuCampaignOpen); menuItem2.Items.Add(menuSeparator4); - menuItem2.Items.Add(MenuCampaignInvite); + menuItem2.Items.Add(MenuCampaignPlayers); MenuMapNew = new MenuItem(); MenuMapNew.Text = "&New"; @@ -348,7 +348,7 @@ namespace Sledgemapper.UI public MenuItem MenuConnectUpload; public MenuItem MenuCampaingNew; public MenuItem MenuCampaignOpen; - public MenuItem MenuCampaignInvite; + public MenuItem MenuCampaignPlayers; public MenuItem MenuMapNew; public MenuItem MenuMapOpen; public MenuItem MenuViewShowNotes; diff --git a/Sledgemapper/UI/mainwidget.xmmp b/Sledgemapper/UI/mainwidget.xmmp index 076b0cb..0bae61b 100644 --- a/Sledgemapper/UI/mainwidget.xmmp +++ b/Sledgemapper/UI/mainwidget.xmmp @@ -27,7 +27,7 @@ - +