I have created a custom Theme for Keycloak email.
I know how to add the realm name in email body, but I didn't find how to add the realm name in the subject.
Is there any possibility to add this without the need of a custom Provider ?
Thank's !
Had the same question.
So did some research(went through code), and find out that as of writing this answer, the default Freemarker-based Email Implementation doesn't even supply the subject attributes(call them variables or arguments), and the final subject string formatting is done based on an empty list of attributes.
https://github.com/keycloak/keycloak/blob/bfce612641a70e106b20b136431f0e4046b5c37f/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java#L162
#Override
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
Map<String, Object> attributes = new HashMap<>(this.attributes);
attributes.put("user", new ProfileBean(user));
addLinkInfoIntoAttributes(link, expirationInMinutes, attributes);
attributes.put("realmName", getRealmName());
send("executeActionsSubject", "executeActions.ftl", attributes);
}
Which in return calls
#Override
public void send(String subjectFormatKey, String bodyTemplate, Map<String, Object> bodyAttributes) throws EmailException {
send(subjectFormatKey, Collections.emptyList(), bodyTemplate, bodyAttributes);
}
Finally, this function gets executed
protected EmailTemplate processTemplate(String subjectKey, List<Object> subjectAttributes, String template, Map<String, Object> attributes) throws EmailException {
try {
........
String subject = new MessageFormat(rb.getProperty(subjectKey, subjectKey), locale).format(subjectAttributes.toArray());
........
Throughout the code trail for executeAction I nowhere found any logic which allows adding dynamic-values/variables in the subject line.
To solve this I am raising an issue. I will link it once done.
Update 1
Issue opened: https://github.com/keycloak/keycloak/issues/11883
Update 2
PR raised: https://github.com/keycloak/keycloak/pull/11885
Related
How to authenticate and redirect a user to his own page i.e to www.mysite.com/"user's email".
I am using the following algo which is not working...
userDB in User class:
Map<String,String> userdata=new HashMap<String,String>();
First my login process form :
#Path("/login")
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void login(
#FormParam("email") String emailc,
#FormParam("password") String pass,
#Context HttpServletResponse servletResponse
) throws IOException,RuntimeException {
User u1=new User();
pass=u1.getPassword();
emailc=u1.getEmailaddrs();
boolean checked=false;
boolean exists;
exists=u1.userdata.containsKey(emailc);
if(exists){
String mypass =u1.userdata.get(emailc);
if(mypass==pass){
checked=true;
}else{
checked=false;
}
}else{
checked=false;
}
if(!checked){
//User Doesn't exists
servletResponse.sendRedirect("http://localhost:8080/MySite/pages/Create_Profile.html");
}else{
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
}
}
createprofile
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newUser(
#FormParam("email") String email,
#FormParam("password") String password,
#Context HttpServletResponse servletResponse
) throws IOException {
User u = new User(email,password);
User.userdata.put(email,password);
}
Your usage of userdata [Map] looks wrong to me. Is it a part of user class, is it non static or static ?
If it is non static then every time you will do new User() .. that map will be initialized and it will have no data in it. Hence u1.userdata.containsKey(emailc); will be always false.
If you are using a hashmap as a temporary database for dev purposes then, make it static rather keep it in a different class like UserStore or some DB access layer. Exmaple below:
public class UserDAO(){
private static Map<String,User> userdata = new HashMap<String,User>();
public boolean hasUser(String email){
return userdata.contains(email);
}
public User saveUser(String email, String password ...){
//make user object save it in map and return the same
}
// more methods for delete and edit etc.
}
And use this in your REST layer classes like this
exists = userDao.hasUser(email);
Advantages :
Your problem will be solved.
Later on when you move to actual db implementation you will just have to change your UserDao code and rest application code will be just fine. -- Loose coupling :)
Also regarding forward using email
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
add the email parameter there in the url only, if thats what you want:
servletResponse.sendRedirect("http://localhost:8080/MySite/"+emailc);
UPDATE :
See the fundamental thing is that you get request parameters [email , password]. You check it whether it is present in map or not. Now what you are doing wrong here is you create a new user like this User u = new User(); and then get email and password from it emailc = u.getEmail();. This emailc will always be null and your userdata map will always return false for that. You have two choices :
Either set email and password in user object and then get the data from user object.
Use the email and password obtained from request parameters for your logic. Do not alter them
One good practice to follow while programming is that at all times think of your method parameters as final parameters.
UPDATE 2 :
if(mypass==pass){
checked=true;
}else{
checked=false;
}
Change == to equals method. String matching should be done by equals or equalsIgnoreCase method not ==.
You always create a new User without any parameters: User u1=new User();. All these User instances will have the same property values and probably exists is always false.
I am using Liferay 6.1, and I want to override default Liferay Login authentication and want to set up my custom authentication.
Till now what I have done is, I have created a hook-plugin and have setup following properties in portal.properties file
auth.pipeline.pre=com.liferay.portal.security.auth.MyCustomAuthenticator
auth.pipeline.enable.liferay.check=false
where MyCustomAuthenticator is my custom authenticator class ( which implements Authenticator).
Currently, Liferay checks this custom authentication 1st, but then again it goes to Liferay itself for further Liferay authentication too.
I want to override this Liferay validation. Please help me solve this issue.
Here is my authenticator class:
public class MyCustomAuthenticator implements Authenticator {
public int authenticateByEmailAddress(long arg0, String arg1, String arg2, Map<String, String[]> arg3, Map<String, String[]> arg4) throws AuthException {
System.out.println("succeeded by mail");
return SUCCESS;
}
public int authenticateByScreenName(long arg0, String arg1, String arg2, Map<String, String[]> arg3, Map<String, String[]> arg4) throws AuthException {
System.out.println("succeeded by screen name");
return SUCCESS;
}
public int authenticateByUserId(long arg0, long arg1, String arg2, Map<String, String[]> arg3, Map<String, String[]> arg4) throws AuthException {
System.out.println("succeeded by user id");
return SUCCESS;
}
}
Add the following property in portal-ext.properties and then restart the server
auth.pipeline.enable.liferay.check=false
Remembered in your hook project in file portal.properties
place auth.pipeline.pre =com.liferay.portal.security.auth.MyCustomAuthenticator
auth.pipeline.enable.liferay.check = false
and also in the portal-ext-properties
I had made a hook the same way, with these two lines in my hook portal.properties override, AND in portal-ext.properties for good measure:
auth.pipeline.pre=com.liferay.portal.security.auth.MyCustomAuthenticator
auth.pipeline.enable.liferay.check=false
However, it seemed to not want to login to Liferay even when the account already existed. I was able to get it fully working and skip Liferay authentication altogether. The hook overriding portal.properties is all I needed, I removed the 2 lines from portal-ext. In your Custom Authenticator, instead of just returning SUCCESS, (com.liferay.portal.security.auth.Authenticator.SUCCESS)
You want to return SKIP_LIFERAY_CHECK . This is the same as a SUCCESS, except making sure the authentication pipeline knows to skip the liferay check.
This should force it to work. I believe the source code (for Liferay 6.2 ga5) does not properly take into account the "Skip Liferay Check" property, and this essentially forces it.
I am looking for a solution in GWT to bundle properties file, like i do in java.util.ResourceBundle
ResourceBundle messageBundle = ResourceBundle.getBundle(baseName.properties , new Locale(language));
Set<String> messagesKey = messageBundle.keySet();
Map<String, String> messagesMap = new HashMap<String, String> (messagesKey.size());
for (String key : messagesKey) {
messagesMap.put(key, messageBundle.getString(key));
}
The closest thing to it is ContstantsWithLookup (but you can't get list of keys from it) or Dictionary (but it doesn't work with *.properties files, only with js objects)
To use resource bundle on client side just use Messages interface.
1) Define your interface MyMessages in client package
public interface MyMessages extends Messages {
String hello(String username);
String bye(String username);
String empty();
}
2) Create properties file\files beside your interface MyMessages.properties and MyMessages_ru.properties for example. Properties keys must be equals methods names.
hello=Hello {0}, how are you?
bye=Bye-bye {0}!
empty=Just message!
3) Create your messages object in gwt module
MyMessages messages = GWT.create(MyMessages .class);
4) Use messages
new AlertMessageBox("", messages.empty()).show();
Also you can define default messages via annotations right in your interface code, for example
public interface MyMessages extends Messages {
#DefaultMessage("Hello {0}, how are you?")
String hello(String username);
#DefaultMessage("Bye-bye {0}!")
String bye(String username);
#DefaultMessage("Just message!")
String empty();
}
Does anyone know for an example of GWT's CellTable using RequestFactory and that table is being edited? I would like to list objects in a table (each row is one object and each column is one property), be able to easily add new objects and edit. I know for Google's DynaTableRf example, but that one doesn't edit.
I searched Google and stackoverflow but wasn't able to find one. I got a bit confused with RF's context and than people also mentioned some "driver".
To demonstrate where I currently arrived, I attach code for one column:
// Create name column.
Column<PersonProxy, String> nameColumn = new Column<PersonProxy, String>(
new EditTextCell()) {
#Override
public String getValue(PersonProxy person) {
String ret = person.getName();
return ret != null ? ret : "";
}
};
nameColumn.setFieldUpdater(new FieldUpdater<PersonProxy, String>() {
#Override
public void update(int index, PersonProxy object, String value) {
PersonRequest req = FaceOrgFactory.getInstance().requestFactory().personRequest();
PersonProxy eObject = req.edit(object);
eObject.setName(value);
req.persist().using(eObject).fire();
}
});
and my code for data provider:
AsyncDataProvider<PersonProxy> personDataProvider = new AsyncDataProvider<PersonProxy>() {
#Override
protected void onRangeChanged(HasData<PersonProxy> display) {
final Range range = display.getVisibleRange();
fetch(range.getStart());
}
};
personDataProvider.addDataDisplay(personTable);
...
private void fetch(final int start) {
lastFetch = start;
requestFactory.personRequest().getPeople(start, numRows).fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> response) {
if (lastFetch != start){
return;
}
int responses = response.size();
if (start >= (personTable.getRowCount()-numRows)){
PersonProxy newP = requestFactory.personRequest().create(PersonProxy.class);
response.add(newP);
responses++;
}
personTable.setRowData(start, response);
personPager.setPageStart(start);
}
});
requestFactory.personRequest().countPersons().fire(new Receiver<Integer>() {
#Override
public void onSuccess(Integer response) {
personTable.setRowCount(response+1, true);
}
});
}
I try to insert last object a new empty object. And when user would fill it, I'd insert new one after it. But the code is not working. I says that user is "attempting" to edit a object previously edited by another RequestContext.
Dilemmas:
* am I creating too many context'es?
* how to properly insert new object into celltable, created on the client side?
* on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
Thanks for any help.
am I creating too many context'es?
Yes.
You should have one context per HTTP request (per fire()), and a context that is not fire()d is useless (only do that if you/the user change your/his mind and don't want to, e.g., save your/his changes).
You actually have only one context to remove here (see below).
Note that your approach of saving on each field change can lead to "race conditions", because a proxy can be edit()ed by at most one context at a time, and it remains attached to a context until the server responds (and once a context is fired, the proxy is frozen –read-only– also until the server responds).
(this is not true in all cases: when onConstraintViolation is called, the context and its proxies are unfrozen so you can "fix" the constraint violations and fire the context again; this should be safe because validation is done on the server-side before any service method is called).
how to properly insert new object into celltable, created on the client side?
Your code looks OK, except that you should create your proxy in the same context as the one you'll use to persist it.
on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
I'm not 100% certain but I think you should refresh the table (something like setRowData(index, Collections.singletonList(object)))
BTW, the driver people mention is probably the RequestFactoryEditorDriver from the Editor framework. It won't help you here (quite the contrary actually).
How do I define my own feedback messages in Wicket?
For example: if I give an incorrect username, I want to get an error message like "The user name in incorrect, try to login again." instead of using the default error message.
What would an example be like?
You can display your own error messages using error() and warn() and info(). If you want to show errors dependent on validators or the required flag you can define a properties file with the same name as the class which contains a mapping of field -> message. For example:
Index.java
Form form = new Form("myform");
form.add(new TextField("name").setRequired(true));
form.add(new PasswordTextField("password").setRequired(true));
form.add(new TextField("phonenumber").setRequired(true));
Index.properties
Required=Provide a ${label} or else...
All required fields
myform.name.Required=You have to provide a name
The field name in the form myform when it is required.
password.Required=You have to provide a password
Any field with the name password when it is required.
phonenumber.Required=A telephone number is obligatory.
Any field with the name phonenumber when it is required.
This shows a variety of ways of setting a feedback message for specific components.
You can also put the properties files next to the following component level (in order of importance, top highest):
Page Class
Component Class
Your Application Class
Wickets Application Class
Hope that helps
#user1090145: I've used overloaded Component's error() method in Validator's class:
private void error(IValidatable<String> validatable, String errorKey) {
ValidationError error = new ValidationError();
error.addMessageKey(errorKey);
validatable.error(error);
}
and invoked it in validate() by
error(validatable, "your-form.field.text-id");
Properties your-form.field.text-id must be defined in YourPage.properties
Sources:
Create custom validator in Wicket and
Form validation messages
you should set Feed back message to Session
message = "message";
Session.get().getFeedbackMessages().success(null, message);
You can use an anonymous IValidationError class and the messageSource.getMessage method to get a custom message from your property file:
error(new IValidationError() {
#Override
public Serializable getErrorMessage(IErrorMessageSource messageSource) {
//create a list of the arguments that you will use in your message string
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("invalidUsername", getInvalidUsernameInput());
//get the message string from the property file
return messageSource.getMessage("mysettings.invalid_username", vars);
}
});
Sample property file:
mysettings.invalid_username=The user name "${invalidUsername}" is incorrect.