Skip to content

Commit

Permalink
add external auth tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DmyMi committed Nov 18, 2024
1 parent 8b1195f commit 433affd
Show file tree
Hide file tree
Showing 2 changed files with 302 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
using OpenIddict.Abstractions;
using OpenIddict.Client.AspNetCore;
using OutOfSchool.AuthCommon;
using OutOfSchool.AuthCommon.Config;
using OutOfSchool.AuthCommon.Controllers;
using OutOfSchool.AuthCommon.Models;
using OutOfSchool.AuthCommon.Services.Interfaces;
using OutOfSchool.Services;
using OutOfSchool.Services.Models;

namespace OutOfSchool.AuthServer.Tests.Controllers;

[TestFixture]
public class ExternalAuthControllerTests
{
private const string LoginViewCshtml = "~/Views/Auth/Login.cshtml";
private const string RedirectUrl = "/test";
private const string TestRnkopp = "1234567";
private Mock<FakeSignInManager> signInManager;
private Mock<FakeUserManager> userManager;
private Mock<FakeRoleManager> roleManager;
private Mock<ILogger<ExternalAuthController>> logger;
private Mock<IOptions<AuthorizationServerConfig>> authServerOptions;
private Mock<IStringLocalizer<SharedResource>> localizer;
private Mock<IGovIdentityCommunicationService> communicationService;
private OutOfSchoolDbContext dbContext;
private Mock<HttpContext> httpContext;
private Mock<IServiceProvider> serviceProvider;
private Mock<IAuthenticationService> authenticationService;
private AuthorizationServerConfig authServerConfig;
private ExternalAuthController controller;

[OneTimeSetUp]
public void OneTimeSetUp()
{
authServerConfig = new AuthorizationServerConfig
{
ExternalLogin = new ExternalLogin
{
Parameters = new Parameters
{
AuthType = new AuthType
{
Key = "key",
Value = "value"
}
}
}
};
dbContext = GetContext();
}

[SetUp]
public void Setup()
{
userManager = new Mock<FakeUserManager>();
signInManager = new Mock<FakeSignInManager>();
roleManager = new Mock<FakeRoleManager>();
logger = new Mock<ILogger<ExternalAuthController>>();
localizer = new Mock<IStringLocalizer<SharedResource>>();
communicationService = new Mock<IGovIdentityCommunicationService>();
authServerOptions = new Mock<IOptions<AuthorizationServerConfig>>();

authServerOptions.Setup(o => o.Value).Returns(authServerConfig);

localizer
.Setup(localizer => localizer[It.IsAny<string>()])
.Returns(new LocalizedString("mock", "error"));

serviceProvider = new Mock<IServiceProvider>();
authenticationService = new Mock<IAuthenticationService>();
serviceProvider.Setup(s => s.GetService(typeof(IAuthenticationService)))
.Returns(authenticationService.Object);
httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.RequestServices).Returns(serviceProvider.Object);


controller = new ExternalAuthController(
signInManager.Object,
userManager.Object,
roleManager.Object,
logger.Object,
authServerOptions.Object,
localizer.Object,
communicationService.Object,
dbContext
);
controller.ControllerContext.HttpContext = httpContext.Object;
controller.TempData = new TempDataDictionary(httpContext.Object, Mock.Of<ITempDataProvider>());
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
}

[Test]
public async Task ExternalLogin_WithExistingRole_ReturnsChallengeResult()
{
// Arrange
var expectedRole = "provider";
var externalAuthProvider = "test";
var returnUrl = "https://example.com";
roleManager.Setup(r => r.RoleExistsAsync(expectedRole)).ReturnsAsync(true);
signInManager.Setup(s => s.ConfigureExternalAuthenticationProperties(externalAuthProvider, returnUrl, null))
.Returns(new AuthenticationProperties());

// Act
var result = await controller.ExternalLogin(externalAuthProvider, expectedRole, returnUrl);

// Assert
Assert.IsInstanceOf<ChallengeResult>(result);
var challengeResult = (ChallengeResult) result;
Assert.AreEqual(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme,
challengeResult.AuthenticationSchemes.First());
var role = challengeResult.Properties?.Items[AuthServerConstants.ExternalAuthSelectedRoleKey];
Assert.AreEqual(expectedRole, role);
}

[Test]
public async Task ExternalLogin_WithNonExistingRole_ReturnsViewResult()
{
// Arrange
roleManager.Setup(r => r.RoleExistsAsync(It.IsAny<string>())).ReturnsAsync(false);

// Act
var result = await controller.ExternalLogin("test", "kfdjhg", "https://example.com");

// Assert
Assert.IsInstanceOf<ViewResult>(result);
var viewResult = (ViewResult) result;
Assert.AreEqual(LoginViewCshtml, viewResult.ViewName);
Assert.NotNull(viewResult.Model);
}

[Test]
public async Task ExternalLoginCallback_WithCorrectAuthFlow_ReturnsRedirectResult()
{
//Arrange
var user = GetUser();

SetupSuccessAuth();

userManager.Setup(u => u.FindByNameAsync(TestRnkopp)).ReturnsAsync(user);

// Act
var result = await controller.ExternalLoginCallback();

// Assert
signInManager.Verify(s =>
s.SignInWithClaimsAsync(user, It.IsAny<AuthenticationProperties>(), It.IsAny<IEnumerable<Claim>>()),
Times.Once);

Assert.IsInstanceOf<RedirectResult>(result);
var viewResult = (RedirectResult) result;
Assert.AreEqual(RedirectUrl, viewResult.Url);
}

[Test]
public async Task ExternalLoginCallback_WhenUserNotExist_CreateUser()
{
//Arrange
var user = GetUser();

SetupSuccessAuth();

userManager.SetupSequence(u => u.FindByNameAsync(TestRnkopp))
.ReturnsAsync((User) null)
.ReturnsAsync(user);
userManager.Setup(u => u.CreateAsync(It.IsAny<User>())).ReturnsAsync(IdentityResult.Success);

// Act
var result = await controller.ExternalLoginCallback();

// Assert
userManager.Verify(u => u.CreateAsync(It.IsAny<User>()), Times.Once);

Assert.IsInstanceOf<RedirectResult>(result);
var viewResult = (RedirectResult) result;
Assert.AreEqual(RedirectUrl, viewResult.Url);
}

private void SetupSuccessAuth()
{
var principal = new ClaimsPrincipal();
principal.AddIdentity(new ClaimsIdentity());
principal.SetClaim(AuthServerConstants.ExternalAuthUserIdKey, "test");
principal.SetClaim(OpenIddictConstants.Claims.Private.ProviderName, "external");
var properties = new AuthenticationProperties
{
RedirectUri = RedirectUrl,
Items =
{
{".Token." + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken, "secret"},
{AuthServerConstants.ExternalAuthSelectedRoleKey, "provider"}
},
};
var ticket = new AuthenticationTicket(principal, properties,
OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);
var authResult = AuthenticateResult.Success(ticket);
authenticationService.Setup(a =>
a.AuthenticateAsync(httpContext.Object, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme))
.ReturnsAsync(authResult);
communicationService.Setup(c => c.GetUserInfo("test", "secret"))
.ReturnsAsync(GetUserInfoResponse());
}

private static User GetUser()
{
return new User
{
UserName = TestRnkopp,
FirstName = "test",
LastName = "test",
MiddleName = "test",
Email = "test@test.com",
CreatingTime = DateTimeOffset.UtcNow,
IsRegistered = false,
IsBlocked = false,
MustChangePassword = false,
};
}

private static UserInfoResponse GetUserInfoResponse()
{
return new UserInfoResponse
{
DrfoCode = TestRnkopp,
GivenName = "test",
LastName = "test",
MiddleName = "test",
Email = "test@test.com",
EdrpouCode = "0987654321"
};
}

[Test]
public async Task ExternalLoginCallback_WithFailedAuthFlow_ReturnsViewResult()
{
//Arrange
var authResult = AuthenticateResult.Fail(new Exception(), new AuthenticationProperties
{
RedirectUri = RedirectUrl
});
authenticationService.Setup(a =>
a.AuthenticateAsync(httpContext.Object, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme))
.ReturnsAsync(authResult);
signInManager.Setup(s => s.GetExternalAuthenticationSchemesAsync()).ReturnsAsync([
new AuthenticationScheme("test", "test", typeof(IAuthenticationHandler))
]);

// Act
var result = await controller.ExternalLoginCallback();

// Assert
Assert.IsInstanceOf<ViewResult>(result);
var viewResult = (ViewResult) result;
Assert.AreEqual(LoginViewCshtml, viewResult.ViewName);
Assert.NotNull(viewResult.Model);
}

private static OutOfSchoolDbContext GetContext()
{
return new OutOfSchoolDbContext(
new DbContextOptionsBuilder<OutOfSchoolDbContext>()
.UseInMemoryDatabase(databaseName: "OutOfSchoolTestDB")
.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Moq;

namespace OutOfSchool.AuthServer.Tests.Controllers;

public class FakeRoleManager : RoleManager<IdentityRole>
{
public FakeRoleManager()
: base(
new Mock<IRoleStore<IdentityRole>>().Object,
[new Mock<IRoleValidator<IdentityRole>>().Object],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<ILogger<RoleManager<IdentityRole>>>().Object)
{
}
}

0 comments on commit 433affd

Please sign in to comment.