Setting a User Attribute in Event Listener SPI - Keycloak - keycloak

I'm trying to set a user's attribute after they register in my custom Keycloak extension. My event listener implementation looks as follows:
#AutoService(EventListenerProviderFactory.class)
public class EventListener implements EventListenerProvider {
private final KeycloakSession session;
public EventListener(KeycloakSession session) {
this.session = session;
}
#Override
public void onEvent(Event event) {
if (event.getType() != EventType.REGISTER)
return;
RealmModel realm = session.realms().getRealm(event.getRealmId());
UserModel user = session.users().getUserById(realm, event.getUserId());
user.setSingleAttribute("hello", "world");
}
#Override
public void onEvent(AdminEvent event, boolean includeRepresentation) {
}
#Override
public void close() {
}
}
My extension is recognized by Keycloak and successfully triggers onEvent() when an event occurs (hence why I didn't include the factory class).
However, the attribute isn't added to the user. How do I actually persist the changes to the user?
While searching for a solution to the above, I came across this discussion of a very similar issue. Extending RegistrationUserCreation instead of EventListenerProvider and using the solution given by #dvlpphb did actually manage to solve my problem; however, the solution only worked when overriding the RegistrationUserCreation's validate() method, which is called every time the user attempts to register.
If anyone knows a way to set a user attribute without EventListenerProvider through RegistrationUserCreation's success() callback, that would also solve my issue.
Thank you!

Related

Keycloak - read-only user attributes

I want to keep some information in Keycloak as custom user attributes.
Some of them should be managed by the user itself. Other attributes should be managed only by a Keycloak administrator. Attributes managed by the administrator should be read-only visible in the "Edit account" web page for the user.
I went through the guide to add custom user attributes in this page and customized the "Edit account" web page.
My question is:
Is it ensured that the user cannot change the attribute that is meant as read-only for the user? E.g. by submitting a form where he/she sends correct data that will be automatically mapped on the server side to the user attribute.
For what you've said, it seems that you have three choices.
One would be to keep the keycloak "Edit Account" page and use an update profile listener to check what attributes are stored or which ones are updated by who, something like this:
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();
}
}
What I don't know is if this listener will be called when you update the profile from your client application too. If it gets called, you'll need to check which is the logged in client, if it's the public client do not let update the attributes, if it's your service client, let it.
The second one would be to only let your service client update the user profiles and make a custom view in your application which sends a form POST to your client, instead of to keycloak directly. This way you can validate it in the service before sending it to keycloak.
The third one is to implement a FormAction interface, which would allow you to validate the incoming form at server side:
The core interface you have to implement is the FormAction interface. A FormAction is responsible for rendering and processing a portion of the page. Rendering is done in the buildPage() method, validation is done in the validate() method, post validation operations are done in 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();
}
perform an update to version 12.0.4.
There were some issues < 12.0.4 with dropping all attributes if user updates his profile.
Additionally with 12.0.4 you can create user- and admin-read only attributes.
Check documentation: https://www.keycloak.org/docs/latest/server_admin/#_read_only_user_attributes
Cheers

How to save/edit object and refresh datagrid using request factory

I started with dynatableref example of Request Factory. I read request factory document. but still I am unclear about life cycle or flow of client to server.
I want to make a call to server. Insert data and update grid also. It is easy with RPC call. But I don't understand how to do with Request Factory.
This is one method of request factory. It call persist method automatically of server. It refresh grid also automatically. can I anybody tell how is it working?
context.fire(new Receiver<Void>() {
#Override
public void onConstraintViolation(Set<ConstraintViolation<?>> errors) {
// Otherwise, show ConstraintViolations in the UI
dialog.setText("Errors detected on the server");
editorDriver.setConstraintViolations(errors);
}
#Override
public void onSuccess(Void response) {
// If everything went as planned, just dismiss the dialog box
dialog.hide();
}
});
I want to edit some data in to grid also. is this method help me? or I have to write other method.
I wrote other method like
requestFactory.schoolCalendarRequest().savePerson(personProxy).fire(new Receiver<PersonProxy>() {
#Override
public void onSuccess(PersonProxy person) {
// Re-check offset in case of changes while waiting for data
dialog.hide();
}
});
This is not refreshing grid. why?
The flow client-server of the RuequestFactory is similar to RPC or any XMLHTTP request
1) You invoke a remote method on the server.
2) You receive a response in the Receiver object (which is the Callback object). In onSeccess Method you get the returned object if everything went well. onFailure you get an error if something went wrong.
So to populate the Person table from data retrieved from the server the code should look something like this
requestFactory.schoolCalendarRequest().getPersonList(param1).fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> personList) {
personTable.getDataProvider().setList(personList);
}
});
Now when you edit a Person (e.g. name ) it's important to initialize and use the same RequestContext until you call fire on the request. So the part where you update the Person's name should look something like this
column.setFieldUpdater(new FieldUpdater<Person, String>() {
#Override
public void update(PersonProxy personProxy, String value) {
RequestContext requestContext = requestFactory.schoolCalendarRequest()
PersonProxy personProxy= requestContext.edit(personProxy);
personProxy.setName(value);
requestFactory.schoolCalendarRequest().savePerson(personProxy).fire(new Receiver<PersonProxy>() {
#Override
public void onSuccess(PersonProxy person) {
//Do something after the update
}
});
}
});
The interaction with the RequestFactory should be placed in a Presenter, so you should probably consider implementing a MVP pattern.

RxJava (or Rx.NET) equivalent of ReactiveCocoa's RACObserve

Given an arbitrary field on a Java object, I want to create an Observable that will watch that field and push a new result to an Observer every time the value of the field changes. ReactiveCocoa has a macro called RACObserve, which appears to do exactly this.
I want to know how to implement similar functionality using RxJava.
For example, say I had the following simple class:
public class Foo {
enum State {
Idle,
Ready,
Error
}
private State currentState = State.Idle;
//methods that can change currentState
}
I want to create an Observable<State> that will push the new state to an Observer every time something changes the value of currentState.
In ReactiveCocoa, it looks like I would write something sort of like the following (please excuse my pseudo Objective-C):
[RACObserve(self, currentState) subscribeNext:^(NSString *newState) {
NSLog(#"%#", newState);
}];
How would I achieve similar functionality in RxJava? I'm thinking that I may need to wrap all changes to currentState in a setter, but it's not clear to me where I should then call Observable.create and how to feed the changes of currentState to an Observer.
ReactiveCocoa is actually more similar to ReactiveUI (http://www.reactiveui.net) than just plain Rx. And in ReactiveUI, you can use this.WhenAnyValue(x => x.PropName) to do exactly what you want.
I stumbled across this same problem recently, I ended up using PropertyChangeListener, which will emit an object when a property is changed, see the following:
Update Listener:
public class GameUpdateListener {
public static Observable<Object> changed(Game game) {
final BehaviorSubject<Object> subject = BehaviorSubject.create((Object)game);
game.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
subject.onNext( (Object)propertyChangeEvent.getNewValue());
}
});
return subject;
}
}
Some custom object:
public class Game {
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
...
public setSomeField(String field){
this.field = field;
pcs.firePropertyChange("field", this.field, field);
}
public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
pcs.addPropertyChangeListener(propertyChangeListener);
}
...
}
Observe:
Game game = new Game();
GameUpdateListener listener = new GameUpdateListener();
final Observable<Object> gameObserver = listener.changed(game);
gameObserver.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {
Log.e(TAG, "Object Changed");
}
});
game.setSomeField("New value");
This will work fine as long as you don't need to instantiate your object again. Perhaps a solution to this is to create a local setter method and emit a change there.
Since your question title contains "or Rx.NET", here is my suggestion (I dunno bout RxJava, you may find something similar).
You probably will have to leverage some sort of mechanism in the setter. The standard way in .NET is by using the INotifyPropertyChanged interface.
Then by firing the events, you can create an IObservable<T> from this stream by using
Observable.FromEvent<TEvent, TArgs>()
You can find a really good example of what you want to do (.NET) here.
(credits to Rob Foncesa-Ensor)
I think what you are after is a Subject<T>. It implements IObserver<T>, so you can call OnNext(T) to fire a new value, as well as IObservable<T>, which you can expose it as publicly so it can be subscribed to.
If you need it to fire the latest value to new subscribers, you can use a ReplaySubject<T> with a buffer size of 1.
Here's a basic implementation:
public class SomeService
{
private Subject<int> values = new Subject<int>();
public IObservable<T> Values
{
get
{
// AsObservable prevents it from being cast back to Subject
return values.AsObservable();
}
}
// Private; called by some internal mechanism
private void SetValue(int newValue)
{
newValue.OnNext(newValue);
}
}

ZK Fire Event from Java API

I have defined a custom Command in ZK and want to call it by clicking on a Menu Item.
I see that we can define a AuRequest object, but can't find a way to send this AuRequest like we do in JavaScript using zkau.send function.
is something possible at all? If not, is it possible to define the zkau.send in a JavaScript function and call it in MeunItem Click Event?
public class MyCustomCommand extends Command
{
protected MyCustomCommand(final String id, final int flags)
{
super(id, flags);
}
#Override
protected void process(final AuRequest request)
{
System.out.println("Menu Item Click");
}
}
register the command:
<bean id="myCustomCommand" class="com.test.commands.MyCustomCommand">
<constructor-arg value="onMenuEdit" />
<constructor-arg><util:constant static-field="org.zkoss.zk.au.Command.IGNORE_OLD_EQUIV"/></constructor-arg>
</bean>
and MenuItem Event
menuItem.addEventListener(Events.ON_CLICK, new EventListener()
{
#Override
public void onEvent(final Event event) throws Exception
{
final Tree tree = (Tree) parent;
final Treeitem treeitem = tree.getSelectedItem();
final AuRequest auRequest = new AuRequest(treeitem.getDesktop(), treeitem.getUuid(), "onMenuEdit", new String[]{});
//how to send the auRequest??
}
});
I can't comment on the use of the Command or AuRequest objects as you're suggesting here. I've never seen them used and have never used them myself. If there is a way to use them to solve this problem, hopefully you will get an answer. That said, there are other ways to achieve what you are looking to do.
As detailed in the Event Firing section of the Developer Reference, you can fire an event from the static Events object.
Events.postEvent("onMenuEdit", myTree, myDataEgTheTreeItem);
or..
Events.sendEvent("onMenuEdit", myTree, myDataEgTheTreeItem);
or..
Events.echoEvent("onMenuEdit", myTree, myDataEgTheTreeItem);
Any of these can be handled in a Composer using..
#Listen("onMenuItem = #myTree")
public void onTreeMenuItemEvent(Event event) {
// Handle event
}
Hope that helps.

class member returns null after osgi bind method

My problem is that in the main class I have some osgi references that work just fine when the class is call. But after that all the references became null. When I close the main windows and call shutdown method, the hubService reference returns null. What do I do wrong here?
private void shutdown() {
if(hubService == null) {
throw new NullPointerException();
}
hubService.shutdownHub(); // why is hubService null?
}
// bind hub service
public synchronized void setHubService(IHubService service) {
hubService = service;
try {
hubService.startHub(PORT, authenticationHandler);
} catch (Exception e) {
JOptionPane.showMessageDialog(mainFrame, e.toString(), "Server", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
}
// remove hub service
public synchronized void unsetHubService(IHubService service) {
hubService.shutdownHub();
hubService = null;
}
If a field can be read and written by multiple threads, you must protect access to read as well as write. Your first method, shutdown, does not protect the read of hubService so that the value of hubService can change between the first read and the second read. You don't show the declaration of the hubService field. You could make it volatile or only read when synchronized (on the same object used to synchronized when writing the field). Then your shutdown implementation could look like:
private volatile IHubService hubService;
private void shutdown() {
IHubService service = hubService; // make a copy of the field in a local variable
if (service != null) // use local var from now on since the field could have changed
service.shutdownHub();
}
I assume your shutdown method is the DS deactivate method? If so, why do you shutdown in the unset method as well in the shutdown method?
Overall the design does not seem very sound. The IHubService is used as a factory and should return some object that is then closed in the deactivate method. You made the IHubService effectively a singleton. Since it must come from another bundle, it should handle its life cycle itself.
Since you also do not use annotations, it is not clear if your set/unset methods are static/dynamic and/or single/multiple. The following code should not have your problems (exammple code with bnd annotations):
#Component public class MyImpl {
IHubService hub;
#Activate
void activate() {
hubService.startHub(PORT, authenticationHandler);
}
#DeActivate
void deactivate() {
hubService.shutdown();
}
#Reference
void setHub(IHubService hub) { this.hub = hub; }
}