Skip to content

Commit

Permalink
First release with example app (#7)
Browse files Browse the repository at this point in the history
* added nuspec

* switch back to csproj from nuspec

* added some help to disable default options

* refactor spv

* refactored tests

* updated ver num

* added some doc comments, renamed NoDefaults for clarity's sake

* Sample app (#6)

* bootsrapped example app

* added basic auth scheme

* replaced example app with sample app

* updated nuget pkg

* updated nuget

* updated to stable

* Update README.md

added usage and installation sections
  • Loading branch information
dim5 authored Jul 5, 2019
1 parent 5c519d8 commit 5db6d89
Show file tree
Hide file tree
Showing 15 changed files with 252 additions and 25 deletions.
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,26 @@ encourages the use of easy to remember, yet secure passphrases instead of hard t

## Installation

Using nuget

coming soon
Using the dotnet-cli
```bash
dotnet add package StanfordPasswordPolicy
```
or with the nuget package manager console:
```bash
Install-Package StanfordPasswordPolicy
```

## Usage

coming soon
```csharp
services.AddIdentity<AppUser, IdentityRole>(opt =>
{
// If you don't want Identity's defaults to interfere with your new policy
opt.Password = StanfordPasswordValidatorBase.NoDefaultPasswordOptions;
})
.AddPasswordValidator<StanfordPasswordValidator<AppUser>>();
```
You can also check out SampleApp for a more complete example.

## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Expand Down
29 changes: 29 additions & 0 deletions SampleApp/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using SampleApp.Models;
using SampleApp.Models.DTO;

namespace SampleApp.Controllers
{
[Route("api/[action]")]
[ApiController]
public class AccountController : ControllerBase
{
// GET api/values
private readonly UserManager<AppUser> _userManager;

public AccountController(UserManager<AppUser> userManager)
{
_userManager = userManager;
}

[HttpPost]
public async Task<IActionResult> Register([FromBody] AppUserDTO userDto)
{
var user = new AppUser {UserName = userDto.Username};
var registrationResult = await _userManager.CreateAsync(user, userDto.Password);
return registrationResult.Succeeded ? Ok() : (IActionResult) BadRequest(registrationResult.Errors);
}
}
}
14 changes: 14 additions & 0 deletions SampleApp/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.EntityFrameworkCore;
using SampleApp.Models;

namespace SampleApp.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}

public DbSet<AppUser> AppUsers { get; set; }
}
}
8 changes: 8 additions & 0 deletions SampleApp/Models/AppUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Identity;

namespace SampleApp.Models
{
public class AppUser : IdentityUser
{
}
}
8 changes: 8 additions & 0 deletions SampleApp/Models/DTO/AppUserDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SampleApp.Models.DTO
{
public class AppUserDTO
{
public string Username { get; set; }
public string Password { get; set; }
}
}
17 changes: 17 additions & 0 deletions SampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace SampleApp
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
14 changes: 14 additions & 0 deletions SampleApp/SampleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="StanfordPasswordPolicy" Version="1.0.0" />
</ItemGroup>

</Project>
51 changes: 51 additions & 0 deletions SampleApp/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SampleApp.Data;
using SampleApp.Models;
using StanfordPasswordPolicy;

namespace SampleApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(opt => opt.UseInMemoryDatabase(databaseName: "SampleAppDb"));

services.AddIdentity<AppUser, IdentityRole>(opt =>
{
opt.Password = StanfordPasswordValidatorBase.NoDefaultPasswordOptions;
}
)
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders()
.AddPasswordValidator<StanfordPasswordValidator<AppUser>>();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseMvc();
}
}
}
9 changes: 9 additions & 0 deletions SampleApp/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
8 changes: 8 additions & 0 deletions SampleApp/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
6 changes: 6 additions & 0 deletions StanfordPasswordPolicy.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StanfordPasswordPolicy", "S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{9FBADD1F-7AB7-4A83-9456-458D3773BD11}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp", "SampleApp\SampleApp.csproj", "{102779CA-963C-4826-AA96-0ECFB4D4FF33}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -21,6 +23,10 @@ Global
{9FBADD1F-7AB7-4A83-9456-458D3773BD11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FBADD1F-7AB7-4A83-9456-458D3773BD11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FBADD1F-7AB7-4A83-9456-458D3773BD11}.Release|Any CPU.Build.0 = Release|Any CPU
{102779CA-963C-4826-AA96-0ECFB4D4FF33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{102779CA-963C-4826-AA96-0ECFB4D4FF33}.Debug|Any CPU.Build.0 = Debug|Any CPU
{102779CA-963C-4826-AA96-0ECFB4D4FF33}.Release|Any CPU.ActiveCfg = Release|Any CPU
{102779CA-963C-4826-AA96-0ECFB4D4FF33}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
24 changes: 24 additions & 0 deletions StanfordPasswordPolicy/StanfordPasswordPolicy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,34 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<Authors>Daniel Marczin</Authors>
<Company />
<Copyright>Daniel Marczin, 2019</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/dim5/stanford-password-policy-dotnet</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageProjectUrl>https://github.com/dim5/stanford-password-policy-dotnet</PackageProjectUrl>
<Description>Stanford-password-policy-dotnet is a password validator library for ASP.NET Core.
The Stanford password policy is a dynamic password policy that encourages the use of easy to remember, yet secure passphrases instead of hard to remember passwords.</Description>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Version>1.0.0</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageTags>stanford password policy validator</PackageTags>
<PackageReleaseNotes>First stable release.</PackageReleaseNotes>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="2.2.0" />
</ItemGroup>

<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>

</Project>
23 changes: 6 additions & 17 deletions StanfordPasswordPolicy/StanfordPasswordValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@

namespace StanfordPasswordPolicy
{
public class StanfordPasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
public sealed class StanfordPasswordValidator<TUser> : StanfordPasswordValidatorBase, IPasswordValidator<TUser> where TUser : class
{
public static class ErrorCode
{
public static readonly string ShortLength = "ShortPassword";
public static readonly string NoSymbol = "NoSymbols";
public static readonly string NoNumber = "NoNumbers";
public static readonly string NoMixedCase = "NotMixedCase";
}

public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
/// <summary>
/// Validates a password according to the Stanford Password Policy
/// </summary>
/// <returns>IdentityResult with an array of IdentityErrors if the password isn't valid, else with IdentityResult.Success</returns>
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
{
var errors = new List<IdentityError>();

Expand Down Expand Up @@ -58,12 +54,5 @@ public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user
var res = errors.Any() ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
return Task.FromResult(res);
}

private static bool CheckMixedCase(string password) =>
password.Any(char.IsUpper) && password.Any(char.IsLower);

private static bool CheckNumber(string password) => password.Any(char.IsNumber);

private static bool CheckSymbol(string password) => !password.All(char.IsLetterOrDigit);
}
}
37 changes: 37 additions & 0 deletions StanfordPasswordPolicy/StanfordPasswordValidatorBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Linq;
using Microsoft.AspNetCore.Identity;

namespace StanfordPasswordPolicy
{
public abstract class StanfordPasswordValidatorBase
{
public static class ErrorCode
{
public static readonly string ShortLength = "ShortPassword";
public static readonly string NoSymbol = "NoSymbols";
public static readonly string NoNumber = "NoNumbers";
public static readonly string NoMixedCase = "NotMixedCase";
}

/// <summary>
/// A clear set of PasswordOptions, to reset Identity's defaults, since StanfordPasswordValidator ignores these options.
/// </summary>
public static PasswordOptions NoDefaultPasswordOptions =>
new PasswordOptions
{
RequireDigit = false,
RequiredLength = 0,
RequiredUniqueChars = 1,
RequireLowercase = false,
RequireNonAlphanumeric = false,
RequireUppercase = false
};

protected static bool CheckMixedCase(string password) =>
password.Any(char.IsUpper) && password.Any(char.IsLower);

protected static bool CheckNumber(string password) => password.Any(char.IsNumber);

protected static bool CheckSymbol(string password) => !password.All(char.IsLetterOrDigit);
}
}
8 changes: 4 additions & 4 deletions Test/SPPUnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public async Task TestCorrectAsync(string password)
[Fact]
public async Task TestLengthAsync()
{
var errorCode = StanfordPasswordValidator<PocoUser>.ErrorCode.ShortLength;
var errorCode = StanfordPasswordValidatorBase.ErrorCode.ShortLength;
var password = string.Empty;
IdentityResultAssert.IsFailure(await validator.ValidateAsync(manager, user, password),
errorCode);
Expand All @@ -56,7 +56,7 @@ public async Task TestLengthAsync()
[Fact]
public async Task TestSymbolAsync()
{
var errorCode = StanfordPasswordValidator<PocoUser>.ErrorCode.NoSymbol;
var errorCode = StanfordPasswordValidatorBase.ErrorCode.NoSymbol;
var password = "aaa";
IdentityResultAssert.IsFailure(await validator.ValidateAsync(manager, user, password),
errorCode);
Expand All @@ -74,7 +74,7 @@ public async Task TestSymbolAsync()
[Fact]
public async Task TestNumbersAsync()
{
var errorCode = StanfordPasswordValidator<PocoUser>.ErrorCode.NoNumber;
var errorCode = StanfordPasswordValidatorBase.ErrorCode.NoNumber;
var password = "aaa";
IdentityResultAssert.IsFailure(await validator.ValidateAsync(manager, user, password),
errorCode);
Expand All @@ -91,7 +91,7 @@ public async Task TestNumbersAsync()
[Fact]
public async Task TestMixedAsync()
{
var errorCode = StanfordPasswordValidator<PocoUser>.ErrorCode.NoMixedCase;
var errorCode = StanfordPasswordValidatorBase.ErrorCode.NoMixedCase;
var password = "aaa";
IdentityResultAssert.IsFailure(await validator.ValidateAsync(manager, user, password),
errorCode);
Expand Down

0 comments on commit 5db6d89

Please sign in to comment.