diff --git a/EveryPinApi.Entites/Models/Post.cs b/EveryPinApi.Entites/Models/Post.cs index a3229ed..da402da 100644 --- a/EveryPinApi.Entites/Models/Post.cs +++ b/EveryPinApi.Entites/Models/Post.cs @@ -14,6 +14,9 @@ public class Post public string? PostContent { get; set; } [ForeignKey("User")] public required string UserId { get; set; } + public string? Address { get; set; } + public double? latitude { get; set; } + public double? longitude { get; set; } public ICollection PostPhotos { get; } = new List(); public ICollection Likes { get; } = new List(); public ICollection Comments { get; } = new List(); diff --git a/EveryPinApi.Entites/Models/User.cs b/EveryPinApi.Entites/Models/User.cs index 3da2003..b627475 100644 --- a/EveryPinApi.Entites/Models/User.cs +++ b/EveryPinApi.Entites/Models/User.cs @@ -17,6 +17,8 @@ public class User : IdentityUser public ICollection Like { get; set; } = new List(); public string? Name { get; set; } //public string? Email { get; set; } + public string? RefreshToken { get; set; } + public DateTime RefreshTokenExpiryTime { get; set; } public DateTime CreatedDate { get; set; } public DateTime LastLoginDate { get; set; } public bool DeleteCheck { get; set; } diff --git a/EveryPinApi.Presentation/Controllers/AuthenticationController.cs b/EveryPinApi.Presentation/Controllers/AuthenticationController.cs index 0d9c229..d388638 100644 --- a/EveryPinApi.Presentation/Controllers/AuthenticationController.cs +++ b/EveryPinApi.Presentation/Controllers/AuthenticationController.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using Service.Contracts; using Shared.DataTransferObject; +using Shared.DataTransferObject.Auth; using System; using System.Collections.Generic; using System.Linq; @@ -24,12 +25,11 @@ public AuthenticationController(ILogger logger, IServi _service = service; } - [HttpPost] + [HttpPost("regist")] //[ServiceFilter(typeof(ValidationFilterAttribute))] public async Task RegisterUser([FromBody] RegistUserDto registUserDto) { - var result = await - _service.AuthenticationService.RegisterUser(registUserDto); + var result = await _service.AuthenticationService.RegisterUser(registUserDto); if (!result.Succeeded) { @@ -42,6 +42,18 @@ public async Task RegisterUser([FromBody] RegistUserDto registUse return StatusCode(201); } + + [HttpPost("login")] + public async Task Authenticate([FromBody] UserAutenticationDto user) + { + if (!await _service.AuthenticationService.ValidateUser(user)) + return Unauthorized(); + + var tokenDto = await _service.AuthenticationService.CreateToken(populateExp: true); + + return Ok(tokenDto); + + } } } diff --git a/EveryPinApi.Presentation/Controllers/CommentController.cs b/EveryPinApi.Presentation/Controllers/CommentController.cs index e5a9f7c..61b29c5 100644 --- a/EveryPinApi.Presentation/Controllers/CommentController.cs +++ b/EveryPinApi.Presentation/Controllers/CommentController.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Authorization; namespace EveryPinApi.Presentation.Controllers { @@ -24,7 +25,8 @@ public CommentController(ILogger logger, IServiceManager serv _service = service; } - [HttpGet] + [HttpGet(Name = "GetComment")] + [Authorize(Roles ="NormalUser")] public IActionResult GetAllComment() { var companies = _service.CommentService.GetAllComment(trackChanges: false); diff --git a/EveryPinApi.Presentation/Controllers/LikeController.cs b/EveryPinApi.Presentation/Controllers/LikeController.cs index 0316799..ae897c9 100644 --- a/EveryPinApi.Presentation/Controllers/LikeController.cs +++ b/EveryPinApi.Presentation/Controllers/LikeController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Service.Contracts; using System; @@ -23,6 +24,7 @@ public LikeController(ILogger logger, IServiceManager service) } [HttpGet] + [Authorize(Roles = "NormalUser")] public IActionResult GetAllLike() { var likes = _service.LikeService.GetAllLike(trackChanges: false); diff --git a/EveryPinApi.Presentation/Controllers/PostController.cs b/EveryPinApi.Presentation/Controllers/PostController.cs index 9089ec7..b9c2fa3 100644 --- a/EveryPinApi.Presentation/Controllers/PostController.cs +++ b/EveryPinApi.Presentation/Controllers/PostController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Service.Contracts; using System; @@ -23,6 +24,7 @@ public PostController(ILogger logger, IServiceManager service) } [HttpGet] + [Authorize(Roles = "NormalUser")] public IActionResult GetAllPost() { var posts = _service.PostService.GetAllPost(trackChanges: false); diff --git a/EveryPinApi.Presentation/Controllers/PostPhotoController.cs b/EveryPinApi.Presentation/Controllers/PostPhotoController.cs index ad2e76a..11386f1 100644 --- a/EveryPinApi.Presentation/Controllers/PostPhotoController.cs +++ b/EveryPinApi.Presentation/Controllers/PostPhotoController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Service.Contracts; using System; @@ -23,6 +24,7 @@ public PostPhotoController(ILogger logger, IServiceManager } [HttpGet] + [Authorize(Roles = "NormalUser")] public IActionResult GetAllPostPhoto() { var postPhotos = _service.PostPhotoService.GetAllPostPhoto(trackChanges: false); diff --git a/EveryPinApi.Presentation/Controllers/ProfileController.cs b/EveryPinApi.Presentation/Controllers/ProfileController.cs index 0d0e72e..bd52bb0 100644 --- a/EveryPinApi.Presentation/Controllers/ProfileController.cs +++ b/EveryPinApi.Presentation/Controllers/ProfileController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Service.Contracts; using System; @@ -23,6 +24,7 @@ public ProfileController(ILogger logger, IServiceManager serv } [HttpGet] + [Authorize(Roles = "NormalUser")] public IActionResult GetAllProfile() { var profiles = _service.ProfileService.GetAllProfile(trackChanges: false); diff --git a/EveryPinApi.Presentation/Controllers/TokenController.cs b/EveryPinApi.Presentation/Controllers/TokenController.cs new file mode 100644 index 0000000..9ebad34 --- /dev/null +++ b/EveryPinApi.Presentation/Controllers/TokenController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Service.Contracts; +using Shared.DataTransferObject.Auth; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EveryPinApi.Presentation.Controllers +{ + [Route("api/token")] + [ApiController] + public class TokenController : ControllerBase + { + private readonly IServiceManager _service; + + public TokenController(IServiceManager service) => _service = service; + + [HttpPost("refresh")] + public async Task Refresh([FromBody] TokenDto tokenDto) + { + var tokenDtoToReturn = await _service.AuthenticationService.RefreshToken(tokenDto); + return Ok(tokenDtoToReturn); + } + } +} diff --git a/EveryPinApi/EveryPinApi.csproj b/EveryPinApi/EveryPinApi.csproj index 5d23e01..2049ead 100644 --- a/EveryPinApi/EveryPinApi.csproj +++ b/EveryPinApi/EveryPinApi.csproj @@ -16,6 +16,7 @@ + diff --git a/EveryPinApi/Migrations/20240128060203_AdditionalUserFiledsForRefreshToken.Designer.cs b/EveryPinApi/Migrations/20240128060203_AdditionalUserFiledsForRefreshToken.Designer.cs new file mode 100644 index 0000000..0caeaef --- /dev/null +++ b/EveryPinApi/Migrations/20240128060203_AdditionalUserFiledsForRefreshToken.Designer.cs @@ -0,0 +1,546 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Repository; + +#nullable disable + +namespace EveryPinApi.Migrations +{ + [DbContext(typeof(RepositoryContext))] + [Migration("20240128060203_AdditionalUserFiledsForRefreshToken")] + partial class AdditionalUserFiledsForRefreshToken + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.15") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Entites.Models.CodeOAuthPlatform", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("PlatformCodeId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PlatformName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CodeOAuthPlatforms"); + }); + + modelBuilder.Entity("Entites.Models.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("CommentId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CommentMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("Entites.Models.Like", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("LikeId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("Likes"); + }); + + modelBuilder.Entity("Entites.Models.Post", b => + { + b.Property("PostId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("PostId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("PostId")); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PostContent") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("PostId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("Entites.Models.PostPhoto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("PostPhotoId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("photoUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("PostPhotos"); + }); + + modelBuilder.Entity("Entites.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ProfileId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PhotoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("SelfIntroduction") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Profiles"); + }); + + modelBuilder.Entity("Entites.Models.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeleteCheck") + .HasColumnType("bit"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LastLoginDate") + .HasColumnType("datetime2"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("PlatformCodeId") + .HasColumnType("int"); + + b.Property("RefreshToken") + .HasColumnType("nvarchar(max)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime2"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = "2179ba12-d152-45d3-9d76-d2f5eaca01cf", + Name = "NormalUser", + NormalizedName = "NORMALUSER" + }, + new + { + Id = "8160263c-668a-4495-89dc-35b9296d3a0f", + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Entites.Models.Comment", b => + { + b.HasOne("Entites.Models.Post", "Post") + .WithMany("Comments") + .HasForeignKey("PostId"); + + b.HasOne("Entites.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Entites.Models.Like", b => + { + b.HasOne("Entites.Models.Post", "Post") + .WithMany("Likes") + .HasForeignKey("PostId"); + + b.HasOne("Entites.Models.User", "User") + .WithMany("Like") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Entites.Models.PostPhoto", b => + { + b.HasOne("Entites.Models.Post", "Post") + .WithMany("PostPhotos") + .HasForeignKey("PostId"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Entites.Models.Profile", b => + { + b.HasOne("Entites.Models.User", "User") + .WithOne("Profile") + .HasForeignKey("Entites.Models.Profile", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + 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("Entites.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Entites.Models.User", 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("Entites.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Entites.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Entites.Models.Post", b => + { + b.Navigation("Comments"); + + b.Navigation("Likes"); + + b.Navigation("PostPhotos"); + }); + + modelBuilder.Entity("Entites.Models.User", b => + { + b.Navigation("Like"); + + b.Navigation("Profile"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EveryPinApi/Migrations/20240128060203_AdditionalUserFiledsForRefreshToken.cs b/EveryPinApi/Migrations/20240128060203_AdditionalUserFiledsForRefreshToken.cs new file mode 100644 index 0000000..a23d850 --- /dev/null +++ b/EveryPinApi/Migrations/20240128060203_AdditionalUserFiledsForRefreshToken.cs @@ -0,0 +1,80 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace EveryPinApi.Migrations +{ + /// + public partial class AdditionalUserFiledsForRefreshToken : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "7936c632-4ef6-4ff9-9417-555941ceadd4"); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "c5127bc6-4ae3-40e2-b9c9-8c297f6810b1"); + + migrationBuilder.AddColumn( + name: "RefreshToken", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "RefreshTokenExpiryTime", + table: "AspNetUsers", + type: "datetime2", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { "2179ba12-d152-45d3-9d76-d2f5eaca01cf", null, "NormalUser", "NORMALUSER" }, + { "8160263c-668a-4495-89dc-35b9296d3a0f", null, "Administrator", "ADMINISTRATOR" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "2179ba12-d152-45d3-9d76-d2f5eaca01cf"); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "8160263c-668a-4495-89dc-35b9296d3a0f"); + + migrationBuilder.DropColumn( + name: "RefreshToken", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "RefreshTokenExpiryTime", + table: "AspNetUsers"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { "7936c632-4ef6-4ff9-9417-555941ceadd4", null, "NormalUser", "NORMALUSER" }, + { "c5127bc6-4ae3-40e2-b9c9-8c297f6810b1", null, "Administrator", "ADMINISTRATOR" } + }); + } + } +} diff --git a/EveryPinApi/Migrations/20240131103624_AddMapAttribute.Designer.cs b/EveryPinApi/Migrations/20240131103624_AddMapAttribute.Designer.cs new file mode 100644 index 0000000..2804433 --- /dev/null +++ b/EveryPinApi/Migrations/20240131103624_AddMapAttribute.Designer.cs @@ -0,0 +1,555 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Repository; + +#nullable disable + +namespace EveryPinApi.Migrations +{ + [DbContext(typeof(RepositoryContext))] + [Migration("20240131103624_AddMapAttribute")] + partial class AddMapAttribute + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.15") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Entites.Models.CodeOAuthPlatform", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("PlatformCodeId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PlatformName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CodeOAuthPlatforms"); + }); + + modelBuilder.Entity("Entites.Models.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("CommentId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CommentMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("Entites.Models.Like", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("LikeId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("Likes"); + }); + + modelBuilder.Entity("Entites.Models.Post", b => + { + b.Property("PostId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("PostId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("PostId")); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("PostContent") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("latitude") + .HasColumnType("float"); + + b.Property("longitude") + .HasColumnType("float"); + + b.HasKey("PostId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("Entites.Models.PostPhoto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("PostPhotoId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("photoUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("PostPhotos"); + }); + + modelBuilder.Entity("Entites.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ProfileId"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PhotoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("SelfIntroduction") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Profiles"); + }); + + modelBuilder.Entity("Entites.Models.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("DeleteCheck") + .HasColumnType("bit"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LastLoginDate") + .HasColumnType("datetime2"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("PlatformCodeId") + .HasColumnType("int"); + + b.Property("RefreshToken") + .HasColumnType("nvarchar(max)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime2"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = "fafbb86f-5268-49e8-a8a0-c35be787f05a", + Name = "NormalUser", + NormalizedName = "NORMALUSER" + }, + new + { + Id = "923a3112-78df-4baf-8127-5aa87babcea7", + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Entites.Models.Comment", b => + { + b.HasOne("Entites.Models.Post", "Post") + .WithMany("Comments") + .HasForeignKey("PostId"); + + b.HasOne("Entites.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Entites.Models.Like", b => + { + b.HasOne("Entites.Models.Post", "Post") + .WithMany("Likes") + .HasForeignKey("PostId"); + + b.HasOne("Entites.Models.User", "User") + .WithMany("Like") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Entites.Models.PostPhoto", b => + { + b.HasOne("Entites.Models.Post", "Post") + .WithMany("PostPhotos") + .HasForeignKey("PostId"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Entites.Models.Profile", b => + { + b.HasOne("Entites.Models.User", "User") + .WithOne("Profile") + .HasForeignKey("Entites.Models.Profile", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + 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("Entites.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Entites.Models.User", 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("Entites.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Entites.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Entites.Models.Post", b => + { + b.Navigation("Comments"); + + b.Navigation("Likes"); + + b.Navigation("PostPhotos"); + }); + + modelBuilder.Entity("Entites.Models.User", b => + { + b.Navigation("Like"); + + b.Navigation("Profile"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EveryPinApi/Migrations/20240131103624_AddMapAttribute.cs b/EveryPinApi/Migrations/20240131103624_AddMapAttribute.cs new file mode 100644 index 0000000..b1eeb4b --- /dev/null +++ b/EveryPinApi/Migrations/20240131103624_AddMapAttribute.cs @@ -0,0 +1,88 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace EveryPinApi.Migrations +{ + /// + public partial class AddMapAttribute : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "2179ba12-d152-45d3-9d76-d2f5eaca01cf"); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "8160263c-668a-4495-89dc-35b9296d3a0f"); + + migrationBuilder.AddColumn( + name: "Address", + table: "Posts", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "latitude", + table: "Posts", + type: "float", + nullable: true); + + migrationBuilder.AddColumn( + name: "longitude", + table: "Posts", + type: "float", + nullable: true); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { "923a3112-78df-4baf-8127-5aa87babcea7", null, "Administrator", "ADMINISTRATOR" }, + { "fafbb86f-5268-49e8-a8a0-c35be787f05a", null, "NormalUser", "NORMALUSER" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "923a3112-78df-4baf-8127-5aa87babcea7"); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: "fafbb86f-5268-49e8-a8a0-c35be787f05a"); + + migrationBuilder.DropColumn( + name: "Address", + table: "Posts"); + + migrationBuilder.DropColumn( + name: "latitude", + table: "Posts"); + + migrationBuilder.DropColumn( + name: "longitude", + table: "Posts"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { "2179ba12-d152-45d3-9d76-d2f5eaca01cf", null, "NormalUser", "NORMALUSER" }, + { "8160263c-668a-4495-89dc-35b9296d3a0f", null, "Administrator", "ADMINISTRATOR" } + }); + } + } +} diff --git a/EveryPinApi/Migrations/RepositoryContextModelSnapshot.cs b/EveryPinApi/Migrations/RepositoryContextModelSnapshot.cs index f3bad17..1ab87e2 100644 --- a/EveryPinApi/Migrations/RepositoryContextModelSnapshot.cs +++ b/EveryPinApi/Migrations/RepositoryContextModelSnapshot.cs @@ -108,6 +108,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("PostId")); + b.Property("Address") + .HasColumnType("nvarchar(max)"); + b.Property("CreatedDate") .HasColumnType("datetime2"); @@ -121,6 +124,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("latitude") + .HasColumnType("float"); + + b.Property("longitude") + .HasColumnType("float"); + b.HasKey("PostId"); b.ToTable("Posts"); @@ -241,6 +250,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PlatformCodeId") .HasColumnType("int"); + b.Property("RefreshToken") + .HasColumnType("nvarchar(max)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime2"); + b.Property("SecurityStamp") .HasColumnType("nvarchar(max)"); @@ -293,13 +308,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasData( new { - Id = "7936c632-4ef6-4ff9-9417-555941ceadd4", + Id = "fafbb86f-5268-49e8-a8a0-c35be787f05a", Name = "NormalUser", NormalizedName = "NORMALUSER" }, new { - Id = "c5127bc6-4ae3-40e2-b9c9-8c297f6810b1", + Id = "923a3112-78df-4baf-8127-5aa87babcea7", Name = "Administrator", NormalizedName = "ADMINISTRATOR" }); diff --git a/EveryPinApi/Program.cs b/EveryPinApi/Program.cs index 4cc59b2..460ba76 100644 --- a/EveryPinApi/Program.cs +++ b/EveryPinApi/Program.cs @@ -2,66 +2,96 @@ using Service; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Logging; - +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); +{ + // Add services to the container. + builder.Services.ConfigureCors(); // CORS + builder.Services.ConfigureRepositoryManager(); // RepositoryManager ߰ + builder.Services.ConfigureServiceManager(); // ServiceManager ߰ + builder.Services.ConfigureSqlContext(builder.Configuration); + + // Presentation Layer ControllerBase ϵ + builder.Services.AddControllers() + .AddApplicationPart(typeof(EveryPinApi.Presentation.AssemblyReference).Assembly); + + // Swagger/OpenAPI + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(option => + { + option.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Description = "Please enter a valid token", + Name = "Authorization", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT", + Scheme = JwtBearerDefaults.AuthenticationScheme + }); + option.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = JwtBearerDefaults.AuthenticationScheme + } + }, + new string[]{} + } + }); + }); + + // Azure Logging + builder.Logging.AddAzureWebAppDiagnostics(); + builder.Services.ConfigureLoggerFile(); + builder.Services.ConfigureLoggerBlob(); + + // Auth + builder.Services.AddAuthentication(); + builder.Services.ConfigureIdentity(); + builder.Services.ConfigureJWT(builder.Configuration); + + // AutoMapper + builder.Services.AddAutoMapper(typeof(Program)); +} - - -// Add services to the container. -builder.Services.ConfigureCors(); // CORS -builder.Services.ConfigureRepositoryManager(); // RepositoryManager ߰ -builder.Services.ConfigureServiceManager(); // ServiceManager ߰ -builder.Services.ConfigureSqlContext(builder.Configuration); - -// Presentation Layer ControllerBase ϵ -builder.Services.AddControllers() - .AddApplicationPart(typeof(EveryPinApi.Presentation.AssemblyReference).Assembly); - -// Swagger/OpenAPI -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); - -// Azure Logging -builder.Logging.AddAzureWebAppDiagnostics(); -builder.Services.ConfigureLoggerFile(); -builder.Services.ConfigureLoggerBlob(); - -// Auth -builder.Services.AddAuthentication(); -builder.Services.ConfigureIdentity(); -builder.Services.ConfigureJWT(builder.Configuration); - -// AutoMapper -builder.Services.AddAutoMapper(typeof(Program)); var app = builder.Build(); +{ + app.ConfigureExceptionHandler(app.Logger); -app.ConfigureExceptionHandler(app.Logger); + // Configure the HTTP request pipeline. + //if (app.Environment.IsDevelopment()) + //{ + // app.UseDeveloperExceptionPage(); + //} -// Configure the HTTP request pipeline. -//if (app.Environment.IsDevelopment()) -//{ -// app.UseDeveloperExceptionPage(); -//} + // Swagger + app.UseSwagger(); + app.UseSwaggerUI(); -// Swagger -app.UseSwagger(); -app.UseSwaggerUI(); + app.UseStaticFiles(); + app.UseForwardedHeaders(new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.All + }); -app.UseStaticFiles(); -app.UseForwardedHeaders(new ForwardedHeadersOptions -{ - ForwardedHeaders = ForwardedHeaders.All -}); + //app.UseDeveloperExceptionPage(); // 뵵. Ȱȭ , ۷ι α X -//app.UseDeveloperExceptionPage(); // 뵵. Ȱȭ , ۷ι α X + // auth + app.UseAuthentication(); + app.UseAuthorization(); -// auth -app.UseAuthentication(); -app.UseAuthorization(); + app.UseCors("CorsPolicy"); -app.UseCors("CorsPolicy"); + app.MapControllers(); +} -app.MapControllers(); app.Run(); diff --git a/Service.Contracts/Models/IAuthenticationService.cs b/Service.Contracts/Models/IAuthenticationService.cs index 96b6551..90a9d1b 100644 --- a/Service.Contracts/Models/IAuthenticationService.cs +++ b/Service.Contracts/Models/IAuthenticationService.cs @@ -5,12 +5,16 @@ using System.Text; using System.Threading.Tasks; using Shared.DataTransferObject; +using Shared.DataTransferObject.Auth; namespace Service.Contracts.Models { public interface IAuthenticationService { Task RegisterUser(RegistUserDto registUserDto); + Task ValidateUser(UserAutenticationDto userForAuths); + Task CreateToken(bool populateExp); + Task RefreshToken(TokenDto tokenDto); } } diff --git a/Service/Models/AuthenticationService.cs b/Service/Models/AuthenticationService.cs index 9863a86..8b3a429 100644 --- a/Service/Models/AuthenticationService.cs +++ b/Service/Models/AuthenticationService.cs @@ -7,22 +7,29 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Text; using System.Threading.Tasks; +using System.IdentityModel.Tokens.Jwt; +using Microsoft.IdentityModel.Tokens; +using Microsoft.Extensions.Logging; +using Shared.DataTransferObject.Auth; +using System.Security.Cryptography; namespace Service.Models { internal sealed class AuthenticationService : IAuthenticationService { - //private readonly ILoggerManager _logger; + private readonly ILogger _logger; private readonly IMapper _mapper; private readonly UserManager _userManager; private readonly IConfiguration _configuration; + private User? _user; - public AuthenticationService(IMapper mapper, - UserManager userManager, IConfiguration configuration) + public AuthenticationService(ILogger logger, IMapper mapper, + UserManager userManager, IConfiguration configuration) { - //_logger = logger; + _logger = logger; _mapper = mapper; _userManager = userManager; _configuration = configuration; @@ -36,6 +43,141 @@ public async Task RegisterUser(RegistUserDto registUserDto) await _userManager.AddToRolesAsync(user, registUserDto.Roles); return result; } - } + + public async Task ValidateUser(UserAutenticationDto userForAuth) + { + _user = await _userManager.FindByEmailAsync(userForAuth.Email); + //var result = (_user != null && await _userManager.CheckPasswordAsync(_user, userForAuth.Password)); + var result = _user != null; + + if (!result) + _logger.LogWarning($"{nameof(ValidateUser)}: 인증 실패. 잘못된 사용자 이름 또는 비밀번호."); + + return result; + } + + //public async Task CreateToken() + //{ + // var signingCredentials = GetSigningCredentials(); + // var claims = await GetClaims(); + // var tokenOptions = GenerateTokenOptions(signingCredentials, claims); + // return new JwtSecurityTokenHandler().WriteToken(tokenOptions); + //} + + public async Task CreateToken(bool populateExp) + { + var signingCredentials = GetSigningCredentials(); + var claims = await GetClaims(); + var tokenOptions = GenerateTokenOptions(signingCredentials, claims); + var refreshToken = GenerateRefreshToken(); + + _user.RefreshToken = refreshToken; + + if (populateExp) + _user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7); + + await _userManager.UpdateAsync(_user); + + var accessToken = new JwtSecurityTokenHandler().WriteToken(tokenOptions); + + return new TokenDto(accessToken, refreshToken); + } + + + private SigningCredentials GetSigningCredentials() + { + var key = Encoding.UTF8.GetBytes(_configuration.GetConnectionString("JwtSettings-SECRET")); + var secret = new SymmetricSecurityKey(key); + return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256); + } + + private async Task> GetClaims() + { + var claims = new List + { + new Claim(ClaimTypes.Email, _user.Email) + }; + + var roles = await _userManager.GetRolesAsync(_user); + foreach (var role in roles) + { + claims.Add(new Claim(ClaimTypes.Role, role)); + } + + return claims; + } + + private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, List claims) + { + var validIssuer = _configuration.GetConnectionString("JwtSettings-validIssuer"); + var validAudience = _configuration.GetConnectionString("JwtSettings-validAudience"); + var expire = _configuration.GetConnectionString("JwtSettings-expire"); + + var tokenOptions = new JwtSecurityToken + ( + issuer: validIssuer, + audience: validAudience, + claims: claims, + expires: DateTime.Now.AddMinutes(Convert.ToDouble(expire)), + signingCredentials: signingCredentials + ); + + return tokenOptions; + } + + private string GenerateRefreshToken() + { + var randomNumber = new byte[32]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomNumber); + return Convert.ToBase64String(randomNumber); + } + } + + private ClaimsPrincipal GetPrincipalFromExpiredToken(string token) + { + var validIssuer = _configuration.GetConnectionString("JwtSettings-validIssuer"); + var validAudience = _configuration.GetConnectionString("JwtSettings-validAudience"); + var key = Encoding.UTF8.GetBytes(_configuration.GetConnectionString("JwtSettings-SECRET")); + + var tokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = true, + ValidateIssuer = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateLifetime = true, + ValidIssuer = validIssuer, + ValidAudience = validAudience + }; + + var tokenHandler = new JwtSecurityTokenHandler(); + SecurityToken securityToken; + var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken); + var jwtSecurityToken = securityToken as JwtSecurityToken; + + if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) + { + throw new SecurityTokenException("Invalid token"); + } + + return principal; + } + + public async Task RefreshToken(TokenDto tokenDto) + { + var principal = GetPrincipalFromExpiredToken(tokenDto.AccessToken); + var user = await _userManager.FindByNameAsync(principal.Identity.Name); + + if (user == null || user.RefreshToken != tokenDto.RefreshToken || user.RefreshTokenExpiryTime <= DateTime.Now) + //throw new RefreshTokenBadRequest(); + throw new Exception("리프레시 토큰 오류"); + + _user = user; + return await CreateToken(populateExp: false); + } + + } } diff --git a/Service/Models/CommentService.cs b/Service/Models/CommentService.cs index 1c643c9..ade5298 100644 --- a/Service/Models/CommentService.cs +++ b/Service/Models/CommentService.cs @@ -14,11 +14,13 @@ namespace Service.Models { internal sealed class CommentService : ICommentService { + private readonly ILogger _logger; private readonly IRepositoryManager _repository; private readonly IMapper _mapper; - public CommentService(IRepositoryManager repository, IMapper mapper) + public CommentService(ILogger logger, IRepositoryManager repository, IMapper mapper) { + _logger = logger; _repository = repository; _mapper = mapper; } diff --git a/Service/Models/LikeService.cs b/Service/Models/LikeService.cs index ba5a3c9..84d4685 100644 --- a/Service/Models/LikeService.cs +++ b/Service/Models/LikeService.cs @@ -2,6 +2,7 @@ using Contracts.Repository; using Entites.Models; using Microsoft.Extensions.Logging; +using Service.Models; using Shared.DataTransferObject; using System; using System.Collections.Generic; @@ -14,11 +15,13 @@ namespace Service.Contracts.Models { internal sealed class LikeService : ILikeService { + private readonly ILogger _logger; private readonly IRepositoryManager _repository; private readonly IMapper _mapper; - public LikeService(IRepositoryManager repository, IMapper mapper) + public LikeService(ILogger logger, IRepositoryManager repository, IMapper mapper) { + _logger = logger; _repository = repository; _mapper = mapper; } diff --git a/Service/Models/PostPhotoService.cs b/Service/Models/PostPhotoService.cs index 42a5daf..3c32241 100644 --- a/Service/Models/PostPhotoService.cs +++ b/Service/Models/PostPhotoService.cs @@ -2,6 +2,7 @@ using Contracts.Repository; using Entites.Models; using Microsoft.Extensions.Logging; +using Service.Models; using Shared.DataTransferObject; using System; using System.Collections.Generic; @@ -14,11 +15,13 @@ namespace Service.Contracts.Models { internal sealed class PostPhotoService : IPostPhotoService { + private readonly ILogger _logger; private readonly IRepositoryManager _repository; private readonly IMapper _mapper; - public PostPhotoService(IRepositoryManager repository, IMapper mapper) + public PostPhotoService(ILogger logger, IRepositoryManager repository, IMapper mapper) { + _logger = logger; _repository = repository; _mapper = mapper; } diff --git a/Service/Models/PostService.cs b/Service/Models/PostService.cs index 200f867..7d40a39 100644 --- a/Service/Models/PostService.cs +++ b/Service/Models/PostService.cs @@ -2,6 +2,7 @@ using Contracts.Repository; using Entites.Models; using Microsoft.Extensions.Logging; +using Service.Models; using Shared.DataTransferObject; using System; using System.Collections.Generic; @@ -14,11 +15,13 @@ namespace Service.Contracts.Models { internal sealed class PostService : IPostService { + private readonly ILogger _logger; private readonly IRepositoryManager _repository; private readonly IMapper _mapper; - public PostService(IRepositoryManager repository, IMapper mapper) + public PostService(ILogger logger, IRepositoryManager repository, IMapper mapper) { + _logger = logger; _repository = repository; _mapper = mapper; } diff --git a/Service/Models/ProfileService.cs b/Service/Models/ProfileService.cs index a1fcc21..a775601 100644 --- a/Service/Models/ProfileService.cs +++ b/Service/Models/ProfileService.cs @@ -2,6 +2,7 @@ using Contracts.Repository; using Entites.Models; using Microsoft.Extensions.Logging; +using Service.Models; using Shared.DataTransferObject; using System; using System.Collections.Generic; @@ -14,11 +15,13 @@ namespace Service.Contracts.Models { internal sealed class ProfileService : IProfileService { + private readonly ILogger _logger; private readonly IRepositoryManager _repository; private readonly IMapper _mapper; - public ProfileService(IRepositoryManager repository, IMapper mapper) + public ProfileService(ILogger logger, IRepositoryManager repository, IMapper mapper) { + _logger = logger; _repository = repository; _mapper = mapper; } diff --git a/Service/Service.csproj b/Service/Service.csproj index 1bae81d..c25b22d 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -8,6 +8,7 @@ + diff --git a/Service/ServiceManager.cs b/Service/ServiceManager.cs index 9fe40e6..701266a 100644 --- a/Service/ServiceManager.cs +++ b/Service/ServiceManager.cs @@ -23,16 +23,17 @@ public sealed class ServiceManager : IServiceManager private readonly Lazy _postService; private readonly Lazy _profileService; private readonly Lazy _authenticationService; + - public ServiceManager(IRepositoryManager repositoryManager, IMapper mapper, UserManager userManager, IConfiguration configuration) + public ServiceManager(IRepositoryManager repositoryManager, IMapper mapper, UserManager userManager, IConfiguration configuration, ILoggerFactory loggerFactory) { - _commentService = new Lazy(() => new CommentService(repositoryManager, mapper)); - _likeService = new Lazy(() => new LikeService(repositoryManager, mapper)); - _postPhotoService = new Lazy(() => new PostPhotoService(repositoryManager, mapper)); - _postService = new Lazy(() => new PostService(repositoryManager, mapper)); - _profileService = new Lazy(() => new ProfileService(repositoryManager, mapper)); + _commentService = new Lazy(() => new CommentService(loggerFactory.CreateLogger(), repositoryManager, mapper)); + _likeService = new Lazy(() => new LikeService(loggerFactory.CreateLogger(), repositoryManager, mapper)); + _postPhotoService = new Lazy(() => new PostPhotoService(loggerFactory.CreateLogger(), repositoryManager, mapper)); + _postService = new Lazy(() => new PostService(loggerFactory.CreateLogger(), repositoryManager, mapper)); + _profileService = new Lazy(() => new ProfileService(loggerFactory.CreateLogger(), repositoryManager, mapper)); //_authenticationService = new Lazy(() => new AuthenticationService(logger, mapper, userManager, configuration)); - _authenticationService = new Lazy(() => new AuthenticationService(mapper, userManager, configuration)); + _authenticationService = new Lazy(() => new AuthenticationService(loggerFactory.CreateLogger(), mapper, userManager, configuration)); } public ICommentService CommentService => _commentService.Value; diff --git a/Shared/DataTransferObject/Auth/TokenDto.cs b/Shared/DataTransferObject/Auth/TokenDto.cs new file mode 100644 index 0000000..adab6b1 --- /dev/null +++ b/Shared/DataTransferObject/Auth/TokenDto.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Shared.DataTransferObject.Auth +{ + public record TokenDto(string AccessToken, string RefreshToken); +} diff --git a/Shared/DataTransferObject/Auth/UserAutenticationDto.cs b/Shared/DataTransferObject/Auth/UserAutenticationDto.cs new file mode 100644 index 0000000..8c444f1 --- /dev/null +++ b/Shared/DataTransferObject/Auth/UserAutenticationDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Shared.DataTransferObject.Auth +{ + public record UserAutenticationDto + { + public string? UserId { get; init; } + public string? Email { get; init; } + public string? PhoneNumber { get; init; } + } +}