Implement Config options

This commit is contained in:
Hao Kung 2014-05-05 15:39:34 -07:00
parent b623f725ba
commit ab84228c37
8 changed files with 122 additions and 149 deletions

View File

@ -20,11 +20,6 @@ namespace Microsoft.AspNet.Identity
UserName = ClaimTypes.Name;
}
public ClaimTypeOptions(IConfiguration config) : this()
{
IdentityOptions.Read(this, config);
}
/// <summary>
/// Claim type used for role claims
/// </summary>

View File

@ -18,53 +18,6 @@ namespace Microsoft.AspNet.Identity
Lockout = new LockoutOptions();
}
public IdentityOptions(IConfiguration config)
{
ClaimType = new ClaimTypeOptions(config.GetSubKey("identity:claimtype"));
User = new UserOptions(config.GetSubKey("identity:user"));
Password = new PasswordOptions(config.GetSubKey("identity:password"));
//Lockout = new LockoutOptions(config.GetSubKey("identity:lockout"));
}
public static void Read(object obj, IConfiguration config)
{
var type = obj.GetType();
var props = type.GetTypeInfo().DeclaredProperties;
foreach (var prop in props)
{
// TODO: handle non string types?
if (!prop.CanWrite)
{
continue;
}
var configValue = config.Get(prop.Name);
if (configValue == null)
{
continue;
}
if (prop.PropertyType == typeof(string))
{
prop.SetValue(obj, configValue);
}
else if (prop.PropertyType == typeof(int))
{
// todo: TryParse/ errors?
prop.SetValue(obj, int.Parse(configValue));
}
else if (prop.PropertyType == typeof(bool))
{
// todo: TryParse/ errors?
prop.SetValue(obj, bool.Parse(configValue));
}
//else if (prop.PropertyType == typeof(TimeSpan))
//{
// // todo: TryParse/ errors?
// prop.SetValue(obj, TimeSpan.Parse(configValue));
//}
}
}
public ClaimTypeOptions ClaimType { get; set; }
public UserOptions User { get; set; }

View File

@ -0,0 +1,64 @@
using System;
using System.Reflection;
using Microsoft.AspNet.ConfigurationModel;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Configuration for lockout
/// </summary>
public class IdentityOptionsSetup : IOptionsSetup<IdentityOptions>
{
private readonly IConfiguration _config;
public IdentityOptionsSetup(IConfiguration config)
{
_config = config;
}
public int Order { get; set; }
public void Setup(IdentityOptions options)
{
ReadProperties(options.ClaimType, _config.GetSubKey("identity:claimtype"));
ReadProperties(options.User, _config.GetSubKey("identity:user"));
ReadProperties(options.Password, _config.GetSubKey("identity:password"));
ReadProperties(options.Lockout, _config.GetSubKey("identity:lockout"));
}
// TODO: Move this somewhere common (Config?)
public static void ReadProperties(object obj, IConfiguration config)
{
if (obj == null || config == null)
{
return;
}
var props = obj.GetType().GetTypeInfo().DeclaredProperties;
foreach (var prop in props)
{
if (!prop.CanWrite)
{
continue;
}
var configValue = config.Get(prop.Name);
if (configValue == null)
{
continue;
}
try
{
// No convert on portable
#if NET45 || K10
prop.SetValue(obj, Convert.ChangeType(configValue, prop.PropertyType));
#endif
}
catch
{
// TODO: what do we do about errors?
}
}
}
}
}

View File

@ -28,6 +28,7 @@ namespace Microsoft.AspNet.DependencyInjection
{
services.Add(IdentityServices.GetDefaultUserServices<TUser>());
services.Add(IdentityServices.GetDefaultRoleServices<TRole>());
services.AddTransient<IOptionsSetup<IdentityOptions>, IdentityOptionsSetup>();
services.AddSingleton<IOptionsAccessor<IdentityOptions>, OptionsAccessor<IdentityOptions>>();
actionBuilder(new IdentityBuilder<TUser, TRole>(services));
return services;

View File

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
// NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing
// permissions and limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Configuration for lockout
/// </summary>
public class LockoutPolicy
{
/// <summary>
/// If true, will enable user lockout when users are created
/// </summary>
public bool UserLockoutEnabledByDefault { get; set; }
/// <summary>
/// Number of access attempts allowed for a user before lockout (if enabled)
/// </summary>
public int MaxFailedAccessAttemptsBeforeLockout { get; set; }
/// <summary>
/// Default amount of time an user is locked out for after MaxFailedAccessAttempsBeforeLockout is reached
/// </summary>
public TimeSpan DefaultAccountLockoutTimeSpan { get; set; }
}
}

View File

@ -13,11 +13,6 @@ namespace Microsoft.AspNet.Identity
RequiredLength = 6;
}
public PasswordOptions(IConfiguration config) : this()
{
IdentityOptions.Read(this, config);
}
/// <summary>
/// Minimum required length
/// </summary>

View File

@ -10,11 +10,6 @@ namespace Microsoft.AspNet.Identity
//User.RequireUniqueEmail = true; // TODO: app decision?
}
public UserOptions(IConfiguration config) : this()
{
IdentityOptions.Read(this, config);
}
/// <summary>
/// Only allow [A-Za-z0-9@_] in UserNames
/// </summary>

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNet.ConfigurationModel;
using Microsoft.AspNet.ConfigurationModel.Sources;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -58,11 +60,21 @@ namespace Microsoft.AspNet.Identity.Test
{"identity:password:RequireNonLetterOrDigit", "false"},
{"identity:password:RequireUpperCase", "false"},
{"identity:password:RequireDigit", "false"},
{"identity:password:RequireLowerCase", "false"}
{"identity:password:RequireLowerCase", "false"},
{"identity:lockout:EnabledByDefault", "TRUe"},
{"identity:lockout:MaxFailedAccessAttempts", "1000"}
};
var config = new ConfigurationModel.Configuration { new MemoryConfigurationSource(dic) };
Assert.Equal(roleClaimType, config.Get("identity:claimtype:role"));
var options = new IdentityOptions(config);
var setup = new IdentityOptionsSetup(config);
var services = new ServiceCollection();
services.AddInstance<IConfiguration>(config);
services.AddTransient<IOptionsSetup<IdentityOptions>, IdentityOptionsSetup>();
services.AddTransient<IOptionsAccessor<IdentityOptions>, OptionsAccessor<IdentityOptions>>();
var accessor = services.BuildServiceProvider().GetService<IOptionsAccessor<IdentityOptions>>();
Assert.NotNull(accessor);
var options = accessor.Options;
Assert.Equal(roleClaimType, options.ClaimType.Role);
Assert.Equal(useridClaimType, options.ClaimType.UserId);
Assert.Equal(usernameClaimType, options.ClaimType.UserName);
@ -75,51 +87,53 @@ namespace Microsoft.AspNet.Identity.Test
Assert.False(options.Password.RequireNonLetterOrDigit);
Assert.False(options.Password.RequireUppercase);
Assert.Equal(10, options.Password.RequiredLength);
Assert.True(options.Lockout.EnabledByDefault);
Assert.Equal(1000, options.Lockout.MaxFailedAccessAttempts);
}
[Fact]
public void ClaimTypeOptionsFromConfig()
{
const string roleClaimType = "rolez";
const string usernameClaimType = "namez";
const string useridClaimType = "idz";
const string securityStampClaimType = "stampz";
//[Fact]
//public void ClaimTypeOptionsFromConfig()
//{
// const string roleClaimType = "rolez";
// const string usernameClaimType = "namez";
// const string useridClaimType = "idz";
// const string securityStampClaimType = "stampz";
var dic = new Dictionary<string, string>
{
{"role", roleClaimType},
{"username", usernameClaimType},
{"userid", useridClaimType},
{"securitystamp", securityStampClaimType}
};
var config = new ConfigurationModel.Configuration {new MemoryConfigurationSource(dic)};
Assert.Equal(roleClaimType, config.Get("role"));
var options = new ClaimTypeOptions(config);
Assert.Equal(roleClaimType, options.Role);
Assert.Equal(useridClaimType, options.UserId);
Assert.Equal(usernameClaimType, options.UserName);
Assert.Equal(securityStampClaimType, options.SecurityStamp);
}
// var dic = new Dictionary<string, string>
// {
// {"role", roleClaimType},
// {"username", usernameClaimType},
// {"userid", useridClaimType},
// {"securitystamp", securityStampClaimType}
// };
// var config = new ConfigurationModel.Configuration {new MemoryConfigurationSource(dic)};
// Assert.Equal(roleClaimType, config.Get("role"));
// var options = new ClaimTypeOptions(config);
// Assert.Equal(roleClaimType, options.Role);
// Assert.Equal(useridClaimType, options.UserId);
// Assert.Equal(usernameClaimType, options.UserName);
// Assert.Equal(securityStampClaimType, options.SecurityStamp);
//}
[Fact]
public void PasswordOptionsFromConfig()
{
var dic = new Dictionary<string, string>
{
{"RequiredLength", "10"},
{"RequireNonLetterOrDigit", "false"},
{"RequireUpperCase", "false"},
{"RequireDigit", "false"},
{"RequireLowerCase", "false"}
};
var config = new ConfigurationModel.Configuration { new MemoryConfigurationSource(dic) };
var options = new PasswordOptions(config);
Assert.False(options.RequireDigit);
Assert.False(options.RequireLowercase);
Assert.False(options.RequireNonLetterOrDigit);
Assert.False(options.RequireUppercase);
Assert.Equal(10, options.RequiredLength);
}
//[Fact]
//public void PasswordOptionsFromConfig()
//{
// var dic = new Dictionary<string, string>
// {
// {"RequiredLength", "10"},
// {"RequireNonLetterOrDigit", "false"},
// {"RequireUpperCase", "false"},
// {"RequireDigit", "false"},
// {"RequireLowerCase", "false"}
// };
// var config = new ConfigurationModel.Configuration { new MemoryConfigurationSource(dic) };
// var options = new PasswordOptions(config);
// Assert.False(options.RequireDigit);
// Assert.False(options.RequireLowercase);
// Assert.False(options.RequireNonLetterOrDigit);
// Assert.False(options.RequireUppercase);
// Assert.Equal(10, options.RequiredLength);
//}
}
}