/* * Part of the CCNx Java Library. * * Copyright (C) 2010-2012 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library 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 this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.profiles.security.access; import java.io.IOException; import java.security.InvalidKeyException; import java.util.ArrayList; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.config.ConfigurationException; import org.ccnx.ccn.impl.CCNFlowControl.SaveType; import org.ccnx.ccn.impl.encoding.CCNProtocolDTags; import org.ccnx.ccn.impl.encoding.GenericXMLEncodable; import org.ccnx.ccn.impl.encoding.XMLDecoder; import org.ccnx.ccn.impl.encoding.XMLEncoder; import org.ccnx.ccn.io.ErrorStateException; import org.ccnx.ccn.io.content.CCNEncodableObject; import org.ccnx.ccn.io.content.ContentDecodingException; import org.ccnx.ccn.io.content.ContentEncodingException; import org.ccnx.ccn.io.content.ContentGoneException; import org.ccnx.ccn.io.content.ContentNotReadyException; import org.ccnx.ccn.io.content.KeyValueSet; import org.ccnx.ccn.profiles.namespace.NamespaceProfile; import org.ccnx.ccn.profiles.namespace.ParameterizedName; import org.ccnx.ccn.profiles.namespace.PolicyMarker; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; /** * We identify policies that apply to a given namespace (subtree of the name tree) by * placing policy markers, as data, into that namespace. (Questions of how to authenticate * these markers is up to the policy and namespace; they are signed as regular CCNx data * and authentication policies can be based on signer information.) * * This class specifies a policy marker used to indicat that a given namespace is under * access control, and to specify what access control scheme should be used to protect * and retrieve data in that namespace (questions of whether organizing access control * by namespace are left to future work). This object contains a small amount of data -- * the access control profile used for the namespace (a string, used to index into a * map of classes implementing that policy string), a set of ParameterizedName, defining * mappings from strings to names within this namespace of interest to a given access control * scheme (e.g. a prefix where access control groups might be defined, etc), and then a * KeyValueSet of other, arbitrary parameters, for use by an access control scheme to store * additional policy information that it requires. */ public class AccessControlPolicyMarker extends GenericXMLEncodable implements PolicyMarker { ProfileName _profileName; ArrayList<ParameterizedName> _parameterizedNames = new ArrayList<ParameterizedName>(); KeyValueSet _parameters; public static class AccessControlPolicyMarkerObject extends CCNEncodableObject<AccessControlPolicyMarker> { public AccessControlPolicyMarkerObject(ContentName name, CCNHandle handle) throws IOException { super(AccessControlPolicyMarker.class, true, name, handle); } public AccessControlPolicyMarkerObject(ContentName name, AccessControlPolicyMarker r, SaveType saveType, CCNHandle handle) throws IOException { super(AccessControlPolicyMarker.class, true, name, r, saveType, handle); } public AccessControlPolicyMarkerObject(ContentObject firstBlock, CCNHandle handle) throws ContentDecodingException, IOException { super(AccessControlPolicyMarker.class, true, firstBlock, handle); } public ContentName namespace() { return _baseName.cut(_baseName.count()-2); } public AccessControlPolicyMarker policy() throws ContentNotReadyException, ContentGoneException, ErrorStateException { return data(); } } public static class ProfileName extends ContentName { private static final long serialVersionUID = 7724253492801976571L; public ProfileName(ContentName other) { super(other); } public ProfileName() {} @Override public long getElementLabel() { return CCNProtocolDTags.ProfileName; } } /** * Set up a part of the namespace to be under access control. * This method writes the root block to a repository. Type-specific * initialization (e.g. writing ACLs) needs to be handled by the appropriate * subclass. * @param name The top of the namespace to be under access control * @throws IOException * @throws ConfigurationException */ public static void create(ContentName name, SaveType saveType, CCNHandle handle) throws IOException { AccessControlPolicyMarker r = new AccessControlPolicyMarker(); ContentName policyPrefix = NamespaceProfile.policyNamespace(name); ContentName policyMarkerName = AccessControlProfile.getAccessControlPolicyName(policyPrefix); AccessControlPolicyMarkerObject ro = new AccessControlPolicyMarkerObject(policyMarkerName, r, saveType, handle); ro.save(); } /** * Set up a part of the namespace to be under access control. * This method writes the root block to a repository. * This needs to be generic, and can't * know about particular access control types. It will make and initialize * an access control manager of the appropriate type for this namespace, * load it into the search path, and hand it back. Type-specific initialization * must be done by the caller. * @param name The top of the namespace to be under access control * @param parameterizedNames * @param parameters * @param saveType * @param handle * @throws IOException * @throws ConfigurationException */ public static AccessControlManager create(ContentName name, ContentName profileName, ArrayList<ParameterizedName> parameterizedNames, KeyValueSet parameters, SaveType saveType, CCNHandle handle) throws IOException, InvalidKeyException { AccessControlPolicyMarker r = new AccessControlPolicyMarker(profileName, parameterizedNames, parameters); ContentName policyPrefix = NamespaceProfile.policyNamespace(name); ContentName policyMarkerName = AccessControlProfile.getAccessControlPolicyName(policyPrefix); AccessControlPolicyMarkerObject ro = new AccessControlPolicyMarkerObject(policyMarkerName, r, saveType, handle); ro.save(); AccessControlManager acm; try { acm = AccessControlManager.createAccessControlManager(ro, handle); } catch (InstantiationException e) { throw new IOException("Cannot create access control manager of type " + profileName + ": " + e); } catch (IllegalAccessException e) { throw new IOException("Cannot create access control manager of type " + profileName + ": " + e); } handle.keyManager().rememberAccessControlManager(acm); return acm; } public AccessControlPolicyMarker(ContentName profileName) { _profileName = new ProfileName(profileName); } public AccessControlPolicyMarker(ContentName profileName, ArrayList<ParameterizedName> parameterizedNames, KeyValueSet parameters) { this(profileName); _parameterizedNames.addAll(parameterizedNames); _parameters = parameters; } public AccessControlPolicyMarker() {} public void addParameterizedName(ParameterizedName name) { _parameterizedNames.add(name); } public ContentName profileName() { return _profileName; } public ArrayList<ParameterizedName> parameterizedNames() { return _parameterizedNames; } public KeyValueSet parameters() { return _parameters; } public boolean emptyParameters() { return (null == parameters()); } @Override public void decode(XMLDecoder decoder) throws ContentDecodingException { decoder.readStartElement(getElementLabel()); _profileName = new ProfileName(); _profileName.decode(decoder); while (decoder.peekStartElement(CCNProtocolDTags.ParameterizedName)) { ParameterizedName pn = new ParameterizedName(); pn.decode(decoder); _parameterizedNames.add(pn); } if (decoder.peekStartElement(CCNProtocolDTags.Parameters)) { _parameters = new KeyValueSet(); _parameters.decode(decoder); } decoder.readEndElement(); } @Override public void encode(XMLEncoder encoder) throws ContentEncodingException { if (!validate()) { throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing."); } encoder.writeStartElement(getElementLabel()); profileName().encode(encoder); // not technically thread-safe, but odds we get used from more than one thread low.... // make caller lock for (ParameterizedName pn : parameterizedNames()) { pn.encode(encoder); } if (!emptyParameters()) { parameters().encode(encoder); } encoder.writeEndElement(); } @Override public boolean validate() { return (null != _profileName); } @Override public long getElementLabel() { return CCNProtocolDTags.Root; } }