Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@ public async Task<Dictionary<string, object>> ResolveClaims(LtiPrincipal princip
{
var launchInfo = CanvasLaunchInfo.FromPrincipal(principal);
var organization = await userService.GetOrganizationForUser(launchInfo.UvanetId);

var existingUser = await userService.GetUser(launchInfo.UvanetId, CancellationToken.None);
var existingPicture = existingUser?.Picture;

var user = await userService.AddOrUpdateUser(
launchInfo.UvanetId,
launchInfo.DisplayName,
launchInfo.Email,
UserProviderKeys.Internal,
organization);
organization,
launchInfo.Picture);

if (launchInfo.Picture != null && existingPicture != launchInfo.Picture)
await userService.SyncUserInInstances(user, ["Picture"], CancellationToken.None);

var target = await targetResolver.ResolveTarget(user, launchInfo, CancellationToken.None);

return new Dictionary<string, object>
Expand Down
6 changes: 4 additions & 2 deletions UvA.Workflow.Api.Authentication/CanvasLti/CanvasLaunchInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public record CanvasLaunchInfo(
string Email,
string[] CourseIdentifiers,
bool IsTeacher,
string Locale)
string Locale,
string? Picture)
{
private const string InstructorRole = "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor";

Expand Down Expand Up @@ -37,7 +38,8 @@ public static CanvasLaunchInfo FromPrincipal(LtiPrincipal principal)
email,
courseIdentifiers,
HasTeacherRole(principal.Roles),
principal.Locale ?? "en");
principal.Locale ?? "en",
principal.Picture);
}

private static bool HasTeacherRole(IEnumerable<string>? roles)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
<PackageReference Include="UvA.LTI" Version="0.2.7" />
<PackageReference Include="UvA.LTI" Version="0.2.8" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion UvA.Workflow.Api/Users/Dtos/UserDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public record UserDto(
string DisplayName,
string Email,
string? PreferredLanguage,
string? Picture,
Organization? Organization,
bool IsExternal,
bool IsSuperAdmin,
Expand All @@ -28,6 +29,7 @@ public static UserDto Create(User user, bool isSuperAdmin = false)
user.DisplayName,
user.Email,
user.PreferredLanguage,
user.Picture,
user.Organization,
UserProviderKeys.IsExternal(user.ProviderKey),
isSuperAdmin,
Expand All @@ -39,6 +41,6 @@ public static UserDto Create(User user, bool isSuperAdmin = false)
/// Creates a UserDto from an Instance User entity
/// </summary>
public static UserDto CreateFromInstanceUser(InstanceUser u) =>
new(u.Id, u.UserName, u.DisplayName, u.Email, u.PreferredLanguage, u.Organization, u.IsExternal,
new(u.Id, u.UserName, u.DisplayName, u.Email, u.PreferredLanguage, u.Picture, u.Organization, u.IsExternal,
false, u.InvitationState == UserInvitationState.Required);
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ private static CanvasLaunchInfo StudentLaunch(params string[] courseIdentifiers)
"teacher@example.com",
courseIdentifiers,
isTeacher,
"en");
"en",
null);

private static WorkflowInstance Course(string id, string externalId) => new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public async Task AuthenticateAsync_EduIdActiveUser_Succeeds()
userServiceMock.Verify(s => s.AddOrUpdateUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<Organization?>(),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()), Times.Never);
}

Expand Down Expand Up @@ -107,6 +108,7 @@ public async Task AuthenticateAsync_NonEduId_UsesRegularUserProvisioning()
"jane.doe@uva.nl",
UserProviderKeys.Internal,
organization,
null,
It.IsAny<CancellationToken>()))
.ReturnsAsync(new User
{
Expand Down
6 changes: 6 additions & 0 deletions UvA.Workflow.Tests/InstanceUserStorageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public async Task ConvertToValue_ForMissingExternalUser_DoesNotCreateFromAnswerP
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<Organization?>(),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()),
Times.Never);
}
Expand Down Expand Up @@ -179,6 +180,7 @@ public async Task ConvertToValue_ForActivatedExternalUser_FallsBackToEmailLookup
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<Organization?>(),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()),
Times.Never);
}
Expand All @@ -204,6 +206,7 @@ public async Task ConvertToValue_ForMissingInternalUser_IgnoresSubmittedProvider
"student@uva.nl",
UserProviderKeys.Internal,
It.Is<Organization?>(o => o == null),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(user);
var service = new AnswerConversionService(userService.Object, userRepository.Object);
Expand All @@ -227,6 +230,7 @@ public async Task ConvertToValue_ForMissingInternalUser_IgnoresSubmittedProvider
"student@uva.nl",
UserProviderKeys.Internal,
It.Is<Organization?>(o => o == null),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()),
Times.Once);
}
Expand All @@ -253,6 +257,7 @@ public async Task ConvertToValue_ForMissingInternalUser_PreservesExplicitOrganiz
"student2@uva.nl",
UserProviderKeys.Internal,
It.Is<Organization>(o => o.Id == user.Organization.Id && o.Name == "FNWI"),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(user);
var service = new AnswerConversionService(userService.Object, userRepository.Object);
Expand All @@ -279,6 +284,7 @@ public async Task ConvertToValue_ForMissingInternalUser_PreservesExplicitOrganiz
"student2@uva.nl",
UserProviderKeys.Internal,
It.Is<Organization>(o => o.Id == user.Organization.Id && o.Name == "FNWI"),
It.IsAny<string?>(),
It.IsAny<CancellationToken>()),
Times.Once);
}
Expand Down
21 changes: 17 additions & 4 deletions UvA.Workflow.Tests/Users/UserServiceEduIdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
using Moq;
using UvA.Workflow.Api.Authentication;
using UvA.Workflow.Organizations;
using UvA.Workflow.Tests.Helpers;
using UvA.Workflow.Users;
using UvA.Workflow.Users.DataNose;
using UvA.Workflow.Users.EduId;
using UvA.Workflow.WorkflowInstances;

namespace UvA.Workflow.Tests.Users;

Expand All @@ -27,7 +29,9 @@ private static UserService CreateService(
[
new DataNoseUserSearchSource(dataNoseApiClientMock.Object),
new RepositoryUserSearchSource(userRepositoryMock.Object)
]);
],
Mock.Of<IWorkflowInstanceRepository>(),
new ModelService(UnitTestsHelpers.CreateModelParser()));

[Fact]
public async Task GetRoles_EduIdUser_ReturnsEmpty_WithoutCallingDataNose()
Expand Down Expand Up @@ -298,13 +302,16 @@ public async Task AddOrUpdateUser_ExternalUser_CreatesWithEduIdProviderKey()
organizationServiceMock.Object,
new MemoryCache(new MemoryCacheOptions()),
[],
[]);
[],
Mock.Of<IWorkflowInstanceRepository>(),
new ModelService(UnitTestsHelpers.CreateModelParser()));

var result = await service.AddOrUpdateUser("external-123",
"External User",
"external@example.org",
EduIdDirectoryKeys.ProviderKey,
null,
null,
CancellationToken.None);

Assert.Same(createdUser, result);
Expand All @@ -330,13 +337,16 @@ public async Task AddOrUpdateUser_ExistingExternalUser_UpdatesProviderKey()
organizationServiceMock.Object,
new MemoryCache(new MemoryCacheOptions()),
[],
[]);
[],
Mock.Of<IWorkflowInstanceRepository>(),
new ModelService(UnitTestsHelpers.CreateModelParser()));

await service.AddOrUpdateUser("external-123",
"External User",
"external@example.org",
EduIdDirectoryKeys.ProviderKey,
null,
null,
CancellationToken.None);

Assert.Equal(EduIdDirectoryKeys.ProviderKey, user.ProviderKey);
Expand Down Expand Up @@ -378,7 +388,10 @@ public async Task GetRoles_UnknownProvider_ReturnsEmpty_WhenNoSourceResolves()
organizationServiceMock.Object,
new MemoryCache(new MemoryCacheOptions()),
[new EduIdUserDirectory()],
[]);
[],
Mock.Of<IWorkflowInstanceRepository>(),
new ModelService(UnitTestsHelpers.CreateModelParser()));

var user = new User
{
UserName = "unknown-123",
Expand Down
Loading
Loading