Spring LdapTemplate : How to change the connection user? - spring-ldap

I started with
a LdapContextSource bean which sets the Url, Base, UserDn and Password
a LdapTemplate bean with previously set contexteSource in constructor
It works BUT I cannot change user name and password initially set and have LdapTemplate subsequently working.
In my project, each http requests transports the connection user in its header, so connection user may differ at each endpoint
Hence I tried to reference an authenticationSource as follows :
public LdapContextSource contextSource () {
LdapContextSource contextSource= new LdapContextSource();
contextSource.setUrl(ldapUrls);
contextSource.setBase(ldapBaseDn);
contextSource.setAuthenticationSource(new SpringSecurityAuthenticationSource());
return contextSource;}
When I got a new connection user, I tried to update my LdapTemplate instance like that :
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(userDn,credentials));
but when a method of my LdapTemplate instance is invoked (for instance search)I get :
java.lang.IllegalArgumentException: The principal property of the authentication object needs to be an LdapUserDetails

Related

How close the gab between websocket server endpoint and session scoped backing beans

In short, I don't know how I notify a session scoped bean from a application scoped bean. I found a dirty hack that work, but I'm not sure there is a better way to solve this problem (or my dirty hack is not dirty :-D ).
Every websocket in java ee has a sessionid. But this sessionid is not the same like in jsf, and there is no way for a easy mapping. In my environment I have a jsf webpage, an underlying sessionscoped backing bean and a websocket, that is connected to an external service via jms. When the jsf page is loaded, and the websocket is also connected to the browser, the backing bean sends a request to the external service. When I got a async answer message via jms I dont know which websocket is connected with the jsf page/backing bean that send the request.
To solve this problem with a partly dirty hack, I wrote an application scoped mediator class.
#Named
#ApplicationScoped
public class WebsocketMediator {
#Inject
private Event<UUID> notifyBackingBeans;
private Integer newSequenceId=0;
// I need this map for the dirty hack
private Map<UUID, BackingBean> registrationIdBackingBeanMap;
private Map<Integer, UUID> sequenceIdRegistrationIdMap;
registrationIdWebSocketMap = new ConcurrentHashMap<>();
public UUID register(BackingBean backingBean) {
UUID registrationId = UUID.randomUUID();
registrationIdSequenceMap.put(registrationId, new HashSet<>());
registrationIdBackinBeanMap.put(registrationId, backingBean);
}
public Integer getSequenceId(UUID registrationId) {
sequenceId++;
sequenceIdRegistrationIdMap.put(sequenceId, registrationId);
registrationIdSequenceMap.get(registrationId).add(sequenceId);
return sequenceId;
}
// this is called from the ws server enpoint
public void registerWebsocket(UUID registrationId, Session wsSession) {
registrationIdWebSocketMap.put(registrationId, wsSession);
websocketRegistrationIdMap.put(wsSession.getId(), registrationId);
notifyBackingBeans.fire(registrationId); // This does not work
SwitchDataModel switchDataModel = registrationIdSwitchDataModelMap.get(registrationId);
if (backingBean != null) {
backingBean.dirtyHackTrigger();
}
}
public void unregisterWebsocket(String wsSessionId) {
...
}
}
The backing bean calls a registration method and gets a uniq random registration id (uuid) . The registration id is placed in a jsf table as a hidden data attribute (f:passTrough). When the websocket is connected, the ws.open function is called in the browser and send the registration id via the websocket to the websocket server endpoint class. The Server endpoint class call the public void registerWebsocket(UUID registrationId, Session wsSession) method in the mediator and the registration id is mapped. When the backing bean is timed out, I call a unregister method from an #PreDestroyed annotated method. Every time, when the external system is called via jms, I put a sequence id into the payload. The Sequence Id is registered in the Mediator class. Every time when the external system sends a message I can lookup the correct websocket in the mediator to bypass the message via the websocket to the correct browser.
Now the system is able to receive async events via the external system, but the backing bean doesn't know that. I tried to send the registration id in a cdi event to the session scoped backing bean, but the event never reach an observer in a session scoped backing bean. So I realized this gab with a dirty hack. I put the instance of every backing bean with the registration id as key into a map in the mediator in the registration method. I placed in the public void registerWebsocket(UUID registrationId, Session wsSession) a dirty hack trigger call of the backing bean. Is there a better solution?
I use Wildfly 13 with CDI 1.2.
Thanks in advance!
I found a solution. When the webpage is called, the #SessionScoped bean is created. I calculate a unique registration id and put it as attribute in the HttpSession:
#PostConstruct
public void register() {
registrationId = switchPortMediator.register(this);
HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);
session.setAttribute("switchRegistrationId", registrationId);
log.debug("Registered at mediator - ID=" + registrationId + " http session = "+ session.getId());
}
At the #ServerEndpoint annotated web socket endpoint, is the #OnOpen annotated method. This method is called when the webpage is loaded and the websocket is established. The websocket endpoint class is #ApplicationScoped. The attribute map, from the #SessionScoped bean, where the registration id is stored, is accessable in the EndpointConfig of the websocket endpoint. Here is the #OnOpen annotated method:
#OnOpen
public void onOpen(Session wsSession, EndpointConfig config) {
UUID registrationId = (UUID) config.getUserProperties().get("switchRegistrationId");
websocketRegistrationIdMap.put(registrationId, wsSession);
}
When the JSF page is loaded, the #SessionScoped bean calculate the registration id, put it in the attribute map and sent an async message via jms to an external system. The external system send an answer message, with the registration id inside and a payload. When the external message arrives via JMS in the websocket endpoint class, the resulting wsSession can be retrived from the websocketRegistrationIdMap and the payload can be send via the websocket to the browser, who initiate the async message. The dom updates in the website are processed by javascript.

FacesContext and what is ClientId

Can you explain me why we need in String clientId in the method
void addMessage(String ClientId, FacesMessage msg);?
Is it true that when client sent an initial request to server then server assign to this client a specific String ClientId. As i understand FacesContext created when aplication deployed and runnig the first time and this FacesContext is unique for application. Is it true?
Client id in FacesContext#addMessage(String clientId, FacesMessage message) is needed to be able to add a specific message to a specific component. You can either specify a client id of the component you'd like to assign the message to, or null to add the message to the messages stack not tied to a specific component. The latter will be updated in e.g. <h:message for="clientId>, while the latter in e.g. <h:messages globalonly="true">. You can add a message to global messages in case it is not component-specific, for instance, when database operation failed.

Invalidate Session of a specific user

So for my webapp, if I remove a user that is currently logged in, and I want to invalidate his/her session. So that as soon as he/she refresh the page or navigate, they are no longer log in. The way I have now is that if a User logged in successfully, I will store the user object in my SessionScoped bean, and store the HttpSession to the Application Map. Below is my code
This is my SessionScoped bean
#PostConstruct
public void init() {
User user = UserDAO.findById(userId, password);
Map<String, Object> appMap = FacesContext.getCurrentInstance().
getExternalContext().getApplicationMap();
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().
getExternalContext().getSession(false);
appMap.put(userId, session);
}
Is this a correct approach? If so, how do I clean up my application map?
Is this a correct approach?
There are basically 2 ways.
Store the HttpSession handle in the application scope by the user ID as key so that you can get a handle of it and invalidate it. This may work for a small web application running on a single server, but may not work on a web application running on a cluster of servers, depending on its configuration.
I would only store it in another map in the application scope, not directly in the application scope like as you did, so that you can easier get an overview of all users and that you can guarantee that an arbitrary user ID won't clash with an existing application scoped managed bean name, for example.
Add a new boolean/bit column to some DB table associated with the user which is checked on every HTTP request. If the admin sets it to true, then the session associated with the request will be invalidated and the value in the DB will be set back to false.
how do I clean up my application map?
You could use HttpSessionListener#sessionDestroyed() for this. E.g.
public void sessionDestroyed(HttpSessionEvent event) {
User user = (User) event.getSession().getAttribute("user");
if (user != null) {
Map<User, HttpSession> logins = (Map<User, HttpSession>) event.getSession().getServletContext().getAttribute("logins");
logins.remove(user);
}
}
I think you can use your approach (with some modifications proposed by #BalusC) plus some notification mechanism (to make it work in distributed environment). You can do one of the following:
Use a topic queue subscribed by all your servers. When you remove user from your admin panel the JMS message will be created and sent to the topic. Every server will be responsible for invalidating the user session if it exists on the particular server (if the session is referenced in servletContext map).
Implement some action to invalidate the user session and run this action on every server in the cluster (The admin panel should send HTTP request to every server).
Use JGroups and TCP reliable multicast.
All of these solutions are not simple but much faster than polling the DB server on every request.

Why does CredentialCache.DefaultCredential contain empty strings for domain, username, and password

Does anyone have any ideas as to why CredentialCache.DefaultCredential would return an ICredential instance with empty strings for domain, username, and password? I'm running a WCF service on IIS 7.5. It works fine on one server but never works on another. I have verified that the IIS application has Windows Authentication enabled....
Here is how it's being used:
string url = string.Format("{0}/departments/finance/_vti_bin/listdata.svc", _IntranetAddress);
var financeDataContext = new FinanceDataContext(new Uri(url))
{
Credentials = CredentialCache.DefaultCredentials
};
I am not sure how it is working in one of your servers? I hope you already read this
http://msdn.microsoft.com/en-us/library/system.net.credentialcache.defaultcredentials.aspx
but it clearly says "The ICredentials instance returned by DefaultCredentials cannot be used to view the user name, password, or domain of the current security context."
The NetworkCredential returned from CredentialCache.DefaultCredential is just a placeholder. If you look at it using the Debugger, you'll see that it's of type SystemNetworkCredential. Internal API check for this type to see if integrated authentication should be used or not. There are other ways to get the current username (like WindowsIdentity.GetCurrent()).
EDIT:
To specify impersonation for a WCF operation, add this attribute to the method implementing a contract:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void SomeMethod()
{
// do something here
}

SocialAuthManager object ('manager') becomes NULL after redirection in case of SocialAuth libraries with JSF application?

I am using SocialAuth libraries in my JSF application for providing 'login with google/facebook'. As shown below it requires me to stores the SocialAuthManager object ('manager') in the session and then redirect to 'google/facebook' URL
//Create an instance of SocialAuthManager and set config
SocialAuthManager manager = new SocialAuthManager();
manager.setSocialAuthConfig(config);
// URL of YOUR application which will be called after authentication
String successUrl= "http://opensource.brickred.com/socialauthdemo/socialAuthSuccessAction.do";
// get Provider URL to which you should redirect for authentication.
// id can have values "facebook", "twitter", "yahoo" etc. or the OpenID URL
String url = manager.getAuthenticationUrl(id, successUrl);
// Store in session
session.setAttribute("authManager", manager);
Then get the 'manager' from session on succssfull/failure redirection from facebook/redirect as shown below:
// get the social auth manager from session
SocialAuthManager manager = (SocialAuthManager)session.getAttribute("authManager");
// call connect method of manager which returns the provider object.
// Pass request parameter map while calling connect method.
AuthProvider provider = manager.connect(SocialAuthUtil.getRequestParametersMap(request));
// get profile
Profile p = provider.getUserProfile();
The problem is if I am already logged in to facebook or google in a one of the 'tab' of the browser then this works perfectly OK. But if I am not logged in already then session becomes NULL and consequently 'manager' as well.
In other words if redirection from 'my application to facebook to my application' happens then it fails. If I am already logged in to facebook then redirection does not happens and it works.
Can someone help?
NOTE: this works perfectly well in case of IE but does not work in case of Chrome & Mozila
the reason for this behavior is that you are calling the redirected page from different domain so when page redirection happens your session data is lost.
please have a look at this link
http://31stdimension.blogspot.in/2012/04/how-to-connect-facebook-using-jsfjava.html

Resources