Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make IExternalLoginEventHandler support update User‘s properties #12845

Merged
merged 96 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
6940e3c
IExternalLoginEventHandler . UpdateUser
hyzx86 Nov 20, 2022
efeade1
check context.PropertiesToUpdate if null
hyzx86 Nov 20, 2022
6275ee5
merge main
hyzx86 Dec 6, 2023
064c1b2
Update Login settings description
hyzx86 Dec 6, 2023
7620a38
merge main
hyzx86 Mar 15, 2024
a9ea4ed
Merge branch 'main' into UpdateExternalUser
hyzx86 Mar 15, 2024
6adbc1a
undone
hyzx86 Mar 15, 2024
e3ed35a
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 1, 2024
9d9c6a0
merge main
Apr 12, 2024
1bce3cb
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
hyzx86 Apr 14, 2024
5e8e68e
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 14, 2024
61b18d3
merge settings static
hyzx86 Apr 14, 2024
d28459f
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
hyzx86 Apr 14, 2024
c81996b
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 16, 2024
466d429
Merge branch 'main' into UpdateExternalUser
hishamco Apr 21, 2024
203a7a9
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hyzx86 Apr 22, 2024
0a764df
Update src/OrchardCore.Modules/OrchardCore.Users/Handlers/ScriptExter…
hyzx86 Apr 22, 2024
399cdbb
Update src/OrchardCore/OrchardCore.Users.Abstractions/Handlers/Update…
hyzx86 Apr 22, 2024
c9f2634
Update src/OrchardCore/OrchardCore.Users.Abstractions/Handlers/Update…
hyzx86 Apr 22, 2024
1c43ddc
Update src/OrchardCore/OrchardCore.Users.Abstractions/Handlers/IExter…
hyzx86 Apr 22, 2024
9c2544c
Update src/OrchardCore.Modules/OrchardCore.Users/Handlers/ScriptExter…
hyzx86 Apr 22, 2024
39b50e9
Update src/OrchardCore.Modules/OrchardCore.Users/Handlers/ScriptExter…
hyzx86 Apr 22, 2024
3cff7fe
Update src/OrchardCore.Modules/OrchardCore.Users/Handlers/ScriptExter…
hyzx86 Apr 22, 2024
e4582a5
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hishamco Apr 22, 2024
a748497
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 23, 2024
7c002e1
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 25, 2024
3833267
update code
Apr 25, 2024
c698a48
add unittest
Apr 25, 2024
16c315d
update
Apr 25, 2024
3f6c44d
update docs
Apr 25, 2024
2adf215
update document
Apr 25, 2024
9ba430b
update doc
Apr 25, 2024
90607c6
update ckv
Apr 25, 2024
382aef7
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hyzx86 Apr 25, 2024
ea1c64f
Update src/OrchardCore.Modules/OrchardCore.Users/Handlers/ScriptExter…
hyzx86 Apr 25, 2024
881222d
Update src/OrchardCore/OrchardCore.Users.Abstractions/Handlers/IExter…
hyzx86 Apr 25, 2024
642bd20
update UserClaimsToUpdate to list
Apr 25, 2024
edf9af2
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
Apr 25, 2024
8f3d0c9
code suggestion
Apr 25, 2024
8e98ff8
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 25, 2024
3734d65
fix page logic
Apr 25, 2024
3b2b558
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
Apr 25, 2024
05132e9
update default script
Apr 25, 2024
674b17c
fix format
Apr 25, 2024
04b06ed
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 25, 2024
fbd68e6
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 26, 2024
877da10
lock doceditor's scroll bar
hyzx86 Apr 28, 2024
02a6131
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
hyzx86 Apr 28, 2024
b364498
Merge branch 'main' into UpdateExternalUser
hyzx86 Apr 29, 2024
1ba61cc
Merge branch 'main' into UpdateExternalUser
hyzx86 May 1, 2024
6ee83af
Merge branch 'main' into UpdateExternalUser
hyzx86 May 6, 2024
e2855f2
Merge branch 'main' into UpdateExternalUser
hyzx86 May 8, 2024
1969039
fix externalLogin script
hyzx86 May 8, 2024
9b23b7f
Merge branch 'main' into UpdateExternalUser
hyzx86 May 8, 2024
9e14df5
Merge branch 'main' into UpdateExternalUser
hyzx86 May 10, 2024
ed23f5b
Merge branch 'main' into UpdateExternalUser
hyzx86 May 13, 2024
66787e5
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hyzx86 May 14, 2024
2c8b4b0
Update src/docs/guides/microsoft-entra-id-integration/README.md
hyzx86 May 14, 2024
a5d1f64
Update src/OrchardCore.Modules/OrchardCore.Users/Views/LoginSettings.…
hyzx86 May 14, 2024
631c4de
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hyzx86 May 14, 2024
7460529
Apply suggestions from code review
hyzx86 May 14, 2024
6640a76
Merge branch 'main' into UpdateExternalUser
hyzx86 May 14, 2024
42a0cf6
update
hyzx86 May 14, 2024
45cca3f
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
hyzx86 May 14, 2024
0184dbd
add SafeUser
hyzx86 May 14, 2024
f858718
fix test
hyzx86 May 14, 2024
bd44ff5
Merge branch 'main' into UpdateExternalUser
hyzx86 May 14, 2024
b94647f
Merge branch 'main' into UpdateExternalUser
hyzx86 May 15, 2024
1e2391b
update UpdateUserContext class
hyzx86 May 15, 2024
5ad1d4f
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
hyzx86 May 15, 2024
04bfb38
Merge branch 'main' into UpdateExternalUser
hyzx86 May 15, 2024
16f68be
update
hyzx86 May 15, 2024
75b9959
Merge branch 'UpdateExternalUser' of https://github.com/hyzx86/Orchar…
hyzx86 May 15, 2024
2db78fd
update
hyzx86 May 15, 2024
2748a01
Update src/OrchardCore/OrchardCore.Users.Abstractions/Handlers/Update…
hyzx86 May 15, 2024
1257eea
Update src/OrchardCore/OrchardCore.Users.Abstractions/Handlers/Update…
hyzx86 May 15, 2024
3285529
Merge branch 'main' into UpdateExternalUser
hyzx86 May 20, 2024
37ba12c
Merge branch 'main' into UpdateExternalUser
hyzx86 May 20, 2024
c9ae0bd
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hyzx86 May 27, 2024
3b90e88
Merge branch 'main' into UpdateExternalUser
hyzx86 May 27, 2024
44a2bb1
Merge branch 'main' into UpdateExternalUser
hishamco May 27, 2024
359b0bd
Merge branch 'main' into UpdateExternalUser
hishamco May 27, 2024
e27de30
Merge branch 'main' into UpdateExternalUser
hishamco May 27, 2024
a9362dd
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hyzx86 May 28, 2024
826608e
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hyzx86 May 28, 2024
4fb8783
Update src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountC…
hyzx86 May 28, 2024
d9b31bb
Update AccountControllerTests.cs
hyzx86 May 28, 2024
06eea35
Adjustment code
hyzx86 May 28, 2024
7dc3fc3
Merge remote-tracking branch 'origin/main' into UpdateExternalUser
hyzx86 May 28, 2024
339e4d1
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hyzx86 May 29, 2024
030679d
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hyzx86 May 29, 2024
2aeca73
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hyzx86 May 29, 2024
7cc8724
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hyzx86 May 29, 2024
b008dca
Merge branch 'main' into UpdateExternalUser
hyzx86 May 29, 2024
0e746ce
Update test/OrchardCore.Tests/OrchardCore.Users/AccountControllerTest…
hishamco May 29, 2024
dcba3fa
Merge branch 'main' into UpdateExternalUser
hishamco May 29, 2024
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 @@ -2,6 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Settings;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -12,6 +15,8 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using OrchardCore.ContentManagement;
using OrchardCore.DisplayManagement;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Notify;
Expand Down Expand Up @@ -48,6 +53,12 @@ public class AccountController : AccountBaseController
private readonly IDistributedCache _distributedCache;
private readonly IEnumerable<IExternalLoginEventHandler> _externalLoginHandlers;

private static readonly JsonMergeSettings _jsonMergeSettings = new()
{
MergeArrayHandling = MergeArrayHandling.Replace,
MergeNullValueHandling = MergeNullValueHandling.Merge
};

protected readonly IHtmlLocalizer H;
protected readonly IStringLocalizer S;

Expand Down Expand Up @@ -300,24 +311,31 @@ public IActionResult ExternalLogin(string provider, string returnUrl = null)

private async Task<SignInResult> ExternalLoginSignInAsync(IUser user, ExternalLoginInfo info)
{
var claims = info.Principal.GetSerializableClaims();
var externalClaims = info.Principal.GetSerializableClaims();
var userRoles = await _userManager.GetRolesAsync(user);
var context = new UpdateRolesContext(user, info.LoginProvider, claims, userRoles);
var userInfo = user as User;

var context = new UpdateUserContext(user, info.LoginProvider, externalClaims, userInfo.Properties)
{
UserClaims = userInfo.UserClaims,
UserRoles = userRoles,
};
foreach (var item in _externalLoginHandlers)
{
try
{
await item.UpdateRoles(context);
await item.UpdateUserAsync(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "{ExternalLoginHandler}.UpdateRoles threw an exception", item.GetType());
_logger.LogError(ex, "{ExternalLoginHandler}.UpdateUserAsync threw an exception", item.GetType());
}
}

await _userManager.AddToRolesAsync(user, context.RolesToAdd.Distinct());
await _userManager.RemoveFromRolesAsync(user, context.RolesToRemove.Distinct());
if (await UpdateUserPropertiesAsync(_userManager, userInfo, context))
{
await _userManager.UpdateAsync(user);
hyzx86 marked this conversation as resolved.
Show resolved Hide resolved
}

var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);

Expand Down Expand Up @@ -779,6 +797,61 @@ public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel model)
return RedirectToAction(nameof(ExternalLogins));
}

public static async Task<bool> UpdateUserPropertiesAsync(UserManager<IUser> userManager, User user, UpdateUserContext context)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gvkries It's actually checked here, but there's no way for us to get current user information using just these checks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for adding SafeUser is simply because it would make the parameter list too long if we were to add some user information to UpdateUserContext.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you want to allow the script to check against existing properties. Do I understand that correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly !

{
await userManager.AddToRolesAsync(user, context.RolesToAdd.Distinct());
await userManager.RemoveFromRolesAsync(user, context.RolesToRemove.Distinct());

var userNeedUpdate = false;
if (context.PropertiesToUpdate != null)
{
var currentProperties = user.Properties.DeepClone();
user.Properties.Merge(context.PropertiesToUpdate, _jsonMergeSettings);
userNeedUpdate = !JsonNode.DeepEquals(currentProperties, user.Properties);
}

var currentClaims = user.UserClaims.
Where(x => !x.ClaimType.IsNullOrEmpty()).
DistinctBy(x => new { x.ClaimType, x.ClaimValue }).
ToList();

var claimsChanged = false;
if (context.ClaimsToRemove != null)
{
var claimsToRemove = context.ClaimsToRemove.ToHashSet();
foreach (var item in claimsToRemove)
{
var exists = currentClaims.FirstOrDefault(claim => claim.ClaimType == item.ClaimType && claim.ClaimValue == item.ClaimValue);
if (exists is not null)
{
currentClaims.Remove(exists);
claimsChanged = true;
}
}
}

if (context.ClaimsToUpdate != null)
{
foreach (var item in context.ClaimsToUpdate)
{
var existing = currentClaims.FirstOrDefault(claim => claim.ClaimType == item.ClaimType && claim.ClaimValue == item.ClaimValue);
if (existing is null)
{
currentClaims.Add(item);
claimsChanged = true;
}
}
}

if (claimsChanged)
{
user.UserClaims = currentClaims;
userNeedUpdate = true;
}

return userNeedUpdate;
}

private async Task<string> GenerateUsernameAsync(ExternalLoginInfo info)
{
var ret = string.Concat("u", IdGenerator.GenerateId());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Settings;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using OrchardCore.Scripting;
Expand All @@ -14,6 +16,11 @@ public class ScriptExternalLoginEventHandler : IExternalLoginEventHandler
private readonly ILogger _logger;
private readonly IScriptingManager _scriptingManager;
private readonly ISiteService _siteService;
private static readonly JsonMergeSettings _jsonMergeSettings = new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union,
MergeNullValueHandling = MergeNullValueHandling.Merge
};
hyzx86 marked this conversation as resolved.
Show resolved Hide resolved

public ScriptExternalLoginEventHandler(
ISiteService siteService,
Expand Down Expand Up @@ -45,15 +52,46 @@ public async Task<string> GenerateUserName(string provider, IEnumerable<Serializ
return string.Empty;
}

public async Task UpdateRoles(UpdateRolesContext context)
public async Task UpdateUserAsync(UpdateUserContext context)
{
var loginSettings = (await _siteService.GetSiteSettingsAsync()).As<LoginSettings>();
UpdateUserInternal(context, loginSettings);
}

public void UpdateUserInternal(UpdateUserContext context, LoginSettings loginSettings)
{
if (loginSettings.UseScriptToSyncRoles)
{
var script = $"js: function syncRoles(context) {{\n{loginSettings.SyncRolesScript}\n}}\nvar context={JConvert.SerializeObject(context, JOptions.CamelCase)};\nsyncRoles(context);\nreturn context;";
dynamic evaluationResult = _scriptingManager.Evaluate(script, null, null, null);
context.RolesToAdd.AddRange((evaluationResult.rolesToAdd as object[]).Select(i => i.ToString()));
context.RolesToRemove.AddRange((evaluationResult.rolesToRemove as object[]).Select(i => i.ToString()));

if (evaluationResult.claimsToUpdate is not null)
{
var claimsToUpdate = ((JsonArray)JArray.FromObject(evaluationResult.claimsToUpdate)).Deserialize<List<UserClaim>>(JOptions.CamelCase);
context.ClaimsToUpdate.AddRange(claimsToUpdate);
}

if (evaluationResult.claimsToRemove is not null)
{
var claimsToRemove = ((JsonArray)JArray.FromObject(evaluationResult.claimsToRemove)).Deserialize<List<UserClaim>>(JOptions.CamelCase);
context.ClaimsToRemove.AddRange(claimsToRemove);
}

if (evaluationResult.propertiesToUpdate is not null)
{
var result = (JsonObject)JObject.FromObject(evaluationResult.propertiesToUpdate);
if (context.PropertiesToUpdate is not null)
{
// Perhaps other provider will fill some values. we should keep exists value.
context.PropertiesToUpdate.Merge(result, _jsonMergeSettings);
}
else
{
context.PropertiesToUpdate = result;
}
}
}
}
}
Expand Down
Loading
Loading