add-api-project #29

Merged
jleicher merged 8 commits from add-api-project into master 2020-12-22 14:01:59 +01:00
9 changed files with 251 additions and 57 deletions
Showing only changes of commit 0ead15ce5a - Show all commits

20
.vscode/launch.json vendored
View File

@ -4,6 +4,26 @@
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"name": ".NET Core Launch (api)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Apps/Api/bin/Debug/net5.0/Api.dll",
"args": [],
"cwd": "${workspaceFolder}/Apps/Api",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{ {
"name": ".NET Core Launch (web)", "name": ".NET Core Launch (web)",
"type": "coreclr", "type": "coreclr",

View File

@ -11,6 +11,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" />
<PackageReference Include="NSwag.AspNetCore" Version="13.9.4" /> <PackageReference Include="NSwag.AspNetCore" Version="13.9.4" />
<PackageReference Include="NSwag.MSBuild" Version="13.9.4"> <PackageReference Include="NSwag.MSBuild" Version="13.9.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -0,0 +1,70 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Api.Models;
using HN.Infrastructure.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
namespace Api.Controllers
{
[ApiController]
[Route("api/[controller]")]
public sealed class AccountsController : ControllerBase
{
private readonly UserManager<User> _usersManager;
private readonly SignInManager<User> _signinManager;
private readonly TokenValidationParameters _tokenParameters;
public AccountsController(UserManager<User> usersManager, SignInManager<User> signinManager, TokenValidationParameters tokenParameters)
{
_usersManager = usersManager;
_signinManager = signinManager;
_tokenParameters = tokenParameters;
}
[Authorize]
public IActionResult GetUsers()
{
return Ok(_usersManager.Users.ToArray());
}
[HttpPost("login")]
public async Task<IActionResult> Login(LoginViewModel command)
{
var user = await _usersManager.FindByNameAsync(command.Username);
if (user == null)
{
return NotFound();
}
var result = await _signinManager.CheckPasswordSignInAsync(user, command.Password, false);
if (!result.Succeeded)
{
return BadRequest();
}
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, command.Username),
}),
Expires = DateTime.UtcNow.AddDays(7),
Issuer = _tokenParameters.ValidIssuer,
Audience = _tokenParameters.ValidAudience,
SigningCredentials = new SigningCredentials(_tokenParameters.IssuerSigningKey, SecurityAlgorithms.HmacSha256Signature)
};
return Ok(new JwtSecurityTokenHandler().CreateEncodedJwt(tokenDescriptor));
}
}
}

View File

@ -102,7 +102,7 @@ namespace Api.Controllers
{ {
var result = await _bus.Send(command); var result = await _bus.Send(command);
return CreatedAtAction(nameof(GetLinkById), new { id = result }); return CreatedAtAction(nameof(GetLinkById), new { id = result }, null);
} }
} }
} }

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Api.Models
{
public sealed class LoginViewModel
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
}

View File

@ -1,24 +1,19 @@
using System; using System.Text;
using HN.Application;
using HN.Infrastructure; using HN.Infrastructure;
using HN.Infrastructure.Identity;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
namespace Api namespace Api
{ {
public class MockExecutingContext : IExecutingUserProvider
{
public Guid GetCurrentUserId()
{
return Guid.NewGuid();
}
}
public class Startup public class Startup
{ {
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
@ -32,7 +27,7 @@ namespace Api
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddHN(Configuration).ResolveConnectedUserWith<MockExecutingContext>(); services.AddHN(Configuration).ResolveConnectedUserWith<HttpExecutingUserProvider>();
services.AddHttpContextAccessor(); services.AddHttpContextAccessor();
// Permet d'avoir des routes en lowercase // Permet d'avoir des routes en lowercase
@ -42,6 +37,31 @@ namespace Api
options.LowercaseQueryStrings = true; options.LowercaseQueryStrings = true;
}); });
// Ajout de l'authentification
var tokenParams = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]))
};
services.AddSingleton(tokenParams);
services.AddIdentityCore<User>()
.AddRoles<Role>()
.AddEntityFrameworkStores<HNDbContext>()
.AddSignInManager();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenParams;
});
services.AddControllers(); services.AddControllers();
services.AddSwaggerDocument(d => services.AddSwaggerDocument(d =>
{ {
@ -66,6 +86,9 @@ namespace Api
app.UseRouting(); app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();

View File

@ -9,5 +9,8 @@
"ConnectionStrings": { "ConnectionStrings": {
"Default": "Data Source=../Website/hn.db" "Default": "Data Source=../Website/hn.db"
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"JwtIssuer": "http://localhost",
"JwtAudience": "http://localhost",
"JwtSecurityKey": "CTtgxbcSFbpJmdmLDnr3Y8h5RWseN7t5"
} }

View File

@ -6,6 +6,60 @@
"version": "1.0.0" "version": "1.0.0"
}, },
"paths": { "paths": {
"/api/accounts": {
"get": {
"tags": [
"Accounts"
],
"operationId": "Accounts_GetUsers",
"responses": {
"200": {
"description": "",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
}
}
}
},
"/api/accounts/login": {
"post": {
"tags": [
"Accounts"
],
"operationId": "Accounts_Login",
"requestBody": {
"x-name": "command",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoginViewModel"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
}
}
}
},
"/api/links": { "/api/links": {
"get": { "get": {
"tags": [ "tags": [
@ -232,6 +286,24 @@
}, },
"components": { "components": {
"schemas": { "schemas": {
"LoginViewModel": {
"type": "object",
"additionalProperties": false,
"required": [
"username",
"password"
],
"properties": {
"username": {
"type": "string",
"minLength": 1
},
"password": {
"type": "string",
"minLength": 1
}
}
},
"LinkDto": { "LinkDto": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,

View File

@ -65,15 +65,7 @@ namespace Website.Controllers
return View(); return View();
} }
var user = await _userManager.FindByNameAsync(command.Username); var result = await _signInManager.PasswordSignInAsync(command.Username, command.Password, true, false);
if (user == null)
{
ModelState.AddModelError(nameof(LoginViewModel.Username), "Could not verify user identity");
return View();
}
var result = await _signInManager.PasswordSignInAsync(user, command.Password, true, false);
if (!result.Succeeded) if (!result.Succeeded)
{ {