/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* @created Jul 12, 2005
* @author James Dixon, Angelo Rodriguez, Steven Barkdull
*/
package org.pentaho.platform.repository.solution;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IAclSolutionFile;
import org.pentaho.platform.api.engine.ICacheManager;
import org.pentaho.platform.api.engine.IContentInfo;
import org.pentaho.platform.api.engine.IFileInfo;
import org.pentaho.platform.api.engine.IPentahoAclEntry;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.IPermissionMask;
import org.pentaho.platform.api.engine.IPermissionRecipient;
import org.pentaho.platform.api.engine.IPluginManager;
import org.pentaho.platform.api.engine.IPluginOperation;
import org.pentaho.platform.api.engine.ISolutionFile;
import org.pentaho.platform.api.engine.PentahoAccessControlException;
import org.pentaho.platform.api.repository.ISolutionRepository;
import org.pentaho.platform.api.repository.ISolutionRepositoryService;
import org.pentaho.platform.api.repository.SolutionRepositoryServiceException;
import org.pentaho.platform.engine.core.solution.ActionInfo;
import org.pentaho.platform.engine.core.solution.FileInfo;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.security.SecurityHelper;
import org.pentaho.platform.engine.security.SimplePermissionMask;
import org.pentaho.platform.engine.security.SimpleRole;
import org.pentaho.platform.engine.security.SimpleUser;
import org.pentaho.platform.engine.services.messages.Messages;
import org.pentaho.platform.util.StringUtil;
import org.pentaho.platform.util.messages.LocaleHelper;
import org.pentaho.platform.util.web.MimeHelper;
import org.pentaho.platform.util.xml.XmlHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class SolutionRepositoryServiceImpl implements ISolutionRepositoryService {
private static final long serialVersionUID = -5870073658756939643L;
private static final Log logger = LogFactory.getLog(SolutionRepositoryServiceImpl.class);
private static final String RESPONSE_DOCUMENT_ENCODING = "UTF-8"; //$NON-NLS-1$
private static final String RESPONSE_DOCUMENT_VERSION_NUM = "1.0"; //$NON-NLS-1$
/**
* contains instance of a sax parser factory. Use getSAXParserFactory() method to get a copy of the factory.
*/
private static final ThreadLocal<SAXParserFactory> SAX_FACTORY = new ThreadLocal<SAXParserFactory>();
static {
// The SolutionRepositoryService creates/uses the ICacheManager from PentahoSystem to create a new
// cache region specifically for the caching of the solution repository document. This is not put
// into a session cache intentionally. Client tools like PRD do not maintain a session and would
// thus never have any benefit from this. Since we are using a cache manager, if the cache is
// unused long enough entries will age out.
// We are caching the solution repository document on a per-user basis, as required, because the
// document is that user's view of the repository, with respect to ACLs.
// Upon publish, reload, or reset repository calls this cache is cleared in the reset method
// of SolutionRepositoryBase.
ICacheManager cacheManager = PentahoSystem.getCacheManager(null);
if (!cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
cacheManager.addCacheRegion(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION);
}
}
public SolutionRepositoryServiceImpl() {
super();
}
/**
* This method will delete a file from the ISolutionRepository and respects IPentahoAclEntry.PERM_DELETE.
*
* @param userSession
* An IPentahoSession for the user requesting the delete operation
* @param solution
* The name of the solution, such as 'steel-wheels'
* @param path
* The path within the solution to the file/folder to be deleted (does not include the file/folder itself)
* @param name
* The name of the file or folder which will be deleted in the given solution/path
* @return Success of the delete operation is returned
* @throws IOException
*/
public boolean delete(final IPentahoSession userSession, final String solution, final String path,
final String name) throws IOException {
ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, userSession);
String fullPath = ActionInfo.buildSolutionPath(solution, path, name);
return repository.removeSolutionFile(fullPath);
}
/**
* This method creates a folder along with it's index.xml file.
* This method also verifies that the user has PERM_CREATE permissions before
* creating the folder.
*
* @param userSession the current user
* @param solution the solution path
* @param path the folder path
* @param name the name of the new folder
* @param desc the description of the new folder
* @return true if success
* @throws IOException
*/
public boolean createFolder(IPentahoSession userSession, String solution, String path, String name, String desc)
throws IOException {
ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, userSession);
if (solution == null) {
solution = ""; //$NON-NLS-1$
}
// verify that the name does not contain a path separator before creating the folder
if (name == null || name.indexOf("/") >= 0 || name.indexOf("\\") >= 0 || //$NON-NLS-1$ //$NON-NLS-2$
name.indexOf(ISolutionRepository.SEPARATOR) >= 0) {
return false;
}
String parentFolderPath = ActionInfo.buildSolutionPath(solution, path, "" + ISolutionRepository.SEPARATOR); //$NON-NLS-1$
ISolutionFile parentSolutionFile = repository.getSolutionFile(parentFolderPath, ISolutionRepository.ACTION_CREATE);
if (parentSolutionFile != null && parentSolutionFile.isDirectory()) {
File parent = new File(PentahoSystem.getApplicationContext().getSolutionPath(parentFolderPath));
File newFolder = new File(parent, name);
if (newFolder.exists()) {
// if the new folder already exists, we need to get out
return false;
}
repository.createFolder(newFolder);
// create the index file content
String defaultIndex = "<index><name>" + name + "</name><description>" + (desc != null ? desc : name) //$NON-NLS-1$ //$NON-NLS-2$
+ "</description><icon>reporting.png</icon><visible>true</visible><display-type>list</display-type></index>"; //$NON-NLS-1$
// add the index file to the repository
String indexPath = ActionInfo.buildSolutionPath(solution, path, name);
String baseURL = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$
repository
.addSolutionFile(baseURL, indexPath, ISolutionRepository.INDEX_FILENAME, defaultIndex.getBytes(), false);
return true;
}
return false;
}
private boolean acceptFilter(String name, String[] filters) {
if (filters == null || filters.length == 0) {
return false;
}
for (int i = 0; i < filters.length; i++) {
if (name.endsWith(filters[i])) {
return true;
}
}
return false;
}
private boolean accept(boolean isAdministrator, ISolutionRepository repository, ISolutionFile file) {
return isAdministrator || repository.hasAccess(file, IPentahoAclEntry.PERM_EXECUTE);
}
private void processRepositoryFile(IPentahoSession session, boolean isAdministrator, ISolutionRepository repository, Node parentNode, ISolutionFile file,
String[] filters) {
final String name = file.getFileName();
// MDD 10/16/2008 Not always.. what about 'system'
if (name.startsWith("system") || name.startsWith("tmp") || name.startsWith(".")) { //$NON-NLS-1$
// slip hidden files (starts with .)
// skip the system & tmp dir, we DO NOT ever want this to hit the client
return;
}
if (!accept(isAdministrator, repository, file)) {
// we don't want this file, skip to the next one
return;
}
if (file.isDirectory()) {
// we always process directories
// maintain legacy behavior
if (repository.getRootFolder(ISolutionRepository.ACTION_EXECUTE).getFullPath().equals(file.getFullPath())) {
// never output the root folder as part of the repo doc; skip root and process its children
ISolutionFile[] children = file.listFiles();
for (ISolutionFile childSolutionFile : children) {
processRepositoryFile(session, isAdministrator, repository, parentNode, childSolutionFile, filters);
}
return;
}
Element child = null;
final String key = file.getFullPath() + file.getLastModified() + LocaleHelper.getLocale();
ICacheManager cacheManager = PentahoSystem.getCacheManager(null);
if (cacheManager != null && cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
child = (Element) cacheManager.getFromRegionCache(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION, key);
}
if (child == null) {
child = parentNode instanceof Document ? ((Document) parentNode).createElement("file") : parentNode.getOwnerDocument().createElement("file"); //$NON-NLS-1$ //$NON-NLS-2$
try {
String localizedName = repository.getLocalizedFileProperty(file, "name", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
child.setAttribute("localized-name", localizedName == null || "".equals(localizedName) ? name : localizedName); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
child.setAttribute("localized-name", name); //$NON-NLS-1$
}
try {
String visible = repository.getLocalizedFileProperty(file, "visible", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
child.setAttribute("visible", visible == null || "".equals(visible) ? "false" : visible); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} catch (Exception e) {
e.printStackTrace();
child.setAttribute("visible", "false"); //$NON-NLS-1$ //$NON-NLS-2$
}
String description = repository.getLocalizedFileProperty(file, "description", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
child.setAttribute("description", description == null || "".equals(description) ? name : description); //$NON-NLS-1$ //$NON-NLS-2$
child.setAttribute("name", name); //$NON-NLS-1$
child.setAttribute("isDirectory", "true"); //$NON-NLS-1$ //$NON-NLS-2$
child.setAttribute("lastModifiedDate", "" + file.getLastModified()); //$NON-NLS-1$ //$NON-NLS-2$
if (cacheManager != null && cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
cacheManager.putInRegionCache(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION, key, child);
}
} else {
Element newChild = parentNode instanceof Document ? ((Document) parentNode).createElement("file") : parentNode.getOwnerDocument().createElement("file"); //$NON-NLS-1$//$NON-NLS-2$
NamedNodeMap attributes = child.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
newChild.setAttribute(attribute.getNodeName(), attribute.getNodeValue());
}
child = newChild;
}
parentNode.appendChild(child);
ISolutionFile[] children = file.listFiles();
for (ISolutionFile childSolutionFile : children) {
processRepositoryFile(session, isAdministrator, repository, child, childSolutionFile, filters);
}
} else {
InputStream pluginInputStream = null;
try {
int lastPoint = name.lastIndexOf('.');
String extension = ""; //$NON-NLS-1$
if (lastPoint != -1) {
// ignore anything with no extension
extension = name.substring(lastPoint + 1).toLowerCase();
}
// xaction and URL support are built in
boolean addFile = acceptFilter(name, filters) || "xaction".equals(extension) || "url".equals(extension); //$NON-NLS-1$ //$NON-NLS-2$
boolean isPlugin = false;
// see if there is a plugin for this file type
IPluginManager pluginManager = PentahoSystem.get(IPluginManager.class, session);
if (pluginManager != null) {
Set<String> types = pluginManager.getContentTypes();
isPlugin = types != null && types.contains(extension);
addFile |= isPlugin;
}
if (addFile) {
ICacheManager cacheManager = PentahoSystem.getCacheManager(null);
Element child = null;
final String key = file.getFullPath() + file.getLastModified() + LocaleHelper.getLocale();
if (cacheManager != null && cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
child = (Element) cacheManager.getFromRegionCache(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION, key);
}
if (child == null) {
child = parentNode instanceof Document ? ((Document) parentNode).createElement("file") : parentNode.getOwnerDocument().createElement("file"); //$NON-NLS-1$ //$NON-NLS-2$
if (cacheManager != null && cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
cacheManager.putInRegionCache(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION, key, child);
}
} else {
Element newChild = parentNode instanceof Document ? ((Document) parentNode).createElement("file") : parentNode.getOwnerDocument().createElement("file"); //$NON-NLS-1$ //$NON-NLS-2$
NamedNodeMap attributes = child.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
newChild.setAttribute(attribute.getNodeName(), attribute.getNodeValue());
}
parentNode.appendChild(newChild);
return;
}
parentNode.appendChild(child);
IFileInfo fileInfo = null;
try {
// the visibility flag for action-sequences is controlled by
// /action-sequence/documentation/result-type
// and we should no longer be looking at 'visible' because it was
// never actually used!
String visible = "none".equals(repository.getLocalizedFileProperty(file, "documentation/result-type", ISolutionRepository.ACTION_EXECUTE)) ? "false" : "true"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
child.setAttribute("visible", (visible == null || "".equals(visible) || "true".equals(visible)) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
} catch (Exception e) {
child.setAttribute("visible", "true"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (name.endsWith(".xaction")) { //$NON-NLS-1$
// add special props?
// localization..
} else if (name.endsWith(".url")) { //$NON-NLS-1$
// add special props
String props = new String(file.getData());
StringTokenizer tokenizer = new StringTokenizer(props, "\n"); //$NON-NLS-1$
while (tokenizer.hasMoreTokens()) {
String line = tokenizer.nextToken();
int pos = line.indexOf('=');
if (pos > 0) {
String propname = line.substring(0, pos);
String value = line.substring(pos + 1);
if ((value != null) && (value.length() > 0) && (value.charAt(value.length() - 1) == '\r')) {
value = value.substring(0, value.length() - 1);
}
if ("URL".equalsIgnoreCase(propname)) { //$NON-NLS-1$
child.setAttribute("url", value); //$NON-NLS-1$
}
}
}
} else if (isPlugin) {
// must be a plugin - make it look like a URL
try {
// get the file info object for this file
// not all plugins are going to actually use the inputStream, so we have a special
// wrapper inputstream so that we can pay that price when we need to (2X speed boost)
pluginInputStream = new PluginFileInputStream(repository, file);
fileInfo = pluginManager.getFileInfo(extension, session, file, pluginInputStream);
String handlerId = pluginManager.getContentGeneratorIdForType(extension, session);
String fileUrl = pluginManager.getContentGeneratorUrlForType(extension, session);
String solution = file.getSolutionPath();
String path = ""; //$NON-NLS-1$
if (solution.startsWith(ISolutionRepository.SEPARATOR + "")) { //$NON-NLS-1$
solution = solution.substring(1);
}
int pos = solution.indexOf(ISolutionRepository.SEPARATOR);
if (pos != -1) {
path = solution.substring(pos + 1);
solution = solution.substring(0, pos);
}
String url = null;
if (!"".equals(fileUrl)) { //$NON-NLS-1$
url = PentahoSystem.getApplicationContext().getBaseUrl() + fileUrl + "?solution=" + solution + "&path=" + path + "&action=" + name; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else {
IContentInfo info = pluginManager.getContentInfoFromExtension(extension, session);
for (IPluginOperation operation : info.getOperations()) {
if (operation.getId().equalsIgnoreCase("RUN")) { //$NON-NLS-1$
String command = operation.getCommand();
command = command.replaceAll("\\{solution\\}", solution); //$NON-NLS-1$
command = command.replaceAll("\\{path\\}", path); //$NON-NLS-1$
command = command.replaceAll("\\{name\\}", name); //$NON-NLS-1$
url = PentahoSystem.getApplicationContext().getBaseUrl() + command;
break;
}
}
if (url == null) {
url = PentahoSystem.getApplicationContext().getBaseUrl()
+ "content/" + handlerId + "?solution=" + solution + "&path=" + path + "&action=" + name; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}
child.setAttribute("url", url); //$NON-NLS-1$
String paramServiceUrl = PentahoSystem.getApplicationContext().getBaseUrl()
+ "content/" + handlerId + "?solution=" + solution + "&path=" + path + "&action=" + name; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
child.setAttribute("param-service-url", paramServiceUrl); //$NON-NLS-1$
} catch (Throwable t) {
t.printStackTrace();
}
}
// localization
try {
String localizedName = null;
if (name.endsWith(".url")) { //$NON-NLS-1$
localizedName = repository.getLocalizedFileProperty(file, "url_name", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
} else if (fileInfo != null) {
localizedName = fileInfo.getTitle();
} else {
localizedName = repository.getLocalizedFileProperty(file, "title", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
}
child.setAttribute("localized-name", localizedName == null || "".equals(localizedName) ? name : localizedName); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
child.setAttribute("localized-name", name); //$NON-NLS-1$
}
try {
// only folders, urls and xactions have descriptions
if (name.endsWith(".url")) { //$NON-NLS-1$
String url_description = repository.getLocalizedFileProperty(file, "url_description", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
String description = repository.getLocalizedFileProperty(file, "description", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
if (url_description == null && description == null) {
child.setAttribute("description", name); //$NON-NLS-1$
} else {
child.setAttribute("description", url_description == null || "".equals(url_description) ? description //$NON-NLS-1$ //$NON-NLS-2$
: url_description);
}
} else if (name.endsWith(".xaction")) { //$NON-NLS-1$
String description = repository.getLocalizedFileProperty(file, "description", ISolutionRepository.ACTION_EXECUTE); //$NON-NLS-1$
child.setAttribute("description", description == null || "".equals(description) ? name : description); //$NON-NLS-1$ //$NON-NLS-2$
} else if (fileInfo != null) {
child.setAttribute("description", fileInfo.getDescription()); //$NON-NLS-1$
} else {
child.setAttribute("description", name); //$NON-NLS-1$
}
} catch (Exception e) {
child.setAttribute("description", "xxxxxxx"); //$NON-NLS-1$ //$NON-NLS-2$
}
// add permissions for each file/folder
child.setAttribute("name", name); //$NON-NLS-1$
child.setAttribute("isDirectory", "" + file.isDirectory()); //$NON-NLS-1$ //$NON-NLS-2$
child.setAttribute("lastModifiedDate", "" + file.getLastModified()); //$NON-NLS-1$ //$NON-NLS-2$
}
} finally {
IOUtils.closeQuietly(pluginInputStream);
}
} // else isfile
}
public org.w3c.dom.Document getSolutionRepositoryDoc(IPentahoSession session, String[] filters) throws ParserConfigurationException {
// The SolutionRepositoryService creates/uses the ICacheManager from PentahoSystem to create a new
// cache region specifically for the caching of the solution repository document. This is not put
// into a session cache intentionally. Client tools like PRD do not maintain a session and would
// thus never have any benefit from this. Since we are using a cache manager, if the cache is
// unused long enough entries will age out.
// We are caching the solution repository document on a per-user basis, as required, because the
// document is that user's view of the repository, with respect to ACLs.
// Upon publish, reload, or reset repository calls this cache is cleared in the reset method
// of SolutionRepositoryBase.
ICacheManager cacheManager = PentahoSystem.getCacheManager(null);
org.w3c.dom.Document document = null;
if (cacheManager != null && cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
document = (org.w3c.dom.Document) cacheManager.getFromRegionCache(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION, session.getName() + LocaleHelper.getLocale());
}
if (document == null) {
ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, session);
ISolutionFile rootFile = repository.getRootFolder(ISolutionRepository.ACTION_EXECUTE);
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
org.w3c.dom.Element root = document.createElement("repository"); //$NON-NLS-1$
document.appendChild(root);
root.setAttribute("path", rootFile.getFullPath()); //$NON-NLS-1$
boolean isAdministrator = SecurityHelper.isPentahoAdministrator(session);
processRepositoryFile(session, isAdministrator, repository, root, rootFile, filters);
// only attempt to add to the cache if by this point it exists, it's possible that
// the implementation of the ICacheManager might not allow the creation of new
// or custom caches like this.
if (cacheManager != null && cacheManager.cacheEnabled(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION)) {
cacheManager.putInRegionCache(ISolutionRepository.REPOSITORY_SERVICE_CACHE_REGION, session.getName() + LocaleHelper.getLocale(), document);
}
}
return document;
}
/**
* Returns an XML snippet consisting of a single <code>file</code> element. The <code>file</code> element is the same
* as would have been returned by <code>getSolutionRepositoryDoc</code>.
* @param session current session
* @return doc
* @throws ParserConfigurationException
*/
public org.w3c.dom.Document getSolutionRepositoryFileDetails(IPentahoSession session, String fullPath)
throws ParserConfigurationException {
ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, session);
ISolutionFile rootFile = repository.getSolutionFile(fullPath, ISolutionRepository.ACTION_EXECUTE);
org.w3c.dom.Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
boolean isAdministrator = SecurityHelper.isPentahoAdministrator(session);
processRepositoryFile(session, isAdministrator, repository, document, rootFile, new String[0]);
return document;
}
private Map<IPermissionRecipient, IPermissionMask> createAclFromXml(final String strXml) throws ParserConfigurationException,
SAXException, IOException {
SAXParser parser = getSAXParserFactory().newSAXParser();
Map<IPermissionRecipient, IPermissionMask> m = new HashMap<IPermissionRecipient, IPermissionMask>();
DefaultHandler h = new AclParserHandler(m);
String encoding = XmlHelper.getEncoding(strXml);
InputStream is = new ByteArrayInputStream(strXml.getBytes(encoding));
parser.parse(is, h);
return m;
}
private class AclParserHandler extends DefaultHandler {
Map<IPermissionRecipient, IPermissionMask> acl;
public AclParserHandler(final Map<IPermissionRecipient, IPermissionMask> acl) {
this.acl = acl;
}
@Override
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
throws SAXException {
if (qName.equalsIgnoreCase("entry")) { //$NON-NLS-1$
String permissions = attributes.getValue("", "permissions"); //$NON-NLS-1$ //$NON-NLS-2$
IPermissionRecipient permRecipient = null;
String user = attributes.getValue("", "user"); //$NON-NLS-1$ //$NON-NLS-2$
if (null != user) {
permRecipient = new SimpleUser(user);
} else {
permRecipient = new SimpleRole(attributes.getValue("", "role")); //$NON-NLS-1$ //$NON-NLS-2$
}
this.acl.put(permRecipient, new SimplePermissionMask(Integer.parseInt(permissions)));
}
}
}
/**
* Sets the ACL Xml for a particular file in the solution repository
*
* @param solution
* @param path
* @param filename
* @param strAclXml
* @param userSession
*/
public void setAcl(final String solution, final String path, final String filename, final String strAclXml,
final IPentahoSession userSession) throws SolutionRepositoryServiceException,
IOException, PentahoAccessControlException {
if (StringUtil.doesPathContainParentPathSegment(solution) || StringUtil.doesPathContainParentPathSegment(path)) {
String msg = Messages.getInstance().getString("AdhocWebService.ERROR_0008_MISSING_OR_INVALID_REPORT_NAME"); //$NON-NLS-1$
throw new SolutionRepositoryServiceException(msg);
}
ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, userSession);
String fullPath = ActionInfo.buildSolutionPath(solution, path, filename);
ISolutionFile solutionFile = repository.getSolutionFile(fullPath, ISolutionRepository.ACTION_SHARE);
// ouch, i hate instanceof
if (solutionFile instanceof IAclSolutionFile) {
Map<IPermissionRecipient, IPermissionMask> acl;
try {
acl = createAclFromXml(strAclXml);
// TODO sbarkdull, fix these really really lame exception msgs
} catch (ParserConfigurationException e) {
throw new SolutionRepositoryServiceException("ParserConfigurationException", e); //$NON-NLS-1$
} catch (SAXException e) {
throw new SolutionRepositoryServiceException("SAXException", e); //$NON-NLS-1$
} catch (IOException e) {
throw new SolutionRepositoryServiceException("IOException", e); //$NON-NLS-1$
}
repository.setPermissions(solutionFile, acl);
}
// TODO sbarkdull, what if its not instanceof
}
/**
* Gets ACLs based on a solution repository file.
*
* @param parameterProvider
* @param outputStream
* @param userSession
*
* @return acl xml
*/
public String getAclXml(final String solution, final String path, final String filename, final IPentahoSession userSession) throws SolutionRepositoryServiceException,
IOException {
if (StringUtil.doesPathContainParentPathSegment(solution) || StringUtil.doesPathContainParentPathSegment(path)) {
String msg = Messages.getInstance().getString("AdhocWebService.ERROR_0008_MISSING_OR_INVALID_REPORT_NAME"); //$NON-NLS-1$
throw new SolutionRepositoryServiceException(msg);
}
ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, userSession);
String fullPath = ActionInfo.buildSolutionPath(solution, path, filename);
ISolutionFile solutionFile = repository.getSolutionFile(fullPath, ISolutionRepository.ACTION_EXECUTE);
String strXml = null;
// ouch, i hate instanceof
if (solutionFile instanceof IAclSolutionFile) {
Map<IPermissionRecipient, IPermissionMask> filePermissions = repository.getPermissions((solutionFile));
String processingInstruction = XmlHelper
.createXmlProcessingInstruction(RESPONSE_DOCUMENT_VERSION_NUM,
RESPONSE_DOCUMENT_ENCODING);
strXml = processingInstruction + getAclAsXml(filePermissions);
} else {
strXml = "<acl notsupported='true'/>"; //$NON-NLS-1$
}
return strXml;
}
// TODO sbarkdull, this method belongs in an AclUtils class?
// turn acl into an XML representation, and return the document.
// probably belongs in the SecurityHelper class, but does this class still exist?
private String getAclAsXml(final Map<IPermissionRecipient, IPermissionMask> filePermissions) {
StringBuffer sb = new StringBuffer(XmlHelper.createXmlProcessingInstruction(
RESPONSE_DOCUMENT_VERSION_NUM, RESPONSE_DOCUMENT_ENCODING));
sb.append("<acl>"); //$NON-NLS-1$
for (Map.Entry<IPermissionRecipient, IPermissionMask> filePerm : filePermissions.entrySet()) {
IPermissionRecipient permRecipient = filePerm.getKey();
if (permRecipient instanceof SimpleRole) {
sb
.append("<entry role='" + permRecipient.getName() + "' permissions='" + filePerm.getValue().getMask() //$NON-NLS-1$ //$NON-NLS-2$
+ "'/>"); //$NON-NLS-1$
} else {
// entry belongs to a user
sb
.append("<entry user='" + permRecipient.getName() + "' permissions='" + filePerm.getValue().getMask() //$NON-NLS-1$ //$NON-NLS-2$
+ "'/>"); //$NON-NLS-1$
}
}
sb.append("</acl>"); //$NON-NLS-1$
return sb.toString();
}
/**
* Get a SAX Parser Factory
*
* NOTE: Need sax parser factory per thread for thread safety. See: http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/parsers/SAXParserFactory.html
*
* @return
*/
private static SAXParserFactory getSAXParserFactory() {
SAXParserFactory threadLocalSAXParserFactory = SAX_FACTORY.get();
if (null == threadLocalSAXParserFactory) {
threadLocalSAXParserFactory = SAXParserFactory.newInstance();
SAX_FACTORY.set(threadLocalSAXParserFactory);
}
return threadLocalSAXParserFactory;
}
/**
* This class is basically a wrapper for the solution file inputstream, but it will only
* pay the price *IF* we need to actually open the inputstream. This has a huge performance
* benefit (9s down to 3s).
*/
private class PluginFileInputStream extends InputStream {
private ISolutionRepository repository;
private ISolutionFile file;
private InputStream inputStream;
public PluginFileInputStream(ISolutionRepository repository, ISolutionFile file) {
this.repository = repository;
this.file = file;
}
public int read() throws IOException {
if (inputStream == null) {
inputStream = repository.getResourceInputStream(file.getFullPath(), true, ISolutionRepository.ACTION_EXECUTE);
}
return inputStream.read();
}
public int read(byte[] b) throws IOException {
if (inputStream == null) {
inputStream = repository.getResourceInputStream(file.getFullPath(), true, ISolutionRepository.ACTION_EXECUTE);
}
return inputStream.read(b);
}
public int read(byte[] b, int off, int len) throws IOException {
if (inputStream == null) {
inputStream = repository.getResourceInputStream(file.getFullPath(), true, ISolutionRepository.ACTION_EXECUTE);
}
return inputStream.read(b, off, len);
}
public synchronized void mark(int readlimit) {
if (inputStream != null) {
inputStream.mark(readlimit);
}
}
public boolean markSupported() {
if (inputStream != null) {
return inputStream.markSupported();
}
return super.markSupported();
}
public synchronized void reset() throws IOException {
if (inputStream != null) {
inputStream.reset();
}
super.reset();
}
public long skip(long n) throws IOException {
if (inputStream != null) {
inputStream.skip(n);
}
return super.skip(n);
}
public void close() throws IOException {
if (inputStream != null) {
inputStream.close();
}
}
public int available() throws IOException {
if (inputStream != null) {
return inputStream.available();
}
return 0;
}
}
}