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

Missing terms_accepted in user attributes #43

Closed
dvlpphb opened this issue Apr 21, 2022 · 9 comments
Closed

Missing terms_accepted in user attributes #43

dvlpphb opened this issue Apr 21, 2022 · 9 comments

Comments

@dvlpphb
Copy link

dvlpphb commented Apr 21, 2022

Step to reproduce

SPI Correctly builded, deployed.
Registration flow configured and registration.ftl changed following the README.md

I register a new user.

If I check the "Attributes" tab in the user detail I can't find the attribute terms_accepted.

This code runs without errors.

Other debug info

I have added two debug row

Map<String,List<String>> Attributes = context.getUser().getAttributes();
LOG.debugf("\n\n\n\n\n Attributes after " + Attributes);

before and after the row

context.getUser().setSingleAttribute(TERMS_ACCEPTED_ATTRIBUTE, String.valueOf(System.currentTimeMillis()));

This way:

Map<String,List<String>> Attributes = context.getUser().getAttributes();
LOG.debugf("\n\n\n\n\n Attributes after " + Attributes);
context.getUser().setSingleAttribute(TERMS_ACCEPTED_ATTRIBUTE, String.valueOf(System.currentTimeMillis()));
Map<String,List<String>> Attributes2 = context.getUser().getAttributes();
LOG.debugf("\n\n\n\n\n Attributes after " + Attributes2);

I obtain in my log:

Attributes before {firstName=[David], lastName=[Lippi], acceptMarketing=[1], acceptTerms=[1], email=[david.lippi@example.org], username=[david.lippi@example.org]}                                           
Attributes after {terms_accepted=[1650562125788], firstName=[David], lastName=[Lippi], acceptMarketing=[1], acceptTerms=[1], email=[david.lippi@example.org], username=[david.lippi@example.org]} 

terms_accepted is present!

But in the admin area:

image

term_accepted is missing

In the DB terms_accepted is missing...

What's is wrong?

@zalavaari
Copy link

Same here. (with KeyCloak 16.1.1)

@dvlpphb
Copy link
Author

dvlpphb commented May 27, 2022

The code is wrong.

In the success function keycloak has already inserted data into database. If you add attribute to userModel there that attribute will never be saved into database.

I have changed the approch, i removed the success function and changed the validate function like:

    public void validate(ValidationContext context) {
        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
        // Now i use a radio instead of checkbox
        String termsAccepted = formData.getFirst(TERMS_FIELD);

        if (! termsAccepted.equals("1")) {
            context.error(Errors.INVALID_REGISTRATION);
            formData.remove(TERMS_FIELD);

            List<FormMessage> errors = List.of(new FormMessage(TERMS_FIELD, TERMS_REQUIRED_MESSAGE));
            context.validationError(formData, errors);
            return;
        } else {
	    String termsTimestamp = String.valueOf(System.currentTimeMillis());
            formData.add(TERMS_ACCEPTED_ATTRIBUTE, termsTimestamp);
	}
        super.validate(context);
    }

@dvlpphb dvlpphb closed this as completed May 27, 2022
@zalavaari
Copy link

Thank you for sharing!

@zalavaari
Copy link

Hmm... are you sure formData.add(TERMS_ACCEPTED_ATTRIBUTE, termsTimestamp); is the correct line here?

@zalavaari
Copy link

Okay, my mistake. I've forgot to add the user.attributes prefix to my attributes.

@redbaron09
Copy link

Thanks for sharing!
I'm experiencing a similar issue with custom attributes not saving during registration.
However, I'm unable to get it working with the example validate function you've provided.
Can you provide a more complete example?

@zalavaari
Copy link

Hi!

Two things I learned here:

  1. The custom atttibutes must begin with the user.attributes. prefix.
  2. As @dvlpphb mentioned the current success(FormContext context) not works, but it can be fixed:
public void success(FormContext context) {
   MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
   formData.add(TERMS_ACCEPTED_ATTRIBUTE, String.valueOf(System.currentTimeMillis()));
   super.success(context);
}

@redbaron09
Copy link

Thanks for taking the time to get back to me.
The first part was the problem for me. I was missing the link between your comment about prefixing the attribute and the problem I was seeing.
Here's something close to what I did to get it working:

public void success(FormContext context) {
   MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
   formData.add("user.attributes.my_terms_attr", String.valueOf(System.currentTimeMillis()));
   super.success(context);
}

@dvlpphb
Copy link
Author

dvlpphb commented Jun 8, 2022

This is the full code that is working for us:

package xxx;

import com.google.auto.service.AutoService;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormContext;
import org.keycloak.authentication.ValidationContext;
import org.keycloak.authentication.forms.RegistrationUserCreation;
import org.keycloak.events.Errors;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.utils.FormMessage;
//import org.jboss.logging.Logger;

import javax.ws.rs.core.MultivaluedMap;
import java.util.List;
//import java.util.Map;

@AutoService(FormActionFactory.class)
public class CustomRegistrationUserCreation extends RegistrationUserCreation {
	//private static final Logger LOG = Logger.getLogger(CustomRegistrationUserCreation.class);

    public static final String ID = "xxx-registration-user-creation";

    public static final String TERMS_FIELD = "user.attributes.termsAccepted";
    public static final String TERMS_ACCEPTED_ATTRIBUTE = "user.attributes.termsLastChangeTimestamp";
    public static final String TERMS_IP_ADDRESS_ATTRIBUTE = "user.attributes.termsLastChangeIpAddress";
    public static final String TERMS_REQUIRED = "termsRequired";
    public static final String TERMS_REQUIRED_MESSAGE = "termsRequiredMessage";

    public static final String MARKETING_FIELD = "user.attributes.marketingAccepted";
    public static final String MARKETING_ACCEPTED_ATTRIBUTE = "user.attributes.marketingLastChangeTimestamp";
    public static final String MARKETING_IP_ADDRESS_ATTRIBUTE = "user.attributes.marketingLastChangeIpAddress";

    @Override
    public String getId() {
        return ID;
    }

    @Override
    public String getDisplayType() {
        return "Custom: Registration User Creation with Terms and Conditions";
    }

    @Override
    public void validate(ValidationContext context) {

        //MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
        //LOG.debugf("\n\n\n\n\ntermsAccepted formData" + formData);
        //LOG.debugf("\n\n\n\n\ntermsAccepted field" + TERMS_FIELD);

//boolean termsAccepted = formData.containsKey(TERMS_FIELD);
        String termsAccepted = formData.getFirst(TERMS_FIELD);
        //String termsAccepted = formData.get(TERMS_FIELD);
        //LOG.debugf("\n\n\n\n\ntermsAccepted" + String.valueOf(termsAccepted));

        if (! termsAccepted.equals("1")) {
            context.error(Errors.INVALID_REGISTRATION);
            formData.remove(TERMS_FIELD);

            List<FormMessage> errors = List.of(new FormMessage(TERMS_FIELD, TERMS_REQUIRED_MESSAGE));
            context.validationError(formData, errors);
            return;
		} else {
			String termsTimestamp = String.valueOf(System.currentTimeMillis());
			//LOG.debugf("\n\n\n\n\ntermsAccepted" + String.valueOf(termsTimestamp));
            formData.add(TERMS_ACCEPTED_ATTRIBUTE, termsTimestamp);

			String termsIpAddress = context.getConnection().getRemoteAddr();
			//LOG.debugf("\n\n\n\n\ntermsAccepted" + String.valueOf(termsIpAddress));
            formData.add(TERMS_IP_ADDRESS_ATTRIBUTE, termsIpAddress);

			boolean marketingAccepted = formData.containsKey(MARKETING_FIELD);
			//LOG.debugf("\n\n\n\n\ntermsAccepted" + String.valueOf(marketingAccepted));

			String marketingTimestamp = String.valueOf(System.currentTimeMillis());
			//LOG.debugf("\n\n\n\n\ntermsAccepted" + String.valueOf(marketingTimestamp));
			formData.add(MARKETING_ACCEPTED_ATTRIBUTE, marketingTimestamp);

			String marketingIpAddress = context.getConnection().getRemoteAddr();
			//LOG.debugf("\n\n\n\n\ntermsAccepted" + String.valueOf(marketingIpAddress));
			formData.add(MARKETING_IP_ADDRESS_ATTRIBUTE, marketingIpAddress);
		}
        super.validate(context);
    }

    @Override
    public void buildPage(FormContext context, LoginFormsProvider form) {
        form.setAttribute(TERMS_REQUIRED, true);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants