/* (c) 2014, 2015 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.geofence.services.rest.impl; import com.vividsolutions.jts.geom.MultiPolygon; import java.util.HashMap; import java.util.List; import java.util.Map; import org.geoserver.geofence.core.model.GFUser; import org.geoserver.geofence.core.model.GSInstance; import org.geoserver.geofence.core.model.GSUser; import org.geoserver.geofence.core.model.LayerDetails; import org.geoserver.geofence.core.model.UserGroup; import org.geoserver.geofence.core.model.Rule; import org.geoserver.geofence.core.model.RuleLimits; import org.geoserver.geofence.services.GFUserAdminService; import org.geoserver.geofence.services.GetProviderService; import org.geoserver.geofence.services.InstanceAdminService; import org.geoserver.geofence.services.UserGroupAdminService; import org.geoserver.geofence.services.RuleAdminService; import org.geoserver.geofence.services.UserAdminService; import org.geoserver.geofence.services.dto.ShortGroup; import org.geoserver.geofence.services.dto.ShortInstance; import org.geoserver.geofence.services.exception.NotFoundServiceEx; import org.geoserver.geofence.services.rest.RESTBatchService; import org.geoserver.geofence.services.rest.RESTConfigService; import org.geoserver.geofence.services.rest.exception.BadRequestRestEx; import org.geoserver.geofence.services.rest.exception.InternalErrorRestEx; import org.geoserver.geofence.services.rest.exception.NotFoundRestEx; import org.geoserver.geofence.services.rest.model.RESTBatch; import org.geoserver.geofence.services.rest.model.RESTBatchOperation; import org.geoserver.geofence.services.rest.model.RESTInputGroup; import org.geoserver.geofence.services.rest.model.RESTInputInstance; import org.geoserver.geofence.services.rest.model.RESTInputRule; import org.geoserver.geofence.services.rest.model.RESTInputUser; import org.geoserver.geofence.services.rest.model.RESTLayerConstraints; import org.geoserver.geofence.services.rest.model.RESTOutputRule; import org.geoserver.geofence.services.rest.model.RESTOutputRuleList; import org.geoserver.geofence.services.rest.model.RESTShortInstanceList; import org.geoserver.geofence.services.rest.model.RESTShortUser; import org.geoserver.geofence.services.rest.model.RESTShortUserList; import org.geoserver.geofence.services.rest.model.config.RESTConfigurationRemapping; import org.geoserver.geofence.services.rest.model.config.RESTFullConfiguration; import org.geoserver.geofence.services.rest.model.config.RESTFullGRUserList; import org.geoserver.geofence.services.rest.model.config.RESTFullGSInstanceList; import org.geoserver.geofence.services.rest.model.config.RESTFullUserGroupList; import org.geoserver.geofence.services.rest.model.config.RESTFullRuleList; import org.geoserver.geofence.services.rest.model.config.RESTFullUserList; import org.geoserver.geofence.services.rest.model.util.IdName; import org.geoserver.geofence.services.rest.model.util.RESTBatchOperationFactory; import org.geoserver.geofence.services.rest.utils.InstanceCleaner; import java.util.ArrayList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.geoserver.geofence.services.rest.model.RESTRulePosition; /** * * @author ETj (etj at geo-solutions.it) */ public class RESTConfigServiceImpl implements RESTConfigService { private static final Logger LOGGER = LogManager.getLogger(RESTConfigServiceImpl.class); private UserAdminService userAdminService; private UserGroupAdminService userGroupAdminService; private RuleAdminService ruleAdminService; private InstanceAdminService instanceAdminService; private GFUserAdminService grUserAdminService; private RESTBatchService restBatchService; private InstanceCleaner instanceCleaner; public RESTFullConfiguration getConfiguration() { return getConfiguration(false); } protected RESTBatch collectUsers(RESTBatch backup) { for (GSUser user : userAdminService.getFullList(null, null, null, true)) { RESTBatchOperation op = RESTBatchOperationFactory.createUserInputOp(); RESTInputUser input = new RESTInputUser(); op.setPayload(input); input.setAdmin(user.isAdmin()); input.setEmailAddress(user.getEmailAddress()); input.setEnabled(user.getEnabled()); input.setExtId(user.getExtId()); input.setFullName(user.getFullName()); input.setName(user.getName()); input.setPassword(user.getPassword()); if(user.getGroups() != null) { input.setGroups(new ArrayList<IdName>(user.getGroups().size())); for (UserGroup userGroup : user.getGroups()) { input.getGroups().add(new IdName(userGroup.getName())); } } backup.add(op); } return backup; } protected RESTBatch collectGroups(RESTBatch backup) { for (ShortGroup shortGroup : userGroupAdminService.getList(null, null, null)) { RESTBatchOperation op = RESTBatchOperationFactory.createGroupInputOp(shortGroup.getName()); RESTInputGroup input = (RESTInputGroup)op.getPayload(); input.setExtId(shortGroup.getExtId()); input.setEnabled(shortGroup.isEnabled()); backup.add(op); } return backup; } protected RESTBatch collectInstances(RESTBatch backup) { for (GSInstance instance : instanceAdminService.getFullList(null, null, null)) { RESTBatchOperation op = RESTBatchOperationFactory.createInstanceInputOp(); RESTInputInstance input = new RESTInputInstance(); op.setPayload(input); input.setBaseURL(instance.getBaseURL()); input.setDescription(instance.getDescription()); input.setName(instance.getName()); input.setPassword(instance.getPassword()); input.setUsername(instance.getUsername()); backup.add(op); } return backup; } protected RESTBatch collectRules(RESTBatch backup) { for (Rule rule : ruleAdminService.getListFull(null, null, null)) { RESTBatchOperation op = RESTBatchOperationFactory.createRuleInputOp(); RESTInputRule input = new RESTInputRule(); op.setPayload(input); input.setGrant(rule.getAccess()); input.setPosition(new RESTRulePosition(RESTRulePosition.RulePosition.fixedPriority, rule.getPriority())); if(rule.getInstance() != null) input.setInstanceName(rule.getInstance().getName()); input.setRolename(rule.getRolename()); input.setUsername(rule.getUsername()); input.setService(rule.getService()); input.setRequest(rule.getRequest()); input.setWorkspace(rule.getWorkspace()); input.setLayer(rule.getLayer()); RESTLayerConstraints constraints = new RESTLayerConstraints(); if(rule.getRuleLimits() != null ) { RuleLimits limits = rule.getRuleLimits(); MultiPolygon mp = limits.getAllowedArea(); constraints.setRestrictedAreaWkt(mp.toText()); input.setConstraints(constraints); } if(rule.getLayerDetails() != null) { LayerDetails details = rule.getLayerDetails(); constraints.setAllowedStyles(details.getAllowedStyles()); constraints.setAttributes(details.getAttributes()); constraints.setCqlFilterRead(details.getCqlFilterRead()); constraints.setCqlFilterWrite(details.getCqlFilterWrite()); constraints.setDefaultStyle(details.getDefaultStyle()); constraints.setType(details.getType()); input.setConstraints(constraints); } backup.add(op); } return backup; } @Override public RESTBatch backupGroups() { return collectGroups(new RESTBatch()); } @Override public RESTBatch backupUsers() { return collectUsers(new RESTBatch()); } @Override public RESTBatch backupInstances() { return collectInstances(new RESTBatch()); } @Override public RESTBatch backupRules() { return collectRules(new RESTBatch()); } @Override public RESTBatch backup(Boolean includeGRUsers) { RESTBatch backup = new RESTBatch(); collectGroups(backup); collectUsers(backup); collectInstances(backup); collectRules(backup); if ( includeGRUsers.booleanValue() ) { LOGGER.warn("TODO::: GF users not handled"); // RESTFullGRUserList grUsers = new RESTFullGRUserList(); // grUsers.setList(grUserAdminService.getFullList(null, null, null)); // backup.setGrUserList(grUsers); } return backup; } @Override public void restore(RESTBatch batch) throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { LOGGER.warn("Restoring GeoFence using batch with " + batch.getList().size() + " operations"); instanceCleaner.removeAll(); restBatchService.runBatch(batch); } @Override public void cleanup() throws InternalErrorRestEx { LOGGER.warn("Cleaning up GeoFence data"); instanceCleaner.removeAll(); } /** * @deprecated misbehaves since usergroups introduction. Please use backup() */ @Override public RESTFullConfiguration getConfiguration(Boolean includeGRUsers) { RESTFullConfiguration cfg = new RESTFullConfiguration(); RESTFullUserList users = new RESTFullUserList(); List<GSUser> userlist = userAdminService.getFullList(null, null, null); for (GSUser user : userlist) { user.setGroups(userAdminService.getUserGroups(user.getId())); } users.setList(userlist); cfg.setUserList(users); RESTFullUserGroupList profiles = new RESTFullUserGroupList(); profiles.setList(userGroupAdminService.getList(null, null, null)); cfg.setUserGroupList(profiles); RESTFullGSInstanceList instances = new RESTFullGSInstanceList(); instances.setList(instanceAdminService.getFullList(null, null, null)); cfg.setGsInstanceList(instances); RESTFullRuleList rules = new RESTFullRuleList(); rules.setList(ruleAdminService.getListFull(null, null, null)); cfg.setRuleList(rules); if ( includeGRUsers.booleanValue() ) { RESTFullGRUserList grUsers = new RESTFullGRUserList(); grUsers.setList(grUserAdminService.getFullList(null, null, null)); cfg.setGrUserList(grUsers); } return cfg; } public synchronized RESTConfigurationRemapping setConfiguration(RESTFullConfiguration config) { return setConfiguration(config, false); } @Override public synchronized RESTConfigurationRemapping setConfiguration(RESTFullConfiguration config, Boolean includeGRUsers) { LOGGER.warn("SETTING CONFIGURATION"); if ( includeGRUsers ) { if ( (config.getGrUserList() == null) || (config.getGrUserList().getList() == null) || config.getGrUserList().getList().isEmpty() ) { throw new BadRequestRestEx("Can't restore internal users: no internal user defined"); } } instanceCleaner.removeAll(); RESTConfigurationRemapping remap = new RESTConfigurationRemapping(); RemapperCache<UserGroup, UserGroupAdminService> groupCache = new RemapperCache<UserGroup, UserGroupAdminService>(userGroupAdminService, remap.getUserGroups()); RemapperCache<GSUser, UserAdminService> userCache = new RemapperCache<GSUser, UserAdminService>(userAdminService, remap.getUsers()); RemapperCache<GSInstance, InstanceAdminService> instanceCache = new RemapperCache<GSInstance, InstanceAdminService>(instanceAdminService, remap.getInstances()); try { // === UserGroups for (ShortGroup sp : config.getUserGroupList().getList()) { Long oldId = sp.getId(); long newId = userGroupAdminService.insert(sp); LOGGER.info("Remapping userGroup " + oldId + " -> " + newId); remap.getUserGroups().put(oldId, newId); } // === Users for (GSUser user : config.getUserList().getList()) { for (UserGroup userGroup : user.getGroups()) { Long oldGroupId = userGroup.getId(); UserGroup group = groupCache.get(oldGroupId); user.getGroups().add(group); } Long oldId = user.getId(); user.setId(null); long newId = userAdminService.insert(user); LOGGER.info("Remapping user " + oldId + " -> " + newId); remap.getUsers().put(oldId, newId); } // === GSInstances for (GSInstance instance : config.getGsInstanceList().getList()) { Long oldId = instance.getId(); instance.setId(null); long newId = instanceAdminService.insert(instance); LOGGER.info("Remapping gsInstance " + oldId + " -> " + newId); remap.getInstances().put(oldId, newId); } // === Rules for (Rule rule : config.getRuleList().getList()) { Long oldId = rule.getId(); rule.setId(null); if ( rule.getInstance() != null ) { rule.setInstance(instanceCache.get(rule.getInstance().getId())); } // the prob here is that layerdetails is a reverse reference, so only hibernate should be setting it. // using JAXB, it's injected, but we have to make hibernate eat it. LayerDetails ld = rule.getLayerDetails(); rule.setLayerDetails(null); long newId = ruleAdminService.insert(rule); LOGGER.info("Remapping rule " + oldId + " -> " + newId); remap.getRules().put(oldId, newId); if ( ld != null ) { ruleAdminService.setDetails(newId, ld); } } } catch (RemapperException e) { LOGGER.error("Exception in remapping: Configuration will be erased"); instanceCleaner.removeAll(); throw new BadRequestRestEx(e.getMessage()); } catch (NotFoundRestEx e) { LOGGER.error("Internal exception in remapping: Configuration will be erased"); instanceCleaner.removeAll(); throw e; } // === Internal users if ( includeGRUsers ) { instanceCleaner.removeAllGFUsers(); for (GFUser grUser : config.getGrUserList().getList()) { Long oldId = grUser.getId(); grUser.setId(null); long newId = grUserAdminService.insert(grUser); LOGGER.info("Remapping internal user " + oldId + " -> " + newId); remap.remap(oldId, grUser); } } return remap; } @Override public RESTFullUserList getUsers() throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { List<GSUser> users = userAdminService.getFullList(null, null, null); RESTFullUserList ret = new RESTFullUserList(); ret.setList(users); return ret; } // not @Override: not available as a standalone service public RESTFullGRUserList getGRUsers() throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { List<GFUser> users = grUserAdminService.getFullList(null, null, null); RESTFullGRUserList ret = new RESTFullGRUserList(); ret.setList(users); return ret; } @Override public RESTFullUserGroupList getUserGroups() throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { List<ShortGroup> groups = userGroupAdminService.getList(null, null, null); return new RESTFullUserGroupList(groups); } // ========================================================================== // ========================================================================== /** * Simplified operation used for quick re-insertion of data extracted with a GET op in the related service */ @Override public void setUserGroups(RESTFullUserGroupList groups) throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { int okCnt = 0; for (ShortGroup group : groups) { LOGGER.info("Adding group " +group ); try { userGroupAdminService.insert(group); okCnt++; } catch (Exception e) { LOGGER.info("Could not add group " +group +": " + e.getMessage()); } } LOGGER.info(okCnt+"/"+groups.getList().size() + " items inserted"); } /** * Simplified operation used for quick re-insertion of data extracted with a GET op in the related service */ @Override public void setUsers(RESTShortUserList users) throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { int okCnt = 0; for (RESTShortUser su : users) { LOGGER.info("Adding user " +su ); try { GSUser u = new GSUser(); u.setExtId(su.getExtId()); u.setName(su.getUserName()); u.setEnabled(su.isEnabled()); // group list is not retrievable in shortuser :| userAdminService.insert(u); okCnt++; } catch (Exception e) { LOGGER.info("Could not add user " +su +": " + e.getMessage()); } } LOGGER.info(okCnt+"/"+users.getUserList().size() + " items inserted"); } /** * Simplified operation used for quick re-insertion of data extracted with a GET op in the related service */ @Override public void setInstances(RESTShortInstanceList instances) throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { int okCnt = 0; for (ShortInstance si : instances) { LOGGER.info("Adding instance " +si ); try { GSInstance i = new GSInstance(); i.setName(si.getName()); i.setDescription(si.getDescription()); i.setBaseURL(si.getUrl()); i.setUsername("unknown"); i.setPassword("unknown"); instanceAdminService.insert(i); okCnt++; } catch (Exception e) { LOGGER.info("Could not add instance " +si +": " + e.getMessage()); } } LOGGER.info(okCnt+"/"+instances.getList().size() + " items inserted"); } /** * Simplified operation used for quick re-insertion of data extracted with a GET op in the related service */ @Override public void setRules(RESTOutputRuleList rules) throws BadRequestRestEx, NotFoundRestEx, InternalErrorRestEx { int okCnt = 0; Map<String, UserGroup> groups = new HashMap<String, UserGroup>(); Map<String, GSUser> users = new HashMap<String, GSUser>(); Map<String, GSInstance> instances = new HashMap<String, GSInstance>(); for (RESTOutputRule in : rules) { try { Rule out = new Rule(); out.setAccess(in.getGrant()); out.setPriority(in.getPriority()); out.setUsername(in.getUsername()); out.setRolename(in.getRolename()); if (in.getInstance()!= null) { String name = in.getInstance().getName(); GSInstance instance = instances.get(name); if(instance == null) { instance = instanceAdminService.get(name); instances.put(name, instance); } out.setInstance(instance); } out.setService(in.getService()); out.setRequest(in.getRequest()); out.setWorkspace(in.getWorkspace()); out.setLayer(in.getLayer()); long ruleid = ruleAdminService.insert(out); okCnt++; if (in.getConstraints() != null) { LOGGER.warn("TODO::: Constraints exist but will not be inserted for rule " + out); } } catch (Exception e) { LOGGER.info("Could not add rule " +in +": " + e.getMessage()); } } LOGGER.info(okCnt+"/"+rules.getList().size() + " items inserted"); } // ========================================================================== // ========================================================================== public void setInstanceCleaner(InstanceCleaner instanceCleaner) { this.instanceCleaner = instanceCleaner; } // ========================================================================== public void setUserGroupAdminService(UserGroupAdminService service) { this.userGroupAdminService = service; } public void setUserAdminService(UserAdminService service) { this.userAdminService = service; } public void setInstanceAdminService(InstanceAdminService service) { this.instanceAdminService = service; } public void setRuleAdminService(RuleAdminService service) { this.ruleAdminService = service; } public void setGrUserAdminService(GFUserAdminService service) { this.grUserAdminService = service; } public void setRestBatchService(RESTBatchService restBatchService) { this.restBatchService = restBatchService; } // ========================================================================== class RemapperCache<TYPE, SERVICE extends GetProviderService<TYPE>> { private Map<Long, TYPE> cache = new HashMap<Long, TYPE>(); private final Map<Long, Long> idRemapper; private final SERVICE service; public RemapperCache(SERVICE service, Map<Long, Long> idRemapper) { this.idRemapper = idRemapper; this.service = service; } TYPE get(Long oldId) throws RemapperException, NotFoundRestEx { Long newId = idRemapper.get(oldId); if ( newId == null ) { LOGGER.error("Can't remap " + oldId); throw new RemapperException("Can't remap " + oldId); } TYPE cached = cache.get(newId); try { if ( cached == null ) { cached = service.get(newId.longValue()); // may throw NotFoundServiceEx cache.put(newId, cached); } return cached; } catch (NotFoundServiceEx ex) { LOGGER.error(ex.getMessage(), ex); throw new NotFoundRestEx(ex.getMessage()); } } } class RemapperException extends Exception { public RemapperException(Throwable cause) { super(cause); } public RemapperException(String message, Throwable cause) { super(message, cause); } public RemapperException(String message) { super(message); } public RemapperException() { } } }