LDAP-273: Converted to slf4j
This commit is contained in:
@@ -1,161 +1,161 @@
|
||||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ldap.core;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import sun.misc.BASE64Encoder;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Extends {@link javax.naming.directory.BasicAttributes} to add specialized support
|
||||
* for DNs.
|
||||
* <p>
|
||||
* While DNs appear to be and can be treated as attributes, they have a special
|
||||
* meaning in that they define the address to which the object is bound. DNs must
|
||||
* conform to special formatting rules and are typically required to be handled
|
||||
* separately from other attributes.
|
||||
* <p>
|
||||
* This class makes this distinction between the DN and other
|
||||
* attributes prominent and apparent.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class LdapAttributes extends BasicAttributes {
|
||||
|
||||
private static final long serialVersionUID = 97903297123869138L;
|
||||
|
||||
private static Log log = LogFactory.getLog(LdapAttributes.class);
|
||||
|
||||
private static final String SAFE_CHAR = "[\\p{ASCII}&&[^\\x00\\x0A\\x0D]]"; //Any ASCII except NUL, LF, and CR
|
||||
|
||||
private static final String SAFE_INIT_CHAR = "[\\p{ASCII}&&[^ \\x00\\x0A\\x0D\\x3A\\x3C]]"; //Any ASCII except NUL, LF, CR, SPACE, colon, and less-than
|
||||
|
||||
/**
|
||||
* Distinguished name to which the object is bound.
|
||||
*/
|
||||
protected LdapName dn = LdapUtils.emptyLdapName();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public LdapAttributes() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for specifying whether or not the object is case sensitive.
|
||||
*
|
||||
* @param ignoreCase boolean indicator.
|
||||
*/
|
||||
public LdapAttributes(boolean ignoreCase) {
|
||||
super(ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distinguished name to which the object is bound.
|
||||
*
|
||||
* @return {@link org.springframework.ldap.core.DistinguishedName} specifying the name to which the object is bound.
|
||||
* @deprecated {@link DistinguishedName and associated classes and methods are deprecated as of 2.0}.
|
||||
* use {@link #getName()} instead.
|
||||
*/
|
||||
public DistinguishedName getDN() {
|
||||
return new DistinguishedName(dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distinguished name to which the object is bound.
|
||||
*
|
||||
* @return {@link LdapName} specifying the name to which the object is bound.
|
||||
*/
|
||||
public LdapName getName() {
|
||||
return LdapUtils.newLdapName(dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the distinguished name of the object.
|
||||
*
|
||||
* @param dn {@link org.springframework.ldap.core.DistinguishedName} specifying the name to which the object is bound.
|
||||
* @deprecated {@link DistinguishedName and associated classes and methods are deprecated as of 2.0}.
|
||||
* use {@link #setName(javax.naming.Name)} instead.
|
||||
*/
|
||||
public void setDN(DistinguishedName dn) {
|
||||
this.dn = LdapUtils.newLdapName(dn);
|
||||
}
|
||||
|
||||
public void setName(Name name) {
|
||||
this.dn = LdapUtils.newLdapName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object in LDIF format.
|
||||
*
|
||||
* @return {@link java.lang.String} formated to RFC2849 LDIF specifications.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
try {
|
||||
|
||||
LdapName dn = getName();
|
||||
|
||||
if (!dn.toString().matches(SAFE_INIT_CHAR + SAFE_CHAR + "*")) {
|
||||
sb.append("dn:: " + new BASE64Encoder().encode(dn.toString().getBytes()) + "\n");
|
||||
} else {
|
||||
sb.append("dn: " + getDN() + "\n");
|
||||
}
|
||||
|
||||
NamingEnumeration<Attribute> attributes = getAll();
|
||||
|
||||
while (attributes.hasMore()) {
|
||||
Attribute attribute = attributes.next();
|
||||
NamingEnumeration<?> values = attribute.getAll();
|
||||
|
||||
while (values.hasMore()) {
|
||||
Object value = values.next();
|
||||
|
||||
if (value instanceof String)
|
||||
sb.append(attribute.getID() + ": " + (String) value + "\n");
|
||||
|
||||
else if (value instanceof byte[])
|
||||
sb.append(attribute.getID() + ":: " + new BASE64Encoder().encode((byte[]) value) + "\n");
|
||||
|
||||
else if (value instanceof URI)
|
||||
sb.append(attribute.getID() + ":< " + (URI) value + "\n");
|
||||
|
||||
else {
|
||||
sb.append(attribute.getID() + ": " + value + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NamingException e) {
|
||||
log.error("Error formating attributes for output.", e);
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ldap.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import sun.misc.BASE64Encoder;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Extends {@link javax.naming.directory.BasicAttributes} to add specialized support
|
||||
* for DNs.
|
||||
* <p>
|
||||
* While DNs appear to be and can be treated as attributes, they have a special
|
||||
* meaning in that they define the address to which the object is bound. DNs must
|
||||
* conform to special formatting rules and are typically required to be handled
|
||||
* separately from other attributes.
|
||||
* <p>
|
||||
* This class makes this distinction between the DN and other
|
||||
* attributes prominent and apparent.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class LdapAttributes extends BasicAttributes {
|
||||
|
||||
private static final long serialVersionUID = 97903297123869138L;
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(LdapAttributes.class);
|
||||
|
||||
private static final String SAFE_CHAR = "[\\p{ASCII}&&[^\\x00\\x0A\\x0D]]"; //Any ASCII except NUL, LF, and CR
|
||||
|
||||
private static final String SAFE_INIT_CHAR = "[\\p{ASCII}&&[^ \\x00\\x0A\\x0D\\x3A\\x3C]]"; //Any ASCII except NUL, LF, CR, SPACE, colon, and less-than
|
||||
|
||||
/**
|
||||
* Distinguished name to which the object is bound.
|
||||
*/
|
||||
protected LdapName dn = LdapUtils.emptyLdapName();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public LdapAttributes() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for specifying whether or not the object is case sensitive.
|
||||
*
|
||||
* @param ignoreCase boolean indicator.
|
||||
*/
|
||||
public LdapAttributes(boolean ignoreCase) {
|
||||
super(ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distinguished name to which the object is bound.
|
||||
*
|
||||
* @return {@link org.springframework.ldap.core.DistinguishedName} specifying the name to which the object is bound.
|
||||
* @deprecated {@link DistinguishedName and associated classes and methods are deprecated as of 2.0}.
|
||||
* use {@link #getName()} instead.
|
||||
*/
|
||||
public DistinguishedName getDN() {
|
||||
return new DistinguishedName(dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distinguished name to which the object is bound.
|
||||
*
|
||||
* @return {@link LdapName} specifying the name to which the object is bound.
|
||||
*/
|
||||
public LdapName getName() {
|
||||
return LdapUtils.newLdapName(dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the distinguished name of the object.
|
||||
*
|
||||
* @param dn {@link org.springframework.ldap.core.DistinguishedName} specifying the name to which the object is bound.
|
||||
* @deprecated {@link DistinguishedName and associated classes and methods are deprecated as of 2.0}.
|
||||
* use {@link #setName(javax.naming.Name)} instead.
|
||||
*/
|
||||
public void setDN(DistinguishedName dn) {
|
||||
this.dn = LdapUtils.newLdapName(dn);
|
||||
}
|
||||
|
||||
public void setName(Name name) {
|
||||
this.dn = LdapUtils.newLdapName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object in LDIF format.
|
||||
*
|
||||
* @return {@link java.lang.String} formated to RFC2849 LDIF specifications.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
try {
|
||||
|
||||
LdapName dn = getName();
|
||||
|
||||
if (!dn.toString().matches(SAFE_INIT_CHAR + SAFE_CHAR + "*")) {
|
||||
sb.append("dn:: " + new BASE64Encoder().encode(dn.toString().getBytes()) + "\n");
|
||||
} else {
|
||||
sb.append("dn: " + getDN() + "\n");
|
||||
}
|
||||
|
||||
NamingEnumeration<Attribute> attributes = getAll();
|
||||
|
||||
while (attributes.hasMore()) {
|
||||
Attribute attribute = attributes.next();
|
||||
NamingEnumeration<?> values = attribute.getAll();
|
||||
|
||||
while (values.hasMore()) {
|
||||
Object value = values.next();
|
||||
|
||||
if (value instanceof String)
|
||||
sb.append(attribute.getID() + ": " + (String) value + "\n");
|
||||
|
||||
else if (value instanceof byte[])
|
||||
sb.append(attribute.getID() + ":: " + new BASE64Encoder().encode((byte[]) value) + "\n");
|
||||
|
||||
else if (value instanceof URI)
|
||||
sb.append(attribute.getID() + ":< " + (URI) value + "\n");
|
||||
|
||||
else {
|
||||
sb.append(attribute.getID() + ": " + value + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NamingException e) {
|
||||
log.error("Error formating attributes for output.", e);
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,367 +1,367 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.ldif.parser;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.ldap.core.LdapAttributes;
|
||||
import org.springframework.ldap.ldif.InvalidRecordFormatException;
|
||||
import org.springframework.ldap.ldif.support.AttributeValidationPolicy;
|
||||
import org.springframework.ldap.ldif.support.DefaultAttributeValidationPolicy;
|
||||
import org.springframework.ldap.ldif.support.LineIdentifier;
|
||||
import org.springframework.ldap.ldif.support.SeparatorPolicy;
|
||||
import org.springframework.ldap.schema.DefaultSchemaSpecification;
|
||||
import org.springframework.ldap.schema.Specification;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* The {@link LdifParser LdifParser} is the main class of the {@link org.springframework.ldap.ldif} package.
|
||||
* This class reads lines from a resource and assembles them into an {@link LdapAttributes LdapAttributes} object.
|
||||
* The {@link LdifParser LdifParser} does ignores <i>changetype</i> LDIF entries as their usefulness in the
|
||||
* context of an application has yet to be determined.
|
||||
* <p>
|
||||
* <b>Design</b><br/>
|
||||
* {@link LdifParser LdifParser} provides the main interface for operation but requires three supporting classes to
|
||||
* enable operation:
|
||||
* <ul>
|
||||
* <li>{@link SeparatorPolicy SeparatorPolicy} - establishes the mechanism by which lines are assembled into attributes.</li>
|
||||
* <li>{@link AttributeValidationPolicy AttributeValidationPolicy} - ensures that attributes are correctly structured prior to parsing.</li>
|
||||
* <li>{@link Specification Specification} - provides a mechanism by which object structure can be validated after assembly.</li>
|
||||
* </ul>
|
||||
* Together, these 4 classes read from the resource line by line and translate the data into objects for use.
|
||||
* <p>
|
||||
* <b>Usage</b><br/>
|
||||
* {@link #getRecord() getRecord()} reads the next available record from the resource. Lines are read and
|
||||
* passed to the {@link SeparatorPolicy SeparatorPolicy} for interpretation. The parser continues to read
|
||||
* lines and appends them to the buffer until it encounters the start of a new attribute or an end of record
|
||||
* delimiter. When the new attribute or end of record is encountered, the buffer is passed to the
|
||||
* {@link AttributeValidationPolicy AttributeValidationPolicy} which ensures the buffer conforms to a valid
|
||||
* attribute definition as defined in RFC2849 and returns an {@link org.springframework.ldap.core.LdapAttribute LdapAttribute} object
|
||||
* which is then added to the record, an {@link LdapAttributes LdapAttributes} object. Upon encountering the
|
||||
* end of record, the record is validated by the {@link Specification Specification} policy and,
|
||||
* if valid, returned to the requester.
|
||||
* <p>
|
||||
* <i>NOTE: By default, objects are not validated. If validation is required,
|
||||
* an appropriate specification object must be set.</i>
|
||||
* <p>
|
||||
* The parser requires the resource to be {@link #open() open()} prior to an invocation of {@link #getRecord() getRecord()}.
|
||||
* {@link #hasMoreRecords() hasMoreRecords()} can be used to loop over the resource until all records have been
|
||||
* retrieved. Likewise, the {@link #reset() reset()} method will reset the resource.
|
||||
* <p>
|
||||
* Objects implementing the {@link javax.naming.directory.Attributes Attributes} interface are required to support a case sensitivity setting
|
||||
* which controls whether or not the attribute IDs of the object are case sensitive. The {@link #caseInsensitive caseInsensitive}
|
||||
* setting of the {@link LdifParser LdifParser} is passed to the constructor of any {@link javax.naming.directory.Attributes Attributes} created. The
|
||||
* default value for this setting is true so that case insensitive objects are created.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class LdifParser implements Parser, InitializingBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LdifParser.class);
|
||||
|
||||
/**
|
||||
* The resource to parse.
|
||||
*/
|
||||
private Resource resource;
|
||||
|
||||
/**
|
||||
* A BufferedReader to read the file.
|
||||
*/
|
||||
private BufferedReader reader;
|
||||
|
||||
/**
|
||||
* The SeparatorPolicy to use for interpreting attributes from the lines of the resource.
|
||||
*/
|
||||
private SeparatorPolicy separatorPolicy = new SeparatorPolicy();
|
||||
|
||||
/**
|
||||
* The AttributeValidationPolicy to use to interpret attributes.
|
||||
*/
|
||||
private AttributeValidationPolicy attributePolicy = new DefaultAttributeValidationPolicy();
|
||||
|
||||
/**
|
||||
* The RecordSpecification for validating records produced.
|
||||
*/
|
||||
private Specification<LdapAttributes> specification = new DefaultSchemaSpecification();
|
||||
|
||||
/**
|
||||
* This setting is used to control the case sensitivity of LdapAttribute objects returned by the parser.
|
||||
*/
|
||||
private boolean caseInsensitive = true;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public LdifParser() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LdifParser with the indicated case sensitivity setting.
|
||||
*
|
||||
* @param caseInsensitive Case sensitivity setting for LdapAttributes objects returned by the parser.
|
||||
*/
|
||||
public LdifParser(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an LdifParser for the specified resource with the provided case sensitivity setting.
|
||||
*
|
||||
* @param resource The resource to parse.
|
||||
* @param caseInsensitive Case sensitivity setting for LdapAttributes objects returned by the parser.
|
||||
*/
|
||||
public LdifParser(Resource resource, boolean caseInsensitive) {
|
||||
this.resource = resource;
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor for resource specification.
|
||||
*
|
||||
* @param resource The resource to parse.
|
||||
*/
|
||||
public LdifParser(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor: accepts a File object.
|
||||
*
|
||||
* @param file The file to parse.
|
||||
*/
|
||||
public LdifParser(File file) {
|
||||
this.resource = new FileSystemResource(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the separator policy.
|
||||
*
|
||||
* The default separator policy should suffice for most needs.
|
||||
*
|
||||
* @param separatorPolicy Separator policy.
|
||||
*/
|
||||
public void setSeparatorPolicy(SeparatorPolicy separatorPolicy) {
|
||||
this.separatorPolicy = separatorPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Policy object enforcing the rules for acceptable attributes.
|
||||
*
|
||||
* @param avPolicy Attribute validation policy.
|
||||
*/
|
||||
public void setAttributeValidationPolicy(AttributeValidationPolicy avPolicy) {
|
||||
this.attributePolicy = avPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Policy object for enforcing rules to acceptable LDAP objects.
|
||||
*
|
||||
* This policy may be used to enforce schema restrictions.
|
||||
* @param specification
|
||||
*/
|
||||
public void setRecordSpecification(Specification<LdapAttributes> specification) {
|
||||
this.specification = specification;
|
||||
}
|
||||
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public void setCaseInsensitive(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
public void open() throws IOException {
|
||||
Assert.notNull(resource, "Resource must be set.");
|
||||
reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
|
||||
}
|
||||
|
||||
public boolean isReady() throws IOException {
|
||||
return reader.ready();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (resource.isOpen())
|
||||
reader.close();
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
Assert.notNull(reader, "A reader has not been obtained.");
|
||||
reader.reset();
|
||||
}
|
||||
|
||||
public boolean hasMoreRecords() throws IOException {
|
||||
return reader.ready();
|
||||
}
|
||||
|
||||
public LdapAttributes getRecord() throws IOException {
|
||||
Assert.notNull(reader, "A reader must be obtained: parser not open.");
|
||||
|
||||
if (!reader.ready()) {
|
||||
log.debug("Reader not ready!");
|
||||
return null;
|
||||
}
|
||||
|
||||
LdapAttributes record = null;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String line = reader.readLine();
|
||||
|
||||
while(true) {
|
||||
|
||||
LineIdentifier identifier = separatorPolicy.assess(line);
|
||||
|
||||
switch(identifier) {
|
||||
case NewRecord:
|
||||
log.trace("Starting new record.");
|
||||
//Start new record.
|
||||
record = new LdapAttributes(caseInsensitive);
|
||||
builder = new StringBuilder(line);
|
||||
|
||||
break;
|
||||
|
||||
case Control:
|
||||
log.trace("'control' encountered.");
|
||||
|
||||
//Log WARN and discard record.
|
||||
log.warn("LDIF change records have no implementation: record will be ignored.");
|
||||
builder = null;
|
||||
record = null;
|
||||
|
||||
break;
|
||||
|
||||
case ChangeType:
|
||||
log.trace("'changetype' encountered.");
|
||||
|
||||
//Log WARN and discard record.
|
||||
log.warn("LDIF change records have no implementation: record will be ignored.");
|
||||
builder = null;
|
||||
record = null;
|
||||
|
||||
break;
|
||||
|
||||
case Attribute:
|
||||
//flush buffer.
|
||||
addAttributeToRecord(builder.toString(), record);
|
||||
|
||||
log.trace("Starting new attribute.");
|
||||
//Start new attribute.
|
||||
builder = new StringBuilder(line);
|
||||
|
||||
break;
|
||||
|
||||
case Continuation:
|
||||
log.trace("...appending line to buffer.");
|
||||
//Append line to buffer.
|
||||
builder.append(line.replaceFirst(" ", ""));
|
||||
|
||||
break;
|
||||
|
||||
case EndOfRecord:
|
||||
log.trace("...done parsing record. (EndOfRecord)");
|
||||
|
||||
//Validate record and return.
|
||||
if (record == null) return null;
|
||||
else {
|
||||
try {
|
||||
//flush buffer.
|
||||
addAttributeToRecord(builder.toString(), record);
|
||||
|
||||
if (specification.isSatisfiedBy(record)) {
|
||||
log.debug("record parsed:\n" + record);
|
||||
return record;
|
||||
|
||||
} else {
|
||||
throw new InvalidRecordFormatException("Record [dn: " + record.getDN() + "] does not conform to specification.");
|
||||
}
|
||||
} catch(NamingException e) {
|
||||
log.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
//Take no action -- applies to VersionIdentifier, Comments, and voided records.
|
||||
}
|
||||
|
||||
line = reader.readLine();
|
||||
if(line == null && record == null) {
|
||||
//Never encountered a valid record.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addAttributeToRecord(String buffer, LdapAttributes record) {
|
||||
try {
|
||||
if (StringUtils.hasLength(buffer) && record != null) {
|
||||
//Validate previous attribute and add to record.
|
||||
Attribute attribute = attributePolicy.parse(buffer);
|
||||
|
||||
if (attribute.getID().equalsIgnoreCase("dn")) {
|
||||
log.trace("...adding DN to record.");
|
||||
|
||||
String dn;
|
||||
if (attribute.get() instanceof byte[]) {
|
||||
dn = new String((byte[]) attribute.get());
|
||||
} else {
|
||||
dn = (String) attribute.get();
|
||||
}
|
||||
|
||||
record.setName(LdapUtils.newLdapName(dn));
|
||||
|
||||
} else {
|
||||
log.trace("...adding attribute to record.");
|
||||
Attribute attr = record.get(attribute.getID());
|
||||
|
||||
if (attr != null) {
|
||||
attr.add(attribute.get());
|
||||
} else {
|
||||
record.put(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
log.error(e);
|
||||
} catch (NoSuchElementException e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(resource, "A resource to parse is required.");
|
||||
Assert.isTrue(resource.exists(), resource.getDescription() + ": resource does not exist!");
|
||||
Assert.isTrue(resource.isReadable(), "Resource is not readable.");
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.ldif.parser;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.ldap.core.LdapAttributes;
|
||||
import org.springframework.ldap.ldif.InvalidRecordFormatException;
|
||||
import org.springframework.ldap.ldif.support.AttributeValidationPolicy;
|
||||
import org.springframework.ldap.ldif.support.DefaultAttributeValidationPolicy;
|
||||
import org.springframework.ldap.ldif.support.LineIdentifier;
|
||||
import org.springframework.ldap.ldif.support.SeparatorPolicy;
|
||||
import org.springframework.ldap.schema.DefaultSchemaSpecification;
|
||||
import org.springframework.ldap.schema.Specification;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* The {@link LdifParser LdifParser} is the main class of the {@link org.springframework.ldap.ldif} package.
|
||||
* This class reads lines from a resource and assembles them into an {@link LdapAttributes LdapAttributes} object.
|
||||
* The {@link LdifParser LdifParser} does ignores <i>changetype</i> LDIF entries as their usefulness in the
|
||||
* context of an application has yet to be determined.
|
||||
* <p>
|
||||
* <b>Design</b><br/>
|
||||
* {@link LdifParser LdifParser} provides the main interface for operation but requires three supporting classes to
|
||||
* enable operation:
|
||||
* <ul>
|
||||
* <li>{@link SeparatorPolicy SeparatorPolicy} - establishes the mechanism by which lines are assembled into attributes.</li>
|
||||
* <li>{@link AttributeValidationPolicy AttributeValidationPolicy} - ensures that attributes are correctly structured prior to parsing.</li>
|
||||
* <li>{@link Specification Specification} - provides a mechanism by which object structure can be validated after assembly.</li>
|
||||
* </ul>
|
||||
* Together, these 4 classes read from the resource line by line and translate the data into objects for use.
|
||||
* <p>
|
||||
* <b>Usage</b><br/>
|
||||
* {@link #getRecord() getRecord()} reads the next available record from the resource. Lines are read and
|
||||
* passed to the {@link SeparatorPolicy SeparatorPolicy} for interpretation. The parser continues to read
|
||||
* lines and appends them to the buffer until it encounters the start of a new attribute or an end of record
|
||||
* delimiter. When the new attribute or end of record is encountered, the buffer is passed to the
|
||||
* {@link AttributeValidationPolicy AttributeValidationPolicy} which ensures the buffer conforms to a valid
|
||||
* attribute definition as defined in RFC2849 and returns an {@link org.springframework.ldap.core.LdapAttribute LdapAttribute} object
|
||||
* which is then added to the record, an {@link LdapAttributes LdapAttributes} object. Upon encountering the
|
||||
* end of record, the record is validated by the {@link Specification Specification} policy and,
|
||||
* if valid, returned to the requester.
|
||||
* <p>
|
||||
* <i>NOTE: By default, objects are not validated. If validation is required,
|
||||
* an appropriate specification object must be set.</i>
|
||||
* <p>
|
||||
* The parser requires the resource to be {@link #open() open()} prior to an invocation of {@link #getRecord() getRecord()}.
|
||||
* {@link #hasMoreRecords() hasMoreRecords()} can be used to loop over the resource until all records have been
|
||||
* retrieved. Likewise, the {@link #reset() reset()} method will reset the resource.
|
||||
* <p>
|
||||
* Objects implementing the {@link javax.naming.directory.Attributes Attributes} interface are required to support a case sensitivity setting
|
||||
* which controls whether or not the attribute IDs of the object are case sensitive. The {@link #caseInsensitive caseInsensitive}
|
||||
* setting of the {@link LdifParser LdifParser} is passed to the constructor of any {@link javax.naming.directory.Attributes Attributes} created. The
|
||||
* default value for this setting is true so that case insensitive objects are created.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class LdifParser implements Parser, InitializingBean {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LdifParser.class);
|
||||
|
||||
/**
|
||||
* The resource to parse.
|
||||
*/
|
||||
private Resource resource;
|
||||
|
||||
/**
|
||||
* A BufferedReader to read the file.
|
||||
*/
|
||||
private BufferedReader reader;
|
||||
|
||||
/**
|
||||
* The SeparatorPolicy to use for interpreting attributes from the lines of the resource.
|
||||
*/
|
||||
private SeparatorPolicy separatorPolicy = new SeparatorPolicy();
|
||||
|
||||
/**
|
||||
* The AttributeValidationPolicy to use to interpret attributes.
|
||||
*/
|
||||
private AttributeValidationPolicy attributePolicy = new DefaultAttributeValidationPolicy();
|
||||
|
||||
/**
|
||||
* The RecordSpecification for validating records produced.
|
||||
*/
|
||||
private Specification<LdapAttributes> specification = new DefaultSchemaSpecification();
|
||||
|
||||
/**
|
||||
* This setting is used to control the case sensitivity of LdapAttribute objects returned by the parser.
|
||||
*/
|
||||
private boolean caseInsensitive = true;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public LdifParser() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LdifParser with the indicated case sensitivity setting.
|
||||
*
|
||||
* @param caseInsensitive Case sensitivity setting for LdapAttributes objects returned by the parser.
|
||||
*/
|
||||
public LdifParser(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an LdifParser for the specified resource with the provided case sensitivity setting.
|
||||
*
|
||||
* @param resource The resource to parse.
|
||||
* @param caseInsensitive Case sensitivity setting for LdapAttributes objects returned by the parser.
|
||||
*/
|
||||
public LdifParser(Resource resource, boolean caseInsensitive) {
|
||||
this.resource = resource;
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor for resource specification.
|
||||
*
|
||||
* @param resource The resource to parse.
|
||||
*/
|
||||
public LdifParser(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor: accepts a File object.
|
||||
*
|
||||
* @param file The file to parse.
|
||||
*/
|
||||
public LdifParser(File file) {
|
||||
this.resource = new FileSystemResource(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the separator policy.
|
||||
*
|
||||
* The default separator policy should suffice for most needs.
|
||||
*
|
||||
* @param separatorPolicy Separator policy.
|
||||
*/
|
||||
public void setSeparatorPolicy(SeparatorPolicy separatorPolicy) {
|
||||
this.separatorPolicy = separatorPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Policy object enforcing the rules for acceptable attributes.
|
||||
*
|
||||
* @param avPolicy Attribute validation policy.
|
||||
*/
|
||||
public void setAttributeValidationPolicy(AttributeValidationPolicy avPolicy) {
|
||||
this.attributePolicy = avPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Policy object for enforcing rules to acceptable LDAP objects.
|
||||
*
|
||||
* This policy may be used to enforce schema restrictions.
|
||||
* @param specification
|
||||
*/
|
||||
public void setRecordSpecification(Specification<LdapAttributes> specification) {
|
||||
this.specification = specification;
|
||||
}
|
||||
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public void setCaseInsensitive(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
public void open() throws IOException {
|
||||
Assert.notNull(resource, "Resource must be set.");
|
||||
reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
|
||||
}
|
||||
|
||||
public boolean isReady() throws IOException {
|
||||
return reader.ready();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (resource.isOpen())
|
||||
reader.close();
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
Assert.notNull(reader, "A reader has not been obtained.");
|
||||
reader.reset();
|
||||
}
|
||||
|
||||
public boolean hasMoreRecords() throws IOException {
|
||||
return reader.ready();
|
||||
}
|
||||
|
||||
public LdapAttributes getRecord() throws IOException {
|
||||
Assert.notNull(reader, "A reader must be obtained: parser not open.");
|
||||
|
||||
if (!reader.ready()) {
|
||||
log.debug("Reader not ready!");
|
||||
return null;
|
||||
}
|
||||
|
||||
LdapAttributes record = null;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String line = reader.readLine();
|
||||
|
||||
while(true) {
|
||||
|
||||
LineIdentifier identifier = separatorPolicy.assess(line);
|
||||
|
||||
switch(identifier) {
|
||||
case NewRecord:
|
||||
log.trace("Starting new record.");
|
||||
//Start new record.
|
||||
record = new LdapAttributes(caseInsensitive);
|
||||
builder = new StringBuilder(line);
|
||||
|
||||
break;
|
||||
|
||||
case Control:
|
||||
log.trace("'control' encountered.");
|
||||
|
||||
//Log WARN and discard record.
|
||||
log.warn("LDIF change records have no implementation: record will be ignored.");
|
||||
builder = null;
|
||||
record = null;
|
||||
|
||||
break;
|
||||
|
||||
case ChangeType:
|
||||
log.trace("'changetype' encountered.");
|
||||
|
||||
//Log WARN and discard record.
|
||||
log.warn("LDIF change records have no implementation: record will be ignored.");
|
||||
builder = null;
|
||||
record = null;
|
||||
|
||||
break;
|
||||
|
||||
case Attribute:
|
||||
//flush buffer.
|
||||
addAttributeToRecord(builder.toString(), record);
|
||||
|
||||
log.trace("Starting new attribute.");
|
||||
//Start new attribute.
|
||||
builder = new StringBuilder(line);
|
||||
|
||||
break;
|
||||
|
||||
case Continuation:
|
||||
log.trace("...appending line to buffer.");
|
||||
//Append line to buffer.
|
||||
builder.append(line.replaceFirst(" ", ""));
|
||||
|
||||
break;
|
||||
|
||||
case EndOfRecord:
|
||||
log.trace("...done parsing record. (EndOfRecord)");
|
||||
|
||||
//Validate record and return.
|
||||
if (record == null) return null;
|
||||
else {
|
||||
try {
|
||||
//flush buffer.
|
||||
addAttributeToRecord(builder.toString(), record);
|
||||
|
||||
if (specification.isSatisfiedBy(record)) {
|
||||
log.debug("record parsed:\n" + record);
|
||||
return record;
|
||||
|
||||
} else {
|
||||
throw new InvalidRecordFormatException("Record [dn: " + record.getDN() + "] does not conform to specification.");
|
||||
}
|
||||
} catch(NamingException e) {
|
||||
log.error("Error adding attribute to record", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
//Take no action -- applies to VersionIdentifier, Comments, and voided records.
|
||||
}
|
||||
|
||||
line = reader.readLine();
|
||||
if(line == null && record == null) {
|
||||
//Never encountered a valid record.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addAttributeToRecord(String buffer, LdapAttributes record) {
|
||||
try {
|
||||
if (StringUtils.hasLength(buffer) && record != null) {
|
||||
//Validate previous attribute and add to record.
|
||||
Attribute attribute = attributePolicy.parse(buffer);
|
||||
|
||||
if (attribute.getID().equalsIgnoreCase("dn")) {
|
||||
log.trace("...adding DN to record.");
|
||||
|
||||
String dn;
|
||||
if (attribute.get() instanceof byte[]) {
|
||||
dn = new String((byte[]) attribute.get());
|
||||
} else {
|
||||
dn = (String) attribute.get();
|
||||
}
|
||||
|
||||
record.setName(LdapUtils.newLdapName(dn));
|
||||
|
||||
} else {
|
||||
log.trace("...adding attribute to record.");
|
||||
Attribute attr = record.get(attribute.getID());
|
||||
|
||||
if (attr != null) {
|
||||
attr.add(attribute.get());
|
||||
} else {
|
||||
record.put(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
log.error("Error adding attribute to record", e);
|
||||
} catch (NoSuchElementException e) {
|
||||
log.error("Error adding attribute to record", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(resource, "A resource to parse is required.");
|
||||
Assert.isTrue(resource.exists(), resource.getDescription() + ": resource does not exist!");
|
||||
Assert.isTrue(resource.isReadable(), "Resource is not readable.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,366 +1,366 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ldap.ldif.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.core.LdapAttribute;
|
||||
import org.springframework.ldap.ldif.InvalidAttributeFormatException;
|
||||
import org.springframework.util.StringUtils;
|
||||
import sun.misc.BASE64Decoder;
|
||||
|
||||
import javax.naming.directory.Attribute;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Ensures the buffer represents a valid attribute as defined by RFC2849.
|
||||
*
|
||||
* Meets the standards imposed by RFC 2849 for the "LDAP Data Interchange Format (LDIF)
|
||||
* - Technical Specification".
|
||||
*
|
||||
* Special attention is called to URL support: RFC 2849 requires that
|
||||
* LDIFs support URLs as defined in 1738; however, RFC 1738 has been updated by several RFCs including
|
||||
* RFC 1808, RFC 2396, and RFC 3986 (which obsoleted the formers). Unsupported features of this
|
||||
* implementation of URL identification include query strings and fragments in HTTP URLs.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DefaultAttributeValidationPolicy implements AttributeValidationPolicy {
|
||||
|
||||
private static Log log = LogFactory.getLog(DefaultAttributeValidationPolicy.class);
|
||||
|
||||
/**
|
||||
* Pattern Declarations.
|
||||
*/
|
||||
|
||||
//General Definitions
|
||||
private static final String DIGIT = "\\p{Digit}";
|
||||
|
||||
private static final String LOW_ALPHA = "\\p{Lower}";
|
||||
|
||||
private static final String HIGH_ALPHA = "\\p{Upper}";
|
||||
|
||||
private static final String ALPHA = "\\p{Alpha}";
|
||||
|
||||
private static final String ALPHANUM = "\\p{Alnum}";
|
||||
|
||||
private static final String HEX = "\\p{XDigit}";
|
||||
|
||||
private static final String SAFE = "[\\x24\\x2D\\x5F\\x2E\\x2B]"; //$|-|_|.|+
|
||||
|
||||
private static final String EXTRA = "[\\x21\\x2A\\x27\\x7B\\x7D\\x2C]"; //!|*|'|(|)|,
|
||||
|
||||
private static final String PUNCTUATION = "[\\x3C\\x3E\\x23\\x25\\x22]"; //<|>|#|%|"
|
||||
|
||||
private static final String ESCAPE = "%" + HEX + "{2}";
|
||||
|
||||
private static final String RESERVED = "[\\x3B\\x2F\\x3F\\x3A\\x40\\x26\\x3D]"; //;|/|?|:|@|&|=
|
||||
|
||||
private static final String UNRESERVED = "[" + ALPHA + DIGIT + SAFE + EXTRA + "]";
|
||||
|
||||
private static final String UCHAR = "(?:" + UNRESERVED + "|" + ESCAPE + ")";
|
||||
|
||||
private static final String XCHAR = "(?:" + UNRESERVED + "|" + RESERVED + "|" + ESCAPE + ")";
|
||||
|
||||
private static final String DIGITS = DIGIT + "+";
|
||||
|
||||
//Standard LDAP Attribute Definitions
|
||||
private static final String ATTRIBUTE_SEPARATOR = ":";
|
||||
|
||||
private static final String OPTION_SEPARATOR = ";";
|
||||
|
||||
private static final String BASE64_INDICATOR = ":";
|
||||
|
||||
private static final String URL_INDICATOR = "<";
|
||||
|
||||
private static final String ATTRIBUTE_TYPE_CHARS = ALPHA + DIGIT + "-";
|
||||
|
||||
private static final String LDAP_OID = "[[0-9]|[1-9][0-9]+][\\.(?:[0-9]|[1-9][0-9]+)]+";
|
||||
|
||||
private static final String OPTION = "[" + ATTRIBUTE_TYPE_CHARS + "]+";
|
||||
|
||||
private static final String OPTIONS = "[" + OPTION_SEPARATOR + OPTION + "]*";
|
||||
|
||||
private static final String ATTRIBUTE_TYPE = LDAP_OID + "|" + ALPHANUM + "[" + ATTRIBUTE_TYPE_CHARS + "]*";
|
||||
|
||||
private static final String ATTRIBUTE_DESCRIPTION = "(" + ATTRIBUTE_TYPE + ")(" + OPTIONS + ")";
|
||||
|
||||
private static final String SAFE_CHAR = "[\\p{ASCII}&&[^\\x00\\x0A\\x0D]]"; //Any ASCII except NUL, LF, and CR
|
||||
|
||||
private static final String SAFE_INIT_CHAR = "[\\p{ASCII}&&[^ \\x00\\x0A\\x0D\\x3A\\x3C]]"; //Any ASCII except NUL, LF, CR, SPACE, colon, and less-than
|
||||
|
||||
private static final String SAFE_STRING = "(" + SAFE_INIT_CHAR + SAFE_CHAR + "*)";
|
||||
|
||||
private static final String FILL = "[ ]*"; //Any number of spaces
|
||||
|
||||
//BASE64 Definitions
|
||||
private static final String BASE64_CHAR = "[\\x2B\\x2F\\x30-\\x39\\x3D\\x41-\\x5A\\x61-\\x7A]"; //+, /, 0-9, -, A-Z, a-z
|
||||
|
||||
private static final String BASE64_STRING = "(" + BASE64_CHAR + "*)";
|
||||
|
||||
//URL Components
|
||||
private static final String USER = "[" + UCHAR + "\\x3B\\x3F\\x26\\x3D]*"; //UCHAR|;|?|&|=
|
||||
|
||||
private static final String PASSWORD = "[" + UCHAR + "\\x3B\\x3F\\x26\\x3D]*"; //UCHAR|;|?|&|=
|
||||
|
||||
private static final String DOMAINLABEL = ALPHANUM + "|" + ALPHANUM + "[" + ALPHANUM + "-]*" + ALPHANUM;
|
||||
|
||||
private static final String TOPLABEL = ALPHA + "|" + ALPHA + "[" + ALPHANUM + "-]*" + ALPHANUM;
|
||||
|
||||
private static final String HOSTNAME = "(?:" + DOMAINLABEL + "\\.)*" + TOPLABEL;
|
||||
|
||||
private static final String IPADDRESS = "(?:" + DIGIT + "{1,3}\\.){3}" + DIGIT + "{1,3}";
|
||||
|
||||
private static final String HOST = "(?:" + HOSTNAME + "|" + IPADDRESS + ")";
|
||||
|
||||
private static final String PORT = DIGITS;
|
||||
|
||||
private static final String HOSTPORT = HOST + "(?::" + PORT + ")?";
|
||||
|
||||
private static final String URLPATH = XCHAR + "*";
|
||||
|
||||
private static final String LOGIN = "(?:" + USER + "(?::" + PASSWORD + ")?@)?" + HOSTPORT;
|
||||
|
||||
//URL Definitions
|
||||
private static final String SCHEME = "[" + LOW_ALPHA + DIGIT + "\\x2B\\x2D\\x2E]+";
|
||||
|
||||
private static final String IP_SCHEMEPART = "//" + LOGIN + "(?:/" + URLPATH + ")?";
|
||||
|
||||
private static final String SCHEMEPART = "(?:" + XCHAR + "*|" + IP_SCHEMEPART + ")";
|
||||
|
||||
private static final String GENERIC_URL = SCHEME + ":" + SCHEMEPART;
|
||||
|
||||
//HTTP Definition
|
||||
private static final String HSEGMENT = "[" + UCHAR + "\\x3A\\x3B\\x26\\x3D\\x40]*"; //UCHAR|:|;|&|=|@
|
||||
|
||||
private static final String HPATH = HSEGMENT + "[/" + HSEGMENT + "]*";
|
||||
|
||||
private static final String SEARCH = HSEGMENT;
|
||||
|
||||
private static final String HTTP_URL = "http://" + HOSTPORT + "(?:/" + HPATH + "(?:\\x3F" + SEARCH + ")?)?";
|
||||
|
||||
//FTP
|
||||
private static final String FSEGMENT = "[" + UCHAR + "\\x3F\\x3A\\x26\\x3D\\x40]*"; //UCHAR|?|:|&|=|@
|
||||
|
||||
private static final String FPATH = FSEGMENT + "[/" + FSEGMENT + "]*";
|
||||
|
||||
private static final String FTPTYPE = "[AIDaid]";
|
||||
|
||||
private static final String FTP_URL = "ftp://" + LOGIN + "(?:/" + FPATH + "(?:;type=" + FTPTYPE + ")?)?";
|
||||
|
||||
//NEWS
|
||||
private static final String GROUP = ALPHA + "[" + ALPHA + DIGIT + "\\x2D\\x2E\\x2B\\x5F]*"; //ALPHA [ALPHA|DIGIT|-|.|+|_]*
|
||||
|
||||
private static final String ARTICLE = "[" + UCHAR + "\\x3A\\x3B\\x2F\\x3F\\x26\\x3D]@" + HOST; //[UCHAR|;|/|?|:|&|=]@HOST
|
||||
|
||||
private static final String GROUPPART = "(?:\\x2A|" + GROUP + "|" + ARTICLE + ")";
|
||||
|
||||
private static final String NEWS_URL = "news:" + GROUPPART;
|
||||
|
||||
//NNTP
|
||||
private static final String NNTP_URL = "nntp://" + HOSTPORT + "/" + GROUP + "/" + DIGITS;
|
||||
|
||||
//TELNET
|
||||
private static final String TELNET_URL = "telnet://" + LOGIN + "[/]?";
|
||||
|
||||
//GOPHER
|
||||
private static final String GTYPE = XCHAR;
|
||||
|
||||
private static final String SELECTOR = XCHAR + "*";
|
||||
|
||||
private static final String GOPHER_STRING = XCHAR + "*";
|
||||
|
||||
private static final String GOPHER_URL = "gopher://" + HOSTPORT + "(?:/(?:" + GTYPE + "(?:" + SELECTOR + "(?:%09" + SEARCH + "(?:%09" + GOPHER_STRING + ")?)?)?)?)?";
|
||||
|
||||
//WAIS
|
||||
private static final String WPATH = UCHAR + "*";
|
||||
|
||||
private static final String WTYPE = UCHAR + "*";
|
||||
|
||||
private static final String DATABASE = UCHAR + "*";
|
||||
|
||||
private static final String WAIS_DOC = "wais://" + HOSTPORT + "/" + DATABASE + "/" + WTYPE + "/" + WPATH;
|
||||
|
||||
private static final String WAIS_INDEX = "wais://" + HOSTPORT + "/" + DATABASE + "\\?" + SEARCH;
|
||||
|
||||
private static final String WAIS_DATABASE = "wais://" + HOSTPORT + "/" + DATABASE;
|
||||
|
||||
private static final String WAIS_URL = WAIS_DATABASE + "|" + WAIS_INDEX + "|" + WAIS_DOC;
|
||||
|
||||
//MAILTO
|
||||
private static final String ENCODED_822_ADDR = XCHAR + "+";
|
||||
|
||||
private static final String MAILTO_URL = "mailto:" + ENCODED_822_ADDR;
|
||||
|
||||
//FILE
|
||||
private static final String FILE_URL = "file://(?:" + HOST + "|localhost)?/" + FPATH ;
|
||||
|
||||
//PROPERO
|
||||
private static final String FIELD_VALUE = "[" + UCHAR + "\\x3F\\x3A\\x40\\x26]*"; //[UCHAR|?|:|@|&]*
|
||||
|
||||
private static final String FIELD_NAME = "[" + UCHAR + "\\x3F\\x3A\\x40\\x26]*"; //[UCHAR|?|:|@|&]*
|
||||
|
||||
private static final String FIELD_SPEC = ";" + FIELD_NAME + "=" + FIELD_VALUE;
|
||||
|
||||
private static final String PSEGMENT = "[" + UCHAR + "\\x3F\\x3A\\x40\\x26\\x3D]*"; //[UCHAR|?|:|@|&|=]*
|
||||
|
||||
private static final String PPATH = PSEGMENT + "(?:/" + PSEGMENT + ")*";
|
||||
|
||||
private static final String PROSPERO_URL = "prospero://" + HOSTPORT + "/" + PPATH + "(?:" + FIELD_SPEC + ")*";
|
||||
|
||||
//GENERIC
|
||||
private static final String OTHER_URL = GENERIC_URL;
|
||||
|
||||
private static final String URL = "((?:" + HTTP_URL + ")|(?:" + FTP_URL + ")|(?:" + NEWS_URL + ")|(?:" + NNTP_URL + ")|(?:" + TELNET_URL + ")|(?:" + GOPHER_URL + ")|(?:" + WAIS_URL + ")|(?:" + MAILTO_URL + ")|(?:" + FILE_URL + ")|(?:" + PROSPERO_URL + ")|(?:" + OTHER_URL + "))"; //URL Pattern
|
||||
|
||||
//Expression Definitions
|
||||
private static final String ATTRIBUTE_EXPRESSION = "^" + ATTRIBUTE_DESCRIPTION + ATTRIBUTE_SEPARATOR + FILL + SAFE_STRING + "{0,1}$"; //Regular Attribute
|
||||
|
||||
private static final String BASE64_ATTRIBUTE_EXPRESSION = "^" + ATTRIBUTE_DESCRIPTION + ATTRIBUTE_SEPARATOR + BASE64_INDICATOR + FILL + BASE64_STRING + "$"; //Base 64
|
||||
|
||||
private static final String URL_ATTRIBUTE_EXPRESSION = "^" + ATTRIBUTE_DESCRIPTION + ATTRIBUTE_SEPARATOR + URL_INDICATOR + FILL + URL + "$"; //URL
|
||||
|
||||
//Pattern Declarations
|
||||
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile(ATTRIBUTE_EXPRESSION);
|
||||
|
||||
private static final Pattern BASE64_ATTRIBUTE_PATTERN = Pattern.compile(BASE64_ATTRIBUTE_EXPRESSION);
|
||||
|
||||
private static final Pattern URL_ATTRIBUTE_PATTERN = Pattern.compile(URL_ATTRIBUTE_EXPRESSION);
|
||||
|
||||
private boolean ordered = false;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public DefaultAttributeValidationPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for indicating whether or not attribute values should be ordered alphabetically.
|
||||
*
|
||||
* @param ordered value.
|
||||
*/
|
||||
public DefaultAttributeValidationPolicy(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not the attribute values should be ordered alphabetically.
|
||||
*
|
||||
* @param ordered value.
|
||||
*/
|
||||
public void setOrdered(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates attribute contained in the buffer and returns an LdapAttribute.
|
||||
* <p>
|
||||
* Ensures attributes meets one of three prescribed patterns for valid attributes:
|
||||
* <ol>
|
||||
* <li>A standard attribute pattern of the form: ATTR_ID[;options]: VALUE</li>
|
||||
* <li>A Base64 attribute pattern of the form: ATTR_ID[;options]:: BASE64_VALUE</li>
|
||||
* <li>A url attribute pattern of the form: ATTR_ID[;options]:< URL_VALUE</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Upon success an LdapAttribute object is returned.
|
||||
*
|
||||
* @param buffer {@inheritDoc}
|
||||
* @return {@inheritDoc}
|
||||
* @throws InvalidAttributeFormatException if the attribute does not meet one of the three patterns above
|
||||
* or the attribute cannot be parsed.
|
||||
*/
|
||||
public Attribute parse(String buffer) {
|
||||
log.trace("Parsing --> [" + buffer + "]");
|
||||
|
||||
Matcher matcher = ATTRIBUTE_PATTERN.matcher(buffer);
|
||||
if (matcher.matches()) {
|
||||
//Is a regular attribute...
|
||||
return parseStringAttribute(matcher);
|
||||
}
|
||||
|
||||
matcher = BASE64_ATTRIBUTE_PATTERN.matcher(buffer);
|
||||
if (matcher.matches()) {
|
||||
//Is a base64 attribute...
|
||||
return parseBase64Attribute(matcher);
|
||||
}
|
||||
|
||||
matcher = URL_ATTRIBUTE_PATTERN.matcher(buffer);
|
||||
if (matcher.matches()) {
|
||||
//Is a URL attribute...
|
||||
return parseUrlAttribute(matcher);
|
||||
}
|
||||
|
||||
//default: no match.
|
||||
throw new InvalidAttributeFormatException("Not a valid attribute: [" + buffer + "]");
|
||||
}
|
||||
|
||||
private LdapAttribute parseStringAttribute(Matcher matcher) {
|
||||
String id = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
List<String> options = Arrays.asList((!StringUtils.hasLength(matcher.group(2)) ? new String[] {} : matcher.group(2).replaceFirst(";","").split(OPTION_SEPARATOR)));
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return new LdapAttribute(id, value, ordered);
|
||||
} else {
|
||||
return new LdapAttribute(id, value, options, ordered);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LdapAttribute parseBase64Attribute(Matcher matcher) {
|
||||
try {
|
||||
String id = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
List<String> options = Arrays.asList((StringUtils.isEmpty(matcher.group(2)) ? new String[] {} : matcher.group(2).replaceFirst(";","").split(OPTION_SEPARATOR)));
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return new LdapAttribute(id, new BASE64Decoder().decodeBuffer(value), ordered);
|
||||
} else {
|
||||
return new LdapAttribute(id, new BASE64Decoder().decodeBuffer(value), options, ordered);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InvalidAttributeFormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LdapAttribute parseUrlAttribute(Matcher matcher) {
|
||||
try {
|
||||
String id = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
List<String> options = Arrays.asList((StringUtils.isEmpty(matcher.group(2)) ? new String[] {} : matcher.group(2).replaceFirst(";","").split(OPTION_SEPARATOR)));
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return new LdapAttribute(id, new URI(value), ordered);
|
||||
} else {
|
||||
return new LdapAttribute(id, new URI(value), options, ordered);
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InvalidAttributeFormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ldap.ldif.support;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ldap.core.LdapAttribute;
|
||||
import org.springframework.ldap.ldif.InvalidAttributeFormatException;
|
||||
import org.springframework.util.StringUtils;
|
||||
import sun.misc.BASE64Decoder;
|
||||
|
||||
import javax.naming.directory.Attribute;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Ensures the buffer represents a valid attribute as defined by RFC2849.
|
||||
*
|
||||
* Meets the standards imposed by RFC 2849 for the "LDAP Data Interchange Format (LDIF)
|
||||
* - Technical Specification".
|
||||
*
|
||||
* Special attention is called to URL support: RFC 2849 requires that
|
||||
* LDIFs support URLs as defined in 1738; however, RFC 1738 has been updated by several RFCs including
|
||||
* RFC 1808, RFC 2396, and RFC 3986 (which obsoleted the formers). Unsupported features of this
|
||||
* implementation of URL identification include query strings and fragments in HTTP URLs.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DefaultAttributeValidationPolicy implements AttributeValidationPolicy {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(DefaultAttributeValidationPolicy.class);
|
||||
|
||||
/**
|
||||
* Pattern Declarations.
|
||||
*/
|
||||
|
||||
//General Definitions
|
||||
private static final String DIGIT = "\\p{Digit}";
|
||||
|
||||
private static final String LOW_ALPHA = "\\p{Lower}";
|
||||
|
||||
private static final String HIGH_ALPHA = "\\p{Upper}";
|
||||
|
||||
private static final String ALPHA = "\\p{Alpha}";
|
||||
|
||||
private static final String ALPHANUM = "\\p{Alnum}";
|
||||
|
||||
private static final String HEX = "\\p{XDigit}";
|
||||
|
||||
private static final String SAFE = "[\\x24\\x2D\\x5F\\x2E\\x2B]"; //$|-|_|.|+
|
||||
|
||||
private static final String EXTRA = "[\\x21\\x2A\\x27\\x7B\\x7D\\x2C]"; //!|*|'|(|)|,
|
||||
|
||||
private static final String PUNCTUATION = "[\\x3C\\x3E\\x23\\x25\\x22]"; //<|>|#|%|"
|
||||
|
||||
private static final String ESCAPE = "%" + HEX + "{2}";
|
||||
|
||||
private static final String RESERVED = "[\\x3B\\x2F\\x3F\\x3A\\x40\\x26\\x3D]"; //;|/|?|:|@|&|=
|
||||
|
||||
private static final String UNRESERVED = "[" + ALPHA + DIGIT + SAFE + EXTRA + "]";
|
||||
|
||||
private static final String UCHAR = "(?:" + UNRESERVED + "|" + ESCAPE + ")";
|
||||
|
||||
private static final String XCHAR = "(?:" + UNRESERVED + "|" + RESERVED + "|" + ESCAPE + ")";
|
||||
|
||||
private static final String DIGITS = DIGIT + "+";
|
||||
|
||||
//Standard LDAP Attribute Definitions
|
||||
private static final String ATTRIBUTE_SEPARATOR = ":";
|
||||
|
||||
private static final String OPTION_SEPARATOR = ";";
|
||||
|
||||
private static final String BASE64_INDICATOR = ":";
|
||||
|
||||
private static final String URL_INDICATOR = "<";
|
||||
|
||||
private static final String ATTRIBUTE_TYPE_CHARS = ALPHA + DIGIT + "-";
|
||||
|
||||
private static final String LDAP_OID = "[[0-9]|[1-9][0-9]+][\\.(?:[0-9]|[1-9][0-9]+)]+";
|
||||
|
||||
private static final String OPTION = "[" + ATTRIBUTE_TYPE_CHARS + "]+";
|
||||
|
||||
private static final String OPTIONS = "[" + OPTION_SEPARATOR + OPTION + "]*";
|
||||
|
||||
private static final String ATTRIBUTE_TYPE = LDAP_OID + "|" + ALPHANUM + "[" + ATTRIBUTE_TYPE_CHARS + "]*";
|
||||
|
||||
private static final String ATTRIBUTE_DESCRIPTION = "(" + ATTRIBUTE_TYPE + ")(" + OPTIONS + ")";
|
||||
|
||||
private static final String SAFE_CHAR = "[\\p{ASCII}&&[^\\x00\\x0A\\x0D]]"; //Any ASCII except NUL, LF, and CR
|
||||
|
||||
private static final String SAFE_INIT_CHAR = "[\\p{ASCII}&&[^ \\x00\\x0A\\x0D\\x3A\\x3C]]"; //Any ASCII except NUL, LF, CR, SPACE, colon, and less-than
|
||||
|
||||
private static final String SAFE_STRING = "(" + SAFE_INIT_CHAR + SAFE_CHAR + "*)";
|
||||
|
||||
private static final String FILL = "[ ]*"; //Any number of spaces
|
||||
|
||||
//BASE64 Definitions
|
||||
private static final String BASE64_CHAR = "[\\x2B\\x2F\\x30-\\x39\\x3D\\x41-\\x5A\\x61-\\x7A]"; //+, /, 0-9, -, A-Z, a-z
|
||||
|
||||
private static final String BASE64_STRING = "(" + BASE64_CHAR + "*)";
|
||||
|
||||
//URL Components
|
||||
private static final String USER = "[" + UCHAR + "\\x3B\\x3F\\x26\\x3D]*"; //UCHAR|;|?|&|=
|
||||
|
||||
private static final String PASSWORD = "[" + UCHAR + "\\x3B\\x3F\\x26\\x3D]*"; //UCHAR|;|?|&|=
|
||||
|
||||
private static final String DOMAINLABEL = ALPHANUM + "|" + ALPHANUM + "[" + ALPHANUM + "-]*" + ALPHANUM;
|
||||
|
||||
private static final String TOPLABEL = ALPHA + "|" + ALPHA + "[" + ALPHANUM + "-]*" + ALPHANUM;
|
||||
|
||||
private static final String HOSTNAME = "(?:" + DOMAINLABEL + "\\.)*" + TOPLABEL;
|
||||
|
||||
private static final String IPADDRESS = "(?:" + DIGIT + "{1,3}\\.){3}" + DIGIT + "{1,3}";
|
||||
|
||||
private static final String HOST = "(?:" + HOSTNAME + "|" + IPADDRESS + ")";
|
||||
|
||||
private static final String PORT = DIGITS;
|
||||
|
||||
private static final String HOSTPORT = HOST + "(?::" + PORT + ")?";
|
||||
|
||||
private static final String URLPATH = XCHAR + "*";
|
||||
|
||||
private static final String LOGIN = "(?:" + USER + "(?::" + PASSWORD + ")?@)?" + HOSTPORT;
|
||||
|
||||
//URL Definitions
|
||||
private static final String SCHEME = "[" + LOW_ALPHA + DIGIT + "\\x2B\\x2D\\x2E]+";
|
||||
|
||||
private static final String IP_SCHEMEPART = "//" + LOGIN + "(?:/" + URLPATH + ")?";
|
||||
|
||||
private static final String SCHEMEPART = "(?:" + XCHAR + "*|" + IP_SCHEMEPART + ")";
|
||||
|
||||
private static final String GENERIC_URL = SCHEME + ":" + SCHEMEPART;
|
||||
|
||||
//HTTP Definition
|
||||
private static final String HSEGMENT = "[" + UCHAR + "\\x3A\\x3B\\x26\\x3D\\x40]*"; //UCHAR|:|;|&|=|@
|
||||
|
||||
private static final String HPATH = HSEGMENT + "[/" + HSEGMENT + "]*";
|
||||
|
||||
private static final String SEARCH = HSEGMENT;
|
||||
|
||||
private static final String HTTP_URL = "http://" + HOSTPORT + "(?:/" + HPATH + "(?:\\x3F" + SEARCH + ")?)?";
|
||||
|
||||
//FTP
|
||||
private static final String FSEGMENT = "[" + UCHAR + "\\x3F\\x3A\\x26\\x3D\\x40]*"; //UCHAR|?|:|&|=|@
|
||||
|
||||
private static final String FPATH = FSEGMENT + "[/" + FSEGMENT + "]*";
|
||||
|
||||
private static final String FTPTYPE = "[AIDaid]";
|
||||
|
||||
private static final String FTP_URL = "ftp://" + LOGIN + "(?:/" + FPATH + "(?:;type=" + FTPTYPE + ")?)?";
|
||||
|
||||
//NEWS
|
||||
private static final String GROUP = ALPHA + "[" + ALPHA + DIGIT + "\\x2D\\x2E\\x2B\\x5F]*"; //ALPHA [ALPHA|DIGIT|-|.|+|_]*
|
||||
|
||||
private static final String ARTICLE = "[" + UCHAR + "\\x3A\\x3B\\x2F\\x3F\\x26\\x3D]@" + HOST; //[UCHAR|;|/|?|:|&|=]@HOST
|
||||
|
||||
private static final String GROUPPART = "(?:\\x2A|" + GROUP + "|" + ARTICLE + ")";
|
||||
|
||||
private static final String NEWS_URL = "news:" + GROUPPART;
|
||||
|
||||
//NNTP
|
||||
private static final String NNTP_URL = "nntp://" + HOSTPORT + "/" + GROUP + "/" + DIGITS;
|
||||
|
||||
//TELNET
|
||||
private static final String TELNET_URL = "telnet://" + LOGIN + "[/]?";
|
||||
|
||||
//GOPHER
|
||||
private static final String GTYPE = XCHAR;
|
||||
|
||||
private static final String SELECTOR = XCHAR + "*";
|
||||
|
||||
private static final String GOPHER_STRING = XCHAR + "*";
|
||||
|
||||
private static final String GOPHER_URL = "gopher://" + HOSTPORT + "(?:/(?:" + GTYPE + "(?:" + SELECTOR + "(?:%09" + SEARCH + "(?:%09" + GOPHER_STRING + ")?)?)?)?)?";
|
||||
|
||||
//WAIS
|
||||
private static final String WPATH = UCHAR + "*";
|
||||
|
||||
private static final String WTYPE = UCHAR + "*";
|
||||
|
||||
private static final String DATABASE = UCHAR + "*";
|
||||
|
||||
private static final String WAIS_DOC = "wais://" + HOSTPORT + "/" + DATABASE + "/" + WTYPE + "/" + WPATH;
|
||||
|
||||
private static final String WAIS_INDEX = "wais://" + HOSTPORT + "/" + DATABASE + "\\?" + SEARCH;
|
||||
|
||||
private static final String WAIS_DATABASE = "wais://" + HOSTPORT + "/" + DATABASE;
|
||||
|
||||
private static final String WAIS_URL = WAIS_DATABASE + "|" + WAIS_INDEX + "|" + WAIS_DOC;
|
||||
|
||||
//MAILTO
|
||||
private static final String ENCODED_822_ADDR = XCHAR + "+";
|
||||
|
||||
private static final String MAILTO_URL = "mailto:" + ENCODED_822_ADDR;
|
||||
|
||||
//FILE
|
||||
private static final String FILE_URL = "file://(?:" + HOST + "|localhost)?/" + FPATH ;
|
||||
|
||||
//PROPERO
|
||||
private static final String FIELD_VALUE = "[" + UCHAR + "\\x3F\\x3A\\x40\\x26]*"; //[UCHAR|?|:|@|&]*
|
||||
|
||||
private static final String FIELD_NAME = "[" + UCHAR + "\\x3F\\x3A\\x40\\x26]*"; //[UCHAR|?|:|@|&]*
|
||||
|
||||
private static final String FIELD_SPEC = ";" + FIELD_NAME + "=" + FIELD_VALUE;
|
||||
|
||||
private static final String PSEGMENT = "[" + UCHAR + "\\x3F\\x3A\\x40\\x26\\x3D]*"; //[UCHAR|?|:|@|&|=]*
|
||||
|
||||
private static final String PPATH = PSEGMENT + "(?:/" + PSEGMENT + ")*";
|
||||
|
||||
private static final String PROSPERO_URL = "prospero://" + HOSTPORT + "/" + PPATH + "(?:" + FIELD_SPEC + ")*";
|
||||
|
||||
//GENERIC
|
||||
private static final String OTHER_URL = GENERIC_URL;
|
||||
|
||||
private static final String URL = "((?:" + HTTP_URL + ")|(?:" + FTP_URL + ")|(?:" + NEWS_URL + ")|(?:" + NNTP_URL + ")|(?:" + TELNET_URL + ")|(?:" + GOPHER_URL + ")|(?:" + WAIS_URL + ")|(?:" + MAILTO_URL + ")|(?:" + FILE_URL + ")|(?:" + PROSPERO_URL + ")|(?:" + OTHER_URL + "))"; //URL Pattern
|
||||
|
||||
//Expression Definitions
|
||||
private static final String ATTRIBUTE_EXPRESSION = "^" + ATTRIBUTE_DESCRIPTION + ATTRIBUTE_SEPARATOR + FILL + SAFE_STRING + "{0,1}$"; //Regular Attribute
|
||||
|
||||
private static final String BASE64_ATTRIBUTE_EXPRESSION = "^" + ATTRIBUTE_DESCRIPTION + ATTRIBUTE_SEPARATOR + BASE64_INDICATOR + FILL + BASE64_STRING + "$"; //Base 64
|
||||
|
||||
private static final String URL_ATTRIBUTE_EXPRESSION = "^" + ATTRIBUTE_DESCRIPTION + ATTRIBUTE_SEPARATOR + URL_INDICATOR + FILL + URL + "$"; //URL
|
||||
|
||||
//Pattern Declarations
|
||||
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile(ATTRIBUTE_EXPRESSION);
|
||||
|
||||
private static final Pattern BASE64_ATTRIBUTE_PATTERN = Pattern.compile(BASE64_ATTRIBUTE_EXPRESSION);
|
||||
|
||||
private static final Pattern URL_ATTRIBUTE_PATTERN = Pattern.compile(URL_ATTRIBUTE_EXPRESSION);
|
||||
|
||||
private boolean ordered = false;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public DefaultAttributeValidationPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for indicating whether or not attribute values should be ordered alphabetically.
|
||||
*
|
||||
* @param ordered value.
|
||||
*/
|
||||
public DefaultAttributeValidationPolicy(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not the attribute values should be ordered alphabetically.
|
||||
*
|
||||
* @param ordered value.
|
||||
*/
|
||||
public void setOrdered(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates attribute contained in the buffer and returns an LdapAttribute.
|
||||
* <p>
|
||||
* Ensures attributes meets one of three prescribed patterns for valid attributes:
|
||||
* <ol>
|
||||
* <li>A standard attribute pattern of the form: ATTR_ID[;options]: VALUE</li>
|
||||
* <li>A Base64 attribute pattern of the form: ATTR_ID[;options]:: BASE64_VALUE</li>
|
||||
* <li>A url attribute pattern of the form: ATTR_ID[;options]:< URL_VALUE</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Upon success an LdapAttribute object is returned.
|
||||
*
|
||||
* @param buffer {@inheritDoc}
|
||||
* @return {@inheritDoc}
|
||||
* @throws InvalidAttributeFormatException if the attribute does not meet one of the three patterns above
|
||||
* or the attribute cannot be parsed.
|
||||
*/
|
||||
public Attribute parse(String buffer) {
|
||||
log.trace("Parsing --> [" + buffer + "]");
|
||||
|
||||
Matcher matcher = ATTRIBUTE_PATTERN.matcher(buffer);
|
||||
if (matcher.matches()) {
|
||||
//Is a regular attribute...
|
||||
return parseStringAttribute(matcher);
|
||||
}
|
||||
|
||||
matcher = BASE64_ATTRIBUTE_PATTERN.matcher(buffer);
|
||||
if (matcher.matches()) {
|
||||
//Is a base64 attribute...
|
||||
return parseBase64Attribute(matcher);
|
||||
}
|
||||
|
||||
matcher = URL_ATTRIBUTE_PATTERN.matcher(buffer);
|
||||
if (matcher.matches()) {
|
||||
//Is a URL attribute...
|
||||
return parseUrlAttribute(matcher);
|
||||
}
|
||||
|
||||
//default: no match.
|
||||
throw new InvalidAttributeFormatException("Not a valid attribute: [" + buffer + "]");
|
||||
}
|
||||
|
||||
private LdapAttribute parseStringAttribute(Matcher matcher) {
|
||||
String id = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
List<String> options = Arrays.asList((!StringUtils.hasLength(matcher.group(2)) ? new String[] {} : matcher.group(2).replaceFirst(";","").split(OPTION_SEPARATOR)));
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return new LdapAttribute(id, value, ordered);
|
||||
} else {
|
||||
return new LdapAttribute(id, value, options, ordered);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LdapAttribute parseBase64Attribute(Matcher matcher) {
|
||||
try {
|
||||
String id = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
List<String> options = Arrays.asList((StringUtils.isEmpty(matcher.group(2)) ? new String[] {} : matcher.group(2).replaceFirst(";","").split(OPTION_SEPARATOR)));
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return new LdapAttribute(id, new BASE64Decoder().decodeBuffer(value), ordered);
|
||||
} else {
|
||||
return new LdapAttribute(id, new BASE64Decoder().decodeBuffer(value), options, ordered);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InvalidAttributeFormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LdapAttribute parseUrlAttribute(Matcher matcher) {
|
||||
try {
|
||||
String id = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
List<String> options = Arrays.asList((StringUtils.isEmpty(matcher.group(2)) ? new String[] {} : matcher.group(2).replaceFirst(";","").split(OPTION_SEPARATOR)));
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return new LdapAttribute(id, new URI(value), ordered);
|
||||
} else {
|
||||
return new LdapAttribute(id, new URI(value), options, ordered);
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InvalidAttributeFormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,116 +1,116 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ldap.ldif.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Policy object for enforcing LDIF record separation rules. Designed explicitly
|
||||
* for use in LdifParser. This default separator policy should really not be
|
||||
* required to be replaced but it is modular just in case.
|
||||
* <p>
|
||||
* This class applies the separation policy prescribed in RFC2849 for LDIF files
|
||||
* and identifies the line type from the input.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class SeparatorPolicy {
|
||||
|
||||
private static Log log = LogFactory.getLog(SeparatorPolicy.class);
|
||||
|
||||
/*
|
||||
* Line Identification Patterns.
|
||||
*/
|
||||
|
||||
private static final String VERSION_IDENTIFIER = "^version: [0-9]+(\\.[0-9]*){0,1}$";
|
||||
|
||||
private static final String CONTROL = "control:";
|
||||
|
||||
private static final String CHANGE_TYPE = "changetype:";
|
||||
|
||||
private static final String CONTINUATION = " ";
|
||||
|
||||
private static final String COMMENT = "#";
|
||||
|
||||
private static final String NewRecord = "^dn:.*$";
|
||||
|
||||
private boolean record = false;
|
||||
|
||||
private boolean skip = false;
|
||||
|
||||
public SeparatorPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assess a read line.
|
||||
* <p>
|
||||
* In LDIF, lines must adhere to a particular format. A line can only contain one attribute
|
||||
* and its value. The value may span multiple lines. Continuation lines are marked by the presence
|
||||
* of a single space in the 1st position. Non-continuation lines must start in the first position.
|
||||
*
|
||||
*/
|
||||
public LineIdentifier assess(String line) {
|
||||
log.trace("Assessing --> [" + line + "]");
|
||||
|
||||
if (record) {
|
||||
if (!StringUtils.hasLength(line)) {
|
||||
record = false;
|
||||
skip = false;
|
||||
return LineIdentifier.EndOfRecord;
|
||||
|
||||
} else if (skip) {
|
||||
return LineIdentifier.Void;
|
||||
|
||||
} else {
|
||||
if (line.startsWith(CONTROL)) {
|
||||
skip = true;
|
||||
return LineIdentifier.Control;
|
||||
|
||||
} else if (line.startsWith(CHANGE_TYPE)) {
|
||||
skip = true;
|
||||
return LineIdentifier.ChangeType;
|
||||
|
||||
} else if (line.startsWith(COMMENT)) {
|
||||
return LineIdentifier.Comment;
|
||||
|
||||
} else if (line.startsWith(CONTINUATION)) {
|
||||
return LineIdentifier.Continuation;
|
||||
|
||||
} else {
|
||||
return LineIdentifier.Attribute;
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (StringUtils.hasLength(line) && line.matches(VERSION_IDENTIFIER) && !skip) {
|
||||
//Version Identifiers are ignored by parser.
|
||||
return LineIdentifier.VersionIdentifier;
|
||||
|
||||
} else if (StringUtils.hasLength(line) && line.matches(NewRecord)) {
|
||||
record = true;
|
||||
skip = false;
|
||||
return LineIdentifier.NewRecord;
|
||||
|
||||
} else {
|
||||
return LineIdentifier.Void;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ldap.ldif.support;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Policy object for enforcing LDIF record separation rules. Designed explicitly
|
||||
* for use in LdifParser. This default separator policy should really not be
|
||||
* required to be replaced but it is modular just in case.
|
||||
* <p>
|
||||
* This class applies the separation policy prescribed in RFC2849 for LDIF files
|
||||
* and identifies the line type from the input.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class SeparatorPolicy {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(SeparatorPolicy.class);
|
||||
|
||||
/*
|
||||
* Line Identification Patterns.
|
||||
*/
|
||||
|
||||
private static final String VERSION_IDENTIFIER = "^version: [0-9]+(\\.[0-9]*){0,1}$";
|
||||
|
||||
private static final String CONTROL = "control:";
|
||||
|
||||
private static final String CHANGE_TYPE = "changetype:";
|
||||
|
||||
private static final String CONTINUATION = " ";
|
||||
|
||||
private static final String COMMENT = "#";
|
||||
|
||||
private static final String NewRecord = "^dn:.*$";
|
||||
|
||||
private boolean record = false;
|
||||
|
||||
private boolean skip = false;
|
||||
|
||||
public SeparatorPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assess a read line.
|
||||
* <p>
|
||||
* In LDIF, lines must adhere to a particular format. A line can only contain one attribute
|
||||
* and its value. The value may span multiple lines. Continuation lines are marked by the presence
|
||||
* of a single space in the 1st position. Non-continuation lines must start in the first position.
|
||||
*
|
||||
*/
|
||||
public LineIdentifier assess(String line) {
|
||||
log.trace("Assessing --> [" + line + "]");
|
||||
|
||||
if (record) {
|
||||
if (!StringUtils.hasLength(line)) {
|
||||
record = false;
|
||||
skip = false;
|
||||
return LineIdentifier.EndOfRecord;
|
||||
|
||||
} else if (skip) {
|
||||
return LineIdentifier.Void;
|
||||
|
||||
} else {
|
||||
if (line.startsWith(CONTROL)) {
|
||||
skip = true;
|
||||
return LineIdentifier.Control;
|
||||
|
||||
} else if (line.startsWith(CHANGE_TYPE)) {
|
||||
skip = true;
|
||||
return LineIdentifier.ChangeType;
|
||||
|
||||
} else if (line.startsWith(COMMENT)) {
|
||||
return LineIdentifier.Comment;
|
||||
|
||||
} else if (line.startsWith(CONTINUATION)) {
|
||||
return LineIdentifier.Continuation;
|
||||
|
||||
} else {
|
||||
return LineIdentifier.Attribute;
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (StringUtils.hasLength(line) && line.matches(VERSION_IDENTIFIER) && !skip) {
|
||||
//Version Identifiers are ignored by parser.
|
||||
return LineIdentifier.VersionIdentifier;
|
||||
|
||||
} else if (StringUtils.hasLength(line) && line.matches(NewRecord)) {
|
||||
record = true;
|
||||
skip = false;
|
||||
return LineIdentifier.NewRecord;
|
||||
|
||||
} else {
|
||||
return LineIdentifier.Void;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user