Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User provisioning via REST API #520

Merged
merged 3 commits into from
Sep 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> roles);

/**
* @return list of all users persisted in database
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> 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<String> roles = new ArrayList<>();
Object roleAttributes = attributes.get(roleAttributeName);
Expand All @@ -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;
}

Expand Down
7 changes: 6 additions & 1 deletion master/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

<!-- Guava libraries -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
</dependencies>

<build>
Expand Down
139 changes: 139 additions & 0 deletions master/src/main/java/eu/unifiedviews/master/api/UserResource.java
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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<String, String> multimap = parseJSONtoMultimap(casResponseAsJSON);

// validate if request contains all required attributes
List<String> 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<String> roles = (List) multimap.get(roleAttributeName);

userFacade.createOrUpdateUser(userName, userFullName, actorId, actorName, roles);
return "{\"success\":true}";
}

private Multimap<String, String> parseJSONtoMultimap(String casResponseAsJSON) {
// parse request attributes to multimap
Multimap<String, String> multimap = ArrayListMultimap.create();
try {
JsonNode rootNode = mapper.readTree(casResponseAsJSON);
Iterator<Map.Entry<String, JsonNode>> nodeIterator = rootNode.fields();
while (nodeIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = nodeIterator.next();
if (entry.getValue().isArray()) {
Iterator<JsonNode> 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> T getFirstValue(Collection<T> col) {
Iterator<T> iterator = col.iterator();
if (iterator.hasNext()) {
return iterator.next();
}
return null;
}
}
3 changes: 3 additions & 0 deletions master/src/main/resources/master-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@

<!-- bean for importing of pipelines -->
<bean class="cz.cuni.mff.xrg.odcs.commons.app.pipeline.transfer.ImportService"/>

<!-- bean for parsing JSON -->
<bean class="com.fasterxml.jackson.databind.ObjectMapper"/>
</beans>
3 changes: 3 additions & 0 deletions master/src/main/resources/master-messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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}''.
3 changes: 3 additions & 0 deletions master/src/main/resources/master-messages_sk.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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}''.