Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • bad1/assignment2
1 result
Show changes
Commits on Source (2)
Showing
with 1673 additions and 81 deletions
using assignment1.Models;
using assignment1.DTO;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace assignment1.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly UserManager<ApiUser> _userManager;
private readonly IConfiguration _configuration;
public AuthController(UserManager<ApiUser> userManager, IConfiguration configuration)
{
_userManager = userManager;
_configuration = configuration;
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterDTO model)
{
var user = new ApiUser
{
UserName = model.Email,
Email = model.Email
};
var result = await _userManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
return BadRequest(result.Errors);
await _userManager.AddToRoleAsync(user, model.Role);
return Ok("User created!");
}
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDTO model)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
return Unauthorized("Invalid credentials");
var userRoles = await _userManager.GetRolesAsync(user);
var authClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
foreach (var role in userRoles)
{
authClaims.Add(new Claim(ClaimTypes.Role, role));
}
var authSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_configuration["JWT:SigningKey"])
);
var token = new JwtSecurityToken(
issuer: _configuration["JWT:Issuer"],
audience: _configuration["JWT:Audience"],
expires: DateTime.Now.AddHours(3),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
}
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace
......@@ -16,72 +17,60 @@ namespace MyApp.Namespace
}
[HttpGet(Name = "ExperiencesGet")]
[AllowAnonymous] // Alle (også ikke-loggede) må se experiences – svarer til Query 2
public async Task<IActionResult> Get()
{
var result = await experienceService.GetExperiences();
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpGet("{name}", Name = "ExperienceGet")]
[AllowAnonymous] // Detaljevisning af én experience – tilladt for alle
public async Task<IActionResult> Get(string name)
{
var result = await experienceService.GetExperience(name);
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpGet("search")]
[AllowAnonymous] // Søgefunktion må bruges uden login
public async Task<IActionResult> Search(
[FromQuery] string? name,
[FromQuery] string? description,
[FromQuery] int? price)
{
var result = await experienceService.SearchExperience(name, description, price);
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpGet("statistics")]
[Authorize(Roles = "Admin,Manager")] // Statistik (min/avg/max pris) – kun ledelse. Svarer til Query 7
public async Task<IActionResult> GetStatistics()
{
var result = await experienceService.GetStatistics();
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpGet("sales")]
[Authorize(Roles = "Admin,Manager")] // Antal gæster og omsætning – følsomt, kun for Query 8
public async Task<IActionResult> GetSales()
{
var result = await experienceService.GetSales();
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpPost(Name = "ExperiencePost")]
[Authorize(Roles = "Admin,Manager")] // Kun autoriserede må oprette experiences
public async Task<IActionResult> Post([FromBody] CreateExperienceModel experience)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await experienceService.CreateExperience(experience);
if (!ModelState.IsValid) return BadRequest(ModelState);
var result = await experienceService.CreateExperience(experience);
if (result == null)
{
return BadRequest("Could not create experience, provider may be non existing.");
......@@ -91,30 +80,24 @@ namespace MyApp.Namespace
}
[HttpPut(Name = "UpdateExperience")]
[Authorize(Roles = "Admin,Manager")] // Kun ledelsen må opdatere experience-priser
public async Task<IActionResult> UpdatePrice([FromBody] UpdateExperienceModel experience)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!ModelState.IsValid) return BadRequest(ModelState);
var result = await experienceService.UpdateExperience(experience);
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpDelete("{id}")]
[Authorize(Roles = "Admin,Manager")] // Kun ledelsen må slette experiences
public async Task<IActionResult> Delete(int id)
{
var result = await experienceService.DeleteExperience(id);
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
}
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace assignment2.Controllers
......@@ -14,25 +15,23 @@ namespace assignment2.Controllers
}
[HttpGet(Name = "ProvidersGet")]
[Authorize(Roles = "Admin, Manager")]
// Returnerer alle providers – kun ledere/admins må se forretningsoplysninger (Query 1)
public async Task<IActionResult> Get()
{
var result = await providerService.GetProviders();
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
[HttpGet("{id}", Name = "ProviderGet")]
[Authorize(Roles = "Admin, Manager")]
// Returnerer én provider baseret på ID – også begrænset til ledelse (Query 1)
public async Task<IActionResult> Get(int id)
{
var result = await providerService.GetProvider(id);
if (result == null)
{
return NotFound();
}
if (result == null) return NotFound();
return Ok(result);
}
}
}
\ No newline at end of file
}
using assignment1.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace assignment1.Controllers
......@@ -15,27 +16,36 @@ namespace assignment1.Controllers
}
[HttpGet]
[AllowAnonymous]
// Returnerer alle shared experiences + dato – må ses af alle (Query 3)
public async Task<IActionResult> Get()
{
return Ok(await _sharedExperienceService.GetAll());
}
[HttpGet("guests/{id}")]
[Authorize(Roles = "Admin, Manager")]
// Returnerer gæster til én shared experience – kun synligt for ledere/admins (Query 4)
public async Task<IActionResult> GetGuests(int id)
{
return Ok(await _sharedExperienceService.GetGuests(id));
}
[HttpGet("experiences/{id}")]
[AllowAnonymous]
// Returnerer experiences i en shared experience – offentligt (Query 5)
public async Task<IActionResult> ExperiencesGet(int id)
{
return Ok(await _sharedExperienceService.GetExperiences(id));
}
[HttpGet("guestsInExperience/{id}/{experienceId}")]
[Authorize(Roles = "Admin, Manager")]
// Returnerer gæster i en specifik experience i en shared experience – kun ledelse (Query 6)
public async Task<IActionResult> GuestsInExperienceGet(int id, int experienceId)
{
return Ok(await _sharedExperienceService.GetGuestsInExperience(id, experienceId));
}
}
}
using Microsoft.EntityFrameworkCore;
using assignment2.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using assignment1.Models;
public class ExperienceDBContext : DbContext
public class ExperienceDBContext : IdentityDbContext<ApiUser>
{
private readonly string connString = Environment.GetEnvironmentVariable("DATABASE_CONNECTION_STRING") ?? "YouDoneFuckedUpEnvironmentVariable";
// ✅ Fallback til EF CLI, men bruger stadig ENV hvis det er sat
private readonly string connString = Environment.GetEnvironmentVariable("DATABASE_CONNECTION_STRING")
?? "Server=(localdb)\\MSSQLLocalDB;Database=ExperienceDb;Trusted_Connection=True;MultipleActiveResultSets=true";
public ExperienceDBContext(DbContextOptions<ExperienceDBContext> options) : base(options)
{
}
......@@ -21,32 +26,34 @@ public class ExperienceDBContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<SharedExperience>(se =>
{
se.HasMany(se => se.Experiences)
.WithMany(e => e.SharedExperiences)
.UsingEntity(t => t.ToTable("SharedExperiencesExperiences"));
.WithMany(e => e.SharedExperiences)
.UsingEntity(t => t.ToTable("SharedExperiencesExperiences"));
});
modelBuilder.Entity<Guest>(g =>
{
g.HasMany(g => g.Experiences)
.WithMany(e => e.Guests)
.UsingEntity(t => t.ToTable("GuestsExperiences"));
.WithMany(e => e.Guests)
.UsingEntity(t => t.ToTable("GuestsExperiences"));
});
modelBuilder.Entity<Guest>(g =>
{
g.HasMany(g => g.SharedExperiences)
.WithMany(se => se.Guests)
.UsingEntity(t => t.ToTable("GuestsSharedExperiences"));
.WithMany(se => se.Guests)
.UsingEntity(t => t.ToTable("GuestsSharedExperiences"));
});
modelBuilder.Entity<Guest> ( g =>
modelBuilder.Entity<Guest>(g =>
{
g.HasMany(g => g.Friends)
.WithMany(g => g.FriendsOf)
.UsingEntity<Dictionary<string, object>>(
.WithMany(g => g.FriendsOf)
.UsingEntity<Dictionary<string, object>>(
"GuestFriends",
j => j.HasOne<Guest>().WithMany().HasForeignKey("GuestId").OnDelete(DeleteBehavior.Restrict),
j => j.HasOne<Guest>().WithMany().HasForeignKey("FriendId").OnDelete(DeleteBehavior.Restrict),
......@@ -61,4 +68,5 @@ public class ExperienceDBContext : DbContext
});
});
}
}
\ No newline at end of file
}
using assignment1.Models;
using Microsoft.AspNetCore.Identity;
namespace assignment1.DB
{
public static class IdentitySeeder
{
public static async Task SeedRolesAndUsersAsync(UserManager<ApiUser> userManager, RoleManager<IdentityRole> roleManager)
{
string[] roles = { "Admin", "Manager", "Provider", "Guest" };
foreach (var role in roles)
{
if (!await roleManager.RoleExistsAsync(role))
{
await roleManager.CreateAsync(new IdentityRole(role));
}
}
var users = new[]
{
new { Email = "admin@example.com", Password = "Admin123!", Role = "Admin" },
new { Email = "manager@example.com", Password = "Manager123!", Role = "Manager" },
new { Email = "provider@example.com", Password = "Provider123!", Role = "Provider" },
new { Email = "guest@example.com", Password = "Guest123!", Role = "Guest" },
};
foreach (var userInfo in users)
{
var existingUser = await userManager.FindByEmailAsync(userInfo.Email);
if (existingUser == null)
{
var user = new ApiUser
{
UserName = userInfo.Email,
Email = userInfo.Email
};
var result = await userManager.CreateAsync(user, userInfo.Password);
if (result.Succeeded)
{
await userManager.AddToRoleAsync(user, userInfo.Role);
}
}
}
}
}
}
namespace assignment1.DTO
{
public class LoginDTO
{
public string Email { get; set; }
public string Password { get; set; }
}
}
namespace assignment1.DTO
{
public class RegisterDTO
{
public string Email { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
}
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace assignment1.Migrations
{
[DbContext(typeof(ExperienceDBContext))]
[Migration("20250404171511_IdentitySetup")]
partial class IdentitySetup
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Discount", b =>
{
b.Property<int>("DiscountId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("DiscountId"));
b.Property<int>("DiscountAmount")
.HasColumnType("int");
b.Property<int>("ExperienceId")
.HasColumnType("int");
b.Property<int>("MinimumParticipants")
.HasColumnType("int");
b.HasKey("DiscountId");
b.HasIndex("ExperienceId");
b.ToTable("Discounts");
});
modelBuilder.Entity("Experience", b =>
{
b.Property<int>("ExperienceId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ExperienceId"));
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Price")
.HasColumnType("int");
b.Property<int>("ProviderId")
.HasColumnType("int");
b.HasKey("ExperienceId");
b.HasIndex("ProviderId");
b.ToTable("Experiences");
});
modelBuilder.Entity("ExperienceGuest", b =>
{
b.Property<int>("ExperiencesExperienceId")
.HasColumnType("int");
b.Property<int>("GuestsGuestID")
.HasColumnType("int");
b.HasKey("ExperiencesExperienceId", "GuestsGuestID");
b.HasIndex("GuestsGuestID");
b.ToTable("GuestsExperiences", (string)null);
});
modelBuilder.Entity("ExperienceSharedExperience", b =>
{
b.Property<int>("ExperiencesExperienceId")
.HasColumnType("int");
b.Property<int>("SharedExperiencesSharedExperienceId")
.HasColumnType("int");
b.HasKey("ExperiencesExperienceId", "SharedExperiencesSharedExperienceId");
b.HasIndex("SharedExperiencesSharedExperienceId");
b.ToTable("SharedExperiencesExperiences", (string)null);
});
modelBuilder.Entity("GuestFriends", b =>
{
b.Property<int>("GuestId")
.HasColumnType("int");
b.Property<int>("FriendId")
.HasColumnType("int");
b.HasKey("GuestId", "FriendId");
b.HasIndex("FriendId");
b.ToTable("GuestFriends", t =>
{
t.HasCheckConstraint("CHK_IDS_ORDER", "(GuestId < FriendId)");
});
});
modelBuilder.Entity("GuestSharedExperience", b =>
{
b.Property<int>("GuestsGuestID")
.HasColumnType("int");
b.Property<int>("SharedExperiencesSharedExperienceId")
.HasColumnType("int");
b.HasKey("GuestsGuestID", "SharedExperiencesSharedExperienceId");
b.HasIndex("SharedExperiencesSharedExperienceId");
b.ToTable("GuestsSharedExperiences", (string)null);
});
modelBuilder.Entity("assignment2.Models.Guest", b =>
{
b.Property<int>("GuestID")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("GuestID"));
b.Property<int>("Age")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Phone")
.HasColumnType("int");
b.HasKey("GuestID");
b.ToTable("Guests");
});
modelBuilder.Entity("assignment2.Models.Provider", b =>
{
b.Property<int>("ProviderId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("ProviderId"));
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("Permitpdf")
.HasColumnType("bit");
b.Property<string>("Phone")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("ProviderId");
b.ToTable("Providers");
});
modelBuilder.Entity("assignment2.Models.SharedExperience", b =>
{
b.Property<int>("SharedExperienceId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("SharedExperienceId"));
b.Property<DateTimeOffset>("Date")
.HasColumnType("datetimeoffset");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.HasKey("SharedExperienceId");
b.ToTable("SharedExperiences");
});
modelBuilder.Entity("Discount", b =>
{
b.HasOne("Experience", "Experience")
.WithMany("Discounts")
.HasForeignKey("ExperienceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Experience");
});
modelBuilder.Entity("Experience", b =>
{
b.HasOne("assignment2.Models.Provider", "Provider")
.WithMany("Experiences")
.HasForeignKey("ProviderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Provider");
});
modelBuilder.Entity("ExperienceGuest", b =>
{
b.HasOne("Experience", null)
.WithMany()
.HasForeignKey("ExperiencesExperienceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("assignment2.Models.Guest", null)
.WithMany()
.HasForeignKey("GuestsGuestID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ExperienceSharedExperience", b =>
{
b.HasOne("Experience", null)
.WithMany()
.HasForeignKey("ExperiencesExperienceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("assignment2.Models.SharedExperience", null)
.WithMany()
.HasForeignKey("SharedExperiencesSharedExperienceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("GuestFriends", b =>
{
b.HasOne("assignment2.Models.Guest", null)
.WithMany()
.HasForeignKey("FriendId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("assignment2.Models.Guest", null)
.WithMany()
.HasForeignKey("GuestId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("GuestSharedExperience", b =>
{
b.HasOne("assignment2.Models.Guest", null)
.WithMany()
.HasForeignKey("GuestsGuestID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("assignment2.Models.SharedExperience", null)
.WithMany()
.HasForeignKey("SharedExperiencesSharedExperienceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Experience", b =>
{
b.Navigation("Discounts");
});
modelBuilder.Entity("assignment2.Models.Provider", b =>
{
b.Navigation("Experiences");
});
#pragma warning restore 612, 618
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace assignment1.Migrations
{
/// <inheritdoc />
public partial class IdentitySetup : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
This diff is collapsed.
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace assignment1.Migrations
{
/// <inheritdoc />
public partial class AddIdentitySupport : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "bit", nullable: false),
PasswordHash = table.Column<string>(type: "nvarchar(max)", nullable: true),
SecurityStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
PhoneNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "bit", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "bit", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
LockoutEnabled = table.Column<bool>(type: "bit", nullable: false),
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
RoleId = table.Column<string>(type: "nvarchar(450)", nullable: false),
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
ClaimValue = table.Column<string>(type: "nvarchar(max)", 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<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
UserId = table.Column<string>(type: "nvarchar(450)", nullable: false),
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
ClaimValue = table.Column<string>(type: "nvarchar(max)", 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<string>(type: "nvarchar(450)", nullable: false),
ProviderKey = table.Column<string>(type: "nvarchar(450)", nullable: false),
ProviderDisplayName = table.Column<string>(type: "nvarchar(max)", nullable: true),
UserId = table.Column<string>(type: "nvarchar(450)", 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<string>(type: "nvarchar(450)", nullable: false),
RoleId = table.Column<string>(type: "nvarchar(450)", 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<string>(type: "nvarchar(450)", nullable: false),
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
Name = table.Column<string>(type: "nvarchar(450)", nullable: false),
Value = table.Column<string>(type: "nvarchar(max)", 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,
filter: "[NormalizedName] IS NOT NULL");
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,
filter: "[NormalizedUserName] IS NOT NULL");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
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");
}
}
}
......@@ -137,6 +137,204 @@ namespace assignment1.Migrations
b.ToTable("GuestsSharedExperiences", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("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);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("assignment1.Models.ApiUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("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("assignment2.Models.Guest", b =>
{
b.Property<int>("GuestID")
......@@ -289,6 +487,57 @@ namespace assignment1.Migrations
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("assignment1.Models.ApiUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("assignment1.Models.ApiUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("assignment1.Models.ApiUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("assignment1.Models.ApiUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Experience", b =>
{
b.Navigation("Discounts");
......
using Microsoft.AspNetCore.Identity;
namespace assignment1.Models
{
public class ApiUser : IdentityUser
{
}
}
using assignment1.Interfaces;
using assignment1.Services;
using DotNetEnv;
using assignment2.Services;
using assignment1.Models;
using assignment1.DTO;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using ZiggyCreatures.Caching.Fusion;
using Microsoft.Extensions.Caching.Memory;
using Serilog;
using Microsoft.Extensions.Options;
using Serilog;
using DotNetEnv;
using System.Text;
using assignment1.DB;
using assignment1.Interfaces;
using assignment1.Services;
using assignment2.Services;
using assignment2.Models;
using assignment2.Interfaces;
using MongoDB.Bson;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Mongo + Logging
builder.Services.Configure<LogsDatabaseSettings>(
builder.Configuration.GetSection("LoggingDB"));
// Add services to the container.
builder.Services.AddControllers();
//Inject the Services
builder.Services.AddDbContext<ExperienceDBContext>();
// Identity + Role support
builder.Services.AddIdentity<ApiUser, IdentityRole>()
.AddEntityFrameworkStores<ExperienceDBContext>()
.AddDefaultTokenProviders();
// JWT authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JWT:Issuer"],
ValidAudience = builder.Configuration["JWT:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["JWT:SigningKey"]))
};
});
builder.Services.AddAuthorization();
// Controllers & services
builder.Services.AddControllers();
builder.Services.AddTransient<IExperienceService, ExperienceService>();
builder.Services.AddTransient<IProviderService, ProviderService>();
builder.Services.AddTransient<ISharedExperienceService, SharedExperienceService>();
builder.Services.AddTransient<IGuestService, GuestService>();
builder.Services.AddSingleton<ILogService, LogsService>();
// ENV support
Env.Load();
builder.Services.AddDbContext<ExperienceDBContext>();
// Caching
builder.Services.AddFusionCache().WithDefaultEntryOptions(new FusionCacheEntryOptions
{
Duration = TimeSpan.FromMinutes(2),
Priority = CacheItemPriority.Low
});
// Adds logger using appsettings.json for configuration
// Logging
builder.Services.AddSerilog(options => options.ReadFrom.Configuration(builder.Configuration));
// Enable Swagger/OpenAPI
// Swagger + JWT UI support
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Experience API",
Version = "v1",
Description = "An API to manage experiences",
Contact = new Microsoft.OpenApi.Models.OpenApiContact
Description = "An API to manage experiences"
});
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "Indtast din token"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
Name = "Vores awesome api",
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
var app = builder.Build();
// Seed the database (from lessson)
using (var scope = app.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ExperienceDBContext>();
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApiUser>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
IdentitySeeder.SeedRolesAndUsersAsync(userManager, roleManager).GetAwaiter().GetResult();
var seeder = new Seeder(context);
seeder.SeedData();
}
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Experience API v1");
c.RoutePrefix = ""; // Swagger loads at the root URL
c.RoutePrefix = "";
});
//app.UseSerilogRequestLogging
app.UseMiddleware<LoggerMiddleware>();
// Conditionally handle HTTPS redirection based on environment
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
Console.WriteLine("Starting the application on http://localhost:5095");
app.Run();
{
"JWT": {
"SigningKey": "SuperUltraMegaMuchSecretKey12345",
"Issuer": "ExperienceApp",
"Audience": "ExperienceUsers"
},
"LoggingDB": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "AssignmentLoggingDB",
......@@ -16,7 +21,7 @@
{
"Name": "MongoDBBson",
"Args": {
"databaseUrl": "mongodb://mongo:27017/AssignmentLoggingDB?appName=MongoDB+Compass&directConnection=true&serverSelectionTimeoutMS=2000",
"databaseUrl": "mongodb://mongo:27017/AssignmentLoggingDB",
"collectionName": "logs",
"cappedMaxSizeMb": "50",
"cappedMaxDocuments": "1000"
......@@ -27,3 +32,4 @@
},
"AllowedHosts": "*"
}
......@@ -9,6 +9,8 @@
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.2" />
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
......