/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.stanbol.commons.usermanagement.resource;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.Policy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.clerezza.platform.config.SystemConfig;
import org.apache.clerezza.commons.rdf.BlankNode;
import org.apache.clerezza.commons.rdf.ImmutableGraph;
import org.apache.clerezza.commons.rdf.Literal;
import org.apache.clerezza.commons.rdf.BlankNodeOrIRI;
import org.apache.clerezza.commons.rdf.RDFTerm;
import org.apache.clerezza.commons.rdf.Triple;
import org.apache.clerezza.commons.rdf.Graph;
import org.apache.clerezza.commons.rdf.IRI;
import org.apache.clerezza.commons.rdf.impl.utils.PlainLiteralImpl;
import org.apache.clerezza.commons.rdf.impl.utils.simple.SimpleGraph;
import org.apache.clerezza.commons.rdf.impl.utils.TripleImpl;
import org.apache.clerezza.rdf.core.serializedform.Parser;
import org.apache.clerezza.rdf.core.serializedform.Serializer;
import org.apache.clerezza.rdf.core.serializedform.SupportedFormat;
import org.apache.clerezza.rdf.ontologies.DC;
import org.apache.clerezza.rdf.ontologies.FOAF;
import org.apache.clerezza.rdf.ontologies.PERMISSION;
import org.apache.clerezza.rdf.ontologies.PLATFORM;
import org.apache.clerezza.rdf.ontologies.RDF;
import org.apache.clerezza.rdf.ontologies.RDFS;
import org.apache.clerezza.rdf.ontologies.SIOC;
import org.apache.clerezza.rdf.utils.GraphNode;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.stanbol.commons.security.PasswordUtil;
import org.apache.stanbol.commons.usermanagement.Ontology;
import org.apache.stanbol.commons.web.viewable.RdfViewable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handles HTTP requests related to a user
*
*/
@Component
@Service({Object.class, UserResource.class})
@Property(name = "javax.ws.rs", boolValue = true)
@Path("user-management")
public class UserResource {
private static Logger log = LoggerFactory.getLogger(UserResource.class);
@Reference(target = SystemConfig.SYSTEM_GRAPH_FILTER)
private Graph systemGraph;
@Reference
private Serializer serializer;
@Reference
private Parser parser;
private static GraphNode dummyNode;
static {
dummyNode = new GraphNode(new BlankNode(), new SimpleGraph());
dummyNode.addProperty(RDF.type, FOAF.Agent);
}
// **********************************
// ****** SHOW USER DETAILS *********
// **********************************
//
// ****** RESTful/RDF *******************
//
/**
* RESTful access to individual user data
*
* [has integration test] currently has a kludge to return an empty graph if
* user not found should return a 404
*
* @param userName
* @return context graph for user
* @throws UnsupportedEncodingException
*/
@GET
@Path("users/{username}")
public Graph getUserContext(@PathParam("username") String userName)
throws UnsupportedEncodingException {
GraphNode userNode = getUser(userName);
if (userNode == null) { // a kludge
return new SimpleGraph();
}
return userNode.getNodeContext();
}
//
// ****** HTML *******************
//
/**
* lookup a user by name.
*
* @param userName
* @return
*/
@GET
@Path("view-user")
@Produces(MediaType.TEXT_HTML)
public RdfViewable viewUser(@QueryParam("userName") String userName) {
return new RdfViewable("edit", getUser(userName), this.getClass());
}
/**
* lookup a user by name presenting it with "editUser" as rendering
* instruction.
*
* @param userName
* @return
*/
@GET
@Path("users/edit/{username}")
public RdfViewable editUser(@PathParam("username") String userName) {
return new RdfViewable("editUser", getUser(userName),
this.getClass());
}
/**
* RESTful access to user roles (and nested permissions right now - may
* change) [has integration test]
*
* @param userName
* @return role graph for user
* @throws UnsupportedEncodingException
*/
@GET
@Path("roles/{username}")
@Produces(SupportedFormat.TURTLE)
public Graph getUserRoles(@PathParam("username") String userName)
throws UnsupportedEncodingException {
Graph rolesGraph = getUserRolesGraph(userName);
// case of no roles not handled - what best to return : empty graph or
// 404?
return rolesGraph;
}
/**
* Update user details adds triples as appropriate to system graph
*
* @param uriInfo
* @param currentLogin
* @param newLogin
* @param fullName
* @param email
* @param password
* @param roles
* @param permissions
* @return
*/
@POST
@Path("store-user")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response storeUser(@Context UriInfo uriInfo,
@FormParam("currentLogin") String currentLogin,
@FormParam("newLogin") String newLogin,
@FormParam("fullName") String fullName,
@FormParam("email") String email,
@FormParam("password") String password,
@FormParam("roles") List<String> roles,
@FormParam("permissions") List<String> permissions) {
GraphNode userNode = null;
if (currentLogin != null) { //
currentLogin = currentLogin.trim();
}
if (currentLogin != null && !currentLogin.equals("")) {
userNode = getUser(currentLogin);
if (userNode != null) {
return store(userNode, uriInfo, currentLogin, newLogin, fullName, email, password, roles, permissions);
}
}
userNode = createUser(newLogin);
return store(userNode, uriInfo, newLogin, newLogin, fullName, email, password, roles, permissions);
}
/**
* Modify user given a graph describing the change.
*
* @param inputGraph change graph
* @return HTTP response
*/
@POST
@Consumes(SupportedFormat.TURTLE)
@Path("change-user")
public Response changeUser(ImmutableGraph inputGraph) {
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
Iterator<Triple> changes = inputGraph.filter(null, null,
Ontology.Change);
Triple oldTriple = null;
Triple newTriple = null;
if (changes.hasNext()) {
Triple changeTriple = changes.next();
BlankNodeOrIRI changeNode = changeTriple.getSubject();
Literal userName = (Literal) inputGraph
.filter(changeNode, PLATFORM.userName, null).next()
.getObject();
Iterator<Triple> userTriples = systemGraph
.filter(null, PLATFORM.userName, userName);
// if (userTriples.hasNext()) {
BlankNodeOrIRI userNode = userTriples.next()
.getSubject();
IRI predicateIRI = (IRI) inputGraph
.filter(changeNode, Ontology.predicate, null).next()
.getObject();
// handle old value (if it exists)
Iterator<Triple> iterator = inputGraph.filter(changeNode,
Ontology.oldValue, null);
RDFTerm oldValue = null;
if (iterator.hasNext()) {
oldValue = iterator.next().getObject();
// Triple oldTriple = systemGraph.filter(null, predicateIRI,
// oldValue).next();
Iterator<Triple> oldTriples = systemGraph.filter(userNode,
predicateIRI, oldValue);
if (oldTriples.hasNext()) {
oldTriple = oldTriples.next();
}
}
RDFTerm newValue = inputGraph
.filter(changeNode, Ontology.newValue, null).next()
.getObject();
newTriple = new TripleImpl(userNode, predicateIRI,
newValue);
// }
}
readLock.unlock();
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
if (oldTriple != null) {
systemGraph.remove(oldTriple);
}
systemGraph.add(newTriple);
writeLock.unlock();
// it's not actually creating a resource so this
// seems the most appropriate response
return Response.noContent().build();
}
/**
* Provides HTML corresponding to a user's roles
*
* all roles are listed with checkboxes, the roles this user has are checked
*
* (isn't very pretty but is just a one-off)
*
* @param userName the user in question
* @return HTML checkboxes as HTTP response
*/
@GET
@Path("users/{username}/rolesCheckboxes")
@Produces(MediaType.TEXT_HTML)
public Response rolesCheckboxes(@PathParam("username") String userName) {
// return new RdfViewable("rolesCheckboxes", getRoleType(), this.getClass());
StringBuffer html = new StringBuffer();
Iterator<Triple> allRoleTriples = systemGraph.filter(null, RDF.type, PERMISSION.Role);
ArrayList<String> allRoleNames = new ArrayList<String>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try { // pulls out all role names
while (allRoleTriples.hasNext()) {
Triple triple = allRoleTriples.next();
GraphNode roleNode = new GraphNode(triple.getSubject(), systemGraph);
Iterator<Literal> titlesIterator = roleNode.getLiterals(DC.title);
while (titlesIterator.hasNext()) {
allRoleNames.add(titlesIterator.next().getLexicalForm());
}
}
} finally {
readLock.unlock();
}
Graph rolesGraph = getUserRolesGraph(userName);
ArrayList<String> userRoleNames = new ArrayList<String>();
Iterator<Triple> userRoleTriples = rolesGraph.filter(null, RDF.type, PERMISSION.Role);
while (userRoleTriples.hasNext()) {
Triple triple = userRoleTriples.next();
GraphNode roleNode = new GraphNode(triple.getSubject(), rolesGraph);
Iterator<Literal> titlesIterator = roleNode.getLiterals(DC.title);
while (titlesIterator.hasNext()) {
userRoleNames.add(titlesIterator.next().getLexicalForm());
}
}
for (int i = 0; i < allRoleNames.size(); i++) {
String role = allRoleNames.get(i);
if (role.equals("BasePermissionsRole")) { // filter out
continue;
}
if (userRoleNames.contains(role)) {
html.append("<input class=\"role\" type=\"checkbox\" id=\"").append(role).append("\" name=\"").append(role).append("\" value=\"").append(role).append("\" checked=\"checked\" />");
} else {
html.append("<input class=\"role\" type=\"checkbox\" id=\"").append(role).append("\" name=\"").append(role).append("\" value=\"").append(role).append("\" />");
}
html.append("<label for=\"").append(role).append("\">").append(role).append("</label>");
html.append("<br />");
}
return Response.ok(html.toString()).build();
}
/**
* List the users. renders the user type with the "listUser" rendering
* template
*
* @return rendering specification
*/
@GET
@Path("users")
@Produces(MediaType.TEXT_HTML)
public RdfViewable listUsers() {
return new RdfViewable("listUser", getUserType(), this.getClass());
}
public GraphNode getUserType() {
return new GraphNode(FOAF.Agent, systemGraph);
}
/*
* RESTful creation of user
* @TODO validity check input
*/
@PUT
@Path("users/{username}")
@Consumes(SupportedFormat.TURTLE)
public Response createUser(@Context UriInfo uriInfo, @PathParam("username") String userName, ImmutableGraph inputGraph) {
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
systemGraph.addAll(inputGraph);
writeLock.unlock();
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
URI createdResource = uriBuilder.replacePath("/user-management/users/" + userName).build();
return Response.created(createdResource).build();
}
/**
* Create a user. returns a dummy use with "editUser" as rendering
* specification (this will be a HTML form)
*
* @param uriInfo request details
* @return rendering specification
*/
@GET
@Path("create-form")
@Produces(MediaType.TEXT_HTML)
public RdfViewable getCreateUserForm(@Context UriInfo uriInfo) {
return new RdfViewable("editUser", dummyNode,
this.getClass());
}
/**
* Endpoint-style user creation takes a little bunch of Turtle e.g. [] a
* foaf:Agent ; cz:userName "Hugo Ball" .
*
* [has test]
*
* @TODO check for password
*
* @param userData
* @return HTTP/1.1 204 No Content
*/
@POST
@Consumes(SupportedFormat.TURTLE)
@Path("add-user")
public Response addUser(@Context UriInfo uriInfo, ImmutableGraph inputGraph) {
Iterator<Triple> agents = inputGraph.filter(null, null, FOAF.Agent);
BlankNodeOrIRI userNode = agents.next().getSubject();
Iterator<Triple> userTriples = inputGraph.filter(userNode, null, null);
String userName = "";
Triple userTriple = null;
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
GraphNode systemUserNode = new GraphNode(userNode, systemGraph);
addRole(systemUserNode, "BasePermissionsRole");
while (userTriples.hasNext()) {
userTriple = userTriples.next();
systemGraph.add(userTriple);
}
userName = ((Literal) userTriple.getObject()).getLexicalForm();
} finally {
writeLock.unlock();
}
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
URI createdResource = uriBuilder.replacePath("/user-management/users/" + userName).build();
return Response.created(createdResource).build();
}
// **********************************
// ****** REMOVE USER ***************
// **********************************
/**
* Deletes a named user
*
* (called from HTML form)
*
* @param userName
*/
@POST
@Path("delete")
public void removeUser(@FormParam("user") String userName) {
remove(userName);
}
/**
* Deletes a named user
*
* @param userName
*/
private void remove(String userName) {
RDFTerm userResource = getNamedUser(userName).getNode();
Iterator<Triple> userTriples = systemGraph.filter((BlankNodeOrIRI) userResource, null, null);
ArrayList<Triple> buffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (userTriples.hasNext()) {
Triple triple = userTriples.next();
buffer.add(triple);
}
} finally {
readLock.unlock();
}
// ImmutableGraph context = getNamedUser(userName).getNodeContext();
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
systemGraph.removeAll(buffer);
} finally {
writeLock.unlock();
}
}
/**
* RESTful user deletion
*
* called direct from the URI, e.g.
* http://localhost:8080/user-management/users/fred
*
* @param userName name of the user to delete
* @return HTTP/1.1 204 No Content
*/
@DELETE
@Path("users/{username}")
public Response delete(@PathParam("username") String userName) {
remove(userName);
return Response.noContent().build();
}
/**
* Endpoint-style user deletion takes a little bunch of Turtle describing
* the user to delete e.g. [] a foaf:Agent ; cz:userName "Hugo Ball" .
*
* @param userData
* @return HTTP/1.1 204 No Content
*/
@POST
@Consumes(SupportedFormat.TURTLE)
@Path("delete-user")
public Response deleteUser(ImmutableGraph inputGraph) {
Iterator<Triple> userNameTriples = inputGraph.filter(null,
PLATFORM.userName, null);
Literal userNameNode = (Literal) userNameTriples.next().getObject();
// gives concurrent mod exception otherwise
ArrayList<Triple> tripleBuffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
Iterator<Triple> userTriples = systemGraph.filter(null, null,
userNameNode);
if (userTriples.hasNext()) {
Triple userTriple = userTriples.next();
Iterator<Triple> systemUserTriples = systemGraph.filter(
userTriple.getSubject(), null, null);
while (systemUserTriples.hasNext()) {
tripleBuffer.add(systemUserTriples.next());
}
}
} finally {
readLock.unlock();
}
systemGraph.removeAll(tripleBuffer);
// it's not actually creating a resource at this URI so this
// seems the most appropriate response
return Response.noContent().build();
}
// **********************************
// ****** SHOW ROLE DETAILS *********
// **********************************
// **********************************
// ****** LIST ROLES ****************
// **********************************
/**
* Lists all roles using a rendering as specified in template listRole
*
* @return
*/
@GET
@Path("roles")
@Produces(MediaType.TEXT_HTML)
public RdfViewable listRoles() {
return new RdfViewable("listRole", getRoleType(), this.getClass());
}
/**
* Provides the node in the system graph corresponding to rdf:type Role
*
* @return Role class node
*/
public GraphNode getRoleType() {
return new GraphNode(PERMISSION.Role,
systemGraph);
}
// **********************************
// ****** ADD ROLE ******************
// **********************************
/**
* Create a role. returns "editRole" as rendering specification (this will
* be a HTML form)
*
* @param uriInfo request details
* @return rendering specification
*/
@GET
@Path("create-role")
@Produces(MediaType.TEXT_HTML)
public RdfViewable getCreateRoleForm(@Context UriInfo uriInfo) {
return new RdfViewable("editRole", dummyNode,
this.getClass());
}
// /user-management/roles/edit/'+roleName,
/**
* lookup a role by name presenting it with "editRole" as rendering
* instruction.
*
* @param userName
* @return
*/
@GET
@Path("roles/edit/{rolename}")
@Produces(MediaType.TEXT_HTML)
public RdfViewable editRole(@PathParam("rolename") String roleName) {
return new RdfViewable("editRole", getRole(roleName),
this.getClass());
}
private GraphNode getRole(@QueryParam("roleName") String roleName) {
return getNamedRole(roleName);
}
/*
* returns an existing user node from the graph.
*/
private GraphNode getNamedRole(String roleName) {
GraphNode roleNode = null;
Iterator<Triple> roleIterator = systemGraph.filter(null, RDF.type, PERMISSION.Role);
//new PlainLiteralImpl(userName));
if (!roleIterator.hasNext()) {
return null;
}
ArrayList<Triple> tripleBuffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (roleIterator.hasNext()) {
BlankNodeOrIRI role = roleIterator.next().getSubject();
Iterator<Triple> roleNameTriples = systemGraph.filter(role, DC.title,
null);
while (roleNameTriples.hasNext()) {
Literal roleLiteral = (Literal) roleNameTriples.next().getObject();
if (roleName.equals(roleLiteral.getLexicalForm())) {
roleNode = new GraphNode(role, systemGraph);
break;
}
}
if (roleNode != null) {
break;
}
}
} finally {
readLock.unlock();
}
return roleNode;
}
// **********************************
// ****** REMOVE ROLE ***************
// **********************************
/**
* Deletes a named role
*
* (called from HTML form)
*
* @param roleName
*/
@POST
@Path("delete-role")
public void removeRole(@FormParam("role") String roleName) {
deleteRole(roleName);
}
/**
* Deletes a named user
*
* @param userName
*/
private void deleteRole(String roleName) {
RDFTerm roleResource = getNamedRole(roleName).getNode();
Iterator<Triple> roleTriples = systemGraph.filter((BlankNodeOrIRI) roleResource, null, null);
ArrayList<Triple> buffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (roleTriples.hasNext()) {
Triple triple = roleTriples.next();
buffer.add(triple);
}
} finally {
readLock.unlock();
}
// is lock needed?
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
systemGraph.removeAll(buffer);
} finally {
writeLock.unlock();
}
}
/**
* Update role details - adds triples as appropriate to system graph
*
*/
@POST
@Path("store-role")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response storeRoleFormHandler(@Context UriInfo uriInfo,
@FormParam("roleName") String roleName,
@FormParam("comment") String comment,
@FormParam("permissions") List<String> permissions) {
GraphNode roleNode = null;
if (roleName != null) { //
roleName = roleName.trim();
}
if (roleName != null && !roleName.equals("")) {
roleNode = getRole(roleName);
if (roleNode != null) {
return storeRole(roleNode, uriInfo, roleName, comment, permissions);
}
}
roleNode = createRole(roleName, comment);
return storeRole(roleNode, uriInfo, roleName, comment, permissions);
}
/**
* Creates a new role wit the the specified role name
*
* @param newUserName
* @return user node in system graph
*/
private GraphNode createRole(String newRoleName, String comment) {
BlankNode subject = new BlankNode();
GraphNode roleNode = new GraphNode(subject, systemGraph);
roleNode.addProperty(RDF.type, PERMISSION.Role);
roleNode.addProperty(DC.title, new PlainLiteralImpl(newRoleName));
roleNode.addProperty(RDFS.comment, new PlainLiteralImpl(comment));
return roleNode;
}
private Response storeRole(GraphNode roleNode, UriInfo uriInfo,
String roleName,
String comment,
List<String> permissions) {
BlankNodeOrIRI roleResource = (BlankNodeOrIRI) roleNode.getNode();
if (permissions != null) {
clearPermissions(roleResource);
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
for (int i = 0; i < permissions.size(); i++) {
permissions.set(i, permissions.get(i).trim());
if (!permissions.get(i).equals("")) {
addPermission(roleNode, permissions.get(i));
}
}
} finally {
writeLock.unlock();
}
}
//refresh the policy so it will recheck the permissions
Policy.getPolicy().refresh();
// showSystem();
URI pageUri = uriInfo.getBaseUriBuilder()
.path("system/console/usermanagement").build();
// header Cache-control: no-cache, just in case intermediaries are
// holding onto old stuff
CacheControl cc = new CacheControl();
cc.setNoCache(true);
//showSystem();
// see other my not be the best response, but does seem the best given
// the jax-rs things available
return Response.seeOther(pageUri).cacheControl(cc).build();
}
// **********************************
// ****** ASSIGN ROLE TO USER *******
// **********************************
// **********************************
// ****** REMOVE ROLE FROM USER *****
// **********************************
// **********************************
// ****** LIST PERMISSIONS **********
// **********************************
/*
* Provides listing of all permissions present in system graph
* rendered according to specification in listPermission template
*/
@GET
@Path("permissions")
@Produces(MediaType.TEXT_HTML)
public RdfViewable listPermissions() {
addClassToPermissions(); // workaround
return new RdfViewable("listPermission", getPermissionType(), this.getClass());
}
/**
* Provides the node in the system graph corresponding to rdf:type
* Permission
*
* @return Permission class node
*/
public GraphNode getPermissionType() {
return new GraphNode(PERMISSION.Permission,
systemGraph);
}
// **********************************
// ****** ADD PERMISSION TO USER ****
// **********************************
// **************************************
// ****** REMOVE PERMISSION FROM USER ***
// **************************************
// ************************************
// ****** ADD PERMISSION TO ROLE ******
// ************************************
// **************************************
// ****** REMOVE PERMISSION FROM ROLE ***
// **************************************
////////////////////////////////////////////////////////////////
/**
* Pushes user data into system graph
*
* @param userNode
* @param uriInfo
* @param currentUserName
* @param newUserName
* @param fullName
* @param email
* @param password
* @param roles
* @param permissions
* @return
*/
private Response store(GraphNode userNode, UriInfo uriInfo,
String currentUserName,
String newUserName,
String fullName,
String email,
String password,
List<String> roles,
List<String> permissions) {
// GraphNode userNode = getUser(currentUserName);
if (newUserName != null && !newUserName.equals("")) {
changeLiteral(userNode, PLATFORM.userName, newUserName);
}
if (fullName != null && !fullName.equals("")) {
changeLiteral(userNode, FOAF.name, fullName);
}
if (password != null && !password.equals("")) {
String passwordSha1 = PasswordUtil.convertPassword(password);
changeLiteral(userNode, PERMISSION.passwordSha1, passwordSha1);
}
if (email != null && !email.equals("")) {
changeResource(userNode, FOAF.mbox, new IRI("mailto:" + email));
}
BlankNodeOrIRI userResource = (BlankNodeOrIRI) userNode.getNode();
if (roles != null) {
clearRoles(userResource);
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
for (int i = 0; i < roles.size(); i++) {
roles.set(i, roles.get(i).trim());
if (!roles.get(i).equals("")) {
addRole(userNode, roles.get(i));
}
}
} finally {
writeLock.unlock();
}
}
if (permissions != null) {
clearPermissions(userResource);
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
for (int i = 0; i < permissions.size(); i++) {
permissions.set(i, permissions.get(i).trim());
if (!permissions.get(i).equals("")) {
addPermission(userNode, permissions.get(i));
}
}
} finally {
writeLock.unlock();
}
}
URI pageUri = uriInfo.getBaseUriBuilder()
.path("system/console/usermanagement").build();
// header Cache-control: no-cache, just in case intermediaries are
// holding onto old stuff
CacheControl cc = new CacheControl();
cc.setNoCache(true);
//showSystem();
// see other my not be the best response, but does seem the best given
// the jax-rs things available
return Response.seeOther(pageUri).cacheControl(cc).build();
}
/**
* a kludge - initially the permissions aren't expressed as instances of
* Permission class, this adds the relevant triples
*/
private void addClassToPermissions() {
Iterator<Triple> permissionTriples = systemGraph.filter(null, PERMISSION.hasPermission, null);
ArrayList<GraphNode> buffer = new ArrayList<GraphNode>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (permissionTriples.hasNext()) {
Triple triple = permissionTriples.next();
RDFTerm permissionResource = triple.getObject();
buffer.add(new GraphNode(permissionResource, systemGraph));
}
} finally {
readLock.unlock();
}
Lock writeLock = systemGraph.getLock().writeLock();
writeLock.lock();
try {
for (int i = 0; i < buffer.size(); i++) {
buffer.get(i).addProperty(RDF.type, PERMISSION.Permission);
}
} finally {
writeLock.unlock();
}
}
/**
* Provides a graph containing Role triples associated with a given user
*
* @param userName
* @return roles graph
*/
private Graph getUserRolesGraph(String userName) {
GraphNode userNode = getUser(userName);
Iterator<RDFTerm> functionIterator = userNode
.getObjects(SIOC.has_function);
SimpleGraph rolesGraph = new SimpleGraph();
while (functionIterator.hasNext()) {
GraphNode functionNode = new GraphNode(functionIterator.next(),
systemGraph);
Iterator<Triple> roleIterator = systemGraph.filter(
(BlankNodeOrIRI) functionNode.getNode(), RDF.type,
PERMISSION.Role);
// needs lock?
while (roleIterator.hasNext()) {
Triple roleTriple = roleIterator.next();
// rolesGraph.add(roleTriple);
BlankNodeOrIRI roleNode = roleTriple.getSubject();
SimpleGraph detailsGraph = new SimpleGraph(systemGraph.filter(
roleNode, null, null));
rolesGraph.addAll(detailsGraph);
}
}
return rolesGraph;
}
/**
* Creates a new user withe the specified user name
*
* @param newUserName
* @return user node in system graph
*/
private GraphNode createUser(String newUserName) {
BlankNode subject = new BlankNode();
GraphNode userNode = new GraphNode(subject, systemGraph);
userNode.addProperty(RDF.type, FOAF.Agent);
userNode.addProperty(PLATFORM.userName, new PlainLiteralImpl(newUserName));
addRole(userNode, "BasePermissionsRole");
return userNode;
}
// move later?
public final static String rolesBase = "urn:x-localhost/role/";
private void clearRoles(BlankNodeOrIRI userResource) {
systemGraph.removeAll(filterToArray(userResource, SIOC.has_function, null));
}
/**
* convenience - used for buffering
*
* @param subject
* @param predicate
* @param object
* @return
*/
private List<Triple> filterToArray(BlankNodeOrIRI subject, IRI predicate, RDFTerm object) {
Iterator<Triple> triples = systemGraph.filter(subject, predicate, object);
ArrayList<Triple> buffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (triples.hasNext()) {
buffer.add(triples.next());
}
} finally {
readLock.unlock();
}
return buffer;
}
/**
* Add a role to a given user in system graph
*
* @param userNode node corresponding to user
* @param roleName name of the role
* @return user node
*/
private GraphNode addRole(GraphNode userNode, String roleName) {
// is this thing already around? (will be a bnode)
GraphNode roleNode = getTitleNode(roleName);
// otherwise make a new one as a named node
if (roleNode == null) {
IRI roleIRI = new IRI(rolesBase + roleName);
roleNode = new GraphNode(roleIRI, systemGraph);
roleNode.addProperty(RDF.type, PERMISSION.Role);
roleNode.addProperty(DC.title, new PlainLiteralImpl(roleName));
userNode.addProperty(SIOC.has_function, roleIRI);
} else {
userNode.addProperty(SIOC.has_function, roleNode.getNode());
}
return userNode;
}
// public final static String permissionsBase = "urn:x-localhost/role/";
private GraphNode addPermission(GraphNode subjectNode, String permissionString) {
if (hasPermission(subjectNode, permissionString)) {
return subjectNode;
}
GraphNode permissionNode = new GraphNode(new BlankNode(), systemGraph);
permissionNode.addProperty(RDF.type, PERMISSION.Permission);
// permissionNode.addProperty(DC.title, new PlainLiteralImpl(permissionName));
subjectNode.addProperty(PERMISSION.hasPermission, permissionNode.getNode());
permissionNode.addProperty(PERMISSION.javaPermissionEntry, new PlainLiteralImpl(permissionString));
return subjectNode;
}
private boolean hasPermission(GraphNode userNode, String permissionString) {
boolean has = false;
Iterator<Triple> existingPermissions = systemGraph.filter((BlankNodeOrIRI) userNode.getNode(), PERMISSION.hasPermission, null);
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try { // check to see if the user already has this permission
while (existingPermissions.hasNext()) {
BlankNodeOrIRI permissionNode = (BlankNodeOrIRI) existingPermissions.next().getObject();
Iterator<Triple> permissionTriples = systemGraph.filter(permissionNode, PERMISSION.javaPermissionEntry, null);
while (permissionTriples.hasNext()) {
Literal permission = (Literal) permissionTriples.next().getObject();
if (permissionString.equals(permission.getLexicalForm())) {
has = true;
}
}
}
} finally {
readLock.unlock();
}
return has;
}
// [] a <http://xmlns.com/foaf/0.1/Agent> ;
// <http://clerezza.org/2008/10/permission#hasPermission>
// [ a <http://clerezza.org/2008/10/permission#Permission> ;
// <http://clerezza.org/2008/10/permission#javaPermissionEntry>
// "(java.security.AllPermission \"\" \"\")"
// ] ;
private void clearPermissions(BlankNodeOrIRI subject) {
ArrayList<Triple> buffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
Iterator<Triple> permissions = systemGraph.filter(subject, PERMISSION.hasPermission, null);
while (permissions.hasNext()) {
Triple permissionTriple = permissions.next();
buffer.add(permissionTriple);
BlankNodeOrIRI permissionNode = (BlankNodeOrIRI) permissionTriple.getObject();
Iterator<Triple> permissionTriples = systemGraph.filter(permissionNode, null, null);
while (permissionTriples.hasNext()) {
buffer.add(permissionTriples.next());
}
}
} finally {
readLock.unlock();
}
systemGraph.removeAll(buffer);
}
/*
* must be a neater way of doing this...
*/
private GraphNode getTitleNode(String title) {
Iterator<Triple> triples = systemGraph.filter(null, DC.title, new PlainLiteralImpl(title));
if (triples.hasNext()) {
RDFTerm resource = triples.next().getSubject();
return new GraphNode(resource, systemGraph);
}
return null;
}
/**
* Replaces/inserts literal value for predicate assumes there is only one
* triple for the given predicate new value is added before deleting old one
* in case user is modifying their own data in which case they need triples
* in place for rights etc.
*
* @param userNode node in systemGraph corresponding to the user to change
* @param predicate property of the triple to change
* @param newValue new value for given predicate
*/
private void changeLiteral(GraphNode userNode, IRI predicate,
String newValue) {
Iterator<Triple> oldTriples = systemGraph.filter(
(BlankNodeOrIRI) userNode.getNode(), predicate, null);
ArrayList<Triple> oldBuffer = new ArrayList<Triple>();
RDFTerm oldObject = null;
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (oldTriples.hasNext()) {
Triple triple = oldTriples.next();
oldObject = triple.getObject();
oldBuffer.add(triple);
}
} finally {
readLock.unlock();
}
// filter appears to see plain literals and xsd:strings as differerent
// so not
// userNode.addPropertyValue(predicate, newValue);
Literal newObject = new PlainLiteralImpl(newValue);
userNode.addProperty(predicate, newObject);
if (newObject.equals(oldObject)) {
return;
}
systemGraph.removeAll(oldBuffer);
}
/**
* Replaces/inserts resource value for predicate assumes there is only one
* triple for the given predicate
*
* @param userNode node in systemGraph corresponding to the user to change
* @param predicate property of the triple to change
* @param newValue new value for given predicate
*/
private void changeResource(GraphNode userNode, IRI predicate,
IRI newValue) {
Iterator<Triple> oldTriples = systemGraph.filter(
(BlankNodeOrIRI) userNode.getNode(), predicate, null);
ArrayList<Triple> oldBuffer = new ArrayList<Triple>();
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
while (oldTriples.hasNext()) {
Triple triple = oldTriples.next();
RDFTerm oldValue = triple.getObject();
if (newValue.equals(oldValue)) {
return;
}
oldBuffer.add(triple);
}
} finally {
readLock.unlock();
}
// filter appears to see plain literals and xsd:strings as differerent
// so not
// userNode.addPropertyValue(predicate, newValue);
userNode.addProperty(predicate, newValue);
systemGraph.removeAll(oldBuffer);
}
private GraphNode getUser(@QueryParam("userName") String userName) {
return getNamedUser(userName);
}
/*
* returns an existing user node from the graph.
*/
// needs lock?
private GraphNode getNamedUser(String userName) {
Iterator<Triple> iter = systemGraph.filter(null, PLATFORM.userName,
new PlainLiteralImpl(userName));
if (!iter.hasNext()) {
return null;
}
return new GraphNode(iter.next().getSubject(), systemGraph);
}
public Set<GraphNode> getUsers() {
return getResourcesOfType(FOAF.Agent);
}
private Set<GraphNode> getResourcesOfType(IRI type) {
Lock readLock = systemGraph.getLock().readLock();
readLock.lock();
try {
final Iterator<Triple> triples = systemGraph.filter(null, RDF.type,
type);
Set<GraphNode> resources = new HashSet<GraphNode>();
while (triples.hasNext()) {
resources.add(new GraphNode(triples.next().getSubject(),
systemGraph));
}
return resources;
} finally {
readLock.unlock();
}
}
/*
* Dumps a Turtle serialization of the system graph to System.out
* handy for debugging
*/
private void showSystem() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.serialize(baos, systemGraph, SupportedFormat.TURTLE);
System.out.println(new String(baos.toByteArray(), "utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
}