/// <summary> /// Gets whether this provider has a standard payload: <see cref="CreatePayload"/> can be called. /// </summary> /// <param name="this">This provider.</param> /// <returns>True if this provider has a payload.</returns> public static bool HasPayload(this IGenericAuthenticationProvider @this) { var t = @this.GetType().GetTypeInfo(); var eI = t.ImplementedInterfaces .FirstOrDefault(iT => iT.GetTypeInfo().IsGenericType&& iT.GetGenericTypeDefinition() == typeof(IGenericAuthenticationProvider <>)); return(eI != null); }
/// <summary> /// Creates an empty payload object. <see cref="HasPayload"/> must be true otherwise /// an <see cref="InvalidOperationException"/> is thrown. /// </summary> /// <param name="this">This provider.</param> /// <returns>An empty payload object.</returns> public static object CreatePayload(this IGenericAuthenticationProvider @this) { var t = @this.GetType().GetTypeInfo(); var eI = t.ImplementedInterfaces .FirstOrDefault(iT => iT.GetTypeInfo().IsGenericType&& iT.GetGenericTypeDefinition() == typeof(IGenericAuthenticationProvider <>)); if (eI == null) { throw new InvalidOperationException(); } return(eI.GetMethod(nameof(IGenericAuthenticationProvider <object> .CreatePayload)) .Invoke(@this, Array.Empty <object>())); }
static public void StandardTestForGenericAuthenticationProvider( Package auth, string schemeOrProviderName, Func <int, string, object> payloadForCreateOrUpdate, Func <int, string, object> payloadForLogin, Func <int, string, object> payloadForLoginFail ) { var user = TestHelper.StObjMap.Default.Obtain <Actor.UserTable>(); IGenericAuthenticationProvider g = auth.FindProvider(schemeOrProviderName); using (var ctx = new SqlStandardCallContext()) { string userName = Guid.NewGuid().ToString(); int userId = user.CreateUser(ctx, 1, userName); using (TestHelper.Monitor.OpenInfo($"StandardTest for generic {schemeOrProviderName} with userId:{userId} and userName:{userName}.")) { IUserAuthInfo info = auth.ReadUserAuthInfo(ctx, 1, userId); info.UserId.Should().Be(userId); info.UserName.Should().Be(userName); info.Schemes.Should().BeEmpty(); using (TestHelper.Monitor.OpenInfo("CreateOrUpdateUser without login")) { g.CreateOrUpdateUser(ctx, 1, userId, payloadForCreateOrUpdate(userId, userName)).OperationResult.Should().Be(UCResult.Created); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Count.Should().Be(0, "Still no scheme since we did not use WithActualLogin."); g.LoginUser(ctx, payloadForLogin(userId, userName), actualLogin: false).UserId.Should().Be(userId); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Should().BeEmpty("Still no scheme since we challenge login but not use WithActualLogin."); g.LoginUser(ctx, payloadForLogin(userId, userName)).UserId.Should().Be(userId); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Count.Should().Be(1); info.Schemes[0].Name.Should().StartWith(g.ProviderName); info.Schemes[0].Name.Should().BeEquivalentTo(schemeOrProviderName); info.Schemes[0].LastUsed.Should().BeCloseTo(DateTime.UtcNow, 1000); g.DestroyUser(ctx, 1, userId); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Should().BeEmpty(); } using (TestHelper.Monitor.OpenInfo("CreateOrUpdateUser WithActualLogin")) { info.UserId.Should().Be(userId); info.UserName.Should().Be(userName); info.Schemes.Should().BeEmpty(); var result = g.CreateOrUpdateUser(ctx, 1, userId, payloadForCreateOrUpdate(userId, userName), UCLMode.CreateOnly | UCLMode.WithActualLogin); result.OperationResult.Should().Be(UCResult.Created); result.LoginResult.UserId.Should().Be(userId); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Should().HaveCount(1); info.Schemes[0].Name.Should().StartWith(g.ProviderName); info.Schemes[0].Name.Should().BeEquivalentTo(schemeOrProviderName); info.Schemes[0].LastUsed.Should().BeCloseTo(DateTime.UtcNow, 1000); g.LoginUser(ctx, payloadForLoginFail(userId, userName)).UserId.Should().Be(0); g.DestroyUser(ctx, 1, userId); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Should().BeEmpty(); } using (TestHelper.Monitor.OpenInfo("Login for an unregistered user.")) { info.UserId.Should().Be(userId); info.UserName.Should().Be(userName); info.Schemes.Should().BeEmpty(); var result = g.LoginUser(ctx, payloadForLogin(userId, userName)); result.IsSuccess.Should().BeFalse(); result.UserId.Should().Be(0); result.FailureCode.Should().Be((int)KnownLoginFailureCode.UnregisteredUser); result.FailureReason.Should().Be("Unregistered user."); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Should().BeEmpty(); g.DestroyUser(ctx, 1, userId); info = auth.ReadUserAuthInfo(ctx, 1, userId); info.Schemes.Should().BeEmpty(); } using (TestHelper.Monitor.OpenInfo("Invalid payload MUST throw an ArgumentException.")) { g.Invoking(sut => sut.CreateOrUpdateUser(ctx, 1, userId, DBNull.Value)).Should().Throw <ArgumentException>(); g.Invoking(sut => sut.LoginUser(ctx, DBNull.Value)).Should().Throw <ArgumentException>(); } } user.DestroyUser(ctx, 1, userId); } }
static public async Task StandardTestForGenericAuthenticationProviderAsync( Package auth, string schemeOrProviderName, Func <int, string, object> payloadForCreateOrUpdate, Func <int, string, object> payloadForLogin, Func <int, string, object> payloadForLoginFail ) { var user = TestHelper.StObjMap.Default.Obtain <Actor.UserTable>(); IGenericAuthenticationProvider g = auth.FindProvider(schemeOrProviderName); using (var ctx = new SqlStandardCallContext()) { string userName = Guid.NewGuid().ToString(); int userId = await user.CreateUserAsync(ctx, 1, userName); using (TestHelper.Monitor.OpenInfo($"StandardTestAsync for generic {schemeOrProviderName} with userId:{userId} and userName:{userName}.")) { IUserAuthInfo info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.UserId.Should().Be(userId); info.UserName.Should().Be(userName); info.Schemes.Should().BeEmpty(); using (TestHelper.Monitor.OpenInfo("CreateOrUpdateUser without login.")) { (await g.CreateOrUpdateUserAsync(ctx, 1, userId, payloadForCreateOrUpdate(userId, userName))).OperationResult.Should().Be(UCResult.Created); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().BeEmpty("Still no scheme since we did not use WithLogin."); (await g.LoginUserAsync(ctx, payloadForLogin(userId, userName), actualLogin: false)).UserId.Should().Be(userId); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().BeEmpty("Still no scheme since we challenge login but not use WithLogin."); (await g.LoginUserAsync(ctx, payloadForLogin(userId, userName))).UserId.Should().Be(userId); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().HaveCount(1); info.Schemes[0].Name.Should().StartWith(g.ProviderName); info.Schemes[0].Name.Should().BeEquivalentTo(schemeOrProviderName); info.Schemes[0].LastUsed.Should().BeCloseTo(DateTime.UtcNow, 1000); await g.DestroyUserAsync(ctx, 1, userId); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().BeEmpty(); } using (TestHelper.Monitor.OpenInfo("CreateOrUpdateUser WithActualLogin.")) { info.UserId.Should().Be(userId); info.UserName.Should().Be(userName); info.Schemes.Should().BeEmpty(); var result = await g.CreateOrUpdateUserAsync(ctx, 1, userId, payloadForCreateOrUpdate( userId, userName ), UCLMode.CreateOnly | UCLMode.WithActualLogin); result.OperationResult.Should().Be(UCResult.Created); result.LoginResult.UserId.Should().Be(userId); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Count.Should().Be(1); info.Schemes[0].Name.Should().StartWith(g.ProviderName); info.Schemes[0].Name.Should().BeEquivalentTo(schemeOrProviderName); info.Schemes[0].LastUsed.Should().BeCloseTo(DateTime.UtcNow, 1000); (await g.LoginUserAsync(ctx, payloadForLoginFail(userId, userName))).UserId.Should().Be(0); await g.DestroyUserAsync(ctx, 1, userId); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().BeEmpty(); } using (TestHelper.Monitor.OpenInfo("Login for an unregistered user.")) { info.UserId.Should().Be(userId); info.UserName.Should().Be(userName); info.Schemes.Should().BeEmpty(); var result = await g.LoginUserAsync(ctx, payloadForLogin( userId, userName )); result.IsSuccess.Should().BeFalse(); result.UserId.Should().Be(0); result.FailureCode.Should().Be((int)KnownLoginFailureCode.UnregisteredUser); result.FailureReason.Should().Be("Unregistered user."); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().BeEmpty(); await g.DestroyUserAsync(ctx, 1, userId); info = await auth.ReadUserAuthInfoAsync(ctx, 1, userId); info.Schemes.Should().BeEmpty(); } using (TestHelper.Monitor.OpenInfo("Invalid payload MUST throw an ArgumentException.")) { g.Awaiting(sut => sut.CreateOrUpdateUserAsync(ctx, 1, userId, DBNull.Value)).Should().Throw <ArgumentException>(); g.Awaiting(sut => sut.LoginUserAsync(ctx, DBNull.Value)).Should().Throw <ArgumentException>(); } using (TestHelper.Monitor.OpenInfo("Injecting disabled user in sAuthUserOnLogin.")) using (auth.Database.TemporaryTransform(@" create transformer on CK.sAuthUserOnLogin as begin inject ""set @FailureCode = 6; -- GloballyDisabledUser"" into ""CheckLoginFailure""; end ")) { UCLResult result = await g.CreateOrUpdateUserAsync(ctx, 1, userId, payloadForCreateOrUpdate( userId, userName ), UCLMode.CreateOnly | UCLMode.WithActualLogin); result.OperationResult.Should().Be(UCResult.Created); result.LoginResult.UserId.Should().Be(0); result.LoginResult.IsSuccess.Should().BeFalse(); result.LoginResult.FailureCode.Should().Be((int)KnownLoginFailureCode.GloballyDisabledUser); LoginResult login = await g.LoginUserAsync(ctx, payloadForLogin( userId, userName )); login.IsSuccess.Should().BeFalse(); login.UserId.Should().Be(0); login.FailureCode.Should().Be((int)KnownLoginFailureCode.GloballyDisabledUser); login = await g.LoginUserAsync(ctx, payloadForLogin( userId, userName ), actualLogin : false); login.IsSuccess.Should().BeFalse(); login.UserId.Should().Be(0); login.FailureCode.Should().Be((int)KnownLoginFailureCode.GloballyDisabledUser); } } await user.DestroyUserAsync(ctx, 1, userId); } }