AuthentificationManager.java
/*
* Copyright (C) 2000 - 2025 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.silverpeas.mobile.client.common;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.*;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.googlecode.gwt.crypto.bouncycastle.InvalidCipherTextException;
import com.googlecode.gwt.crypto.client.TripleDesCipher;
import org.silverpeas.mobile.client.SpMobil;
import org.silverpeas.mobile.client.common.event.ErrorEvent;
import org.silverpeas.mobile.client.common.event.authentication.AuthenticationErrorEvent;
import org.silverpeas.mobile.client.common.navigation.PageHistory;
import org.silverpeas.mobile.client.pages.connexion.TwoFactorPage;
import org.silverpeas.mobile.client.pages.connexion.TwoFactorEnrollmentPage;
import org.silverpeas.mobile.client.common.network.NetworkHelper;
import org.silverpeas.mobile.client.common.network.rest.RestCallback;
import org.silverpeas.mobile.client.common.network.rest.RestMethod;
import org.silverpeas.mobile.client.common.network.rest.RestMethodCallbackOnlineOnly;
import org.silverpeas.mobile.client.common.resources.ResourcesManager;
import org.silverpeas.mobile.client.common.storage.LocalStorageHelper;
import org.silverpeas.mobile.client.common.storage.CacheStorageHelper;
import org.silverpeas.mobile.shared.dto.DetailUserDTO;
import org.silverpeas.mobile.shared.dto.FullUserDTO;
import org.silverpeas.mobile.shared.dto.IFullUser;
import org.silverpeas.mobile.shared.dto.authentication.IUserProfile;
import org.silverpeas.mobile.shared.dto.authentication.UserProfileDTO;
import org.silverpeas.mobile.shared.exceptions.AuthenticationException;
/**
* @author: svu
*/
public class AuthentificationManager {
private static AuthentificationManager instance = null;
private static final String USER_CONNECTED_KEY = "userConnected";
public static final String USER_PROFIL = "userProfil";
public static final String XSTKN = "X-STKN";
public static final String LOCAL_CREDENTIAL = "localCredential";
public static final String XSilverpeasSession = "X-Silverpeas-Session";
public static AuthentificationManager getInstance() {
if (instance == null) {
instance = new AuthentificationManager();
}
return instance;
}
public void addHeader(String key, String value) {
LocalStorageHelper.getInstance().store(key, value);
}
public String getHeader(String key) {
return LocalStorageHelper.getInstance().load(key);
}
public void storeUser(final DetailUserDTO user, final UserProfileDTO profil, String login,
String password, String domainId) {
String encryptedPassword = null;
try {
encryptedPassword = encryptPassword(password);
} catch (InvalidCipherTextException e) {
EventBus.getInstance().fireEvent(new ErrorEvent(e));
}
SpMobil.setUser(user, false);
FullUserDTO u = new FullUserDTO(login, encryptedPassword, domainId, user);
String maintainSession = ResourcesManager.getParam("maintain.session");
if (maintainSession.equalsIgnoreCase("true")) {
LocalStorageHelper.getInstance().store(USER_CONNECTED_KEY, u.getAutoBean());
LocalStorageHelper.getInstance().store(USER_PROFIL, profil.getAutoBean());
}
}
public void updateAvatarInCache(final String avatarData) {
SpMobil.getUser().setAvatar(avatarData);
SpMobil.getUserProfile().setAvatar(avatarData);
FullUserDTO user = FullUserDTO.getBean(LocalStorageHelper.getInstance().load(USER_CONNECTED_KEY, IFullUser.class));
user.setAvatar(avatarData);
String maintainSession = ResourcesManager.getParam("maintain.session");
if (maintainSession.equalsIgnoreCase("true")) {
LocalStorageHelper.getInstance().store(USER_CONNECTED_KEY, user.getAutoBean());
LocalStorageHelper.getInstance().store(USER_PROFIL, SpMobil.getUserProfile().getAutoBean());
}
}
/**
* Clean data in local storage while preserving local credentials.
*/
public void clearLocalStorage() {
LocalStorageHelper.getInstance().clearExcept(LOCAL_CREDENTIAL);
}
public FullUserDTO loadUser() {
FullUserDTO user = FullUserDTO.getBean(LocalStorageHelper.getInstance().load(USER_CONNECTED_KEY, IFullUser.class));
SpMobil.setUser(user, false);
UserProfileDTO profil =
UserProfileDTO.getBean(LocalStorageHelper.getInstance().load(USER_PROFIL, IUserProfile.class));
SpMobil.setUserProfile(profil);
return user;
}
/**
* Decrypt password in local storage.
*
* @param passwordEncrysted
* @return
*/
public String decryptPassword(String passwordEncrysted) {
TripleDesCipher cipher = new TripleDesCipher();
cipher.setKey(ResourcesManager.getParam("mobile.credentials.encryption.key").getBytes());
String plainPassword = null;
try {
plainPassword = cipher.decrypt(passwordEncrysted);
} catch (Exception e) {
EventBus.getInstance().fireEvent(new ErrorEvent(e));
}
return plainPassword;
}
private String encryptPassword(String password) throws InvalidCipherTextException {
TripleDesCipher cipher = new TripleDesCipher();
cipher.setKey(ResourcesManager.getParam("mobile.credentials.encryption.key").getBytes());
String encryptedPassword = cipher.encrypt(password);
return encryptedPassword;
}
public void authenticateOnSilverpeas(String login, String password, String domainId,
Command attempt) {
Notification.activityStart();
if (NetworkHelper.isOnline()) {
ServicesLocator.getRestServiceAuthentication(login, password, domainId).authentication(
new RestCallback<UserProfileDTO>() {
@Override
public void onFailure(final RestMethod method, final Throwable throwable) {
String enrollmentRequired = method.getHeaders() == null
? null : method.getHeaders().get("X-Silverpeas-2FA-Enrollment-Required");
if (method.getStatusCode() == 401 &&
"true".equalsIgnoreCase(enrollmentRequired)) {
showEnrollmentPage(login, password, domainId);
return;
}
String twoFactorRequired = method.getHeaders() == null
? null
: method.getHeaders().get("X-Silverpeas-2FA-Required");
if (method.getStatusCode() == 401 &&
"true".equalsIgnoreCase(twoFactorRequired)) {
authenticateTrustedDevice(login, password, domainId, attempt);
return;
}
handleAuthenticationFailure(login, domainId, method, throwable);
}
@Override
public void onSuccess(final RestMethod method, final UserProfileDTO userProfile) {
completeAuthentication(login, password, domainId, method, userProfile, attempt);
}
});
} else {
Notification.activityStop();
loadUser();
SpMobil.displayMainPage();
}
}
public void authenticateTwoFactor(final String login, final String password,
final String domainId, final String code, final boolean trustDevice,
final Command attempt) {
authenticateTwoFactor(login, password, domainId, code, trustDevice, attempt, null);
}
public void authenticateTwoFactor(final String login, final String password,
final String domainId, final String code, final boolean trustDevice,
final Command attempt, final Command onInvalidCode) {
Notification.activityStart();
ServicesLocator.getRestServiceAuthentication(login, password, domainId)
.authenticateTwoFactor(code, trustDevice, new RestCallback<UserProfileDTO>() {
@Override
public void onFailure(final RestMethod method, final Throwable throwable) {
Notification.activityStop();
if (method.getStatusCode() == 401) {
if (onInvalidCode != null) {
onInvalidCode.execute();
return;
}
EventBus.getInstance().fireEvent(new AuthenticationErrorEvent(throwable));
} else {
EventBus.getInstance().fireEvent(new ErrorEvent(throwable));
}
}
@Override
public void onSuccess(final RestMethod method, final UserProfileDTO userProfile) {
completeAuthentication(login, password, domainId, method, userProfile, attempt);
}
});
}
private void authenticateTrustedDevice(final String login, final String password,
final String domainId, final Command attempt) {
ServicesLocator.getRestServiceAuthentication(login, password, domainId)
.authenticateTrustedDevice(new RestCallback<UserProfileDTO>() {
@Override
public void onFailure(final RestMethod method, final Throwable throwable) {
showTwoFactorPage(login, password, domainId);
}
@Override
public void onSuccess(final RestMethod method, final UserProfileDTO userProfile) {
completeAuthentication(login, password, domainId, method, userProfile, attempt);
}
});
}
private void showEnrollmentPage(final String login, final String password,
final String domainId) {
Notification.activityStop();
com.google.gwt.user.client.ui.RootPanel.get().clear();
com.google.gwt.user.client.ui.RootPanel.get().add(
new TwoFactorEnrollmentPage(login, password, domainId));
}
public void completeEnrollment(final String login, final String password,
final String domainId, final RestMethod method, final UserProfileDTO profile) {
completeAuthentication(login, password, domainId, method, profile, null);
}
private void showTwoFactorPage(final String login, final String password,
final String domainId) {
Notification.activityStop();
TwoFactorPage page = new TwoFactorPage();
page.setIds(login, password, domainId);
com.google.gwt.user.client.ui.RootPanel.get().clear();
com.google.gwt.user.client.ui.RootPanel.get().add(page);
}
private void completeAuthentication(final String login, final String password,
final String domainId, final RestMethod authenticationMethod,
final UserProfileDTO userProfile, final Command attempt) {
addHeader(XSTKN, authenticationMethod.getHeaders().get(XSTKN));
addHeader(XSilverpeasSession, authenticationMethod.getHeaders().get(XSilverpeasSession));
SpMobil.setUserProfile(userProfile);
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<DetailUserDTO>() {
@Override
public void attempt() {
super.attempt();
ServicesLocator.getServiceConnection().login(this);
}
@Override
public void onSuccess(final RestMethod method, final DetailUserDTO user) {
super.onSuccess(method, user);
SpMobil.setUser(user, true);
ServicesLocator.getServiceTermsOfService().show(new RestMethodCallbackOnlineOnly<String>() {
@Override
public void onFailure(final RestMethod method, final Throwable throwable) {
if (throwable instanceof AuthenticationException) {
EventBus.getInstance().fireEvent(new AuthenticationErrorEvent(throwable));
} else {
EventBus.getInstance().fireEvent(new ErrorEvent(throwable));
}
}
@Override
public void onSuccess(final RestMethod method, final String showTermsOfServices) {
Notification.activityStop();
AuthentificationManager.getInstance().storeUser(user, userProfile, login,
password, domainId);
if (Boolean.parseBoolean(showTermsOfServices)) {
SpMobil.displayTermsOfServicePage();
} else if (attempt == null) {
SpMobil.displayMainPage();
} else {
attempt.execute();
}
}
});
}
@Override
public void onFailure(final RestMethod method, final Throwable t) {
if (t instanceof AuthenticationException) {
EventBus.getInstance().fireEvent(new AuthenticationErrorEvent(t));
} else {
EventBus.getInstance().fireEvent(new ErrorEvent(t));
}
}
};
action.attempt();
}
private void handleAuthenticationFailure(final String login, final String domainId,
final RestMethod method, final Throwable throwable) {
int statusError = method.getStatusCode();
if (statusError == 403) {
Notification.activityStop();
SpMobil.displayMainPage();
} else if (statusError == 401 || statusError == 500) {
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<Boolean>() {
@Override
public void attempt() {
super.attempt();
ServicesLocator.getServiceConnection().userExist(login, domainId, this);
}
@Override
public void onFailure(final RestMethod method, final Throwable t) {
super.onFailure(method, t);
EventBus.getInstance().fireEvent(new AuthenticationErrorEvent(t));
}
@Override
public void onSuccess(final RestMethod method, final Boolean exist) {
super.onSuccess(method, exist);
AuthenticationException ex = new AuthenticationException(
exist ? AuthenticationException.AuthenticationError.PwdNotAvailable :
AuthenticationException.AuthenticationError.LoginNotAvailable);
ex.setLogin(login);
EventBus.getInstance().fireEvent(new AuthenticationErrorEvent(ex));
}
};
action.attempt();
} else {
EventBus.getInstance().fireEvent(new ErrorEvent(throwable));
}
}
public void injectAuthenticationHttpHeaders(RequestBuilder builder) {
if (getHeader(AuthentificationManager.XSTKN) != null) {
builder.setHeader(AuthentificationManager.XSTKN, getHeader(AuthentificationManager.XSTKN));
}
if (getHeader(AuthentificationManager.XSilverpeasSession) != null) {
builder.setHeader(AuthentificationManager.XSilverpeasSession,
getHeader(AuthentificationManager.XSilverpeasSession));
}
}
public void logout() {
// logout
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/silverpeas/Logout");
try {
clearCache();
builder.sendRequest("", new RequestCallback() {
@Override
public void onResponseReceived(final Request request, final Response response) {
Notification.activityStop();
AuthentificationManager.getInstance().clearLocalStorage();
PageHistory.getInstance().clear();
Notification.activityStop();
SpMobil.displayLoginPage(null);
SpMobil.destroyMainPage();
}
@Override
public void onError(final Request request, final Throwable throwable) {
LocalStorageHelper.getInstance().storeBuildDate();
}
});
} catch (RequestException e) {
Window.alert(e.getMessage());
}
}
public void clearCache() {
// Clear offline cache without clearing the session or authentication cookies.
CacheStorageHelper.clear();
}
}