// Copyright 2004-2014 Jim Voris
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.qumasoft.server;
import com.qumasoft.qvcslib.QVCSConstants;
import com.qumasoft.qvcslib.Utility;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A class to manage authentication actions for QVCS-Enterprise. Note that all passwords passed in to the methods of this class are
* already in hashed form, or at least they should be. <P> See the Utility method hashPassword().
*
* @author Jim Voris
*/
public final class AuthenticationManager {
private static final AuthenticationManager AUTHENTICATION_MANAGER = new AuthenticationManager();
private boolean isInitializedFlag = false;
private String storeName = null;
private String oldStoreName = null;
private AuthenticationStore store = null;
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.server");
/**
* Creates a new instance of Authentication Manager.
*/
private AuthenticationManager() {
}
/**
* Get the Authentication Manager singleton.
* @return the Authentication Manager singleton.
*/
public static AuthenticationManager getAuthenticationManager() {
return AUTHENTICATION_MANAGER;
}
/**
* Initialize the authentication manager.
* @return true if we initialized successfully.
*/
public synchronized boolean initialize() {
if (!isInitializedFlag) {
storeName = System.getProperty("user.dir")
+ File.separator
+ QVCSConstants.QVCS_ADMIN_DATA_DIRECTORY
+ File.separator
+ QVCSConstants.QVCS_AUTHENTICATION_STORE_NAME + "dat";
oldStoreName = storeName + ".old";
loadStore();
isInitializedFlag = true;
}
return isInitializedFlag;
}
private synchronized void loadStore() {
File storeFile;
FileInputStream fileStream = null;
try {
storeFile = new File(storeName);
fileStream = new FileInputStream(storeFile);
ObjectInputStream inStream = new ObjectInputStream(fileStream);
store = (AuthenticationStore) inStream.readObject();
} catch (FileNotFoundException e) {
// The file doesn't exist yet. Create a default store.
store = new AuthenticationStore();
writeStore();
} catch (IOException | ClassNotFoundException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
if (fileStream != null) {
try {
fileStream.close();
fileStream = null;
} catch (IOException ex) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(ex));
}
}
// Serialization failed. Create a default store.
store = new AuthenticationStore();
writeStore();
} finally {
if (fileStream != null) {
try {
fileStream.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
}
}
}
}
private void writeStore() {
FileOutputStream fileStream = null;
try {
File storeFile = new File(storeName);
File oldStoreFile = new File(oldStoreName);
if (oldStoreFile.exists()) {
oldStoreFile.delete();
}
if (storeFile.exists()) {
storeFile.renameTo(oldStoreFile);
}
File newStoreFile = new File(storeName);
// Make sure the needed directories exists
if (!newStoreFile.getParentFile().exists()) {
newStoreFile.getParentFile().mkdirs();
}
fileStream = new FileOutputStream(newStoreFile);
ObjectOutputStream outStream = new ObjectOutputStream(fileStream);
outStream.writeObject(store);
} catch (IOException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
} finally {
if (fileStream != null) {
try {
fileStream.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
}
}
}
}
/**
* Authenticate a user.
* @param userName user's name.
* @param password the user's hashed password.
* @return true for valid username/password; false otherwise.
*/
public synchronized boolean authenticateUser(String userName, byte[] password) {
return store.authenticateUser(userName, password);
}
/**
* Add a user.
* @param callerUserName caller's user name.
* @param userName user name to add.
* @param password user's password.
* @return true if add was successful.
*/
public synchronized boolean addUser(String callerUserName, String userName, byte[] password) {
boolean retVal = false;
// This can only be done by an admin user
if (0 == callerUserName.compareTo(RoleManager.ADMIN)) {
retVal = store.addUser(userName, password);
}
if (retVal) {
writeStore();
}
return retVal;
}
/**
* Update a user's password.
* @param callerUserName caller's user name.
* @param userName user's name.
* @param oldPassword user's old hashed password. This is not needed/used if the ADMIN user is making the change.
* @param newPassword user's new hashed password.
* @return true if successful; false otherwise.
*/
public synchronized boolean updateUser(String callerUserName, String userName, byte[] oldPassword, byte[] newPassword) {
boolean retVal = false;
// An admin user can force this change...
if (0 == callerUserName.compareTo(RoleManager.ADMIN)) {
retVal = store.updateUser(userName, newPassword);
} else {
// Or the existing user can change their own password
if (authenticateUser(callerUserName, oldPassword)) {
if (0 == callerUserName.compareTo(userName)) {
retVal = store.updateUser(userName, newPassword);
}
}
}
if (retVal) {
writeStore();
}
return retVal;
}
/**
* Remove a user.
* @param callerUserName caller's user name. The caller <i>must</i> be the ADMIN user.
* @param userName user name.
* @return true if the user is removed.
*/
public synchronized boolean removeUser(String callerUserName, String userName) {
boolean retVal = false;
// This can only be done by an admin user
if (0 == callerUserName.compareTo(RoleManager.ADMIN)) {
retVal = store.removeUser(userName);
}
if (retVal) {
writeStore();
}
return retVal;
}
/**
* List users.
* @return a String[] of user names.
*/
public synchronized String[] listUsers() {
return store.listUsers();
}
}