Keycloak - 只读用户属性

4
我希望将一些信息作为自定义用户属性保存在Keycloak中。
其中一些应该由用户本身管理。其他属性应该仅由Keycloak管理员管理。由管理员管理的属性应该在“编辑账户”网页上对用户只可读不可更改。
我阅读了此页面中添加自定义用户属性的指南并定制了“编辑账户”网页。
我的问题是: 确保用户无法更改用于用户只读的属性吗?例如,通过提交表单发送正确数据,在服务器端自动映射到用户属性。

你想让一些自定义属性只读吗?还是指的是Keycloak已经为用户设置的属性? - Aritz
那么它们应该如何由用户设置?或者您是从应用程序的其他部分设置它们的吗? - Aritz
我从应用程序的另一部分进行了设置。 - Apolo-11
我尝试使用Postman POST表单,包括一个在UI模板中未定义的用户属性,并且成功了。这意味着已登录的用户可以创建或更新任何它想要的用户属性,即使它们在UI模板中未定义。这不是一个安全漏洞吗? - Apolo-11
似乎自定义用户属性在后端没有任何限制。我猜这是一种设计选择。您的应用程序将仅使用其中被认为合适的内容。 - Aritz
显示剩余2条评论
2个回答

2
根据您所说的,似乎您有三个选择。其中一个是保留keycloak“编辑帐户”页面,并使用update profile listener来检查存储了哪些属性或由谁更新了哪些属性,类似于这样:
public class UpdateProfile implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
    @Override
    public InitiatedActionSupport initiatedActionSupport() {
        return InitiatedActionSupport.SUPPORTED;
    }

    @Override
    public void evaluateTriggers(RequiredActionContext context) {
    }

    @Override
    public void requiredActionChallenge(RequiredActionContext context) {
        Response challenge = context.form()
                .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
        context.challenge(challenge);
    }

    // Check the custom attribute 1 not being modified by the user
    @Override
    public void processAction(RequiredActionContext context) {
        EventBuilder event = context.getEvent();
        event.event(EventType.UPDATE_PROFILE);
        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
        UserModel user = context.getUser();
        KeycloakSession session = context.getSession();
        RealmModel realm = context.getRealm();

        String newYourCustomAttribute1 = formData.getFirst("yourCustomAttribute1");
        String oldYourCustomAttribute1 = user.getFirstAttribute("yourCustomAttribute1") 

        if (!newYourCustomAttribute1.equals(oldYourCustomAttribute1)) {
            Response challenge = context.form()
                    .setError("User cannot change the attribute")
                    .setFormData(formData)
                    .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
            context.challenge(challenge);
            return;
        }
        context.success();

    }


    @Override
    public void close() {

    }

    @Override
    public RequiredActionProvider create(KeycloakSession session) {
        return this;
    }


    @Override
    public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
        if (displayType == null) return this;
        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
        return ConsoleUpdateProfile.SINGLETON;
    }



    @Override
    public void init(Config.Scope config) {

    }

    @Override
    public void postInit(KeycloakSessionFactory factory) {

    }

    @Override
    public String getDisplayText() {
        return "Update Profile";
    }


    @Override
    public String getId() {
        return UserModel.RequiredAction.UPDATE_PROFILE.name();
    }
}

我不确定的是当你从客户端应用程序更新配置文件时,是否会调用此侦听器。如果它被调用,你需要检查已登录的客户端是哪一个。如果是公共客户端,则不要让其更新属性,如果是服务客户端,则允许其更新。
第二种方案是只允许服务客户端更新用户配置文件,并在你的应用程序中创建一个自定义视图,将表单提交到你的客户端,而不是直接提交到Keycloak。这样你可以在发送到Keycloak之前在服务中进行验证。
第三种方案是实现一个FormAction接口,这将允许你在服务器端验证传入的表单:
引用:“你必须实现的核心接口是FormAction接口。FormAction负责呈现和处理页面的一部分。渲染在buildPage()方法中完成,验证在validate()方法中完成,在success()中完成后验证操作。”
@Override
public void validate(ValidationContext context) {
    MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
    UserModel user = context.getUser();
    KeycloakSession session = context.getSession();
    RealmModel realm = context.getRealm();

    String newYourCustomAttribute1 = formData.getFirst("yourCustomAttribute1");
    String oldYourCustomAttribute1 = user.getFirstAttribute("yourCustomAttribute1") 

    if (!newYourCustomAttribute1.equals(oldYourCustomAttribute1)) {
        Response challenge = context.form()
                .setError("User cannot change the attribute")
                .setFormData(formData)
                .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
        context.challenge(challenge);
        return;
    }
    context.success();
}

第一个选项对我不起作用。它仅在注册后验证用户属性。用户稍后可以通过“编辑帐户”页面更改它们。 - Apolo-11
@Apolo-11,请查看我的回答中的第三个选项,我猜这就是你在上一条评论中提到的。 - Aritz
第三个选项(FormAction)也不起作用。这些操作不适用于编辑用户页面。您只能在身份验证流程中使用它们。 - Apolo-11
@Apolo-11 在我看来,第一个选项不起作用是keycloak的一个很大的设计缺陷。即使您作为管理员没有明确要求最终用户采取行动,监听器也应该被调用。我已经挖掘了一些信息,这位用户恰好遇到了同样的问题。你仍然可以选择第二个选项,但如果我是你,我会给团队发送邮件列表,讨论这个问题。 - Aritz

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接