Commit 0f77a4d3 authored by Matija Obreza's avatar Matija Obreza

Merge branch '252-delete-user-account' into 'master'

Resolve "Delete user account"

Closes #252

See merge request genesys-pgr/genesys-server!149
parents 9edc7fda 0bdca61c
...@@ -97,68 +97,68 @@ dockerize genesys branch: ...@@ -97,68 +97,68 @@ dockerize genesys branch:
- tags - tags
deploy for review: # deploy for review:
stage: deploy # stage: deploy
image: docker:${DOCKER_VERSION} # image: docker:${DOCKER_VERSION}
except: # except:
- master # - master
- /^production-.*/ # - /^production-.*/
# when: manual # # when: manual
allow_failure: false # allow_failure: false
environment: # environment:
name: genesys/${CI_COMMIT_REF_SLUG} # name: genesys/${CI_COMMIT_REF_SLUG}
url: https://${CI_ENVIRONMENT_SLUG}.review.genesys-pgr.org # url: https://${CI_ENVIRONMENT_SLUG}.review.genesys-pgr.org
on_stop: remove review instance # on_stop: remove review instance
script: # script:
# Address the swarm # # Address the swarm
- export DOCKER_HOST=swarm.genesys-pgr.org # - export DOCKER_HOST=swarm.genesys-pgr.org
# Configuration # # Configuration
- TLS_PATH=~/.docker/${DOCKER_HOST}/certs && mkdir -p ${TLS_PATH} # - TLS_PATH=~/.docker/${DOCKER_HOST}/certs && mkdir -p ${TLS_PATH}
- echo "${DOCKER_TLS_CA}" > ${TLS_PATH}/ca.pem # - echo "${DOCKER_TLS_CA}" > ${TLS_PATH}/ca.pem
- echo "${DOCKER_TLS_KEY}" > ${TLS_PATH}/key.pem # - echo "${DOCKER_TLS_KEY}" > ${TLS_PATH}/key.pem
- echo "${DOCKER_TLS_CERT}" > ${TLS_PATH}/cert.pem # - echo "${DOCKER_TLS_CERT}" > ${TLS_PATH}/cert.pem
- export DOCKER_TLS_VERIFY=1 # - export DOCKER_TLS_VERIFY=1
- export DOCKER_CERT_PATH=${TLS_PATH} # - export DOCKER_CERT_PATH=${TLS_PATH}
- DOCKER_CMD=docker # - DOCKER_CMD=docker
# Actions # # Actions
- apk add --no-cache gettext # - apk add --no-cache gettext
- if [ "${CI_COMMIT_REF_SLUG}" = "master" ] ; then # - if [ "${CI_COMMIT_REF_SLUG}" = "master" ] ; then
export IMAGE_TAG="${GENESYS_VERSION}"; # export IMAGE_TAG="${GENESYS_VERSION}";
else # else
export IMAGE_TAG="${GENESYS_VERSION}-${CI_COMMIT_REF_SLUG}"; # export IMAGE_TAG="${GENESYS_VERSION}-${CI_COMMIT_REF_SLUG}";
fi # fi
- echo Deploying ${CI_REGISTRY_IMAGE}:${IMAGE_TAG} for review as https\://${CI_ENVIRONMENT_SLUG}.review.genesys\-pgr.org # - echo Deploying ${CI_REGISTRY_IMAGE}:${IMAGE_TAG} for review as https\://${CI_ENVIRONMENT_SLUG}.review.genesys\-pgr.org
- envsubst < docker/review-compose-template.yml > review-composed.yml # - envsubst < docker/review-compose-template.yml > review-composed.yml
# - cat review-composed.yml # # - cat review-composed.yml
- ${DOCKER_CMD} stack rm ${CI_ENVIRONMENT_SLUG} || true # - ${DOCKER_CMD} stack rm ${CI_ENVIRONMENT_SLUG} || true
- ${DOCKER_CMD} stack deploy -c review-composed.yml ${CI_ENVIRONMENT_SLUG} # - ${DOCKER_CMD} stack deploy -c review-composed.yml ${CI_ENVIRONMENT_SLUG}
remove review instance: # remove review instance:
stage: deploy # stage: deploy
image: docker:${DOCKER_VERSION} # image: docker:${DOCKER_VERSION}
except: # except:
- master # - master
- /^production-.*/ # - /^production-.*/
when: manual # when: manual
variables: # variables:
GIT_STRATEGY: none # GIT_STRATEGY: none
environment: # environment:
name: genesys/${CI_COMMIT_REF_SLUG} # name: genesys/${CI_COMMIT_REF_SLUG}
action: stop # action: stop
script: # script:
- echo Removing review https\://${CI_ENVIRONMENT_SLUG}.review.genesys\-pgr.org # - echo Removing review https\://${CI_ENVIRONMENT_SLUG}.review.genesys\-pgr.org
# Address the swarm # # Address the swarm
- export DOCKER_HOST=swarm.genesys-pgr.org # - export DOCKER_HOST=swarm.genesys-pgr.org
# Configuration # # Configuration
- TLS_PATH=~/.docker/${DOCKER_HOST}/certs && mkdir -p ${TLS_PATH} # - TLS_PATH=~/.docker/${DOCKER_HOST}/certs && mkdir -p ${TLS_PATH}
- echo "${DOCKER_TLS_CA}" > ${TLS_PATH}/ca.pem # - echo "${DOCKER_TLS_CA}" > ${TLS_PATH}/ca.pem
- echo "${DOCKER_TLS_KEY}" > ${TLS_PATH}/key.pem # - echo "${DOCKER_TLS_KEY}" > ${TLS_PATH}/key.pem
- echo "${DOCKER_TLS_CERT}" > ${TLS_PATH}/cert.pem # - echo "${DOCKER_TLS_CERT}" > ${TLS_PATH}/cert.pem
- export DOCKER_TLS_VERIFY=1 # - export DOCKER_TLS_VERIFY=1
- export DOCKER_CERT_PATH=${TLS_PATH} # - export DOCKER_CERT_PATH=${TLS_PATH}
- DOCKER_CMD=docker # - DOCKER_CMD=docker
# Actions # # Actions
- ${DOCKER_CMD} stack rm ${CI_ENVIRONMENT_SLUG} || true # - ${DOCKER_CMD} stack rm ${CI_ENVIRONMENT_SLUG} || true
deploy on staging server: deploy on staging server:
stage: deploy stage: deploy
......
...@@ -73,12 +73,12 @@ public class CreateContentListener extends RunAsAdminListener { ...@@ -73,12 +73,12 @@ public class CreateContentListener extends RunAsAdminListener {
} }
private void createArticles() throws IOException, JsonProcessingException { private void createArticles() throws IOException, JsonProcessingException {
if (!createContent) { // if (!createContent) {
LOG.warn("Skipping content creation on startup."); // LOG.warn("Skipping content creation on startup.");
return; // return;
} // }
LOG.debug("Checking if default content exists"); LOG.info("Checking if default content exists");
final ClassLoader classLoader = CreateContentListener.class.getClassLoader(); final ClassLoader classLoader = CreateContentListener.class.getClassLoader();
final PathMatchingResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(classLoader); final PathMatchingResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(classLoader);
final String resourcePath = "/default-content/*"; final String resourcePath = "/default-content/*";
...@@ -90,10 +90,18 @@ public class CreateContentListener extends RunAsAdminListener { ...@@ -90,10 +90,18 @@ public class CreateContentListener extends RunAsAdminListener {
final ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
try (InputStream stream = r.getInputStream()) { try (InputStream stream = r.getInputStream()) {
final JsonNode json = mapper.readTree(stream); final JsonNode json = mapper.readTree(stream);
JsonNode templateNode = json.get("template");
final boolean isTemplate = templateNode != null && templateNode.booleanValue();
final Iterator<Entry<String, JsonNode>> it = json.fields(); final Iterator<Entry<String, JsonNode>> it = json.fields();
while (it.hasNext()) { while (it.hasNext()) {
final Entry<String, JsonNode> entry = it.next(); final Entry<String, JsonNode> entry = it.next();
if (entry.getKey().length() != 2) {
// Skip over non-language fields
continue;
}
final Locale locale = new Locale(entry.getKey()); final Locale locale = new Locale(entry.getKey());
// Load from default locale if exists // Load from default locale if exists
...@@ -102,10 +110,10 @@ public class CreateContentListener extends RunAsAdminListener { ...@@ -102,10 +110,10 @@ public class CreateContentListener extends RunAsAdminListener {
// If nothing is found, parse the resource and create content // If nothing is found, parse the resource and create content
if (article == null) { if (article == null) {
try { try {
contentService.createGlobalArticle(slug, locale, entry.getValue().get("title").asText(), entry.getValue().get("body").asText(), null, false); contentService.createGlobalArticle(slug, locale, entry.getValue().get("title").asText(), entry.getValue().get("body").asText(), null, isTemplate);
LOG.info("Created article for slug: {} lang={}", slug, locale.getLanguage()); LOG.warn("Created article for slug: {} lang={}", slug, locale.getLanguage());
} catch (CRMException e) { } catch (CRMException e) {
LOG.warn("Failed to create global article slug={}.", slug, e); LOG.error("Failed to create global article slug={}.", slug, e);
} }
} }
} }
......
...@@ -60,6 +60,11 @@ public interface ContentService { ...@@ -60,6 +60,11 @@ public interface ContentService {
public final String USER_PASSWORD_RESET = "user-password-reset"; public final String USER_PASSWORD_RESET = "user-password-reset";
public final String USER_PASSWORD_RESET_EMAIL_SENT = "user-password-reset-email-sent"; public final String USER_PASSWORD_RESET_EMAIL_SENT = "user-password-reset-email-sent";
public final String USER_RESET_PASSWORD_INSTRUCTIONS = "user-reset-password-instructions"; public final String USER_RESET_PASSWORD_INSTRUCTIONS = "user-reset-password-instructions";
public final String DELETE_ACCOUNT = "user-delete-account";
public final String SMTP_DELETE_ACCOUNT = "smtp-delete-account";
public final String DELETE_ACCOUNT_SENT = "account-delete-requested";
public final String DELETE_ACCOUNT_CONFIRMED = "account-delete-confirmed";
public final String SMTP_DELETE_ACCOUNT_INPROGRESS = "smtp-delete-account-inprogress";
List<ActivityPost> lastNews(); List<ActivityPost> lastNews();
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.genesys2.server.service; package org.genesys2.server.service;
import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException; import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys2.server.model.impl.User; import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.TokenVerificationService.NoSuchVerificationTokenException; import org.genesys2.server.service.TokenVerificationService.NoSuchVerificationTokenException;
...@@ -32,4 +33,8 @@ public interface EMailVerificationService { ...@@ -32,4 +33,8 @@ public interface EMailVerificationService {
void validateEMail(String tokenUuid, String key) throws NoSuchVerificationTokenException, TokenExpiredException; void validateEMail(String tokenUuid, String key) throws NoSuchVerificationTokenException, TokenExpiredException;
void changePassword(String tokenUuid, String key, String password) throws NoSuchVerificationTokenException, PasswordPolicyException, TokenExpiredException; void changePassword(String tokenUuid, String key, String password) throws NoSuchVerificationTokenException, PasswordPolicyException, TokenExpiredException;
void requestDeleteAccount(User user);
void confirmDeleteAccount(String tokenUuid, String key) throws NoSuchVerificationTokenException, TokenExpiredException, UserException;
} }
...@@ -66,4 +66,17 @@ public interface UserService extends BasicUserService<UserRole, User> { ...@@ -66,4 +66,17 @@ public interface UserService extends BasicUserService<UserRole, User> {
void setFtpPassword(User user, String ftpPassword) throws PasswordPolicyException; void setFtpPassword(User user, String ftpPassword) throws PasswordPolicyException;
/**
* Disables current user's account
* @throws UserException
*/
void disableMyAccount() throws UserException;
/**
* Disables the account and removes personally identifiable data.
*
* @param user
* @throws UserException
*/
void archiveUser(User user) throws UserException;
} }
...@@ -19,6 +19,8 @@ package org.genesys2.server.service.impl; ...@@ -19,6 +19,8 @@ package org.genesys2.server.service.impl;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Locale; import java.util.Locale;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException; import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys2.server.model.impl.Article; import org.genesys2.server.model.impl.Article;
import org.genesys2.server.model.impl.User; import org.genesys2.server.model.impl.User;
...@@ -61,6 +63,9 @@ public class EMailVerificationServiceImpl implements EMailVerificationService { ...@@ -61,6 +63,9 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
@Value("${base.url}") @Value("${base.url}")
private String baseUrl; private String baseUrl;
@Value("${mail.user.from}")
private String defaultEmailFrom;
@Override @Override
@Transactional @Transactional
public void sendVerificationEmail(User user) { public void sendVerificationEmail(User user) {
...@@ -81,7 +86,7 @@ public class EMailVerificationServiceImpl implements EMailVerificationService { ...@@ -81,7 +86,7 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
@Override @Override
@Transactional @Transactional
public void sendPasswordResetEmail(User user) { public void sendPasswordResetEmail(User user) {
// Generate new token // Generate new token
final VerificationToken verificationToken = tokenVerificationService.generateToken("email-password", user.getUuid()); final VerificationToken verificationToken = tokenVerificationService.generateToken("email-password", user.getUuid());
final Article article = contentService.getGlobalArticle(ContentService.SMTP_EMAIL_PASSWORD, Locale.ENGLISH); final Article article = contentService.getGlobalArticle(ContentService.SMTP_EMAIL_PASSWORD, Locale.ENGLISH);
...@@ -132,4 +137,36 @@ public class EMailVerificationServiceImpl implements EMailVerificationService { ...@@ -132,4 +137,36 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
} }
} }
@Override
@Transactional
public void requestDeleteAccount(User user) {
// Generate new token
final VerificationToken verificationToken = tokenVerificationService.generateToken("delete-account", user.getUuid());
final Article article = contentService.getGlobalArticle(ContentService.SMTP_DELETE_ACCOUNT, Locale.ENGLISH);
final String mailBody = MessageFormat.format(article.getBody(), baseUrl, verificationToken.getUuid(), verificationToken.getKey(), user.getFullName(), user.getEmail());
emailService.sendMail(article.getTitle(), mailBody, user.getEmail());
}
@Override
@Transactional(rollbackFor = Throwable.class)
public void confirmDeleteAccount(String tokenUuid, String key) throws NoSuchVerificationTokenException, TokenExpiredException, UserException {
final VerificationToken consumedToken = tokenVerificationService.consumeToken("delete-account", tokenUuid, key);
String uuid = consumedToken.getData();
User currentUser = SecurityContextUtil.getCurrentUser();
if (currentUser.getUuid().equals(uuid)) {
userService.disableMyAccount();
final Article article = contentService.getGlobalArticle(ContentService.SMTP_DELETE_ACCOUNT_INPROGRESS, Locale.ENGLISH);
final String mailBody = MessageFormat.format(article.getBody(), baseUrl, currentUser.getFullName(), currentUser.getEmail());
emailService.sendMail(article.getTitle(), mailBody, defaultEmailFrom, currentUser.getEmail());
} else {
throw new NoSuchVerificationTokenException();
}
}
} }
...@@ -18,7 +18,9 @@ package org.genesys2.server.service.impl; ...@@ -18,7 +18,9 @@ package org.genesys2.server.service.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
...@@ -384,4 +386,54 @@ public class UserServiceImpl extends BasicUserServiceImpl<UserRole, User> implem ...@@ -384,4 +386,54 @@ public class UserServiceImpl extends BasicUserServiceImpl<UserRole, User> implem
user.setFtpPassword(passwordEncoder.encode(ftpPassword)); user.setFtpPassword(passwordEncoder.encode(ftpPassword));
userRepository.save(user); userRepository.save(user);
} }
@Override
@Transactional
@PreAuthorize("isFullyAuthenticated()")
public void disableMyAccount() throws UserException {
User currentUser = SecurityContextUtil.getMe();
User u = userRepository.findOne(currentUser.getId());
if (u.hasRole(UserRole.ADMINISTRATOR.getName())) {
throw new UserException("Refusing to disable active administrator account");
}
Calendar expires = Calendar.getInstance();
expires.add(Calendar.MONTH, 1);
u.setAccountExpires(expires.getTime());
u.setActive(false);
// u.setAccountType(AccountType.DELETED);
userRepository.save(u);
}
@Override
@Transactional
public void archiveUser(User user) throws UserException {
user = userRepository.findOne(user.getId());
if (user.hasRole(UserRole.ADMINISTRATOR.getName())) {
throw new UserException("Refusing to disable active administrator account");
}
LOG.warn("Archiving user {}", user.getEmail());
Date now = new Date();
user.setAccountExpires(now);
user.setActive(false);
user.setAccountType(AccountType.LOCAL);
// user.setAccountType(AccountType.DELETED);
user.setEmail("deleted@" + now.getTime());
user.setPassword(THIS_IS_NOT_A_PASSWORD);
user.setFtpPassword(null);
user.setFullName("USER ACCOUNT DELETED");
user.setShortName("deleted" + now.getTime());
user.setPasswordExpires(now);
user.getRoles().clear();
userRepository.save(user);
LOG.warn("Removing ACL entries for {}", user.getEmail());
aclEntryRepository.delete(user.getAclEntries());
}
} }
...@@ -42,6 +42,7 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -42,6 +42,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
...@@ -50,10 +51,12 @@ import org.springframework.web.bind.annotation.PathVariable; ...@@ -50,10 +51,12 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@Controller @Controller
@RequestMapping("/content") @RequestMapping("/content")
...@@ -346,6 +349,28 @@ public class ArticleController extends BaseController { ...@@ -346,6 +349,28 @@ public class ArticleController extends BaseController {
return "/content/article-edit"; return "/content/article-edit";
} }
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping(value = "/edit/{url}/{language}", produces = MediaType.APPLICATION_JSON_VALUE, params = { "json" })
public @ResponseBody Object articleAsJson(ModelMap model, @PathVariable(value = "url") String slug, @PathVariable("language") String language) {
Article article = contentService.getArticleBySlugAndLang(slug, language);
if (article == null) {
article = new Article();
article.setSlug(slug);
article.setLang(language);
}
model.addAttribute("article", article);
ObjectMapper mapper=new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
node.put("template", article.isTemplate());
ObjectNode lang = node.putObject(language);
lang.put("title", article.getTitle());
lang.put("body", article.getBody());
return node;
}
/** /**
* Edit article in another language * Edit article in another language
* *
......
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
package org.genesys2.server.servlet.controller; package org.genesys2.server.servlet.controller;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
...@@ -49,7 +51,9 @@ import org.springframework.security.access.prepost.PreAuthorize; ...@@ -49,7 +51,9 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
...@@ -317,6 +321,38 @@ public class UserProfileController extends BaseController { ...@@ -317,6 +321,38 @@ public class UserProfileController extends BaseController {
return "redirect:/profile/" + user.getUuid(); return "redirect:/profile/" + user.getUuid();
} }
@GetMapping("/me/delete")
@PreAuthorize("isFullyAuthenticated()")
public String startDelete(ModelMap model, Locale locale) {
model.addAttribute("info", contentService.getGlobalArticle(ContentService.DELETE_ACCOUNT, locale));
return "/user/delete";
}
@PostMapping("/me/delete")
@PreAuthorize("isFullyAuthenticated()")
public String requestDelete(ModelMap model) {
final User user = userService.getMe();
if (user == null) {
throw new ResourceNotFoundException();
}
emailVerificationService.requestDeleteAccount(user);
return "redirect:/content/" + ContentService.DELETE_ACCOUNT_SENT;
}
@RequestMapping(value = "/me/delete/{tokenUuid:.+}", method = RequestMethod.GET)
@PreAuthorize("isFullyAuthenticated()")
public String confirmDelete(ModelMap model, HttpServletRequest servletRequest, @PathVariable("tokenUuid") String tokenUuid, @RequestParam(value = "key", required = true) String key) throws NoSuchVerificationTokenException, TokenExpiredException, ServletException, UserException {
emailVerificationService.confirmDeleteAccount(tokenUuid, key);
servletRequest.logout();
return "redirect:/content/" + ContentService.DELETE_ACCOUNT_CONFIRMED;
}
@RequestMapping(value = "/{uuid:.+}/update-roles", method = { RequestMethod.POST }) @RequestMapping(value = "/{uuid:.+}/update-roles", method = { RequestMethod.POST })
@PreAuthorize("hasRole('ADMINISTRATOR')") @PreAuthorize("hasRole('ADMINISTRATOR')")
public String updateRoles(ModelMap model, @PathVariable("uuid") String uuid, @RequestParam("role") Set<UserRole> selectedRoles) { public String updateRoles(ModelMap model, @PathVariable("uuid") String uuid, @RequestParam("role") Set<UserRole> selectedRoles) {
......
...@@ -198,6 +198,30 @@ public class UserProfileController extends BaseController { ...@@ -198,6 +198,30 @@ public class UserProfileController extends BaseController {
return "redirect:" + URLBASE + user.getUuid(); return "redirect:" + URLBASE + user.getUuid();
} }
@RequestMapping(value = "/{uuid:.+}/delete", method = RequestMethod.POST)
public String delete(ModelMap model, @PathVariable("uuid") String uuid) throws UserException {
final User user = userService.getUserByUuid(uuid);
if (user==null) {
throw new ResourceNotFoundException();
}
// if (user.getAccountType() == AccountType.DELETED) {
// LOG.warn("Account already archived.");
// return "redirect:" + VIEWBASE;
// }
if (! user.isAccountNonExpired()) {
LOG.warn("Account already expired.");
return "redirect:" + VIEWBASE;