Move samples to Entropy (#120)
This commit is contained in:
parent
aeab73f9f6
commit
829050ec31
|
|
@ -7,4 +7,8 @@ Travis: [](h
|
|||
|
||||
Antiforgery system for generating secure tokens to prevent Cross-Site Request Forgery attacks.
|
||||
|
||||
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
|
||||
This project is part of ASP.NET Core. You can find documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
|
||||
|
||||
Samples can be found in [Entropy](https://github.com/aspnet/Entropy).
|
||||
The [MVC](https://github.com/aspnet/Entropy/tree/dev/samples/Antiforgery.MvcWithAuthAndAjax) sample shows how to use Antiforgery in MVC when making AJAX requests.
|
||||
The [Angular](https://github.com/aspnet/Entropy/tree/dev/samples/Antiforgery.Angular1) sample shows how to use Antiforgery with Angular 1.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp1.1</TargetFrameworks>
|
||||
<RuntimeIdentifier Condition=" '$(TargetFramework)' != 'netcoreapp1.1' ">win7-x64</RuntimeIdentifier>
|
||||
<OutputType>Exe</OutputType>
|
||||
<NetCoreAppImplicitPackageVersion>1.2.0-*</NetCoreAppImplicitPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\**\*;web.config" CopyToPublishDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Antiforgery\Microsoft.AspNetCore.Antiforgery.csproj" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.2.0-*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="1.2.0-*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.2.0-*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.2.0-*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.2.0-*" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AntiforgerySample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Angular's default header name for sending the XSRF token.
|
||||
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
|
||||
|
||||
services.AddSingleton<TodoRepository>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery, IOptions<AntiforgeryOptions> options, TodoRepository repository)
|
||||
{
|
||||
app.Use(next => context =>
|
||||
{
|
||||
if (
|
||||
string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(context.Request.Path.Value, "/index.html", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// We can send the request token as a JavaScript-readable cookie, and Angular will use it by default.
|
||||
var tokens = antiforgery.GetAndStoreTokens(context);
|
||||
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
|
||||
}
|
||||
|
||||
return next(context);
|
||||
});
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.Map("/api/items", a => a.Run(async context =>
|
||||
{
|
||||
if (string.Equals("GET", context.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var items = repository.GetItems();
|
||||
await context.Response.WriteAsync(JsonConvert.SerializeObject(items));
|
||||
}
|
||||
else if (string.Equals("POST", context.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// This will throw if the token is invalid.
|
||||
await antiforgery.ValidateRequestAsync(context);
|
||||
|
||||
var serializer = new JsonSerializer();
|
||||
using (var reader = new JsonTextReader(new StreamReader(context.Request.Body)))
|
||||
{
|
||||
var item = serializer.Deserialize<TodoItem>(reader);
|
||||
repository.Add(item);
|
||||
}
|
||||
|
||||
context.Response.StatusCode = 204;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AntiforgerySample
|
||||
{
|
||||
public class TodoItem
|
||||
{
|
||||
[JsonProperty(PropertyName = "name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AntiforgerySample
|
||||
{
|
||||
public class TodoRepository
|
||||
{
|
||||
private List<TodoItem> _items;
|
||||
|
||||
public TodoRepository()
|
||||
{
|
||||
_items = new List<TodoItem>()
|
||||
{
|
||||
new TodoItem() { Name = "Mow the lawn" },
|
||||
new TodoItem() { Name = "Do the dishes" },
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<TodoItem> GetItems()
|
||||
{
|
||||
return _items;
|
||||
}
|
||||
|
||||
public void Add(TodoItem item)
|
||||
{
|
||||
_items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
|
||||
</handlers>
|
||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8" />
|
||||
<title>Todo List Antiforgery Sample</title>
|
||||
<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css" />
|
||||
</head>
|
||||
<body ng-app="TODO" ng-controller="todoController">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<h1>Todo List Antiforgery Sample</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th colspan="2">TODO List</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in itemList">
|
||||
<td>{{$index + 1}}</td>
|
||||
<td>
|
||||
{{item.name}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="name">New Item:</label>
|
||||
<input type="text" class="form-control" id="name" placeholder="(Enter Todo Item)" ng-model="item.name">
|
||||
</div>
|
||||
<button class="btn btn-default" value="Create" ng-click="create(item)">Create</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//code.angularjs.org/1.4.8/angular.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="services.js"></script>
|
||||
<script src="controllers.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
angular.module('TODO', [
|
||||
'TODO.controllers',
|
||||
'TODO.services'
|
||||
]);
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
angular.module('TODO.controllers', []).
|
||||
controller('todoController', function ($scope, todoApi) {
|
||||
$scope.itemList = [];
|
||||
$scope.item = {};
|
||||
|
||||
$scope.refresh = function (item) {
|
||||
todoApi.getItems().success(function (response) {
|
||||
$scope.itemList = response;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.create = function (item) {
|
||||
todoApi.create(item).success(function (response) {
|
||||
$scope.item = {};
|
||||
$scope.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
// Load initial items
|
||||
$scope.refresh();
|
||||
});
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
angular.module('TODO.services', []).
|
||||
factory('todoApi', function ($http) {
|
||||
|
||||
var todoApi = {};
|
||||
|
||||
todoApi.getItems = function () {
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: '/api/items'
|
||||
});
|
||||
}
|
||||
|
||||
todoApi.create = function (item) {
|
||||
return $http({
|
||||
method: 'POST',
|
||||
url: '/api/items',
|
||||
data: item
|
||||
});
|
||||
};
|
||||
|
||||
return todoApi;
|
||||
});
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||
{
|
||||
public class AntiForgerySampleTestFixture : IDisposable
|
||||
{
|
||||
private readonly TestServer _server;
|
||||
|
||||
public AntiForgerySampleTestFixture()
|
||||
{
|
||||
var configurationBuilder = new ConfigurationBuilder();
|
||||
|
||||
configurationBuilder.AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("webroot", "wwwroot")
|
||||
});
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.UseConfiguration(configurationBuilder.Build())
|
||||
.UseStartup(typeof(AntiforgerySample.Startup));
|
||||
|
||||
_server = new TestServer(builder);
|
||||
|
||||
Client = _server.CreateClient();
|
||||
Client.BaseAddress = new Uri("http://localhost");
|
||||
}
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Client.Dispose();
|
||||
_server.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||
{
|
||||
public class AntiforgerySampleTests : IClassFixture<AntiForgerySampleTestFixture>
|
||||
{
|
||||
public AntiforgerySampleTests(AntiForgerySampleTestFixture fixture)
|
||||
{
|
||||
Client = fixture.Client;
|
||||
}
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Fact]
|
||||
public async Task ItemsPage_SetsXSRFTokens()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync("http://localhost/Index.html");
|
||||
|
||||
// Assert
|
||||
var setCookieHeaderValue = RetrieveAntiforgeryCookie(response);
|
||||
Assert.NotNull(setCookieHeaderValue);
|
||||
Assert.False(string.IsNullOrEmpty(setCookieHeaderValue.Value));
|
||||
Assert.Null(setCookieHeaderValue.Domain);
|
||||
Assert.Equal("/", setCookieHeaderValue.Path);
|
||||
Assert.False(setCookieHeaderValue.Secure);
|
||||
|
||||
setCookieHeaderValue = RetrieveAntiforgeryToken(response);
|
||||
Assert.NotNull(setCookieHeaderValue);
|
||||
Assert.False(string.IsNullOrEmpty(setCookieHeaderValue.Value));
|
||||
Assert.Null(setCookieHeaderValue.Domain);
|
||||
Assert.Equal("/", setCookieHeaderValue.Path);
|
||||
Assert.False(setCookieHeaderValue.Secure);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostItem_NeedsHeader()
|
||||
{
|
||||
// Arrange
|
||||
var httpResponse = await Client.GetAsync("http://localhost");
|
||||
var cookie = RetrieveAntiforgeryCookie(httpResponse);
|
||||
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items");
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<AntiforgeryValidationException>(async () =>
|
||||
{
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Contains($"The required antiforgery cookie \"{cookie.Name}\" is not present.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostItem_XSRFWorks()
|
||||
{
|
||||
// Arrange
|
||||
var httpResponse = await Client.GetAsync("/Index.html");
|
||||
|
||||
var cookie = RetrieveAntiforgeryCookie(httpResponse);
|
||||
var token = RetrieveAntiforgeryToken(httpResponse);
|
||||
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items");
|
||||
|
||||
httpRequestMessage.Headers.Add("Cookie", $"{cookie.Name}={cookie.Value}");
|
||||
httpRequestMessage.Headers.Add("X-XSRF-TOKEN", token.Value);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||
}
|
||||
|
||||
private static SetCookieHeaderValue RetrieveAntiforgeryToken(HttpResponseMessage response)
|
||||
{
|
||||
return response.Headers.GetValues(HeaderNames.SetCookie)
|
||||
.Select(setCookieValue => SetCookieHeaderValue.Parse(setCookieValue))
|
||||
.Where(setCookieHeaderValue => setCookieHeaderValue.Name == "XSRF-TOKEN")
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static SetCookieHeaderValue RetrieveAntiforgeryCookie(HttpResponseMessage response)
|
||||
{
|
||||
return response.Headers.GetValues(HeaderNames.SetCookie)
|
||||
.Select(setCookieValue => SetCookieHeaderValue.Parse(setCookieValue))
|
||||
.Where(setCookieHeaderValue => setCookieHeaderValue.Name.StartsWith(".AspNetCore.Antiforgery."))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp1.1;net451</TargetFrameworks>
|
||||
<RuntimeIdentifier Condition=" '$(TargetFramework)' != 'netcoreapp1.1' ">win7-x64</RuntimeIdentifier>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<PublicSign>false</PublicSign>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\samples\AntiforgerySample\AntiforgerySample.csproj" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-*" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="1.2.0-*" />
|
||||
<PackageReference Include="Moq" Version="4.6.36-*" />
|
||||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue