Initial creation of ClassificationService, as part of RM-1945 and RM-1946.

This check-in adds the basic ClassificationService API, its initial implementation, ClassificationServiceImpl, along with some basic support types such as ClassificationServiceException (for service-specific exceptions) and Configuration.
It also adds unit tests ClassificationServiceImplUnitTest and ConfigurationUnitTest.

The ClassificationService begins our support for ‘Classified Records’, whereby Alfresco content can be given a ClassificationLevel and thereafter will only be accessible to users with the appropriate security clearance.

The vanilla service includes a default set, rm-classification-levels.json (Top Secret etc) which links through to the i18n’d display data via rm-classification.properties in the usual way.

The service is defined in its own spring context file, rm-classified-records-context.xml, as it is distinct from the file plan and should be applicable to content outside that file plan.



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@99932 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Neil McErlean
2015-03-23 18:22:26 +00:00
parent 2c6b254012
commit aee333172a
13 changed files with 684 additions and 1 deletions

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import org.springframework.extensions.surf.util.I18NUtil;
import java.io.Serializable;
/**
* This class is a POJO data type for a Classification Level.
*
* @author Neil Mc Erlean
* @since 3.0
*/
public final class ClassificationLevel implements Serializable
{
private final String id;
private final String displayLabelKey;
public ClassificationLevel(final String id, final String displayLabelKey)
{
if (id == null || id.trim().equals(""))
{
throw new IllegalArgumentException("Illegal id: '" + id + "'");
}
this.id = id;
this.displayLabelKey = displayLabelKey;
}
/** Returns the unique identifier for this classification level. */
public String getId() { return this.id; }
/** Returns the localised (current locale) display label for this classification level. */
public String getDisplayLabel() { return I18NUtil.getMessage(displayLabelKey); }
@Override public String toString()
{
StringBuilder msg = new StringBuilder();
msg.append(ClassificationLevel.class.getSimpleName())
.append(":").append(id);
return msg.toString();
}
@Override public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassificationLevel that = (ClassificationLevel) o;
return this.id.equals(that.id);
}
@Override public int hashCode() { return id.hashCode(); }
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import java.util.List;
/**
* The Classification Service supports the 'Classified Records' feature, whereby Alfresco
* content can be given a {@link ClassificationLevel}. This restricts access to that
* content to users having the appropriate security clearance.
*
* @author Neil Mc Erlean
* @since 3.0
*/
public interface ClassificationService
{
/**
* Returns an immutable list of the defined classification levels.
* @return classification levels in descending order from highest to lowest.
*/
public List<ClassificationLevel> getApplicableLevels();
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Generic class for any runtime exception thrown within the {@link ClassificationService}.
*
* @author Neil Mc Erlean
* @since 3.0
*/
public class ClassificationServiceException extends AlfrescoRuntimeException
{
public ClassificationServiceException(String msgId) { super(msgId); }
public ClassificationServiceException(String msgId, Throwable cause) { super(msgId, cause); }
/** Represents a fatal error due to missing required configuration. */
public static class MissingConfiguration extends ClassificationServiceException
{
public MissingConfiguration(String msgId) { super(msgId); }
}
/** Represents a fatal error due to illegal configuration.
* The configuration was understood by the server, but was rejected as illegal. */
public static class IllegalConfiguration extends ClassificationServiceException
{
public IllegalConfiguration(String msgId) { super(msgId); }
}
/** Represents a fatal error due to malformed configuration.
* The configuration could not be understood by the server. */
public static class MalformedConfiguration extends ClassificationServiceException
{
public MalformedConfiguration(String msgId) { super(msgId); }
public MalformedConfiguration(String msgId, Throwable cause) { super(msgId, cause); }
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MissingConfiguration;
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
/**
* @author Neil Mc Erlean
* @since 3.0
*/
public class ClassificationServiceImpl extends ServiceBaseImpl
implements ClassificationService
{
private static Log logger = LogFactory.getLog(ClassificationServiceImpl.class);
private static final String[] ATTRIBUTE_KEYS = new String[] { "org.alfresco",
"module.org_alfresco_module_rm",
"classification.levels" };
public static final String DEFAULT_CONFIG_LOCATION =
"/alfresco/module/org_alfresco_module_rm/classification/rm-classification-levels.json";
private AttributeService attributeService; // TODO What about other code (e.g. REST API) accessing the AttrService?
/** The classification levels currently configured in this server. */
private List<ClassificationLevel> configuredLevels;
private Configuration config = new Configuration(DEFAULT_CONFIG_LOCATION);
public void setAttributeService(AttributeService service) { this.attributeService = service; }
public void initConfiguredClassificationLevels()
{
final List<ClassificationLevel> allPersistedLevels = getPersistedLevels();
final List<ClassificationLevel> configuredLevels = getConfiguredLevels();
if (logger.isDebugEnabled())
{
// Note! We cannot log the level names or even the size of these lists for security reasons.
logger.debug("Persisted classification levels: " + loggableStatusOf(allPersistedLevels));
logger.debug("Configured classification levels: " + loggableStatusOf(configuredLevels));
}
if (configuredLevels == null || configuredLevels.isEmpty())
{
throw new MissingConfiguration("Classification level configuration is missing.");
}
else if ( !configuredLevels.equals(allPersistedLevels))
{
attributeService.setAttribute((Serializable) configuredLevels, ATTRIBUTE_KEYS);
this.configuredLevels = configuredLevels;
}
else
{
this.configuredLevels = allPersistedLevels;
}
}
/** Helper method for debug-logging of sensitive lists. */
private String loggableStatusOf(List<?> l)
{
if (l == null) { return "null"; }
else if (l.isEmpty()) { return "empty"; }
else { return "non-empty"; }
}
/**
* Gets the list (in descending order) of classification levels - as persisted in the system.
* @return the list of classification levels if they have been persisted, else {@code null}.
*/
List<ClassificationLevel> getPersistedLevels() {
return authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<List<ClassificationLevel>>()
{
@Override public List<ClassificationLevel> doWork() throws Exception
{
return (List<ClassificationLevel>) attributeService.getAttribute(ATTRIBUTE_KEYS);
}
});
}
/** Gets the list (in descending order) of classification levels - as defined in the system configuration. */
List<ClassificationLevel> getConfiguredLevels()
{
return config.getConfiguredLevels();
}
@Override
public List<ClassificationLevel> getApplicableLevels()
{
return configuredLevels == null ? Collections.<ClassificationLevel>emptyList() :
Collections.unmodifiableList(configuredLevels);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MalformedConfiguration;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class is responsible for providing the configured classification levels, dealing with
* JSON schema as part of that.
*
* @author Neil Mc Erlean
* @since 3.0
*/
class Configuration
{
public final String configLocation;
public Configuration(String classpathLocation)
{
this.configLocation = classpathLocation;
}
/**
* Gets the list (in descending order) of classification levels - as defined in the system configuration.
*
* @return the configured classification levels in descending order, or an empty list if there are none.
*/
public List<ClassificationLevel> getConfiguredLevels()
{
List<ClassificationLevel> result;
try (final InputStream in = this.getClass().getResourceAsStream(configLocation))
{
if ( in == null) { result = Collections.emptyList(); }
else
{
final String jsonString = IOUtils.toString(in);
final JSONArray jsonArray = new JSONArray(new JSONTokener(jsonString));
result = new ArrayList<>(jsonArray.length());
for (int i = 0; i < jsonArray.length(); i++) {
final JSONObject nextObj = jsonArray.getJSONObject(i);
final String name = nextObj.getString("name");
final String displayLabelKey = nextObj.getString("displayLabel");
result.add(new ClassificationLevel(name, displayLabelKey));
}
}
}
catch (IOException | JSONException e)
{
throw new MalformedConfiguration("Could not read classification level configuration", e);
}
return result;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* This package contains the various types required for the 'Classified Records' feature.
*
* @since 3.0
*/
package org.alfresco.module.org_alfresco_module_rm.classification;