/* (c) 2017 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.rest; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.geoserver.geofence.core.model.AdminRule; import org.geoserver.geofence.rest.xml.JaxbAdminRule; import org.geoserver.geofence.rest.xml.JaxbAdminRuleList; import org.geoserver.geofence.services.AdminRuleAdminService; import org.geoserver.geofence.services.dto.RuleFilter; import org.geoserver.geofence.services.dto.RuleFilter.SpecialFilterType; import org.geoserver.geofence.services.dto.RuleFilter.TextFilter; import org.geoserver.geofence.services.dto.ShortAdminRule; import org.geoserver.geofence.services.exception.NotFoundServiceEx; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @Controller public class AdminRulesRestController { private AdminRuleAdminService adminService; public AdminRulesRestController(AdminRuleAdminService adminService) { this.adminService = adminService; } @ExceptionHandler(NotFoundServiceEx.class) public void ruleNotFound(NotFoundServiceEx exception, HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendError(404, exception.getMessage()); } @ExceptionHandler(DuplicateKeyException.class) public void rule(DuplicateKeyException exception, HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendError(409, exception.getMessage()); } @RequestMapping(value = "/rest/adminrules", method = RequestMethod.GET, produces={"application/xml", "application/json"}) public @ResponseBody JaxbAdminRuleList get( @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "entries", required = false) Integer entries, @RequestParam(value = "full", required = false, defaultValue = "false") boolean full, @RequestParam(value = "userName", required = false) String userName, @RequestParam(value = "userAny", required = false) Boolean userDefault, @RequestParam(value = "roleName", required = false) String roleName, @RequestParam(value = "roleAny", required = false) Boolean roleDefault, @RequestParam(value = "workspace", required = false) String workspace, @RequestParam(value = "workspaceAny", required = false) Boolean workspaceDefault ) { RuleFilter filter = buildFilter( userName, userDefault, roleName, roleDefault, workspace, workspaceDefault); return new JaxbAdminRuleList(adminService.getListFull(filter, page, entries)); } @RequestMapping(value = "/rest/adminrules/id/{id}", method = RequestMethod.GET, produces={"application/xml", "application/json"}) public @ResponseBody JaxbAdminRule get(@PathVariable ("id") Long id) { return new JaxbAdminRule(adminService.get(id)); } @RequestMapping(value = "/rest/adminrules/count", method = RequestMethod.GET, produces={"application/xml", "application/json"}) public @ResponseBody JaxbAdminRuleList count( @RequestParam(value = "userName", required = false) String userName, @RequestParam(value = "userAny", required = false) Boolean userDefault, @RequestParam(value = "roleName", required = false) String roleName, @RequestParam(value = "roleAny", required = false) Boolean roleDefault, @RequestParam(value = "workspace", required = false) String workspace, @RequestParam(value = "workspaceAny", required = false) Boolean workspaceDefault ) { RuleFilter filter = buildFilter( userName, userDefault, roleName, roleDefault, workspace, workspaceDefault); return new JaxbAdminRuleList(adminService.count(filter)); } @RequestMapping(value = "/rest/adminrules", method = RequestMethod.POST) public ResponseEntity<Long> insert(@RequestBody JaxbAdminRule rule) { long priority = rule.getPriority() == null ? 0 : rule.getPriority(); if (adminService.getRuleByPriority(priority) != null) { adminService.shift(priority, 1); } return new ResponseEntity<>(adminService.insert(rule.toRule()), HttpStatus.CREATED); } @RequestMapping(value = "/rest/adminrules/id/{id}", method = RequestMethod.POST) public @ResponseStatus(HttpStatus.OK) void update(@PathVariable ("id") Long id, @RequestBody JaxbAdminRule rule) { if (rule.getPriority() != null) { ShortAdminRule priorityRule = adminService.getRuleByPriority(rule.getPriority()); if (priorityRule != null && priorityRule.getId().longValue() != id) { adminService.shift(rule.getPriority(), 1); } } adminService.update(rule.toRule(adminService.get(id))); } @RequestMapping(value = "/rest/adminrules/id/{id}", method = RequestMethod.DELETE) public @ResponseStatus(HttpStatus.OK) void delete(@PathVariable("id") Long id) { adminService.delete(id); } protected RuleFilter buildFilter( String userName, Boolean userDefault, String roleName, Boolean groupDefault, String workspace, Boolean workspaceDefault) { RuleFilter filter = new RuleFilter(SpecialFilterType.ANY, true); setFilter(filter.getUser(), userName, userDefault); setFilter(filter.getRole(), roleName, groupDefault); setFilter(filter.getWorkspace(), workspace, workspaceDefault); return filter; } private void setFilter(TextFilter filter, String name, Boolean includeDefault) { if (name != null) { filter.setText(name); if (includeDefault != null) { filter.setIncludeDefault(includeDefault); } } else { if (includeDefault != null && includeDefault) { filter.setType(SpecialFilterType.DEFAULT); } else { filter.setType(SpecialFilterType.ANY); } } } /** * Move the provided rules to the target priority. Rules will be sorted by their priority, * first rule will be updated with a priority equal to the target priority and the next ones * will get an incremented priority value. */ @RequestMapping(value = "/rest/adminrules/move", method = RequestMethod.GET, produces = {"application/xml", "application/json"}) public @ResponseBody ResponseEntity<JaxbAdminRuleList> move( @RequestParam(value = "targetPriority", required = true) int targetPriority, @RequestParam(value = "rulesIds", required = true) String rulesIds ) { // let's find the rules that need to be moved List<AdminRule> rules = findRules(rulesIds); if (rules.isEmpty()) { return ResponseEntity.ok(null); } // shift priorities of rules with a priority equal or lower than the target priority adminService.shift(targetPriority, rules.size()); // update moved rules priority long priority = targetPriority; for (AdminRule rule : rules) { rule.setPriority(priority); adminService.update(rule); priority++; } // return moved rules with their priority updated return ResponseEntity.ok(new JaxbAdminRuleList(rules)); } /** * Helper method that will parse and retrieve the provided rules sorted by their priority. */ private List<AdminRule> findRules(String rulesIds) { return Arrays.stream(rulesIds.split(",")) .map(ruleId -> { try { // parsing the rule id return Long.parseLong(ruleId); } catch (NumberFormatException exception) { // error parsing the rule id throw new InvalidRulesIds(); } }).map(ruleId -> { // search the rule by id return adminService.get(ruleId); }).filter(rule -> rule != null) .sorted((ruleA, ruleB) -> Long.compare(ruleA.getPriority(), ruleB.getPriority())) .collect(Collectors.toList()); } @ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Invalid adminrules ids") private class InvalidRulesIds extends RuntimeException { } }