diff --git a/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacade.java b/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacade.java index a6ff57f3bf..2beea0d8d6 100644 --- a/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacade.java +++ b/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacade.java @@ -40,6 +40,18 @@ public interface UserFacade extends Facade { */ User createUser(String username, String password, EmailAddress email); + /** + * Creates or updates user info, depending if user with 'username' already exists. + * + * @param userName + * @param fullUserName + * @param actorId + * @param actorName + * @param roles + * @return new or updated user instance + */ + User createOrUpdateUser(String userName, String fullUserName, String actorId, String actorName, List roles); + /** * @return list of all users persisted in database */ diff --git a/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacadeImpl.java b/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacadeImpl.java index 7da5cade4e..7f5043927e 100644 --- a/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacadeImpl.java +++ b/commons-app/src/main/java/cz/cuni/mff/xrg/odcs/commons/app/facade/UserFacadeImpl.java @@ -86,6 +86,48 @@ public User createUser(String username, String plainPassword, return user; } + @Override + public User createOrUpdateUser(String userName, String fullUserName, String actorId, String actorName, List roles) { + User user = this.getUserByExtId(userName); + // create user if does not exist + if (user == null) { + user = this.createUser(userName, "*****", new EmailAddress(userName + "@nomail.com")); + if (fullUserName != null) { + user.setFullName(fullUserName); + } else { + user.setFullName(userName); + } + user.setExternalIdentifier(userName); + user.setTableRows(20); + this.saveNoAuth(user); + } + + // update user information + user.getRoles().clear(); + for (String rolename : roles) { + if (rolename != null) { + RoleEntity role = this.getRoleByName(rolename); + if (role != null) { + user.addRole(role); + } + } + } + + if (actorId != null) { + UserActor actor = this.getUserActorByExternalId(actorId); + if (actor == null) { + actor = new UserActor(); + actor.setName(actorName); + actor.setExternalId(actorId); + this.save(actor); + } + user.setUserActor(actor); + } + + this.save(user); + return user; + } + /** * @return list of all users persisted in database */ diff --git a/frontend/src/main/java/cz/cuni/mff/xrg/odcs/frontend/auth/CasAuthenticationUserDetailsService.java b/frontend/src/main/java/cz/cuni/mff/xrg/odcs/frontend/auth/CasAuthenticationUserDetailsService.java index 29819a8022..59d50ff116 100644 --- a/frontend/src/main/java/cz/cuni/mff/xrg/odcs/frontend/auth/CasAuthenticationUserDetailsService.java +++ b/frontend/src/main/java/cz/cuni/mff/xrg/odcs/frontend/auth/CasAuthenticationUserDetailsService.java @@ -63,14 +63,15 @@ public CasAuthenticationUserDetailsService(UserFacade userFacade) { @Override protected UserDetails loadUserDetails(final Assertion assertion) { - String username = assertion.getPrincipal().getName(); + String userName = assertion.getPrincipal().getName(); Map attributes = assertion.getPrincipal().getAttributes(); // FIXME: this is temporal solution; In the future, subject Id should be sent by CAS in username // Currently Actor ID is sent in username CAS parameter String userNameFromAttributes = attributes.get(this.userNameAttributeName) != null ? attributes.get(this.userNameAttributeName).toString() : null; if (userNameFromAttributes != null) { - username = userNameFromAttributes; + userName = userNameFromAttributes; } + String userFullName = attributes.get(this.fullNameAttributeName) != null ? attributes.get(this.fullNameAttributeName).toString() : null; List roles = new ArrayList<>(); Object roleAttributes = attributes.get(roleAttributeName); @@ -80,46 +81,10 @@ protected UserDetails loadUserDetails(final Assertion assertion) { else if (roleAttributes instanceof List) roles.addAll((List) roleAttributes); } - - User user = userFacade.getUserByExtId(username); - - if (user == null) { - user = userFacade.createUser(username, "*****", new EmailAddress(username + "@nomail.com")); - String userFullName = attributes.get(this.fullNameAttributeName) != null ? attributes.get(this.fullNameAttributeName).toString() : null; - if (userFullName != null) { - user.setFullName(userFullName); - } else { - user.setFullName(username); - } - user.setExternalIdentifier(username); - user.setTableRows(20); - this.userFacade.saveNoAuth(user); - } - - user.getRoles().clear(); - - for (String rolename : roles) { - if (rolename != null) { - RoleEntity role = this.userFacade.getRoleByName(rolename); - if (role != null) { - user.addRole(role); - } - } - } - String actorId = attributes.get(this.actorIdAttributeName) != null ? attributes.get(this.actorIdAttributeName).toString() : null; - if (actorId != null) { - UserActor actor = this.userFacade.getUserActorByExternalId(actorId); - if (actor == null) { - actor = new UserActor(); - String actorName = attributes.get(this.actorNameAttributeName).toString(); - actor.setName(actorName); - actor.setExternalId(actorId); - this.userFacade.save(actor); - } - user.setUserActor(actor); - } + String actorName = attributes.get(this.actorNameAttributeName) != null ? attributes.get(this.actorNameAttributeName).toString() : null; + User user = userFacade.createOrUpdateUser(userName, userFullName, actorId, actorName, roles); return user; } diff --git a/master/pom.xml b/master/pom.xml index 8a6bad66d7..65e987a14e 100644 --- a/master/pom.xml +++ b/master/pom.xml @@ -74,7 +74,12 @@ commons-collections4 4.0 - + + + com.google.guava + guava + 18.0 + diff --git a/master/src/main/java/eu/unifiedviews/master/api/UserResource.java b/master/src/main/java/eu/unifiedviews/master/api/UserResource.java new file mode 100644 index 0000000000..2cb07c3915 --- /dev/null +++ b/master/src/main/java/eu/unifiedviews/master/api/UserResource.java @@ -0,0 +1,139 @@ +/** + * This file is part of UnifiedViews. + * + * UnifiedViews is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedViews is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with UnifiedViews. If not, see . + */ +package eu.unifiedviews.master.api; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Joiner; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import cz.cuni.mff.xrg.odcs.commons.app.facade.UserFacade; +import eu.unifiedviews.master.authentication.AuthenticationRequired; +import eu.unifiedviews.master.i18n.Messages; +import eu.unifiedviews.master.model.ApiException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.*; + +@Component +@Path("/users") +@AuthenticationRequired +public class UserResource { + + private static final Logger LOG = LoggerFactory.getLogger(UserResource.class); + + @Autowired + private UserFacade userFacade; + + @Autowired + private ObjectMapper mapper; + + @Value("${cas.attributeName.role}") + private String roleAttributeName; + + @Value("${cas.attributeName.actorId}") + private String actorIdAttributeName; + + @Value("${cas.attributeName.actorName}") + private String actorNameAttributeName; + + @Value("${cas.attributeName.fullName}") + private String fullNameAttributeName; + + @Value("${cas.attributeName.userName}") + private String userNameAttributeName; + + private String[] requiredAttributes; + + @PostConstruct + public void setup() { + // setup array of required attributes + this.requiredAttributes = new String[] { roleAttributeName, actorIdAttributeName, actorNameAttributeName, fullNameAttributeName, userNameAttributeName }; + } + + @POST + @Path("/create") + @Consumes("application/json") + @Produces("application/json") + public String createUser(String casResponseAsJSON) { + LOG.info("Received user create request: " + casResponseAsJSON); + Multimap multimap = parseJSONtoMultimap(casResponseAsJSON); + + // validate if request contains all required attributes + List missingAttributes = new ArrayList<>(); + for (String requiredAttribute : requiredAttributes) { + if (!multimap.containsKey(requiredAttribute)) { + missingAttributes.add(requiredAttribute); + } + } + if (missingAttributes.size() > 0) { + throw new ApiException(Response.Status.BAD_REQUEST, Messages.getString("user.request.parameter.missing", Joiner.on(", ").join(missingAttributes)), "Missing attribute/s: '" + Joiner.on(", ").join(missingAttributes) + "'."); + } + + String userName = getFirstValue(multimap.get(userNameAttributeName)); + String userFullName = getFirstValue(multimap.get(fullNameAttributeName)); + String actorId = getFirstValue(multimap.get(actorIdAttributeName)); + String actorName = getFirstValue(multimap.get(actorNameAttributeName)); + List roles = (List) multimap.get(roleAttributeName); + + userFacade.createOrUpdateUser(userName, userFullName, actorId, actorName, roles); + return "{\"success\":true}"; + } + + private Multimap parseJSONtoMultimap(String casResponseAsJSON) { + // parse request attributes to multimap + Multimap multimap = ArrayListMultimap.create(); + try { + JsonNode rootNode = mapper.readTree(casResponseAsJSON); + Iterator> nodeIterator = rootNode.fields(); + while (nodeIterator.hasNext()) { + Map.Entry entry = nodeIterator.next(); + if (entry.getValue().isArray()) { + Iterator elementsIterator = entry.getValue().elements(); + while (elementsIterator.hasNext()) { + JsonNode element = elementsIterator.next(); + multimap.put(entry.getKey(), element.asText()); + } + } else { + multimap.put(entry.getKey(), entry.getValue().asText()); + } + } + } catch (IOException e) { + throw new ApiException(Response.Status.BAD_REQUEST, Messages.getString("user.request.not.parsed"), e.getMessage()); + } + return multimap; + } + + private T getFirstValue(Collection col) { + Iterator iterator = col.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } + return null; + } +} diff --git a/master/src/main/resources/master-context.xml b/master/src/main/resources/master-context.xml index 737a9c974e..3b5c9fffb4 100644 --- a/master/src/main/resources/master-context.xml +++ b/master/src/main/resources/master-context.xml @@ -33,4 +33,7 @@ + + + diff --git a/master/src/main/resources/master-messages_en.properties b/master/src/main/resources/master-messages_en.properties index ec4e218154..bf215de727 100644 --- a/master/src/main/resources/master-messages_en.properties +++ b/master/src/main/resources/master-messages_en.properties @@ -35,3 +35,6 @@ dpu.create.failed = DPU create failed! unauthorized.request = Request is not authorized. schedule.user.id.not.found = User not found! Schedule could not be created. + +user.request.not.parsed = Could not parse data from request. +user.request.parameter.missing = Request does not contain required attribute/s: ''{0}''. diff --git a/master/src/main/resources/master-messages_sk.properties b/master/src/main/resources/master-messages_sk.properties index e0712c2241..0f6a1ea186 100644 --- a/master/src/main/resources/master-messages_sk.properties +++ b/master/src/main/resources/master-messages_sk.properties @@ -33,3 +33,6 @@ dpu.replace.failed = Nepodarilo sa nahradi\u0165 DPU! dpu.create.failed = Nepodarilo sa vytvori\u0165 DPU! unauthorized.request = Po\u017Eiadavka nie je autorizovan\u00E1! + +user.request.not.parsed = Nepodarilo sa spr\u00e1vne na\u010d\u00edta\u0165 po\u017eiadavku! +user.request.parameter.missing = Po\u017eiadavka neobsahuje potrebn\u00fd atrib\u00fat/y: ''{0}''.