/* Copyright (c) 2012-2014, terrestris GmbH & Co. KG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* (This is the BSD 3-Clause, sometimes called 'BSD New' or 'BSD Simplified',
* see http://opensource.org/licenses/BSD-3-Clause)
*/
package de.terrestris.shogun.init;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import de.terrestris.shogun.dao.DatabaseDao;
import de.terrestris.shogun.exception.ShogunDatabaseAccessException;
import de.terrestris.shogun.model.BaseModelInterface;
import de.terrestris.shogun.model.Group;
import de.terrestris.shogun.model.MapConfig;
import de.terrestris.shogun.model.MapLayer;
import de.terrestris.shogun.model.Module;
import de.terrestris.shogun.model.Role;
import de.terrestris.shogun.model.User;
import de.terrestris.shogun.model.WmsMapLayer;
import de.terrestris.shogun.model.WmsProxyConfig;
/**
* The class handling the initial import of all data needed for a running setup.
*
* @author terrestris GmbH & Co. KG
*
*/
@SuppressWarnings("deprecation")
public class DatabaseContentInitializer {
/**
* the logger instance
*/
private static Logger LOGGER = Logger.getLogger(DatabaseContentInitializer.class);
protected static final String APP_USER_AUTO_CREATED = "auto-create-on-init";
/**
* flag symbolizing if the database will be modified by this class
*/
private Boolean databaseInitializationEnabled;
/**
* a list of module objects for this applications
*/
private List<Module> availableModules;
/**
* The name of the SUPERADMIN
*/
private String superAdminName;
/**
* the password for the SUPERADMIN
*/
private String superAdminPw;
/**
* determines if an anonymous user should be created?
*/
private Boolean autoCreateAnonymousUser = true;
/**
* a default user group
*/
private Group defaultAnonymousGroup;
/**
* a default user group
*/
private Group defaultSuperAdminGroup;
/**
* a default map configuration
*/
private List<MapConfig> mapConfigs;
/**
* modules to be assigned to anonymous user
*/
private List<String> modulesForAnonymous;
/**
* a default WMS map layer
*/
private WmsMapLayer wmsMapLayer;
/**
* the DB dao to access database via Hibernate
*/
private DatabaseDao dbDao;
/**
* The persistant representation of the standard WMS
*/
private WmsMapLayer persistantStdWmsLayer;
/**
* The persistant representation of the standard map configuration
*/
private MapConfig persistantStdMapConfig;
/**
*/
protected Group persistantDefaultAnonymousGroup;
/**
*/
protected Group persistantDefaultSuperAdminGroup;
/**
* The method called on init.
*
* Delegated the tasks to fill the database due to config
*/
public void initializeDatabaseContent() {
if (this.databaseInitializationEnabled == true) {
LOGGER.info("Initializing database content on servlet init.");
try {
this.persistantStdWmsLayer = this.createStandardWmsMapLayer();
this.createAvailableModules();
this.persistantStdMapConfig = this.createAvailableMapConfig();
this.createAvailableRoles();
this.persistantDefaultAnonymousGroup = this.createDefaultAnonymousGroup();
this.persistantDefaultSuperAdminGroup = this.createDefaultSuperAdminGroup();
this.createSuperAdmin();
this.createAnonymousUser(this.persistantStdWmsLayer, this.persistantStdMapConfig, null);
} catch (Exception e) {
LOGGER.error("Caught exception '" + e.getClass().getSimpleName()
+ "':", e);
}
}
}
/**
*
*/
private void createAvailableRoles() {
LOGGER.info("Creating available roles");
List<String> rolenames = Arrays.asList(
Group.ROLENAME_ANONYMOUS,
Group.ROLENAME_USER,
Group.ROLENAME_ADMIN,
Group.ROLENAME_SUPERADMIN
);
List<Role> roles = new ArrayList<Role>();
for (String rolename : rolenames) {
Role role = new Role();
role.setName(rolename);
role.setApp_user(APP_USER_AUTO_CREATED);
roles.add(role);
}
this.dbDao.createEntities(roles);
}
/**
* Creates the available standard {@link WmsMapLayer} entry in the database
*
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
* @throws ShogunDatabaseAccessException
*/
private WmsMapLayer createStandardWmsMapLayer()
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException, ShogunDatabaseAccessException {
WmsMapLayer desiredWmsMapLayer = this.getWmsMapLayer();
HashMap<String, String> fieldsAndValues = new HashMap<String, String>();
fieldsAndValues.put("url", desiredWmsMapLayer.getUrl());
fieldsAndValues.put("layers", desiredWmsMapLayer.getLayers());
WmsMapLayer existingMapLayer = (WmsMapLayer) this.dbDao
.getEntityByStringFields(WmsMapLayer.class, fieldsAndValues);
// check if we have an existing WMS map layer
// create or apply/update a one
existingMapLayer = (WmsMapLayer) this.createOrApplyObjects(
existingMapLayer, desiredWmsMapLayer);
return existingMapLayer;
}
/**
* Creates the available standard {@link MapConfig} entry in the database
*
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws ShogunDatabaseAccessException
*/
private MapConfig createAvailableMapConfig() throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException,
ShogunDatabaseAccessException {
List<MapConfig> allMapConfigs = this.getMapConfigs();
for (Iterator<MapConfig> iterator = allMapConfigs.iterator(); iterator.hasNext();) {
MapConfig desiredMapConfig = (MapConfig) iterator.next();
// check for existing config
MapConfig existingMapConfig = (MapConfig) this.dbDao.getEntityByStringField(MapConfig.class, "mapId", desiredMapConfig.getMapId());
existingMapConfig = (MapConfig) this.createOrApplyObjects(existingMapConfig, desiredMapConfig);
}
MapConfig stdMapConfig = (MapConfig) this.dbDao.getEntityByStringField(MapConfig.class, "mapId", "stdmap");
return stdMapConfig;
}
/**
* Creates the available {@link Module} entries in the database
*
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws ShogunDatabaseAccessException
*/
private void createAvailableModules() throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException,
ShogunDatabaseAccessException {
LOGGER.info("Creating available modules");
List<Module> availableModules = this.getAvailableModules();
for (Module desiredModule : availableModules) {
String moduleName = desiredModule.getModule_name();
Module existingModule = (Module) this.dbDao.getEntityByStringField(
Module.class, "module_name", moduleName);
// check if we have an existing module
// create or apply/update a module
existingModule = (Module) this.createOrApplyObjects(existingModule,
desiredModule);
}
}
/**
* Creates a default group to ensure there is always one
*
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws ShogunDatabaseAccessException
*
*/
private Group createDefaultAnonymousGroup() throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException,
ShogunDatabaseAccessException {
LOGGER.info("Creating default group");
Group desiredGroup = this.defaultAnonymousGroup;
// getting the anonymous role
Role anonRole = (Role) this.dbDao.getEntityByStringField(Role.class, "name", Group.ROLENAME_ANONYMOUS);
Set<Role> roles = new HashSet<Role>();
roles.add(anonRole);
// setting anon role
desiredGroup.setRoles(roles);
// assign all available modules to the anonymous group
// TODO ask TA if this makes sense for the anonymous group
assignAllModulesToGroup(desiredGroup);
Group existingGroup = (Group) this.dbDao.getEntityByStringField(
Group.class, "group_nr", this.defaultAnonymousGroup.getGroup_nr());
// This group isn't deletable as it is needed for the security approach
// we use.
desiredGroup.setDeletable(false);
existingGroup = (Group) this.createOrApplyObjects(existingGroup,
desiredGroup);
return existingGroup;
}
/**
* Creates a default group to ensure there is always one
*
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws ShogunDatabaseAccessException
*
*/
private Group createDefaultSuperAdminGroup() throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException,
ShogunDatabaseAccessException {
LOGGER.info("Creating default group");
Group desiredGroup = this.defaultSuperAdminGroup;
// getting roles
Set<Role> roles = new HashSet<Role>();
roles.add(
(Role) this.dbDao.getEntityByStringField(Role.class, "name", Group.ROLENAME_ANONYMOUS)
);
roles.add(
(Role) this.dbDao.getEntityByStringField(Role.class, "name", Group.ROLENAME_ADMIN)
);
roles.add(
(Role) this.dbDao.getEntityByStringField(Role.class, "name", Group.ROLENAME_USER)
);
roles.add(
(Role) this.dbDao.getEntityByStringField(Role.class, "name", Group.ROLENAME_SUPERADMIN)
);
desiredGroup.setRoles(roles);
// assign all available modules to the superadmin group
assignAllModulesToGroup(desiredGroup);
Group existingGroup = (Group) this.dbDao.getEntityByStringField(
Group.class, "group_nr", this.defaultSuperAdminGroup.getGroup_nr());
// This group isn't deletable as it is needed for the security approach
// we use.
desiredGroup.setDeletable(false);
existingGroup = (Group) this.createOrApplyObjects(existingGroup,
desiredGroup);
return existingGroup;
}
/**
* Assigns all available Modules to the passed group
*
* @param group
*/
protected void assignAllModulesToGroup(Group group) {
@SuppressWarnings("unchecked")
List<Module> allModulesList = (List<Module>) (List<?>) this.dbDao
.getAllEntities(Module.class);
Set<Module> allModules = new HashSet<Module>(allModulesList);
group.setModules(allModules);
LOGGER.info("Assigned a total of " + allModules.size() + " available modules to group " + group.getName());
}
/**
* Creates an SuperAdmin {@link User} with declared properties
* @throws ShogunDatabaseAccessException
*
* @throws Exception
*/
private void createSuperAdmin() throws ShogunDatabaseAccessException {
LOGGER.info("Creating superadmin user");
List<Object> allUsers = this.dbDao.getAllEntities(User.class);
// determine if we hav already a superadmin and save for later
User currentSuperAdmin = null;
for (Iterator<Object> iterator = allUsers.iterator(); iterator
.hasNext();) {
User user = (User) iterator.next();
if (user.hasSuperAdminRole() == true) {
LOGGER.info(" - We already have a superuser. We possibly need to update.");
currentSuperAdmin = user;
break;
}
}
// instanciate a new super admin, because we could not find any
if (currentSuperAdmin == null) {
LOGGER.info(" - We could not find a superuser. Create one.");
currentSuperAdmin = this.dbDao.createUser(new User(), false);
}
currentSuperAdmin.setUser_name(this.superAdminName);
PasswordEncoder pwencoder = new Md5PasswordEncoder();
String hashed = pwencoder.encodePassword(this.superAdminPw, null);
currentSuperAdmin.setUser_password(hashed);
currentSuperAdmin.setApp_user(APP_USER_AUTO_CREATED);
currentSuperAdmin = (User) this.dbDao.createOrUpdateEntity("User", currentSuperAdmin);
// add the anonymous user to the default group
this.persistantDefaultSuperAdminGroup.getUsers().add(currentSuperAdmin);
this.dbDao.updateEntity("Group", this.persistantDefaultSuperAdminGroup);
}
/**
* Creates an anonymous {@link User} with declared properties, such as
* {@link MapLayer} and {@link MapConfig}
* @throws ShogunDatabaseAccessException
*
* @throws Exception
*/
private void createAnonymousUser(WmsMapLayer wmsMapLayer,
MapConfig mapConfig, WmsProxyConfig wmsProxyConfig) throws ShogunDatabaseAccessException {
if (this.getAutoCreateAnonymousUser()) {
LOGGER.info("Creating anonymous user");
List<Object> allUsers = this.dbDao.getAllEntities(User.class);
// determine if we already have an anonymous and save for later
User anon = null;
for (Iterator<Object> iterator = allUsers.iterator(); iterator
.hasNext();) {
User user = (User) iterator.next();
if (user.hasAnonymousRole() == true && user.getUser_name().equals("anonymousUser")) {
anon = user;
break;
}
}
if (anon == null) {
LOGGER.info(" - We could not find an anonymous user. Create one.");
anon = new User();
anon.setUser_name("anonymousUser");
anon.setApp_user(APP_USER_AUTO_CREATED);
anon = this.dbDao.createUser(anon, false);
}
// TODO check for old properties first, before overwriting these
// Set the auto created mapconf
anon.setMapConfig(mapConfig);
// set the auto created WmsMapLayer
Set<MapLayer> wmsMapLayerSet = new HashSet<MapLayer>();
wmsMapLayerSet.add(wmsMapLayer);
// add the stdLayer to the anon-group
this.persistantDefaultAnonymousGroup.setMapLayers(wmsMapLayerSet);
if (wmsProxyConfig != null) {
// set the auto created WmsProxyConfig
anon.setWmsProxyConfig(wmsProxyConfig);
}
// persist the anonymous user
this.dbDao.updateUser(anon);
// add the anonymous user to the default group
this.persistantDefaultAnonymousGroup.getUsers().add(anon);
this.dbDao.updateEntity("Group", this.persistantDefaultAnonymousGroup);
} else {
LOGGER.info("Skipping the creation of an anonymous user.");
}
}
/**
* Method checks if there is an existing target. If yes the source object is
* applied to the target. If there is no existing target, we create a new
* one.
*
* @param target
* @param source
* @return
*
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
*/
private BaseModelInterface createOrApplyObjects(BaseModelInterface target,
BaseModelInterface source) throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
if (target != null && !target.getClass().equals(source.getClass())) {
throw new IllegalArgumentException("Cannot update object of class "
+ target.getClass().getSimpleName()
+ " with object of class "
+ source.getClass().getSimpleName());
}
String className = source.getClass().getSimpleName();
if (target == null) {
// tag the record, that it has been done automatically
source.setApp_user(APP_USER_AUTO_CREATED);
target = (BaseModelInterface) this.dbDao.createEntity(className,
source);
} else {
// we need to store the id of the originally existing object
// because otherwise BeanUtils.copyProperties would set it to 0
// and a new object would be created by this.dbDao.updateEntity
int oldid = target.getId();
Date createdAt = target.getCreated_at();
PropertyUtils.copyProperties(target, source);
target.setId(oldid);
target.setCreated_at(createdAt);
target.setUpdated_at(new Date());
target.setApp_user(APP_USER_AUTO_CREATED);
target = (BaseModelInterface) this.dbDao.updateEntity(className,
target);
}
return target;
}
/**
* @param shogunDatabaseInitializationEnabled
* the shogunDatabaseInitializationEnabled to set
*/
@Autowired
@Qualifier("databaseInitializationEnabled")
public void setDatabaseInitializationEnabled(
Boolean databaseInitializationEnabled) {
this.databaseInitializationEnabled = databaseInitializationEnabled;
}
/**
* @return the autoCreateAnonymousUser
*/
public Boolean getAutoCreateAnonymousUser() {
return autoCreateAnonymousUser;
}
/**
* @param autoCreateAnonymousUser
* the autoCreateAnonymousUser to set
*/
public void setAutoCreateAnonymousUser(Boolean autoCreateAnonymousUser) {
this.autoCreateAnonymousUser = autoCreateAnonymousUser;
}
/**
* @return the superAdminName
*/
public String getSuperAdminName() {
return superAdminName;
}
/**
* @param superAdminName
* the superAdminName to set
*/
public void setSuperAdminName(String superAdminName) {
this.superAdminName = superAdminName;
}
/**
* @return the superAdminPw
*/
public String getSuperAdminPw() {
return superAdminPw;
}
/**
* @param superAdminPw
* the superAdminPw to set
*/
public void setSuperAdminPw(String superAdminPw) {
this.superAdminPw = superAdminPw;
}
/**
* @return the modulesForAnonymous
*/
public List<String> getModulesForAnonymous() {
return modulesForAnonymous;
}
/**
* @param modulesForAnonymous
* the modulesForAnonymous to set
*/
public void setModulesForAnonymous(List<String> modulesForAnonymous) {
this.modulesForAnonymous = modulesForAnonymous;
}
/**
* @return the wmsMapLayer
*/
public WmsMapLayer getWmsMapLayer() {
return wmsMapLayer;
}
/**
* @param wmsMapLayer
* the wmsMapLayer to set
*/
public void setWmsMapLayer(WmsMapLayer wmsMapLayer) {
this.wmsMapLayer = wmsMapLayer;
}
/**
* @return the availableModules
*/
public List<Module> getAvailableModules() {
return availableModules;
}
/**
* @param test
* the availableModules to set
*/
public void setAvailableModules(List<Module> availableModules) {
this.availableModules = availableModules;
}
/**
* @return the dbDao
*/
public DatabaseDao getDbDao() {
return dbDao;
}
/**
* Auto generation of an TsDAO instance via dependency injection.
*
* @param dao
*/
@Autowired
public void setDbDao(DatabaseDao dao) {
this.dbDao = dao;
}
/**
* @return the persistantStdWmsLayer
*/
public WmsMapLayer getPersistantStdWmsLayer() {
return persistantStdWmsLayer;
}
/**
* @return the persistantStdMapConfig
*/
public MapConfig getPersistantStdMapConfig() {
return persistantStdMapConfig;
}
/**
* @return the mapConfigs
*/
public List<MapConfig> getMapConfigs() {
return mapConfigs;
}
/**
* @param mapConfigs the mapConfigs to set
*/
public void setMapConfigs(List<MapConfig> mapConfigs) {
this.mapConfigs = mapConfigs;
}
/**
* @return the defaultAnonymousGroup
*/
public Group getDefaultAnonymousGroup() {
return defaultAnonymousGroup;
}
/**
* @param defaultAnonymousGroup the defaultAnonymousGroup to set
*/
public void setDefaultAnonymousGroup(Group defaultAnonymousGroup) {
this.defaultAnonymousGroup = defaultAnonymousGroup;
}
/**
* @return the defaultSuperAdminGroup
*/
public Group getDefaultSuperAdminGroup() {
return defaultSuperAdminGroup;
}
/**
* @param defaultSuperAdminGroup the defaultSuperAdminGroup to set
*/
public void setDefaultSuperAdminGroup(Group defaultSuperAdminGroup) {
this.defaultSuperAdminGroup = defaultSuperAdminGroup;
}
}