根据外部标识中的角色更新 Umbraco 用户的用户类型



在过去的几天里,我一直致力于将Umbraco Backoffice与IdentityServer v3集成。我已经设法做到了这一点,我从外部对用户进行身份验证,并让Umbraco在后台创建一个具有默认用户类型的用户,并将其链接到外部帐户。

我要做的下一件事是根据用户的角色更新Umbraco用户类型。我想我找到了一种将Umbraco链接到外部帐户的方法,但我看不到任何方法可以在每次登录时不断更新用户类型,以防为用户删除/添加角色。

通过分析Umbraco BackOfficeController中的代码,似乎没有办法进入Umbraco侧的数据验证和更新过程。

var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{   
    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
else
{
    if (await AutoLinkAndSignInExternalAccount(loginInfo) == false)
    {
        ViewBag.ExternalSignInError = new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not been linked to to an account" };
    }   
}

看起来,如果找到umbraco登录,那么用户只是在登录,没有任何暴露的事件或选项。只有在找不到用户的情况下,才开始整个创建和链接过程,在那里我可以对用户属性进行一些更改。

也就是说,有没有任何方法可以在每次登录时根据外部服务器的声明来更新Umbraco用户的用户类型

我的启动类代码如下。

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
});
var idAuth = new OpenIdConnectAuthenticationOptions
{
    Authority = "https://localhost:44332",
    ClientId = "id",
    ClientSecret = "secret",
    RedirectUri = "http://localhost:8081/Umbraco",
    ResponseType = "id_token token",
    Scope = "openid profile roles email",
    Caption = "test",                   
    SignInAsAuthenticationType = Umbraco.Core.Constants.Security.BackOfficeExternalAuthenticationType
};
idAuth.Notifications = new OpenIdConnectAuthenticationNotifications
{
    SecurityTokenValidated = async n =>
        {
            var id = n.AuthenticationTicket.Identity;
            var givenName = id.FindFirst(System.Security.Claims.ClaimTypes.GivenName);
            var familyName = id.FindFirst(System.Security.Claims.ClaimTypes.Surname);
            var roles = id.FindAll(System.Security.Claims.ClaimTypes.Role);
            var nid = new ClaimsIdentity(
                id.AuthenticationType,
                System.Security.Claims.ClaimTypes.GivenName,
                System.Security.Claims.ClaimTypes.Role);
            nid.AddClaim(givenName);
            nid.AddClaim(familyName);
            nid.AddClaims(roles);
            nid.AddClaim(id.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier));
            nid.AddClaim(id.FindFirst(System.Security.Claims.ClaimTypes.Email));
            n.AuthenticationTicket = new AuthenticationTicket(
                nid,
                n.AuthenticationTicket.Properties);
        }
};
//idAuth.AuthenticationType = "https://localhost:44332";
idAuth.ForUmbracoBackOffice("btn-google-plus", "fa-google-plus"); //temporary icon/button
idAuth.AuthenticationType = "https://localhost:44332";
var externalOptions = new ExternalSignInAutoLinkOptions(autoLinkExternalAccount: true, defaultUserType: "admin");
//externalOptions.OnAutoLinking; // TODO: set user type based on roles
idAuth.SetExternalSignInAutoLinkOptions(externalOptions);
app.UseOpenIdConnectAuthentication(idAuth);

前段时间,在Umbraco服务IExternalLoginServiceIUserService的帮助下,通过手动检查SecurityTokenValidated上的角色声明和Umbraco UserType,设法解决了这个问题。如果组合不正确(例如,索赔中不存在管理员角色),我使用Umbraco IUserService更新该用户的UserType

Notifications = 
new OpenIdConnectAuthenticationNotifications
{
    SecurityTokenValidated = async n =>
    {
        var id = n.AuthenticationTicket.Identity;
        var uid = id.FindFirst(ClaimTypes.NameIdentifier);
        var givenName = id.FindFirst(ClaimTypes.GivenName);
        var familyName = id.FindFirst(ClaimTypes.Surname);
        var roles = id.FindAll(ClaimTypes.Role);
        var rolesList = roles as IList<Claim> ?? roles.ToList();
        if (
            !rolesList.Any(
                c =>
                    string.Equals(c.Value, RoleNames.ContentEditor,
                        StringComparison.InvariantCultureIgnoreCase)))
            throw new HttpException(403,
                "You do not have any roles configured for the application");
        // create new identity and set name and role claim type
        var nid = new ClaimsIdentity(
            id.AuthenticationType,
            ClaimTypes.GivenName,
            ClaimTypes.Role);
        UpdateUserType(uid.Value, rolesList, applicationConfiguration.AuthorityUrl);
        nid.AddClaim(givenName);
        nid.AddClaim(familyName);
        nid.AddClaims(rolesList);
        nid.AddClaim(uid);
        nid.AddClaim(id.FindFirst(ClaimTypes.Email));
        n.AuthenticationTicket = new AuthenticationTicket(
            nid,
            n.AuthenticationTicket.Properties);
    }
}

private static void UpdateUserType(string uid, IList<Claim> roles, string providerName)
    {
        var userService = ApplicationContext.Current.Services.UserService;
        var oneUser = ApplicationContext.Current.Services.ExternalLoginService.Find(new UserLoginInfo(providerName, uid)).FirstOrDefault();
        if (oneUser == null)
            return;
        var user = userService.GetUserById(oneUser.UserId);
        if (user == null)
            return;
        if (
            roles.Any(
                r => string.Equals(r.Value, RoleNames.Administrator, StringComparison.InvariantCultureIgnoreCase))
            && !string.Equals(user.UserType.Alias, UmbracoRoleNames.Administrator))
        {
            SetUserType(user, UmbracoRoleNames.Administrator, userService);
            return;
        }
        if (
            roles.Any(
                r => string.Equals(r.Value, RoleNames.ContentEditor, StringComparison.InvariantCultureIgnoreCase))
            && !string.Equals(user.UserType.Alias, UmbracoRoleNames.ContentEditor))
        {
            SetUserType(user, UmbracoRoleNames.ContentEditor, userService);
            return;
        }
    }
private static void SetUserType(Umbraco.Core.Models.Membership.IUser user, string alias, IUserService userService)
    {
        try
        {
            user.UserType = userService.GetUserTypeByAlias(alias);
            userService.Save(user);
        }
        catch (Exception e)
        {
            LogHelper.Error(typeof(ClassName), "Could not update the UserType of a user.", e);
        }
    }

在这种特定的情况下,当有人的角色声明中没有权限时,我不会将UserType改回非管理员/非内容编辑器,因为他们在一步前被过滤掉,并返回403错误代码。

最新更新