From 38e7d072fbc982b42fad544d95828677b4e56938 Mon Sep 17 00:00:00 2001 From: Michael Simon <simon@kit.edu> Date: Wed, 19 Jun 2024 09:41:05 +0200 Subject: [PATCH] ISSUE-197 add oauth for account linking pages --- .../OAuthClientCallbackServiceImpl.java | 4 +- .../scc/webreg/bean/ConnectAccountBean.java | 9 + .../webreg/bean/ConnectAccountOAuthBean.java | 191 ++++++++++++++++++ .../webapp/user/connect-account-oauth.xhtml | 83 ++++++++ .../webapp/user/connect-account-oidc.xhtml | 41 ++-- .../webapp/user/connect-account-saml.xhtml | 41 ++-- .../main/webapp/user/connect-account.xhtml | 35 +++- bwreg-webapp/src/main/webapp/user/index.xhtml | 7 + 8 files changed, 368 insertions(+), 43 deletions(-) create mode 100644 bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountOAuthBean.java create mode 100644 bwreg-webapp/src/main/webapp/user/connect-account-oauth.xhtml diff --git a/bwreg-service/src/main/java/edu/kit/scc/webreg/service/oauth/client/OAuthClientCallbackServiceImpl.java b/bwreg-service/src/main/java/edu/kit/scc/webreg/service/oauth/client/OAuthClientCallbackServiceImpl.java index 16af6fed..533c6550 100644 --- a/bwreg-service/src/main/java/edu/kit/scc/webreg/service/oauth/client/OAuthClientCallbackServiceImpl.java +++ b/bwreg-service/src/main/java/edu/kit/scc/webreg/service/oauth/client/OAuthClientCallbackServiceImpl.java @@ -154,10 +154,10 @@ public class OAuthClientCallbackServiceImpl implements OAuthClientCallbackServic // Store OIDC Data temporarily in Session logger.debug("Storing relevant Oidc data in session"); - session.setSubjectId(userId); + session.setOauthId(userId); session.setAttributeMap(attributeMap); - httpServletResponse.sendRedirect("/user/connect-account-oidc.xhtml"); + httpServletResponse.sendRedirect("/user/connect-account-oauth.xhtml"); return; } } diff --git a/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountBean.java b/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountBean.java index 55539e68..d6019e8c 100644 --- a/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountBean.java +++ b/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountBean.java @@ -32,6 +32,7 @@ import edu.kit.scc.webreg.entity.UserEntity; import edu.kit.scc.webreg.entity.UserEntity_; import edu.kit.scc.webreg.entity.UserProvisionerEntity; import edu.kit.scc.webreg.entity.identity.IdentityEntity; +import edu.kit.scc.webreg.entity.oauth.OAuthRpConfigurationEntity; import edu.kit.scc.webreg.entity.oidc.OidcRpConfigurationEntity; import edu.kit.scc.webreg.entity.oidc.OidcUserEntity; import edu.kit.scc.webreg.service.SamlSpConfigurationService; @@ -144,6 +145,14 @@ public class ConnectAccountBean implements Serializable { } catch (IOException e) { messageGenerator.addErrorMessage("Ein Fehler ist aufgetreten", e.toString()); } + } else if (userProvisioner instanceof OAuthRpConfigurationEntity) { + OAuthRpConfigurationEntity rp = (OAuthRpConfigurationEntity) userProvisioner; + sessionManager.setOauthRelyingPartyId(rp.getId()); + try { + externalContext.redirect("/rpoauth/login"); + } catch (IOException e) { + messageGenerator.addErrorMessage("Ein Fehler ist aufgetreten", e.toString()); + } } else { messageGenerator.addWarningMessage("Keine Auswahl getroffen", "Bitte wählen Sie Ihre Heimatorganisation"); } diff --git a/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountOAuthBean.java b/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountOAuthBean.java new file mode 100644 index 00000000..67c7ca26 --- /dev/null +++ b/bwreg-webapp/src/main/java/edu/kit/scc/webreg/bean/ConnectAccountOAuthBean.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2014 Michael Simon. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Michael Simon - initial + ******************************************************************************/ +package edu.kit.scc.webreg.bean; + +import static edu.kit.scc.webreg.dao.ops.PaginateBy.unlimited; +import static edu.kit.scc.webreg.dao.ops.RqlExpressions.equal; +import static edu.kit.scc.webreg.dao.ops.SortBy.ascendingBy; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.slf4j.Logger; + +import edu.kit.scc.webreg.entity.SamlUserEntity_; +import edu.kit.scc.webreg.entity.UserEntity; +import edu.kit.scc.webreg.entity.UserEntity_; +import edu.kit.scc.webreg.entity.identity.IdentityEntity; +import edu.kit.scc.webreg.entity.oauth.OAuthRpConfigurationEntity; +import edu.kit.scc.webreg.entity.oauth.OAuthUserEntity; +import edu.kit.scc.webreg.exc.UserUpdateException; +import edu.kit.scc.webreg.service.UserService; +import edu.kit.scc.webreg.service.identity.IdentityService; +import edu.kit.scc.webreg.service.oauth.OAuthRpConfigurationService; +import edu.kit.scc.webreg.service.oauth.client.OAuthUserCreateService; +import edu.kit.scc.webreg.session.SessionManager; +import edu.kit.scc.webreg.util.FacesMessageGenerator; +import jakarta.faces.event.ComponentSystemEvent; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +@Named +@ViewScoped +public class ConnectAccountOAuthBean implements Serializable { + + private static final long serialVersionUID = 1L; + + @Inject + private Logger logger; + + @Inject + private UserService userService; + + @Inject + private IdentityService identityService; + + @Inject + private SessionManager sessionManager; + + @Inject + private FacesMessageGenerator messageGenerator; + + @Inject + private OAuthRpConfigurationService rpConfigService; + + @Inject + private OAuthUserCreateService userCreateService; + + private IdentityEntity identity; + private List<UserEntity> userList; + + private OAuthRpConfigurationEntity rpConfig; + + private String pin; + + private Boolean errorState = false; + + private Map<String, String> printableAttributesMap; + private Map<String, String> unprintableAttributesMap; + private List<String> printableAttributesList; + + private OAuthUserEntity entity; + + public void preRenderView(ComponentSystemEvent ev) { + if (identity == null) { + identity = identityService.fetch(sessionManager.getIdentityId()); + } + + if (sessionManager.getOauthRelyingPartyId() == null) { + errorState = true; + messageGenerator.addResolvedErrorMessage("page-not-directly-accessible", + "page-not-directly-accessible-text", true); + return; + } + + rpConfig = rpConfigService.fetch(sessionManager.getOauthRelyingPartyId()); + + if (rpConfig == null) { + errorState = true; + messageGenerator.addResolvedErrorMessage("page-not-directly-accessible", + "page-not-directly-accessible-text", true); + return; + } + + printableAttributesMap = new HashMap<String, String>(); + unprintableAttributesMap = new HashMap<String, String>(); + printableAttributesList = new ArrayList<String>(); + + try { + entity = userCreateService.preCreateUser(rpConfig.getId(), sessionManager.getLocale(), + sessionManager.getAttributeMap()); + + } catch (UserUpdateException e) { + errorState = true; + messageGenerator.addResolvedErrorMessage("missing-mandatory-attributes", e.getMessage(), true); + return; + } + + printableAttributesList.add("subject_id"); + printableAttributesMap.put("subject_id", entity.getOauthId()); + printableAttributesList.add("issuer"); + printableAttributesMap.put("issuer", rpConfig.getServiceUrl()); + printableAttributesList.add("name"); + printableAttributesMap.put("name", entity.getName()); + + @SuppressWarnings("unchecked") + HashMap<String, Object> userMap = (HashMap<String, Object>) sessionManager.getAttributeMap().get("user").get(0); + for (Entry<String, Object> entry : userMap.entrySet()) { + if (entry.getValue() != null) + unprintableAttributesMap.put(entry.getKey(), entry.getValue().toString()); + } + } + + public String save() { + try { + entity = userCreateService.createAndLinkUser(identity, entity, sessionManager.getAttributeMap(), null); + entity = userCreateService.postCreateUser(entity, sessionManager.getAttributeMap(), "user-" + entity.getId()); + } catch (UserUpdateException e) { + logger.warn("An error occured whilst creating user", e); + messageGenerator.addResolvedErrorMessage("error_msg", e.toString(), false); + return null; + } + + return "/user/index.xhtml"; + } + + public IdentityEntity getIdentity() { + return identity; + } + + public List<UserEntity> getUserList() { + if (userList == null) + userList = userService.findAllEagerly(unlimited(), Arrays.asList(ascendingBy(UserEntity_.id)), + equal(UserEntity_.identity, getIdentity()), UserEntity_.genericStore, UserEntity_.attributeStore, + SamlUserEntity_.idp); + + return userList; + } + + public String getPin() { + return pin; + } + + public void setPin(String pin) { + this.pin = pin; + } + + public Map<String, String> getPrintableAttributesMap() { + return printableAttributesMap; + } + + public List<String> getPrintableAttributesList() { + return printableAttributesList; + } + + public OAuthUserEntity getEntity() { + return entity; + } + + public Map<String, String> getUnprintableAttributesMap() { + return unprintableAttributesMap; + } + + public Boolean getErrorState() { + return errorState; + } + +} diff --git a/bwreg-webapp/src/main/webapp/user/connect-account-oauth.xhtml b/bwreg-webapp/src/main/webapp/user/connect-account-oauth.xhtml new file mode 100644 index 00000000..75a55c0b --- /dev/null +++ b/bwreg-webapp/src/main/webapp/user/connect-account-oauth.xhtml @@ -0,0 +1,83 @@ +<?xml version='1.0' encoding='UTF-8' ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:f="jakarta.faces.core" + xmlns:h="jakarta.faces.html" + xmlns:ui="jakarta.faces.facelets" + xmlns:bw="http://www.scc.kit.edu/bwfacelets" + xmlns:p="http://primefaces.org/ui" + xmlns:of="http://omnifaces.org/functions"> +<head> +<title></title> +</head> +<body> + +<f:view> +<f:metadata> + <f:event type="jakarta.faces.event.PreRenderViewEvent" + listener="#{connectAccountOAuthBean.preRenderView}" /> +</f:metadata> + +<ui:composition template="/template/default.xhtml"> + <ui:param name="title" value="#{messages.title}"/> + + <ui:define name="content"> + <h:form id="form" prependId="false" class="full"> + + <h3><h:outputText value="#{messages['connect_account.heading']}" /></h3> + + <div> + <h:outputText value="#{messages['connect_account.intro2']}" /> + </div> + + <p:messages id="messageBox" showDetail="true" /> + + <div class="text"> + <ul> + <ui:repeat var="user" value="#{connectAccountOAuthBean.userList}"> + <li> + <h:panelGroup rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> + <h:outputText value="#{user.idp.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> + <h:outputText value="#{user.issuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OAuthUserEntity'}"> + <h:outputText value="#{user.oauthIssuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + </li> + </ui:repeat> + </ul> + </div> + + <div> + <h:outputText value="#{messages['connect_account.intro3']}" escape="false"/> + </div> + + <p:dataTable var="key" value="#{connectAccountOAuthBean.printableAttributesList}"> + <p:column headerText="#{messages.name}"> + #{resourceBundleHelper.resolveMessage(key)} + </p:column> + <p:column headerText="#{messages.value}"> + #{connectAccountOAuthBean.printableAttributesMap[key]} + </p:column> + </p:dataTable> + + <p:dataList var="key" value="#{connectAccountOAuthBean.unprintableAttributesMap.keySet().toArray()}"> + #{key}: #{connectAccountOAuthBean.unprintableAttributesMap[key]} + </p:dataList> + + <div class="form"> + <p:commandButton id="save" action="#{connectAccountOAuthBean.save}" value="#{messages['connect_account.commit']}" ajax="false" + disabled="#{connectAccountOAuthBean.errorState}" update=":form"/> + </div> + </h:form> + </ui:define> +</ui:composition> +</f:view> +</body> +</html> diff --git a/bwreg-webapp/src/main/webapp/user/connect-account-oidc.xhtml b/bwreg-webapp/src/main/webapp/user/connect-account-oidc.xhtml index 6ec5ceff..d4d846cf 100644 --- a/bwreg-webapp/src/main/webapp/user/connect-account-oidc.xhtml +++ b/bwreg-webapp/src/main/webapp/user/connect-account-oidc.xhtml @@ -23,7 +23,7 @@ <ui:param name="title" value="#{messages.title}"/> <ui:define name="content"> - <h:form id="form" prependId="false" class="full form"> + <h:form id="form" prependId="false" class="full"> <h3><h:outputText value="#{messages['connect_account.heading']}" /></h3> @@ -32,17 +32,27 @@ </div> <p:messages id="messageBox" showDetail="true" /> - - <p:dataList var="user" value="#{connectAccountOidcBean.userList}" type="unordered"> - <p:outputPanel rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> - <h:outputText value="#{user.idp.displayName}" /> - <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> - </p:outputPanel> - <p:outputPanel rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> - <h:outputText value="#{user.issuer.displayName}" /> - <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> - </p:outputPanel> - </p:dataList> + + <div class="text"> + <ul> + <ui:repeat var="user" value="#{connectAccountOidcBean.userList}"> + <li> + <h:panelGroup rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> + <h:outputText value="#{user.idp.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> + <h:outputText value="#{user.issuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OAuthUserEntity'}"> + <h:outputText value="#{user.oauthIssuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + </li> + </ui:repeat> + </ul> + </div> <div> <h:outputText value="#{messages['connect_account.intro3']}" escape="false"/> @@ -61,9 +71,10 @@ #{key}: #{connectAccountOidcBean.unprintableAttributesMap[key]} </p:dataList> - <p:commandButton id="save" action="#{connectAccountOidcBean.save}" value="#{messages['connect_account.commit']}" ajax="false" - disabled="#{connectAccountOidcBean.errorState}" update=":form"/> - + <div class="form"> + <p:commandButton id="save" action="#{connectAccountOidcBean.save}" value="#{messages['connect_account.commit']}" ajax="false" + disabled="#{connectAccountOidcBean.errorState}" update=":form"/> + </div> </h:form> </ui:define> </ui:composition> diff --git a/bwreg-webapp/src/main/webapp/user/connect-account-saml.xhtml b/bwreg-webapp/src/main/webapp/user/connect-account-saml.xhtml index 8bba2867..8feff9d7 100644 --- a/bwreg-webapp/src/main/webapp/user/connect-account-saml.xhtml +++ b/bwreg-webapp/src/main/webapp/user/connect-account-saml.xhtml @@ -23,7 +23,7 @@ <ui:param name="title" value="#{messages.title}"/> <ui:define name="content"> - <h:form id="form" prependId="false" class="full form"> + <h:form id="form" prependId="false" class="full"> <h3><h:outputText value="#{messages['connect_account.heading']}" /></h3> @@ -32,17 +32,27 @@ </div> <p:messages id="messageBox" showDetail="true" /> - - <p:dataList var="user" value="#{connectAccountSamlBean.userList}" type="unordered"> - <p:outputPanel rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> - <h:outputText value="#{user.idp.displayName}" /> - <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> - </p:outputPanel> - <p:outputPanel rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> - <h:outputText value="#{user.issuer.displayName}" /> - <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> - </p:outputPanel> - </p:dataList> + + <div class="text"> + <ul> + <ui:repeat var="user" value="#{connectAccountSamlBean.userList}"> + <li> + <h:panelGroup rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> + <h:outputText value="#{user.idp.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> + <h:outputText value="#{user.issuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OAuthUserEntity'}"> + <h:outputText value="#{user.oauthIssuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + </li> + </ui:repeat> + </ul> + </div> <div> <h:outputText value="#{messages['connect_account.intro3']}" escape="false"/> @@ -61,9 +71,10 @@ #{key}: #{connectAccountSamlBean.unprintableAttributesMap[key]} </p:dataList> - <p:commandButton id="save" action="#{connectAccountSamlBean.save}" value="#{messages['connect_account.commit']}" ajax="false" - disabled="#{connectAccountSamlBean.errorState}" update=":form"/> - + <div class="form"> + <p:commandButton id="save" action="#{connectAccountSamlBean.save}" value="#{messages['connect_account.commit']}" ajax="false" + disabled="#{connectAccountSamlBean.errorState}" update=":form"/> + </div> </h:form> </ui:define> </ui:composition> diff --git a/bwreg-webapp/src/main/webapp/user/connect-account.xhtml b/bwreg-webapp/src/main/webapp/user/connect-account.xhtml index 23c00499..d4ee0a1f 100644 --- a/bwreg-webapp/src/main/webapp/user/connect-account.xhtml +++ b/bwreg-webapp/src/main/webapp/user/connect-account.xhtml @@ -23,7 +23,7 @@ <ui:param name="title" value="#{messages.title}"/> <ui:define name="content"> - <h:form id="form" prependId="false" class="full form"> + <h:form id="form" prependId="false" class="full"> <h3><h:outputText value="#{messages['connect_account.heading']}" /></h3> @@ -34,15 +34,28 @@ <div style="margin-top: 1em;"> <h:outputText value="#{messages['connect_account.list_accounts']}:" /> </div> + <div class="text"> + <ul> + <ui:repeat var="user" value="#{connectAccountBean.userList}"> + <li> + <h:panelGroup rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> + <h:outputText value="#{user.idp.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> + <h:outputText value="#{user.issuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + <h:panelGroup rendered="#{user.class.simpleName eq 'OAuthUserEntity'}"> + <h:outputText value="#{user.oauthIssuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> + </h:panelGroup> + </li> + </ui:repeat> + </ul> + </div> + <p:dataList var="user" value="#{connectAccountBean.userList}" type="unordered"> - <p:outputPanel rendered="#{user.class.simpleName eq 'SamlUserEntity'}"> - <h:outputText value="#{user.idp.displayName}" /> - <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> - </p:outputPanel> - <p:outputPanel rendered="#{user.class.simpleName eq 'OidcUserEntity'}"> - <h:outputText value="#{user.issuer.displayName}" /> - <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(user.id)}"/> - </p:outputPanel> </p:dataList> <div class="grid"> @@ -70,10 +83,10 @@ </p:selectOneListbox> </p:outputPanel> - <p:outputPanel id="btnPanel"> + <h:panelGroup id="btnPanel" style="block" styleClass="form"> <p:commandButton id="login" action="#{connectAccountBean.startConnect()}" value="#{messages.proceed}" disabled="#{empty connectAccountBean.selected}" update=":form" /> - </p:outputPanel> + </h:panelGroup> </div> diff --git a/bwreg-webapp/src/main/webapp/user/index.xhtml b/bwreg-webapp/src/main/webapp/user/index.xhtml index df263683..156cbb3d 100644 --- a/bwreg-webapp/src/main/webapp/user/index.xhtml +++ b/bwreg-webapp/src/main/webapp/user/index.xhtml @@ -44,6 +44,13 @@ <h:outputText value="#{u.issuer.displayName}" /> <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(u.id)}"/> </p:outputPanel> + <p:outputPanel rendered="#{u.class.simpleName eq 'OAuthUserEntity'}"> + <h:outputText value="#{u.eppn} - " rendered="#{u.eppn != null}"/> + <h:outputText value="#{u.email} - " rendered="#{u.eppn == null and u.email != null}"/> + <h:outputText value="#{u.oauthId} - " rendered="#{u.eppn == null and u.email == null}"/> + <h:outputText value="#{u.oauthIssuer.displayName}" /> + <h:outputText value=" (#{messages.currently_logged_id})" rendered="#{sessionManager.loggedInUserList.contains(u.id)}"/> + </p:outputPanel> </p:column> <f:ajax render=":form:tabVw" execute="@this" /> </p:selectOneListbox> -- GitLab