package eu.geoknow.generator.users;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import javax.ws.rs.core.Cookie;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.Logger;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.ISO8601Utils;
import com.google.gson.Gson;
import com.ontos.ldiw.vocabulary.LDIWO;
import eu.geoknow.generator.configuration.FrameworkConfiguration;
import eu.geoknow.generator.rdf.RdfStoreManager;
/**
* This class implements UserManager for Workbench. Users' accounts are stored in special Workbench
* accounts graph specified in configuration. This class uses underlying RDF store UserManager to
* manage RDF graph access and RDF users creation.
*/
public class FrameworkUserManager implements UserManager {
private static final Logger log = Logger.getLogger(FrameworkUserManager.class);
private static final String jsonResponseFormat = "application/sparql-results+json";
private String prefixes = null;
// underlying RDF store UserManager
private UserManager rdfStoreUserManager;
// RDF store manager executes SPARQL requests (save account info, create
// settings graph, etc.)
private RdfStoreManager rdfStoreManager;
private FrameworkConfiguration frameworkConfig;
public FrameworkUserManager(UserManager rdfStoreUserManager, RdfStoreManager rdfStoreManager,
FrameworkConfiguration frameworkConfig) {
this.rdfStoreUserManager = rdfStoreUserManager;
this.rdfStoreManager = rdfStoreManager;
this.frameworkConfig = frameworkConfig;
}
/**
* Creates new user account. This method creates new user account in Workbench and also creates
* RDF store user with the same name and password. It throws Exception if Workbench or RDF store
* user with specified name already exists. This method also creates settings graph for newly
* created user. Only this user and Workbench system admin has permissions for this graph.
* Workbench user profile is stored in special Workbench accounts graph in RDF store.
*
* @param name User name
* @param password User password
* @param email User e-mail
* @throws Exception
*/
public void createUser(String name, String password, String email) throws Exception {
if (name == null || name.isEmpty())
throw new IllegalArgumentException("name cannon be null or empty");
if (password == null || password.isEmpty())
throw new IllegalArgumentException("password cannon be null or empty");
if (email == null || email.isEmpty())
throw new IllegalArgumentException("email cannon be null or empty");
if (checkUserExists(name, email))
throw new Exception("User already exists");
// create RDF store user with the same name and password
rdfStoreUserManager.createUser(name, password);
// create setting graph for user
String userSettingsGraphURI =
frameworkConfig.getResourceNamespace() + URLEncoder.encode(name, "UTF-8")
+ "/settingsGraph";
// grant write permissions to framework - otherwise framework fails to
// create graph
rdfStoreUserManager.setRdfGraphPermissions(frameworkConfig.getWorkbenchSystemAdmin(),
userSettingsGraphURI, GraphPermissions.WRITE);
rdfStoreManager.createGraph(userSettingsGraphURI);
// grant write permissions to user
rdfStoreUserManager.setRdfGraphPermissions(name, userSettingsGraphURI, GraphPermissions.WRITE);
// copy data from initial settings graph
/*
* String query = "INSERT { GRAPH <" + userSettingsGraphURI + "> {?s ?p ?o} } " +
* "WHERE {GRAPH <" + frameworkConfig.getInitialSettingsGraph() + "> {?s ?p ?o} }"; try {
* rdfStoreManager.execute(query, jsonResponseFormat); } catch (IOException e) { // failed to
* write user graph // rollback actions: rdfStoreUserManager.dropUser(name); throw e; }
*/
// get default role uri
RoleManager roles = new RoleManager(rdfStoreManager);
// String role = getDefaultRoleURI();
String role = roles.getDefaultRole().getUri();
// write user account to accounts graph
String query =
getPrefixes() + "\n" + "INSERT DATA { GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {\n" + " :" + name + " rdf:type gkg:Account .\n" + " :" + name
+ " foaf:accountName \"" + name + "\" .\n" + " :" + name + " gkg:passwordSha1Hash \""
+ DigestUtils.sha1Hex(password) + "\" .\n" + " :" + name + " gkg:settingsGraph \""
+ userSettingsGraphURI + "\"^^xsd:anyURI .\n" + " :" + name + " foaf:mbox <mailto:"
+ email + "> .\n" + " :" + name + " dcterms:created \""
+ ISO8601Utils.format(new Date()) + "\"^^xsd:dateTime .\n" + " :" + name
+ " gkg:role <" + role + "> .\n" + "} }";
try {
rdfStoreManager.execute(query, jsonResponseFormat);
} catch (IOException e) { // failed to register user in to the accounts
// graph
// rollback actions:
rdfStoreUserManager.dropUser(name);
rdfStoreManager.dropGraph(userSettingsGraphURI);
throw e;
}
}
@Override
public void createUser(String name, String password) throws Exception {
throw new UnsupportedOperationException(
"Unsupported operation createUser(name, password). Use createUser(name, password, email");
}
@Override
public void dropUser(String name) throws Exception {
if (name == null || name.isEmpty())
throw new IllegalArgumentException("name cannot be null or empty");
// drop rdf store user
rdfStoreUserManager.dropUser(name);
String userSettingsGraph = getSettingsGraph(name);
// remove account triples
String query =
getPrefixes() + "\n" + "DELETE WHERE { GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> { ?s ?p ?o . ?s foaf:accountName \"" + name + "\" . } }";
rdfStoreManager.execute(query, jsonResponseFormat);
// delete user settings graph
rdfStoreManager.dropGraph(userSettingsGraph);
}
@Override
public void setDefaultRdfPermissions(String user, GraphPermissions permissions) throws Exception {
rdfStoreUserManager.setDefaultRdfPermissions(user, permissions);
}
@Override
public void setPublicRdfPermissions(GraphPermissions permissions) throws Exception {
rdfStoreUserManager.setPublicRdfPermissions(permissions);
}
@Override
public void setRdfGraphPermissions(String user, String graph, GraphPermissions permissions)
throws Exception {
String settingsGraph = getDescribedIn(graph);
if (settingsGraph == null)
throw new Exception("Graph " + graph + " description was not found");
UserProfile userProfile = getUserProfile(user);
if (userProfile == null)
throw new Exception("User " + user + " was not found");
// remove old access triples
String query =
getPrefixes() + "\n" + " DELETE {GRAPH <" + settingsGraph + "> {?s ?p ?o}} " + " WHERE { "
+ " {GRAPH <" + settingsGraph + "> {<" + graph + "> gkg:access ?s . ?s acl:agent <"
+ userProfile.getAccountURI() + "> . ?s ?p ?o . } } " + " UNION " + " {GRAPH <"
+ settingsGraph + "> {?s gkg:access ?ao . ?ao acl:agent <"
+ userProfile.getAccountURI() + "> . ?s ?p ?o . FILTER (?s = <" + graph
+ "> && ?p = gkg:access) } } " + "}";
rdfStoreManager.execute(query, jsonResponseFormat);
// add new public access triples
switch (permissions) {
case NO: // no access
break;
case READ:
query =
getPrefixes() + "\n" + "INSERT DATA {GRAPH <" + settingsGraph + "> { " + " <" + graph
+ "> gkg:access _:b1. " + " _:b1 acl:agent <" + userProfile.getAccountURI()
+ "> . " + " _:b1 acl:mode acl:Read . " + "}}";
rdfStoreManager.execute(query, jsonResponseFormat);
break;
case WRITE:
query =
getPrefixes() + "\n" + "INSERT DATA {GRAPH <" + settingsGraph + "> { " + " <" + graph
+ "> gkg:access _:b1. " + " _:b1 acl:agent <" + userProfile.getAccountURI()
+ "> . " + " _:b1 acl:mode acl:Write . " + "} }";
rdfStoreManager.execute(query, jsonResponseFormat);
break;
}
// set rdf store permissions
log.info("Set graph permissions " + permissions + " for graph " + graph + ", user " + user);
rdfStoreUserManager.setRdfGraphPermissions(user, graph, permissions);
}
@Override
public void deleteRdfGraphPermissions(String user, String graph) throws Exception {
String settingsGraph = getDescribedIn(graph);
if (settingsGraph == null)
throw new Exception("Graph " + graph + " description was not found");
UserProfile userProfile = getUserProfile(user);
if (userProfile == null)
throw new Exception("User " + user + " was not found");
// remove old access triples
String query =
getPrefixes() + "\n" + " DELETE {GRAPH <" + settingsGraph + "> {?s ?p ?o} } " + " WHERE { "
+ " {GRAPH <" + settingsGraph + "> {<" + graph + "> gkg:access ?s . ?s acl:agent <"
+ userProfile.getAccountURI() + "> . ?s ?p ?o . } } " + " UNION " + " {GRAPH <"
+ settingsGraph + "> {?s gkg:access ?ao . ?ao acl:agent <"
+ userProfile.getAccountURI() + "> . ?s ?p ?o . FILTER (?s = <" + graph
+ "> && ?p = gkg:access) } } " + "}";
rdfStoreManager.execute(query, jsonResponseFormat);
// remove rdf store permissions
log.info("Remove graph permissions for graph " + graph + ", user " + user);
rdfStoreUserManager.deleteRdfGraphPermissions(user, graph);
}
@Override
public void setDefaultGraphPermissions(String graph, GraphPermissions permissions)
throws Exception {
String settingsGraph = getDescribedIn(graph);
if (settingsGraph == null)
throw new Exception("Graph " + graph + " description was not found");
// remove old public access triples
String query =
getPrefixes() + "\n" + " DELETE {GRAPH <" + settingsGraph + "> {?s ?p ?o} } " + " WHERE { "
+ " {GRAPH <" + settingsGraph + "> {<" + graph
+ "> gkg:access ?s . ?s acl:agentClass foaf:Agent . ?s ?p ?o . } } " + " UNION "
+ " {GRAPH <" + settingsGraph
+ "> {?s gkg:access ?ao . ?ao acl:agentClass foaf:Agent . ?s ?p ?o . FILTER (?s = <"
+ graph + "> && ?p = gkg:access) } } " + "}";
rdfStoreManager.execute(query, jsonResponseFormat);
// add new public access triples
switch (permissions) {
case NO: // no public access
break;
case READ: // public read
query =
getPrefixes() + "\n" + "INSERT DATA {GRAPH <" + settingsGraph + "> { " + " <" + graph
+ "> gkg:access _:b1. " + " _:b1 acl:agentClass foaf:Agent . "
+ " _:b1 acl:mode acl:Read . " + "}}";
rdfStoreManager.execute(query, jsonResponseFormat);
break;
case WRITE: // public write
query =
getPrefixes() + "\n" + "INSERT DATA {GRAPH <" + settingsGraph + "> { " + " <" + graph
+ "> gkg:access _:b1. " + " _:b1 acl:agentClass foaf:Agent . "
+ " _:b1 acl:mode acl:Write . " + "}}";
rdfStoreManager.execute(query, jsonResponseFormat);
break;
}
// set permissions in rdf store
rdfStoreUserManager.setDefaultGraphPermissions(graph, permissions);
}
@Override
public void changePassword(String username, String oldPassword, String newPassword)
throws Exception {
if (username == null || username.isEmpty())
throw new IllegalArgumentException("username cannot be null or empty");
if (oldPassword == null || oldPassword.isEmpty())
throw new IllegalArgumentException("oldPassword cannot be null or empty");
if (newPassword == null || newPassword.isEmpty())
throw new IllegalArgumentException("newPassword cannon be null or empty");
if (!checkPassword(username, oldPassword))
throw new Exception("Invalid old password");
// OntoQuad doesn't support DELETE {...} INSERT {...} WHERE {...}, so
// use 2 queries
String deleteQuery =
getPrefixes() + "\n" + " DELETE {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:passwordSha1Hash ?o} } " + " WHERE {GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {?account foaf:accountName \"" + username
+ "\" . ?account gkg:passwordSha1Hash ?o . } }";
String insertQuery =
getPrefixes() + "\n" + " INSERT {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:passwordSha1Hash \"" + DigestUtils.sha1Hex(newPassword) + "\"} } "
+ " WHERE {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account foaf:accountName \"" + username + "\" . } }";
// TODO: replace 2 queries with this query when OntoQuad will support
// DELETE {...} INSERT {...} WHERE {...} queries
// String query = getPrefixes() + "\n" + "WITH <" +
// frameworkConfig.getAccountsGraph() + "> "
// + " DELETE {?account gkg:passwordSha1Hash ?o} "
// + " INSERT {?account gkg:passwordSha1Hash \"" +
// DigestUtils.sha1Hex(newPassword) + "\"} "
// + " WHERE {?account foaf:accountName \"" + username
// + "\" . ?account gkg:passwordSha1Hash ?o . }";
rdfStoreManager.execute(deleteQuery, jsonResponseFormat);
rdfStoreManager.execute(insertQuery, jsonResponseFormat);
// change underlying RDF store user password
rdfStoreUserManager.changePassword(username, oldPassword, newPassword);
// save new password in password store
PasswordStore.put(username, newPassword);
}
@Override
public void setPassword(String usernameOrEmail, String password) throws Exception {
if (usernameOrEmail == null || usernameOrEmail.isEmpty())
throw new IllegalArgumentException("username cannot be null or empty");
if (password == null || password.isEmpty())
throw new IllegalArgumentException("password cannot be null or empty");
// OntoQuad doesn't support DELETE {...} INSERT {...} WHERE {...}, so
// use 2 queries
String deleteQuery =
getPrefixes() + "\n" + " DELETE {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:passwordSha1Hash ?o} } " + " WHERE { " + " {GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {?account foaf:accountName \""
+ usernameOrEmail + "\" . OPTIONAL { ?account gkg:passwordSha1Hash ?o . } } } "
+ " UNION " + " {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> { ?account foaf:mbox <mailto:" + usernameOrEmail
+ "> . OPTIONAL { ?account gkg:passwordSha1Hash ?o . } } } " + " }";
String insertQuery =
getPrefixes() + "\n" + " INSERT {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:passwordSha1Hash \"" + DigestUtils.sha1Hex(password) + "\"} } "
+ " WHERE { " + " {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account foaf:accountName \"" + usernameOrEmail
+ "\" . OPTIONAL { ?account gkg:passwordSha1Hash ?o . } } } " + " UNION { "
+ " GRAPH <" + frameworkConfig.getAccountsGraph() + "> { ?account foaf:mbox <mailto:"
+ usernameOrEmail + "> . OPTIONAL { ?account gkg:passwordSha1Hash ?o . } } } " + " }";
// TODO: replace 2 queries with this query when OntoQuad will support
// DELETE {...} INSERT {...} WHERE {...} queries
// String query = getPrefixes() + "\n" + "WITH <" +
// frameworkConfig.getAccountsGraph() + "> "
// + " DELETE {?account gkg:passwordSha1Hash ?o} "
// + " INSERT {?account gkg:passwordSha1Hash \"" +
// DigestUtils.sha1Hex(password) + "\"} "
// + " WHERE { " + " {?account foaf:accountName \"" + usernameOrEmail
// + "\" . OPTIONAL { ?account gkg:passwordSha1Hash ?o . } } " +
// " UNION "
// + " { ?account foaf:mbox <mailto:" + usernameOrEmail
// + "> . OPTIONAL { ?account gkg:passwordSha1Hash ?o . } } " + " }";
log.debug(deleteQuery);
log.debug(insertQuery);
rdfStoreManager.execute(deleteQuery, jsonResponseFormat);
rdfStoreManager.execute(insertQuery, jsonResponseFormat);
// set password for underlying RDF store user
rdfStoreUserManager.setPassword(getUsername(usernameOrEmail), password);
}
/**
* Checks password for given user.
*
* @param usernameOrEmail Workbench user name or e-mail
* @param password Password to check
* @return true, if given credentials are correct, false, otherwise
* @throws Exception
*/
public boolean checkPassword(String usernameOrEmail, String password) throws Exception {
String query =
getPrefixes() + "\n" + "SELECT DISTINCT ?passwordHash FROM <"
+ frameworkConfig.getAccountsGraph() + "> " + "WHERE {"
+ " {?account foaf:accountName \"" + usernameOrEmail
+ "\" . ?account gkg:passwordSha1Hash ?passwordHash . } " + " UNION "
+ " {?account foaf:mbox <mailto:" + usernameOrEmail
+ "> . ?account gkg:passwordSha1Hash ?passwordHash . } " + "}";
log.debug(query);
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
if (!bindingsIter.hasNext())
return false;
JsonNode bindingNode = bindingsIter.next();
String correctPasswordHash = bindingNode.path("passwordHash").path("value").textValue();
log.debug(DigestUtils.sha1Hex(password) + " vs " + correctPasswordHash);
return DigestUtils.sha1Hex(password).equals(correctPasswordHash);
}
/**
* Checks session token for given user.
*
* @param username Target user name
* @param token Session token
* @return true, if token exists for given user, false, otherwise
* @throws Exception
*/
public boolean checkToken(String username, String token) throws Exception {
String query =
getPrefixes() + "\n" + "ASK {GRAPH <" + frameworkConfig.getAccountsGraph() + "> "
+ " {?account foaf:accountName \"" + username + "\" . ?account gkg:sessionToken \""
+ token + "\" . } " + "}";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
return rootNode.path("boolean").booleanValue();
}
/**
* Saves session token for given user.
*
* @param usernameOrEmail Target Workbench user name or e-mail
* @param token Session token
* @throws Exception
*/
public void saveSessionToken(String usernameOrEmail, String token) throws Exception {
if (usernameOrEmail == null || usernameOrEmail.isEmpty())
throw new IllegalArgumentException("username cannot be null or empty");
if (token == null || token.isEmpty())
throw new IllegalArgumentException("token cannot be null or empty");
// TODO: replace old token if exists? or add new if user may have more
// than
// one token?
String query =
getPrefixes() + "\n" + " INSERT {GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> { ?account gkg:sessionToken \"" + token + "\" } } " + " WHERE {" + " {GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> { ?account foaf:accountName \""
+ usernameOrEmail + "\" } } " + " UNION " + " {GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> { ?account foaf:mbox <mailto:"
+ usernameOrEmail + "> } } " + "}";
rdfStoreManager.execute(query, jsonResponseFormat);
}
/**
* Clear all session tokens for given user.
*
* @param username Target user name
* @throws Exception
*/
public void removeAllSessionTokens(String username) throws Exception {
if (username == null || username.isEmpty())
throw new IllegalArgumentException("username cannot be null or empty");
String query =
getPrefixes() + "\n" + "DELETE { GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:sessionToken ?o} } " + "WHERE {GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {?account foaf:accountName \"" + username
+ "\" . ?account gkg:sessionToken ?o . } }";
rdfStoreManager.execute(query, jsonResponseFormat);
}
/**
* Retrieves user account information.
*
* @param userId Target user name, e-mail or account URI
* @return User account information
* @throws Exception
*/
public UserProfile getUserProfile(String userId) throws Exception {
String query =
getPrefixes() + "\n" + "SELECT DISTINCT * WHERE {GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {" + " {?account foaf:accountName \""
+ userId + "\" . ?account ?p ?o . } " + " UNION " + " {?account foaf:mbox <mailto:"
+ userId + "> . ?account ?p ?o . } " + " UNION "
+ " {?account ?p ?o . FILTER (?account = <" + userId + ">)} " + "}}";
log.debug(query);
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
if (!bindingsIter.hasNext())
return null;
RoleManager roles = new RoleManager(rdfStoreManager);
UserProfile userProfile = new UserProfile();
userProfile.setAccountURI(bindingsIter.next().path("account").path("value").textValue());
bindingsIter = rootNode.path("results").path("bindings").elements();
while (bindingsIter.hasNext()) {
JsonNode bindingNode = bindingsIter.next();
String predicate = bindingNode.path("p").path("value").textValue();
if (predicate.equals("http://xmlns.com/foaf/0.1/accountName"))
userProfile.setUsername(bindingNode.path("o").path("value").textValue());
else if (predicate.endsWith("/settingsGraph"))
userProfile.setSettingsGraph(bindingNode.path("o").path("value").textValue());
else if (predicate.equals("http://xmlns.com/foaf/0.1/mbox")) {
String mbox = bindingNode.path("o").path("value").textValue();
userProfile.setEmail(mbox.substring("mailto:".length()));
} else if (predicate.equals(LDIWO.role.getURI())) {
String roleURI = bindingNode.path("o").path("value").textValue();
log.debug(roleURI);
UserRole role = roles.getRole(roleURI);
userProfile.setRole(role);
}
}
return userProfile;
}
/**
* Retrieves extended user's account information.
*
* @param username Target user name
* @return Extended user's account information
* @throws Exception
*/
public UserProfileExtended getUserProfileExtended(String username) throws Exception {
UserProfile userProfile = getUserProfile(username);
UserProfileExtended userProfileExtended = new UserProfileExtended();
userProfileExtended.setProfile(userProfile);
userProfileExtended.setOwnGraphs(getOwnGraphs(username));
userProfileExtended.setReadableGraphs(getReadableGraphs(username));
userProfileExtended.setWritableGraphs(getWritableGraphs(username));
return userProfileExtended;
}
// TODO: move to another class?
public RdfStoreManager getRdfStoreManager(String username) throws Exception {
if (username == null || username.isEmpty())
throw new IllegalArgumentException("username cannot be null");
return frameworkConfig.getUserRdfStoreManager(frameworkConfig.getWorkbenchSystemAdmin(),
frameworkConfig.getWorkbenchSystemAdminPassword());
// password store is a nice idea, but useless, since every user needs to login again after
// server restart.
// there are tasks or jobs, that need user credentials and run out of the box after restart, so
// waiting for a login is
// not appropiate. better: have encryption
/**
* String password = PasswordStore.getPassword(username); if (password==null) throw new
* Exception("No password found for user " + username +
* " in local store. Authentication required."); return
* frameworkConfig.getUserRdfStoreManager(username, password);
**/
}
/*
* // TODO: username must be not null (may be null now - for VirtuosoProxy) public
* ObjectPair<String, String> getRdfStoreUser(String frameworkUsername, String token) throws
* Exception { String query = getPrefixes() + "\n" +
* "SELECT ?rdfStoreUsername, ?rdfStorePassword FROM <" + frameworkConfig.getAccountsGraph() +
* "> " + "WHERE { " + (frameworkUsername == null || frameworkUsername.isEmpty() ? "" :
* "?account foaf:accountName \"" + frameworkUsername + "\" . ") + "?account gkg:sessionToken \""
* + token + "\" . " + "?account gkg:rdfStoreUsername ?rdfStoreUsername . " +
* "?account gkg:rdfStorePassword ?rdfStorePassword . " + "}"; String result =
* rdfStoreManager.execute(query, jsonResponseFormat); ObjectMapper mapper = new ObjectMapper();
* JsonNode rootNode = mapper.readTree(result); Iterator<JsonNode> bindingsIter =
* rootNode.path("results").path("bindings").elements(); if (!bindingsIter.hasNext()) throw new
* Exception("Invalid user credentials."); JsonNode bindingNode = bindingsIter.next(); String
* rdfStoreUsername = bindingNode.path("rdfStoreUsername").path("value").textValue(); String
* rdfStorePassword = bindingNode.path("rdfStorePassword").path("value").textValue(); return new
* ObjectPair<String, String>(rdfStoreUsername, rdfStorePassword); }
*/
/**
* Returns list of readable graphs URIs for given user.
*
* @param username Target user name
* @return List of URIs
* @throws Exception
*/
public Collection<String> getReadableGraphs(String username) throws Exception {
String settingsGraph = getSettingsGraph(username);
Collection<String> settingsGraphList = getSettingsGraphs();
String fromGraphsStr = "";
for (String graph : settingsGraphList) {
if (!graph.equals(settingsGraph)) {
fromGraphsStr += " FROM";
fromGraphsStr += " <" + graph + "> \n";
}
}
String query = getPrefixes() + "\n" + "SELECT DISTINCT ?ng " + " FROM ";
query +=
"<"
+ frameworkConfig.getAccountsGraph()
+ ">\n"
+ fromGraphsStr
+ " WHERE { ?ng rdf:type sd:NamedGraph . ?ng gkg:access ?ao . ?ao acl:mode acl:Read . ?ao acl:agent ?account . ?account foaf:accountName \""
+ username + "\" . }";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
Collection<String> readableGraphs = new ArrayList<String>();
while (bindingsIter.hasNext()) {
readableGraphs.add(bindingsIter.next().path("ng").path("value").textValue());
}
return readableGraphs;
}
/**
* Returns list of writable graphs URIs for given user.
*
* @param username Target user name
* @return List of URIs
* @throws Exception
*/
public Collection<String> getWritableGraphs(String username) throws Exception {
String settingsGraph = getSettingsGraph(username);
Collection<String> settingsGraphList = getSettingsGraphs();
String fromGraphsStr = "";
for (String graph : settingsGraphList) {
if (!graph.equals(settingsGraph)) {
fromGraphsStr += " FROM";
fromGraphsStr += " <" + graph + "> \n";
}
}
String query = getPrefixes() + "\n" + "SELECT DISTINCT ?ng " + " FROM ";
query +=
"<"
+ frameworkConfig.getAccountsGraph()
+ ">\n"
+ fromGraphsStr
+ " WHERE { ?ng rdf:type sd:NamedGraph . ?ng gkg:access ?ao . ?ao acl:mode acl:Write . ?ao acl:agent ?account . ?account foaf:accountName \""
+ username + "\" . }";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
Collection<String> writableGraphs = new ArrayList<String>();
while (bindingsIter.hasNext()) {
writableGraphs.add(bindingsIter.next().path("ng").path("value").textValue());
}
return writableGraphs;
}
/**
* Returns list of all users' named graphs URIs.
*
* @return List of URIs
* @throws Exception
*/
public Collection<String> getAllGraphs() throws Exception {
Collection<String> settingsGraphs = getSettingsGraphs();
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append(getPrefixes()).append("\n").append("SELECT DISTINCT ?ng \n");
for (String sg : settingsGraphs) {
queryBuilder.append("FROM <").append(sg).append(">\n");
}
queryBuilder.append("WHERE { ?ng rdf:type sd:NamedGraph }");
String result = rdfStoreManager.execute(queryBuilder.toString(), jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
Collection<String> graphs = new ArrayList<String>();
while (bindingsIter.hasNext()) {
graphs.add(bindingsIter.next().path("ng").path("value").textValue());
}
return graphs;
}
/**
* Gets the named graphs described inside the settingsGraph and the Graph description of each. And
* gkg:access??
*
* @return SPARQL response
* @throws Exception
*/
public String getAllGraphsSparql() throws Exception {
Collection<String> settingsGraphs = getSettingsGraphs();
String fromStr = "";
for (String sg : settingsGraphs) {
fromStr += "FROM";
fromStr += " <" + sg + ">\n";
}
String query =
getPrefixes() + "\n" + "SELECT ?s ?p ?o " + fromStr + " WHERE {"
+ " { ?s rdf:type sd:NamedGraph . ?s ?p ?o . } " + " UNION "
+ " { ?ng rdf:type sd:NamedGraph . ?ng sd:graph ?s . ?s ?p ?o . } " + " UNION "
+ " { ?ng rdf:type sd:NamedGraph . ?ng gkg:access ?s . ?s ?p ?o . } " + "}";
log.debug(query);
return rdfStoreManager.execute(query, jsonResponseFormat);
}
public Collection<UserProfile> getAllUsersProfiles() throws Exception {
Collection<String> usernames = getAllUsernames();
Collection<UserProfile> profiles = new ArrayList<UserProfile>();
for (String name : usernames)
profiles.add(getUserProfile(name));
return profiles;
}
public Collection<UserProfileExtended> getAllUsersProfilesExtended() throws Exception {
Collection<String> usernames = getAllUsernames();
Collection<UserProfileExtended> profiles = new ArrayList<UserProfileExtended>();
for (String name : usernames)
profiles.add(getUserProfileExtended(name));
return profiles;
}
public Collection<String> getUsers(String graph) throws Exception {
String settingsGraph = getDescribedIn(graph);
if (settingsGraph == null)
throw new Exception("Graph " + graph + " description was not found");
String query =
getPrefixes()
+ "\n"
+ "SELECT ?account FROM <"
+ settingsGraph
+ "> WHERE {?ng rdf:type sd:NamedGraph . ?ng gkg:access ?ao . ?ao acl:agent ?account . } ";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
Collection<String> users = new ArrayList<String>();
while (bindingsIter.hasNext()) {
users.add(bindingsIter.next().path("account").path("value").textValue());
}
return users;
}
public String getDescribedIn(String graph) throws Exception {
Collection<String> settingsGraphs = getSettingsGraphs();
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append(getPrefixes()).append("\n").append("SELECT DISTINCT ?sg \n");
queryBuilder.append("FROM <").append(frameworkConfig.getAccountsGraph()).append(">\n");
for (String sg : settingsGraphs) {
queryBuilder.append("FROM <").append(sg).append(">\n");
}
queryBuilder.append("WHERE { <").append(graph).append("> rdf:type sd:NamedGraph . <")
.append(graph).append("> acl:owner ?account . ?account gkg:settingsGraph ?sg }");
String result = rdfStoreManager.execute(queryBuilder.toString(), jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
if (bindingsIter.hasNext()) {
return bindingsIter.next().path("sg").path("value").textValue();
}
return null;
}
private String getPrefixes() {
if (prefixes == null) {
prefixes =
"PREFIX : <" + frameworkConfig.getResourceNamespace() + ">\n" + "PREFIX gkg: <"
+ LDIWO.NS + ">\n" + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+ "PREFIX sd: <http://www.w3.org/ns/sparql-service-description#>\n"
+ "PREFIX acl: <http://www.w3.org/ns/auth/acl#>\n"
+ "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n"
+ "PREFIX dcterms: <http://purl.org/dc/terms/>\n"
+ "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>";
}
return prefixes;
}
private String getSettingsGraph(String username) throws Exception {
String query =
getPrefixes() + "\n" + "SELECT ?settingsGraph FROM <" + frameworkConfig.getAccountsGraph()
+ "> " + "WHERE {?account foaf:accountName \"" + username
+ "\" . ?account gkg:settingsGraph ?settingsGraph .}";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
return bindingsIter.hasNext() ? bindingsIter.next().path("settingsGraph").path("value")
.textValue() : null;
}
public boolean checkUserExists(String username, String email) throws Exception {
String query =
getPrefixes() + "\n" + "ASK {" + " { GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account foaf:accountName \"" + username + "\"} } " + " UNION " + " { GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {?account foaf:mbox <mailto:" + email
+ ">} }" + "}";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
boolean frameworkUserExists = rootNode.path("boolean").booleanValue();
// check also rdf store user with the same name
boolean rdfStoreUserExists = rdfStoreUserManager.checkUserExists(username, email);
return frameworkUserExists || rdfStoreUserExists;
}
@Override
public void setup() {
rdfStoreUserManager.setup();
}
/**
* Validates that the user has a session using the token and returns the UserProfile object
*
* @param userc json object passed in the request cookies
* @param token
* @return user profile object if token valid and null if the opposite
* @throws Exception
*/
public UserProfile validate(Cookie userc, String token) throws Exception {
if (userc == null || token == null)
return null;
String userstr = URLDecoder.decode(userc.getValue(), "utf-8");
return validate(userstr, token);
}
public UserProfile validate(String userc, String token) throws Exception {
String userstr = URLDecoder.decode(userc, "utf-8");
log.debug(" userstr: " + userstr + " token:" + token);
if (userc == null || token == null)
return null;
Gson gson = new Gson();
UserProfile user = gson.fromJson(userstr, UserProfile.class);
boolean checkToken = checkToken(user.getUsername(), token);
if (!checkToken)
return null;
return user;
}
private Collection<String> getSettingsGraphs() throws Exception {
Collection<String> settingsGraphList = new ArrayList<String>();
settingsGraphList.add(frameworkConfig.getSettingsGraph());
String query =
getPrefixes() + "\n" + " SELECT DISTINCT ?sg FROM <" + frameworkConfig.getAccountsGraph()
+ "> " + " WHERE { ?account gkg:settingsGraph ?sg }";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
while (bindingsIter.hasNext()) {
JsonNode bindingNode = bindingsIter.next();
settingsGraphList.add(bindingNode.path("sg").path("value").textValue());
}
return settingsGraphList;
}
public Collection<String> getAllUsernames() throws Exception {
String query =
getPrefixes() + "\n" + "SELECT DISTINCT ?username FROM <"
+ frameworkConfig.getAccountsGraph() + "> "
+ " WHERE {?account rdf:type gkg:Account . ?account foaf:accountName ?username}";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
Collection<String> usernames = new ArrayList<String>();
while (bindingsIter.hasNext()) {
usernames.add(bindingsIter.next().path("username").path("value").textValue());
}
return usernames;
}
private Collection<String> getOwnGraphs(String username) throws Exception {
String settingsGraph = getSettingsGraph(username);
String query = getPrefixes() + "\n" + "SELECT DISTINCT ?ng " + " FROM ";
query += "<" + settingsGraph + "> " + " FROM ";
query +=
"<"
+ frameworkConfig.getAccountsGraph()
+ "> "
+ " WHERE { ?ng rdf:type sd:NamedGraph . ?ng acl:owner ?account . ?account foaf:accountName \""
+ username + "\" . }";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
Collection<String> ownGraphs = new ArrayList<String>();
while (bindingsIter.hasNext()) {
ownGraphs.add(bindingsIter.next().path("ng").path("value").textValue());
}
return ownGraphs;
}
/**
* Checks if given Workbench user has role Administrator.
*
* @param userId User name, e-mail or account URI
* @return true, if given user has role Administrator, false, otherwise
* @throws Exception
*/
public boolean isAdmin(String userId) throws Exception {
String query =
getPrefixes() + "\n" + "SELECT DISTINCT ?role FROM <" + frameworkConfig.getAccountsGraph()
+ "> " + "WHERE {" + " {?account foaf:accountName \"" + userId
+ "\" . ?account gkg:role ?role . } " + " UNION " + " {?account foaf:mbox <mailto:"
+ userId + "> . ?account gkg:role ?role . } " + " UNION "
+ " {?account gkg:role ?role . FILTER (?account = <" + userId + ">)} " + "}";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
if (!bindingsIter.hasNext())
return false;
JsonNode binding = bindingsIter.next();
String role = binding.path("role").path("value").textValue();
return role.equals(frameworkConfig.getResourceNamespace() + RoleType.ADMINISTRATOR);
}
/**
* Sets Workbench role for given user.
*
* @param userId Target user name
* @param role Workbench role
* @throws Exception
*/
public void setRole(String userId, String role) throws Exception {
// OntoQuad doesn't support DELETE {...} INSERT {...} WHERE {...}, so
// use 2 queries
String deleteQuery =
getPrefixes() + "\n" + " DELETE { GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:role ?o} } " + " WHERE { GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {?account foaf:accountName \"" + userId
+ "\" . optional {?account gkg:role ?o .} } }";
String insertQuery =
getPrefixes() + "\n" + " INSERT { GRAPH <" + frameworkConfig.getAccountsGraph()
+ "> {?account gkg:role <" + role + ">} } " + " WHERE { GRAPH <"
+ frameworkConfig.getAccountsGraph() + "> {?account foaf:accountName \"" + userId
+ "\" . optional {?account gkg:role ?o .} } }";
log.debug(deleteQuery);
log.debug(insertQuery);
rdfStoreManager.execute(deleteQuery, jsonResponseFormat);
rdfStoreManager.execute(insertQuery, jsonResponseFormat);
// TODO: replace 2 queries with this query when OntoQuad will support
// DELETE {...} INSERT {...} WHERE {...} queries
// String query = getPrefixes() + "\n"
// + " WITH <" + frameworkConfig.getAccountsGraph() + "> "
// + " DELETE {?account gkg:role ?o} "
// + " INSERT {?account gkg:role <" + role + ">} "
// + " WHERE {?account foaf:accountName \"" + userId +
// "\" . optional {?account gkg:role ?o .} }";
}
/*
* REPLACED FUNCTIONS WITHIN RoleManager
*
* private UserRole getRole(String roleURI) throws Exception { UserRole role = new UserRole();
* role.setUri(roleURI); Collection<String> roleServices = new ArrayList<>(); String query =
* getPrefixes() + "\n" + "SELECT ?s ?p ?o FROM <" + frameworkConfig.getAccountsGraph() + "> " +
* "WHERE {?s ?p ?o . filter(?s=<" + roleURI + ">)}"; log.debug(query); String result =
* rdfStoreManager.execute(query, jsonResponseFormat);
*
* ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(result);
* Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements(); while
* (bindingsIter.hasNext()) { JsonNode bindingNode = bindingsIter.next(); String predicate =
* bindingNode.path("p").path("value").textValue();
*
* log.debug(predicate);
*
* if (predicate.equals(RDFS.label.getURI()))
* role.setName(bindingNode.path("o").path("value").textValue()); else if
* (predicate.equals(LDIWO.isAllowedToUseService.getURI()))
* roleServices.add(bindingNode.path("o").path("value").textValue()); }
* role.setServices(roleServices); return role; }
*
* private String getDefaultRoleURI() throws Exception { String query = getPrefixes() + "\n" +
* "SELECT DISTINCT ?role FROM <" + frameworkConfig.getAccountsGraph() + "> " +
* "WHERE { ?role gkg:isDefault true . }"; String result = rdfStoreManager.execute(query,
* jsonResponseFormat); ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode =
* mapper.readTree(result); Iterator<JsonNode> bindingsIter =
* rootNode.path("results").path("bindings").elements(); String role; if (!bindingsIter.hasNext())
* { role = frameworkConfig.getResourceNamespace() + RoleType.DEFAULT; } else { JsonNode binding =
* bindingsIter.next(); role = binding.path("role").path("value").textValue(); } return role; }
*/
/**
* This method returns Workbench user name.
*
* @param usernameOrEmail Workbench user name or e-mail
* @return Corresponding Workbench user name
* @throws Exception
*/
private String getUsername(String usernameOrEmail) throws Exception {
if (!usernameOrEmail.contains("@"))
return usernameOrEmail;
String query =
getPrefixes() + "\n" + "SELECT DISTINCT ?name FROM <" + frameworkConfig.getAccountsGraph()
+ "> " + "WHERE {?account foaf:accountName ?name . ?account foaf:mbox <mailto:"
+ usernameOrEmail + "> . } ";
String result = rdfStoreManager.execute(query, jsonResponseFormat);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(result);
Iterator<JsonNode> bindingsIter = rootNode.path("results").path("bindings").elements();
if (!bindingsIter.hasNext())
return null;
return bindingsIter.next().path("name").path("value").textValue();
}
}