001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *                               ***
008 *
009 *   Community License: GPL 3.0
010 *
011 *   This file is free software: you can redistribute it and/or modify
012 *   it under the terms of the GNU General Public License as published
013 *   by the Free Software Foundation, either version 3 of the License,
014 *   or (at your option) any later version.
015 *
016 *   This file is distributed in the hope that it will be useful, but
017 *   WITHOUT ANY WARRANTY; without even the implied warranty of
018 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019 *   GNU General Public License for more details.
020 *
021 *   You should have received a copy of the GNU General Public License
022 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
023 *
024 *                               ***
025 *
026 *   Available Commercial License: GraniteDS SLA 1.0
027 *
028 *   This is the appropriate option if you are creating proprietary
029 *   applications and you are not prepared to distribute and share the
030 *   source code of your application under the GPL v3 license.
031 *
032 *   Please visit http://www.granitedataservices.com/license for more
033 *   details.
034 */
035package org.granite.client.tide;
036
037import java.beans.PropertyChangeListener;
038import java.beans.PropertyChangeSupport;
039import java.nio.charset.Charset;
040import java.util.Observable;
041import java.util.Observer;
042import java.util.concurrent.Future;
043
044import org.granite.client.messaging.messages.responses.FaultMessage;
045import org.granite.client.messaging.messages.responses.FaultMessage.Code;
046import org.granite.client.tide.impl.ComponentImpl;
047import org.granite.client.tide.server.ExceptionHandler;
048import org.granite.client.tide.server.ServerSession;
049import org.granite.client.tide.server.SimpleTideResponder;
050import org.granite.client.tide.server.TideFaultEvent;
051import org.granite.client.tide.server.TideResponder;
052import org.granite.client.tide.server.TideResultEvent;
053
054
055public abstract class BaseIdentity extends ComponentImpl implements Identity, ExceptionHandler {
056        
057        private boolean loggedIn;
058        private String username;
059        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
060
061        public boolean isLoggedIn() {
062                return loggedIn;
063        }
064        
065        public void setLoggedIn(boolean loggedIn) {
066                if (loggedIn == this.loggedIn)
067                        return;
068                
069                this.loggedIn = loggedIn;
070                if (loggedIn)
071                        getServerSession().afterLogin();
072                else
073                        setUsername(null);
074                
075                pcs.firePropertyChange("loggedIn", !loggedIn, loggedIn);
076        }
077        
078        public String getUsername() {
079                return username;
080        }
081        
082        protected void setUsername(String username) {
083                String oldUsername = this.username;
084                this.username = username;
085                
086                if ((username == null && oldUsername != null) || (username != null && !username.equals(oldUsername)))
087                        pcs.firePropertyChange("username", oldUsername, username);
088        }
089        
090    
091    public BaseIdentity(final ServerSession serverSession) {
092        super(serverSession);
093        
094        loggedIn = false;
095    }
096    
097    /**
098     *  Triggers a remote call to check is user is currently logged in
099     *  Can be used at application startup to handle browser refresh cases
100     * 
101     *  @param tideResponder a responder for the remote call
102     *  @return future result returning the username if logged in or null
103     */
104    public Future<String> checkLoggedIn(final TideResponder<String> tideResponder) {
105        return super.call("isLoggedIn", new SimpleTideResponder<String>() {
106                        @Override
107                        public void result(TideResultEvent<String> event) {
108                                if (event.getResult() != null) {
109                                        setUsername(event.getResult());
110                                        setLoggedIn(true);
111                                }
112                                else if (isLoggedIn()) {
113                                        setLoggedIn(false);
114                                        
115                                        // Session expired, directly mark the channel as logged out
116                                        getServerSession().sessionExpired();
117                                }
118                                
119                                if (tideResponder != null)
120                                        tideResponder.result(event);
121                        }
122                        
123                        @Override
124                        public void fault(TideFaultEvent event) {
125                                if (event.getFault().getCode() == Code.ACCESS_DENIED) {
126                                        // Not in role for the destination
127                                        setLoggedIn(false);
128                                        
129                                        getServerSession().logout(null);
130                                }
131                                
132                                if (tideResponder != null)
133                                        tideResponder.fault(event);
134                        }
135        });
136    }
137    
138    
139    public void login(final String username, String password, final TideResponder<String> tideResponder) {
140        getServerSession().login(username, password);
141        
142        clearSecurityCache();
143        
144        try {
145            // Force synchronous operation to prevent issues with Spring session fixation protection
146            // so next remote calls use the correct session id
147            checkLoggedIn(tideResponder).get();
148        }
149        catch (Exception e) {
150        }
151    }
152
153    public void login(final String username, String password, Charset charset, final TideResponder<String> tideResponder) {
154        getServerSession().login(username, password, charset);
155        
156        clearSecurityCache();
157        
158        try {
159            // Force synchronous operation to prevent issues with Spring session fixation protection
160            // so next remote calls use the correct session id
161            checkLoggedIn(tideResponder).get();
162        }
163        catch (Exception e) {
164        }
165    }
166    
167    
168    public void logout(final TideResponder<Void> tideResponder) {
169        final Observer observer = new Observer() {
170                        @SuppressWarnings("unchecked")
171                        @Override
172                        public void update(Observable logout, Object event) {
173                        setLoggedIn(false);
174                        
175                        if (tideResponder != null) {
176                                        if (event instanceof TideResultEvent)
177                                        tideResponder.result((TideResultEvent<Void>)event);
178                                        else if (event instanceof TideFaultEvent)
179                                        tideResponder.fault((TideFaultEvent)event);
180                        }
181                        }
182        };
183        
184        getServerSession().logout(observer);
185    }
186
187    
188        @Override
189        public boolean accepts(FaultMessage emsg) {
190                return emsg.getCode() == Code.NOT_LOGGED_IN;
191        }
192
193        @Override
194        public void handle(Context context, FaultMessage emsg, TideFaultEvent faultEvent) {
195                if (isLoggedIn()) {
196                        setLoggedIn(false);
197                        
198                        // Session expired, directly mark the channel as logged out
199                        getServerSession().sessionExpired();
200                }
201        }
202        
203        
204        public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
205                pcs.addPropertyChangeListener(propertyName, listener);
206        }
207        
208        public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
209                pcs.removePropertyChangeListener(propertyName, listener);
210        }
211        
212        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
213                pcs.firePropertyChange(propertyName, oldValue, newValue);
214        }
215        
216    /**
217     *  Clear the security cache
218     */
219    public void clearSecurityCache() {        
220    }
221}