diff --git a/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapErrorCode.java b/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapErrorCode.java new file mode 100644 index 0000000..228a73b --- /dev/null +++ b/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapErrorCode.java @@ -0,0 +1,36 @@ +/* + Copyright 2015 Red Hat, Inc. and/or its affiliates. + + This file is part of lightblue. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package com.redhat.lightblue.common.ldap; + +/** + * Error codes specific to LDAP + * + * @author dcrissman + */ +public final class LdapErrorCode { + + /** An unsupported feature was used. */ + public static final String ERR_UNSUPPORTED_FEATURE = "ldap:UnsupportedFeature:"; + + /** Lightblue-Ldap does not currently support object arrays. This error indicates that such a field was used. */ + public static final String ERR_UNSUPPORTED_FEATURE_OBJECT_ARRAY = ERR_UNSUPPORTED_FEATURE + "ObjectArray"; + + private LdapErrorCode(){} + +} diff --git a/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapFieldNameTranslator.java b/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapFieldNameTranslator.java index d65a723..2a30a48 100644 --- a/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapFieldNameTranslator.java +++ b/lightblue-ldap-common/src/main/java/com/redhat/lightblue/common/ldap/LdapFieldNameTranslator.java @@ -18,6 +18,8 @@ */ package com.redhat.lightblue.common.ldap; +import com.redhat.lightblue.util.Path; + /** * Represents a class that can translate back and forth between a fieldName and an LDAP attributeName. * @@ -30,13 +32,13 @@ public interface LdapFieldNameTranslator { * @param fieldName - metadata field name * @return ldap attributeName or the fieldName back at you if no mapping is present. */ - public String translateFieldName(String fieldName); + public String translateFieldName(Path path); /** * Returns the fieldName with the given attributeName. * @param attributeName - ldap attribute name * @return metadata fieldName or the attributeName back at you if no mapping is present. */ - public String translateAttributeName(String attributeName); + public Path translateAttributeName(String attributeName); } diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/EntryBuilder.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/EntryBuilder.java index ad81fff..db9c35d 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/EntryBuilder.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/EntryBuilder.java @@ -24,19 +24,20 @@ import com.fasterxml.jackson.databind.JsonNode; import com.redhat.lightblue.common.ldap.LdapConstant; +import com.redhat.lightblue.common.ldap.LdapErrorCode; import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; import com.redhat.lightblue.common.ldap.LightblueUtil; import com.redhat.lightblue.metadata.ArrayElement; import com.redhat.lightblue.metadata.ArrayField; import com.redhat.lightblue.metadata.EntityMetadata; -import com.redhat.lightblue.metadata.ObjectField; +import com.redhat.lightblue.metadata.MetadataConstants; import com.redhat.lightblue.metadata.SimpleField; import com.redhat.lightblue.metadata.Type; import com.redhat.lightblue.metadata.types.BinaryType; import com.redhat.lightblue.metadata.types.DateType; +import com.redhat.lightblue.util.Error; import com.redhat.lightblue.util.JsonDoc; import com.redhat.lightblue.util.JsonNodeCursor; -import com.redhat.lightblue.util.Path; import com.unboundid.ldap.sdk.Entry; import com.unboundid.util.StaticUtils; @@ -55,9 +56,17 @@ public EntryBuilder(EntityMetadata md, LdapFieldNameTranslator fieldNameTranslat } public Entry build(String dn, JsonDoc document){ - Entry entry = new Entry(dn); - translate(document, entry); - return entry; + Error.push("build entry"); + Error.push(LdapConstant.ATTRIBUTE_DN + "=" + dn); + try{ + Entry entry = new Entry(dn); + translate(document, entry); + return entry; + } + finally{ + Error.pop(); + Error.pop(); + } } @Override @@ -74,13 +83,12 @@ else if(type instanceof BinaryType){ } @Override - protected void translate(SimpleField field, Path path, JsonNode node, Entry target) { - String attributeName = fieldNameTranslator.translateFieldName(field.getName()); + protected void translate(SimpleField field, JsonNode node, Entry target) { + String attributeName = fieldNameTranslator.translateFieldName(field.getFullPath()); if(LdapConstant.ATTRIBUTE_DN.equalsIgnoreCase(attributeName)){ - throw new IllegalArgumentException( - "'dn' should not be included as it's value will be derived from the metadata.basedn and" + - " the metadata.uniqueattr. Including the 'dn' as an insert attribute is confusing."); + //DN is derived using metadata.uniqueattr, providing it is confusing. + throw Error.get(MetadataConstants.ERR_INVALID_FIELD_REFERENCE, LdapConstant.ATTRIBUTE_DN); } else if(LightblueUtil.isFieldObjectType(attributeName) || LightblueUtil.isFieldAnArrayCount(attributeName, getEntityMetadata().getFields())){ @@ -102,15 +110,10 @@ else if(LightblueUtil.isFieldObjectType(attributeName) } @Override - protected void translate(ObjectField field, Path path, JsonNode node, Entry target) { - throw new UnsupportedOperationException("ObjectField type is not currently supported."); - } - - @Override - protected void translateSimpleArray(ArrayField field, Path path, List items, Entry target) { + protected void translateSimpleArray(ArrayField field, List items, Entry target) { ArrayElement arrayElement = field.getElement(); Type arrayElementType = arrayElement.getType(); - String attributeName = fieldNameTranslator.translateFieldName(field.getName()); + String attributeName = fieldNameTranslator.translateFieldName(field.getFullPath()); if(arrayElementType instanceof BinaryType){ List bytes = new ArrayList(); @@ -130,7 +133,7 @@ protected void translateSimpleArray(ArrayField field, Path path, List it @Override protected void translateObjectArray(ArrayField field, JsonNodeCursor cursor, Entry target) { - throw new UnsupportedOperationException("Object ArrayField type is not currently supported."); + throw Error.get(LdapErrorCode.ERR_UNSUPPORTED_FEATURE_OBJECT_ARRAY, field.getFullPath().toString()); } } diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCRUDController.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCRUDController.java index fff1427..3048e6a 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCRUDController.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCRUDController.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.redhat.lightblue.common.ldap.DBResolver; import com.redhat.lightblue.common.ldap.LdapConstant; import com.redhat.lightblue.common.ldap.LdapDataStore; @@ -44,24 +43,17 @@ import com.redhat.lightblue.crud.CRUDUpdateResponse; import com.redhat.lightblue.crud.CrudConstants; import com.redhat.lightblue.crud.DocCtx; -import com.redhat.lightblue.crud.ldap.model.TrivialLdapFieldNameTranslator; import com.redhat.lightblue.crud.ldap.translator.FilterTranslator; import com.redhat.lightblue.crud.ldap.translator.ResultTranslator; import com.redhat.lightblue.crud.ldap.translator.SortTranslator; import com.redhat.lightblue.eval.FieldAccessRoleEvaluator; import com.redhat.lightblue.eval.Projector; import com.redhat.lightblue.hystrix.ldap.InsertCommand; -import com.redhat.lightblue.metadata.ArrayField; import com.redhat.lightblue.metadata.DataStore; -import com.redhat.lightblue.metadata.EntityInfo; import com.redhat.lightblue.metadata.EntityMetadata; import com.redhat.lightblue.metadata.FieldCursor; -import com.redhat.lightblue.metadata.Fields; -import com.redhat.lightblue.metadata.Metadata; +import com.redhat.lightblue.metadata.MetadataConstants; import com.redhat.lightblue.metadata.MetadataListener; -import com.redhat.lightblue.metadata.SimpleArrayElement; -import com.redhat.lightblue.metadata.SimpleField; -import com.redhat.lightblue.metadata.types.IntegerType; import com.redhat.lightblue.metadata.types.StringType; import com.redhat.lightblue.query.Projection; import com.redhat.lightblue.query.QueryExpression; @@ -107,10 +99,10 @@ public CRUDInsertionResponse insert(CRUDOperationContext ctx, EntityMetadata md = ctx.getEntityMetadata(ctx.getEntityName()); LdapDataStore store = getLdapDataStore(md); - LdapFieldNameTranslator property = getLdapFieldNameTranslator(md); + LdapFieldNameTranslator fieldNameTranslator = LdapCrudUtil.getLdapFieldNameTranslator(md); FieldAccessRoleEvaluator roles = new FieldAccessRoleEvaluator(md, ctx.getCallerRoles()); - EntryBuilder entryBuilder = new EntryBuilder(md, property); + EntryBuilder entryBuilder = new EntryBuilder(md, fieldNameTranslator); //Create Entry instances for each document. List entries = new ArrayList(); @@ -125,12 +117,10 @@ public CRUDInsertionResponse insert(CRUDOperationContext ctx, } } - JsonNode rootNode = document.getRoot(); - - String uniqueFieldName = property.translateAttributeName(store.getUniqueAttribute()); - JsonNode uniqueNode = rootNode.get(uniqueFieldName); + Path uniqueFieldPath = fieldNameTranslator.translateAttributeName(store.getUniqueAttribute()); + JsonNode uniqueNode = document.get(uniqueFieldPath); if(uniqueNode == null){ - throw new IllegalArgumentException(uniqueFieldName + " is a required field"); + throw Error.get(MetadataConstants.ERR_PARSE_MISSING_ELEMENT, store.getUniqueAttribute()); } String dn = createDN(store, uniqueNode.asText()); @@ -208,17 +198,17 @@ public CRUDFindResponse find(CRUDOperationContext ctx, LDAPConnection connection = getNewLdapConnection(store); - LdapFieldNameTranslator property = getLdapFieldNameTranslator(md); + LdapFieldNameTranslator fieldNameTranslator = LdapCrudUtil.getLdapFieldNameTranslator(md); try { //TODO: Support scopes other than SUB SearchRequest request = new SearchRequest( store.getBaseDN(), SearchScope.SUB, - new FilterTranslator(property).translate(query), - translateFieldNames(property, gatherRequiredFields(md, projection, query, sort)).toArray(new String[0])); + new FilterTranslator(fieldNameTranslator).translate(query), + translateFieldNames(fieldNameTranslator, gatherRequiredFields(md, projection, query, sort)).toArray(new String[0])); if(sort != null){ - request.addControl(new ServerSideSortRequestControl(false, new SortTranslator(property).translate(sort))); + request.addControl(new ServerSideSortRequestControl(false, new SortTranslator(fieldNameTranslator).translate(sort))); } if((from != null) && (from > 0)){ int endPos = to.intValue() - from.intValue(); @@ -228,16 +218,14 @@ public CRUDFindResponse find(CRUDOperationContext ctx, SearchResult result = connection.search(request); response.setSize(result.getEntryCount()); - ResultTranslator resultTranslator = new ResultTranslator(ctx.getFactory().getNodeFactory(), md, property); + ResultTranslator resultTranslator = new ResultTranslator(ctx.getFactory().getNodeFactory(), md, fieldNameTranslator); List translatedDocs = new ArrayList(); for(SearchResultEntry entry : result.getSearchEntries()){ try{ translatedDocs.add(resultTranslator.translate(entry)); } catch(Exception e){ - DocCtx erroredDoc = new DocCtx(null); - erroredDoc.addError(Error.get(e)); - translatedDocs.add(erroredDoc); + ctx.addError(Error.get(e)); } } ctx.setDocuments(translatedDocs); @@ -250,7 +238,7 @@ public CRUDFindResponse find(CRUDOperationContext ctx, ctx.getCallerRoles()).getExcludedFields(FieldAccessRoleEvaluator.Operation.find) ), md); - for (DocCtx document : ctx.getDocuments()) { + for (DocCtx document : ctx.getDocumentsWithoutErrors()) { document.setOutputDocument(projector.project(document, ctx.getFactory().getNodeFactory())); } } @@ -269,43 +257,7 @@ public void updatePredefinedFields(CRUDOperationContext ctx, JsonDoc doc) { @Override public MetadataListener getMetadataListener() { - return new MetadataListener() { - - @Override - public void beforeUpdateEntityInfo(Metadata m, EntityInfo ei, boolean newEntity) { - //Do Nothing!! - } - - /** - * Ensure that dn and objectClass are on the entity. - */ - @Override - public void beforeCreateNewSchema(Metadata m, EntityMetadata md) { - LdapFieldNameTranslator property = getLdapFieldNameTranslator(md); - - Fields fields = md.getEntitySchema().getFields(); - String dnFieldName = property.translateAttributeName(LdapConstant.ATTRIBUTE_DN); - if(!fields.has(dnFieldName)){ - fields.addNew(new SimpleField(dnFieldName, StringType.TYPE)); - } - - String objectClassFieldName = property.translateAttributeName(LdapConstant.ATTRIBUTE_OBJECT_CLASS); - if(!fields.has(objectClassFieldName)){ - fields.addNew(new ArrayField(objectClassFieldName, new SimpleArrayElement(StringType.TYPE))); - fields.addNew(new SimpleField(LightblueUtil.createArrayCountFieldName(objectClassFieldName), IntegerType.TYPE)); - } - } - - @Override - public void afterUpdateEntityInfo(Metadata m, EntityInfo ei, boolean newEntity) { - //Do Nothing!! - } - - @Override - public void afterCreateNewSchema(Metadata m, EntityMetadata md) { - //Do Nothing!! - } - }; + return new LdapMetadataListener(); } /** @@ -324,26 +276,6 @@ private LdapDataStore getLdapDataStore(EntityMetadata md){ return (LdapDataStore) store; } - /** - * Shortcut method to get and return the {@link LdapFieldNameTranslator} on the passed - * in {@link EntityMetadata}. - * @param md - {@link EntityMetadata}. - * @return {@link LdapFieldNameTranslator} - * @throws IllegalArgumentException if an invalid object is found. - */ - private LdapFieldNameTranslator getLdapFieldNameTranslator(EntityMetadata md){ - Object o = md.getEntityInfo().getProperties().get(LdapConstant.BACKEND); - - if(o == null){ - return new TrivialLdapFieldNameTranslator(); - } - - if(!(o instanceof LdapFieldNameTranslator)){ - throw new IllegalArgumentException("Object of type " + o.getClass() + " is not supported."); - } - return (LdapFieldNameTranslator) o; - } - /** * Creates and returns a unique DN. * @param store - {@link LdapDataStore} to use as the BaseDN and field that @@ -364,9 +296,9 @@ private String createDN(LdapDataStore store, String uniqueValue){ * @param sort - (optional) {@link Sort}. * @return list of field names. */ - private Set gatherRequiredFields(EntityMetadata md, + private Set gatherRequiredFields(EntityMetadata md, Projection projection, QueryExpression query, Sort sort){ - Set fields = new HashSet(); + Set paths = new HashSet(); FieldCursor cursor = md.getFieldCursor(); while(cursor.next()) { @@ -381,15 +313,15 @@ private Set gatherRequiredFields(EntityMetadata md, * Handles the case of an array count field, which will not actually exist in * the ldap entity. */ - fields.add(LightblueUtil.createArrayFieldNameFromCountField(fieldName)); + paths.add(node.mutableCopy().setLast(LightblueUtil.createArrayFieldNameFromCountField(fieldName)).immutableCopy()); } else{ - fields.add(fieldName); + paths.add(node); } } } - return fields; + return paths; } /** @@ -399,10 +331,10 @@ private Set gatherRequiredFields(EntityMetadata md, * @param fieldNames - Collection of fieldNames to translated * @return Set of translated attributeNames. */ - private Set translateFieldNames(LdapFieldNameTranslator property, Collection fieldNames){ + private Set translateFieldNames(LdapFieldNameTranslator property, Collection fieldNames){ Set attributes = new HashSet(); - for(String fieldName : fieldNames){ - attributes.add(property.translateFieldName(fieldName)); + for(Path path : fieldNames){ + attributes.add(property.translateFieldName(path)); } return attributes; @@ -422,9 +354,9 @@ private void projectChanges(Projection projection, CRUDOperationContext ctx, Map EntityMetadata md = ctx.getEntityMetadata(ctx.getEntityName()); JsonNodeFactory factory = ctx.getFactory().getNodeFactory(); - LdapFieldNameTranslator property = getLdapFieldNameTranslator(md); + LdapFieldNameTranslator fieldNameTranslator = LdapCrudUtil.getLdapFieldNameTranslator(md); - Set requiredAttributeNames = translateFieldNames(property, gatherRequiredFields(md, projection, null, null)); + Set requiredAttributeNames = translateFieldNames(fieldNameTranslator, gatherRequiredFields(md, projection, null, null)); Projector projector = Projector.getInstance( Projection.add( projection, @@ -434,7 +366,7 @@ private void projectChanges(Projection projection, CRUDOperationContext ctx, Map ), md); - String dnFieldName = property.translateAttributeName(LdapConstant.ATTRIBUTE_DN); + Path dnFieldPath = fieldNameTranslator.translateAttributeName(LdapConstant.ATTRIBUTE_DN); for(Entry insertedDn : documentToDnMap.entrySet()){ DocCtx document = insertedDn.getKey(); @@ -443,9 +375,9 @@ private void projectChanges(Projection projection, CRUDOperationContext ctx, Map // If only dn is in the projection, then no need to query LDAP. if((requiredAttributeNames.size() == 1) && requiredAttributeNames.contains(LdapConstant.ATTRIBUTE_DN)){ - ObjectNode node = factory.objectNode(); - node.set(dnFieldName, StringType.TYPE.toJson(factory, dn)); - projectionResponseJson = new DocCtx(new JsonDoc(node)); + JsonDoc jdoc = new JsonDoc(factory.objectNode()); + jdoc.modify(dnFieldPath, StringType.TYPE.toJson(factory, dn), true); + projectionResponseJson = new DocCtx(jdoc); } //TODO: else fetch entity from LDAP and project results. //TODO: Probably want to batch fetch as opposed to individual fetches. diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCrudUtil.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCrudUtil.java new file mode 100644 index 0000000..4a2cd5e --- /dev/null +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapCrudUtil.java @@ -0,0 +1,55 @@ +/* + Copyright 2015 Red Hat, Inc. and/or its affiliates. + + This file is part of lightblue. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package com.redhat.lightblue.crud.ldap; + +import com.redhat.lightblue.common.ldap.LdapConstant; +import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; +import com.redhat.lightblue.crud.ldap.model.TrivialLdapFieldNameTranslator; +import com.redhat.lightblue.metadata.EntityMetadata; + +/** + * Utility methods for LDAP CRUD operations. + * + * @author dcrissman + */ +public final class LdapCrudUtil { + + /** + * Shortcut method to get and return the {@link LdapFieldNameTranslator} on the passed + * in {@link EntityMetadata}. + * @param md - {@link EntityMetadata}. + * @return {@link LdapFieldNameTranslator} + * @throws IllegalArgumentException if an invalid object is found. + */ + public static LdapFieldNameTranslator getLdapFieldNameTranslator(EntityMetadata md){ + Object o = md.getEntityInfo().getProperties().get(LdapConstant.BACKEND); + + if(o == null){ + return new TrivialLdapFieldNameTranslator(); + } + + if(!(o instanceof LdapFieldNameTranslator)){ + throw new IllegalArgumentException("Object of type " + o.getClass() + " is not supported."); + } + return (LdapFieldNameTranslator) o; + } + + private LdapCrudUtil(){} + +} diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapMetadataListener.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapMetadataListener.java new file mode 100644 index 0000000..b71eda0 --- /dev/null +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/LdapMetadataListener.java @@ -0,0 +1,148 @@ +/* + Copyright 2015 Red Hat, Inc. and/or its affiliates. + + This file is part of lightblue. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package com.redhat.lightblue.crud.ldap; + +import com.redhat.lightblue.common.ldap.LdapConstant; +import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; +import com.redhat.lightblue.metadata.ArrayField; +import com.redhat.lightblue.metadata.EntityInfo; +import com.redhat.lightblue.metadata.EntityMetadata; +import com.redhat.lightblue.metadata.Field; +import com.redhat.lightblue.metadata.FieldTreeNode; +import com.redhat.lightblue.metadata.Fields; +import com.redhat.lightblue.metadata.Metadata; +import com.redhat.lightblue.metadata.MetadataConstants; +import com.redhat.lightblue.metadata.MetadataListener; +import com.redhat.lightblue.metadata.ObjectArrayElement; +import com.redhat.lightblue.metadata.ObjectField; +import com.redhat.lightblue.metadata.PredefinedFields; +import com.redhat.lightblue.metadata.SimpleArrayElement; +import com.redhat.lightblue.metadata.SimpleField; +import com.redhat.lightblue.metadata.types.StringType; +import com.redhat.lightblue.util.Error; +import com.redhat.lightblue.util.Path; + +/** + * Implementation of {@link MetadataListener} for LDAP. + * + * @author dcrissman + */ +public class LdapMetadataListener implements MetadataListener{ + + @Override + public void beforeUpdateEntityInfo(Metadata m, EntityInfo ei, boolean newEntity) { + //Do Nothing!! + } + + @Override + public void afterUpdateEntityInfo(Metadata m, EntityInfo ei, boolean newEntity) { + //Do Nothing!! + } + + @Override + public void afterCreateNewSchema(Metadata m, EntityMetadata md) { + //Do Nothing!! + } + + /** + * Ensure that dn and objectClass are on the entity. + */ + @Override + public void beforeCreateNewSchema(Metadata m, EntityMetadata md) { + LdapFieldNameTranslator ldapNameTranslator = LdapCrudUtil.getLdapFieldNameTranslator(md); + //TODO: check for array index or Path.any + + ensureDnField(md, ldapNameTranslator.translateAttributeName(LdapConstant.ATTRIBUTE_DN)); + + ensureObjectClassField(md, + ldapNameTranslator.translateAttributeName(LdapConstant.ATTRIBUTE_OBJECT_CLASS)); + + PredefinedFields.ensurePredefinedFields(md); + } + + /** + * Ensures the objectClass field is present on the entity. If not, then it will added. If so, but + * is defined incorrectly, then an {@link Error} will be thrown. + */ + private void ensureObjectClassField(EntityMetadata md, Path objectClassFieldPath) { + FieldTreeNode objectClassNode; + try{ + objectClassNode = md.resolve(objectClassFieldPath); + } + catch(Error e){ + addFieldToParent(md, objectClassFieldPath, + (Field) (objectClassNode = new ArrayField(objectClassFieldPath.getLast(), new SimpleArrayElement(StringType.TYPE)))); + } + if(!(objectClassNode instanceof ArrayField)){ + throw Error.get(MetadataConstants.ERR_FIELD_WRONG_TYPE, objectClassNode.getFullPath().toString()); + } + ArrayField objectClassField = (ArrayField) objectClassNode; + if(!(objectClassField.getElement().getType() instanceof StringType)){ + throw Error.get(MetadataConstants.ERR_FIELD_WRONG_TYPE, objectClassField.getFullPath().toString()); + } + } + + /** + * Ensures the dn field is present on the entity. If not, then it will added. If so, but + * is defined incorrectly, then an {@link Error} will be thrown. + */ + private void ensureDnField(EntityMetadata md, Path dnFieldPath) { + FieldTreeNode dnNode; + try{ + dnNode = md.resolve(dnFieldPath); + } + catch(Error e){ + addFieldToParent(md, dnFieldPath, + (Field)(dnNode = new SimpleField(dnFieldPath.getLast(), StringType.TYPE))); + } + if(!(dnNode.getType() instanceof StringType)){ + throw Error.get(MetadataConstants.ERR_FIELD_WRONG_TYPE, dnNode.getFullPath().toString()); + } + } + + /** + * Adds the newField to the newFieldPath. + * @param md - {@link EntityMetadata} + * @param newFieldPath - {@link Path} to add + * @param newField - {@link Field} to add at the newFieldPath + */ + private void addFieldToParent(EntityMetadata md, Path newFieldPath, Field newField){ + Fields fields; + + if(newFieldPath.numSegments() == 1){ + fields = md.getFields(); + } + else{ + Path parentPath = newFieldPath.prefix(-1); + FieldTreeNode parent = md.resolve(parentPath); + + if(parent instanceof ObjectField){ + fields = ((ObjectField) parent).getFields(); + } + else if (parent instanceof ObjectArrayElement){ + fields = ((ObjectArrayElement) parent).getFields(); + } + else { + throw Error.get(MetadataConstants.ERR_FIELD_WRONG_TYPE, parentPath.toString()); + } + } + fields.addNew(newField); + } + +} diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/TranslatorFromJson.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/TranslatorFromJson.java index d5fc04d..d6f02b5 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/TranslatorFromJson.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/TranslatorFromJson.java @@ -23,19 +23,21 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.NullNode; +import com.redhat.lightblue.common.ldap.LdapErrorCode; import com.redhat.lightblue.metadata.ArrayElement; import com.redhat.lightblue.metadata.ArrayField; import com.redhat.lightblue.metadata.EntityMetadata; import com.redhat.lightblue.metadata.FieldTreeNode; +import com.redhat.lightblue.metadata.MetadataConstants; import com.redhat.lightblue.metadata.ObjectArrayElement; import com.redhat.lightblue.metadata.ObjectField; import com.redhat.lightblue.metadata.ReferenceField; import com.redhat.lightblue.metadata.SimpleArrayElement; import com.redhat.lightblue.metadata.SimpleField; import com.redhat.lightblue.metadata.Type; +import com.redhat.lightblue.util.Error; import com.redhat.lightblue.util.JsonDoc; import com.redhat.lightblue.util.JsonNodeCursor; -import com.redhat.lightblue.util.Path; /** * Defines a class that translates lightblue json nodes into @@ -45,6 +47,8 @@ * * @param - A entity that the specific datastore knows how * to interact with. + * + * @see #translate(JsonDoc, Object) */ public abstract class TranslatorFromJson { @@ -54,7 +58,7 @@ public TranslatorFromJson(EntityMetadata md){ this.md = md; } - protected EntityMetadata getEntityMetadata(){ + public EntityMetadata getEntityMetadata(){ return md; } @@ -67,7 +71,12 @@ protected Object fromJson(Type type, JsonNode node){ } } - protected void translate(JsonDoc document, T target){ + /** + * Translates an entire {@link JsonDoc} to T. + * @param document - {@link JsonDoc} + * @param target - T + */ + public void translate(JsonDoc document, T target){ JsonNodeCursor cursor = document.cursor(); if (!cursor.firstChild()) { //TODO throw exception? @@ -79,36 +88,44 @@ protected void translate(JsonDoc document, T target){ } while (cursor.nextSibling()); } - private void translate(JsonNodeCursor cursor, T target){ - Path path = cursor.getCurrentPath(); + /** + * Uses the current position of the cursor to translate the current node and any children it may have. + * This is ultimately the driver behind this class and may be called recursively by implementing classes to process child nodes.
+ * NOTE: Calling method is responsible for moving the cursor to where it needs to be. + * @param cursor - {@link JsonNodeCursor} + * @param target - T + */ + protected void translate(JsonNodeCursor cursor, T target){ JsonNode node = cursor.getCurrentNode(); - FieldTreeNode fieldNode = md.resolve(path); - - if (fieldNode == null) { - throw new NullPointerException("No Metadata field found for: " + path.toString()); - } - - if (fieldNode instanceof SimpleField) { - translate((SimpleField) fieldNode, path, node, target); - } - else if (fieldNode instanceof ObjectField) { - translate((ObjectField) fieldNode, path, node, target); + FieldTreeNode fieldNode = md.resolve(cursor.getCurrentPath()); + + Error.push(fieldNode.getFullPath().getLast()); + + try{ + if (fieldNode instanceof SimpleField) { + translate((SimpleField) fieldNode, node, target); + } + else if (fieldNode instanceof ObjectField) { + translate((ObjectField) fieldNode, cursor, target); + } + else if (fieldNode instanceof ArrayField) { + translate((ArrayField) fieldNode, cursor, target); + } + else if (fieldNode instanceof ReferenceField) { + translate((ReferenceField) fieldNode, node, target); + } + else{ + throw Error.get(LdapErrorCode.ERR_UNSUPPORTED_FEATURE + fieldNode.getClass().getName(), fieldNode.getFullPath().toString()); + } } - else if (fieldNode instanceof ArrayField) { - translate((ArrayField) fieldNode, cursor, path, target); - } - else if (fieldNode instanceof ReferenceField) { - translate((ReferenceField) fieldNode, path, node, target); - } - else{ - throw new UnsupportedOperationException("Field type is not supported: " + fieldNode.getClass().getName()); + finally{ + Error.pop(); } } - private void translate(ArrayField field, JsonNodeCursor cursor, Path path, T target){ + private void translate(ArrayField field, JsonNodeCursor cursor, T target){ if(!cursor.firstChild()){ - //TODO: throw exception? - return; + throw Error.get(MetadataConstants.ERR_ILL_FORMED_METADATA, cursor.getCurrentPath().toString()); } ArrayElement arrayElement = field.getElement(); @@ -118,25 +135,36 @@ private void translate(ArrayField field, JsonNodeCursor cursor, Path path, T tar do { items.add(fromJson(arrayElement.getType(), cursor.getCurrentNode())); } while (cursor.nextSibling()); - translateSimpleArray(field, path, items, target); + translateSimpleArray(field, items, target); } else if(arrayElement instanceof ObjectArrayElement){ translateObjectArray(field, cursor, target); } else{ - throw new UnsupportedOperationException("ArrayElement type is not supported: " + arrayElement.getClass().getName()); + throw Error.get(LdapErrorCode.ERR_UNSUPPORTED_FEATURE + arrayElement.getClass().getName(), field.getFullPath().toString()); } cursor.parent(); } - protected void translate(ReferenceField field, Path path, JsonNode node, T target){ + protected void translate(ObjectField field, JsonNodeCursor cursor, T target){ + if(!cursor.firstChild()){ + throw Error.get(MetadataConstants.ERR_ILL_FORMED_METADATA, cursor.getCurrentPath().toString()); + } + + do { + translate(cursor, target); + } while (cursor.nextSibling()); + + cursor.parent(); + } + + protected void translate(ReferenceField field, JsonNode node, T target){ //Do nothing by default! } - protected abstract void translate(SimpleField field, Path path, JsonNode node, T target); - protected abstract void translate(ObjectField field, Path path, JsonNode node, T target); - protected abstract void translateSimpleArray(ArrayField field, Path path, List items, T target); + protected abstract void translate(SimpleField field, JsonNode node, T target); + protected abstract void translateSimpleArray(ArrayField field, List items, T target); protected abstract void translateObjectArray(ArrayField field, JsonNodeCursor cursor, T target); } diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslator.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslator.java index 3ac4008..577e36c 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslator.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslator.java @@ -19,6 +19,7 @@ package com.redhat.lightblue.crud.ldap.model; import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; +import com.redhat.lightblue.util.Path; /** * An implementation of {@link LdapFieldNameTranslator} used by crud when @@ -29,13 +30,13 @@ public class TrivialLdapFieldNameTranslator implements LdapFieldNameTranslator{ @Override - public String translateFieldName(String fieldName) { - return fieldName; + public String translateFieldName(Path path) { + return path.getLast(); } @Override - public String translateAttributeName(String attributeName) { - return attributeName; + public Path translateAttributeName(String attributeName) { + return new Path(attributeName); } } diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/FilterTranslator.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/FilterTranslator.java index 58c922b..e027be2 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/FilterTranslator.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/FilterTranslator.java @@ -86,7 +86,7 @@ else if (query instanceof ValueComparisonExpression){ } private Filter translate(ArrayContainsExpression query){ - String attributeName = fieldNameTranslator.translateFieldName(query.getArray().toString()); + String attributeName = fieldNameTranslator.translateFieldName(query.getArray()); List filters = new ArrayList(); for(Value value : query.getValues()){ @@ -131,7 +131,7 @@ private Filter translate(NaryLogicalExpression query){ } private Filter translate(NaryRelationalExpression query){ - String attributeName = fieldNameTranslator.translateFieldName(query.getField().toString()); + String attributeName = fieldNameTranslator.translateFieldName(query.getField()); List filters = new ArrayList(); for(Value value : query.getValues()){ filters.add(Filter.createEqualityFilter(attributeName, value.getValue().toString())); @@ -162,7 +162,7 @@ private Filter translate(UnaryLogicalExpression query){ } private Filter translate(ValueComparisonExpression query){ - String attributeName = fieldNameTranslator.translateFieldName(query.getField().toString()); + String attributeName = fieldNameTranslator.translateFieldName(query.getField()); String rValue = query.getRvalue().getValue().toString(); switch(query.getOp()){ diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslator.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslator.java index 797a831..c6dd5b1 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslator.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslator.java @@ -26,11 +26,13 @@ import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; import com.redhat.lightblue.common.ldap.LightblueUtil; import com.redhat.lightblue.crud.DocCtx; +import com.redhat.lightblue.metadata.ArrayElement; import com.redhat.lightblue.metadata.ArrayField; import com.redhat.lightblue.metadata.EntityMetadata; import com.redhat.lightblue.metadata.FieldCursor; import com.redhat.lightblue.metadata.FieldTreeNode; import com.redhat.lightblue.metadata.Fields; +import com.redhat.lightblue.metadata.ObjectArrayElement; import com.redhat.lightblue.metadata.ObjectField; import com.redhat.lightblue.metadata.ReferenceField; import com.redhat.lightblue.metadata.SimpleArrayElement; @@ -41,6 +43,7 @@ import com.redhat.lightblue.metadata.types.IntegerType; import com.redhat.lightblue.metadata.types.StringType; import com.redhat.lightblue.util.JsonDoc; +import com.redhat.lightblue.util.Path; import com.unboundid.ldap.sdk.Attribute; import com.unboundid.ldap.sdk.SearchResultEntry; @@ -54,79 +57,93 @@ public class ResultTranslator { private final JsonNodeFactory factory; private final EntityMetadata md; private final LdapFieldNameTranslator fieldNameTranslator; + private final Path dnPath; public ResultTranslator(JsonNodeFactory factory, EntityMetadata md, LdapFieldNameTranslator fieldNameTranslator){ this.factory = factory; this.md = md; this.fieldNameTranslator = fieldNameTranslator; + dnPath = fieldNameTranslator.translateAttributeName(LdapConstant.ATTRIBUTE_DN); } public DocCtx translate(SearchResultEntry entry){ FieldCursor cursor = md.getFieldCursor(); - String entityName = md.getEntityInfo().getName(); Fields fields = md.getFields(); + if (cursor.firstChild()) { - return new DocCtx(new JsonDoc(toJson(entry, cursor, entityName, fields))); + JsonDoc jdoc = new JsonDoc(toJson(entry, cursor, fields)); + jdoc.modify(dnPath, StringType.TYPE.toJson(factory, entry.getDN()), true); + return new DocCtx(jdoc); } //TODO: What to do in case of a null value here? return null; } - private JsonNode toJson(SearchResultEntry entry, FieldCursor fieldCursor, String entityName, Fields fields){ + private ObjectNode toJson(SearchResultEntry entry, FieldCursor fieldCursor, Fields fields){ ObjectNode node = factory.objectNode(); - String dnFieldName = fieldNameTranslator.translateAttributeName(LdapConstant.ATTRIBUTE_DN); + String entityName = md.getEntityInfo().getName(); do { - FieldTreeNode field = fieldCursor.getCurrentNode(); - String fieldName = field.getName(); - if(LightblueUtil.isFieldAnArrayCount(fieldName, fields)){ - /* - * This case will be handled by the array itself, allowing this to - * process runs the risk of nulling out the correct value. - */ - continue; - } - else if(dnFieldName.equalsIgnoreCase(fieldName)){ + Path fieldPath = fieldCursor.getCurrentPath(); + + if(dnPath.equals(fieldPath)){ //DN is not handled as a normal attribute, can be skipped. continue; } - - String attributeName = fieldNameTranslator.translateFieldName(fieldName); - Attribute attr = entry.getAttribute(attributeName); - - JsonNode value = null; - if(attr != null){ - if (field instanceof SimpleField) { - value = toJson((SimpleField)field, attr); - } - else if (field instanceof ObjectField) { - value = toJson((ObjectField)field, attr); - } - else if (field instanceof ArrayField){ - value = toJson((ArrayField)field, attr, fieldCursor); - node.set( - LightblueUtil.createArrayCountFieldName(fieldName), - IntegerType.TYPE.toJson(factory, attr.getValues().length)); - } - else if (field instanceof ReferenceField) { - value = toJson((ReferenceField)field, attr); - } - else{ - throw new UnsupportedOperationException("Unknown Field type: " + field.getClass().getName()); - } + else if(LightblueUtil.isFieldObjectType(fieldPath.toString())){ + node.set(fieldPath.toString(), StringType.TYPE.toJson(factory, entityName)); } - else if(LightblueUtil.isFieldObjectType(fieldName)){ - value = StringType.TYPE.toJson(factory, entityName); + else{ + appendToJsonNode(entry, fieldCursor, node, fields); } - node.set(fieldName, value); } while(fieldCursor.nextSibling()); - node.set(dnFieldName, StringType.TYPE.toJson(factory, entry.getDN())); return node; } + private void appendToJsonNode(SearchResultEntry entry, FieldCursor fieldCursor, ObjectNode targetNode, Fields fields){ + FieldTreeNode field = fieldCursor.getCurrentNode(); + String fieldName = field.getName(); + Path path = fieldCursor.getCurrentPath(); + + String attributeName = fieldNameTranslator.translateFieldName(path); + Attribute attr = entry.getAttribute(attributeName); + + JsonNode value = null; + + if(LightblueUtil.isFieldAnArrayCount(fieldName, fields)){ + /* + * This case will be handled by the array itself, allowing this to + * process runs the risk of nulling out the correct value. + */ + return; + } + else if (field instanceof ObjectField) { + value = toJson((ObjectField)field, fieldCursor, entry); + } + else if(attr != null){ + if (field instanceof SimpleField) { + value = toJson((SimpleField)field, attr); + } + else if (field instanceof ArrayField){ + value = toJson((ArrayField)field, attr, fieldCursor); + targetNode.set( + LightblueUtil.createArrayCountFieldName(fieldName), + IntegerType.TYPE.toJson(factory, attr.getValues().length)); + } + else if (field instanceof ReferenceField) { + value = toJson((ReferenceField)field, attr); + } + else{ + throw new UnsupportedOperationException("Unknown Field type: " + field.getClass().getName()); + } + } + + targetNode.set(fieldName, value); + } + private JsonNode toJson(SimpleField field, Attribute attr){ Type type = field.getType(); @@ -148,28 +165,41 @@ else if(type instanceof BinaryType){ return field.getType().toJson(factory, value); } - private JsonNode toJson(ObjectField field, Attribute attr){ - throw new UnsupportedOperationException("ObjectField type not currently supported."); + private JsonNode toJson(ObjectField field, FieldCursor fieldCursor, SearchResultEntry entry){ + if(!fieldCursor.firstChild()){ + //TODO: Should an exception be thrown here? + return null; + } + + JsonNode node = toJson(entry, fieldCursor, field.getFields()); + + fieldCursor.parent(); + + return node; } private JsonNode toJson(ArrayField field, Attribute attr, FieldCursor fieldCursor){ if(!fieldCursor.firstChild()){ + //TODO: Should an exception be thrown here? return null; } - String[] values = attr.getValues(); FieldTreeNode node = fieldCursor.getCurrentNode(); - if(!(node instanceof SimpleArrayElement)){ - throw new UnsupportedOperationException("ArrayElement type is not supported: " + node.getClass().getName()); - } - - //TODO: Determine if LDAP would support an ObjectArrayElement - + ArrayElement arrayElement = field.getElement(); ArrayNode valueNode = factory.arrayNode(); - for(String value : values){ - valueNode.add(node.getType().toJson(factory, value)); + if (arrayElement instanceof SimpleArrayElement) { + String[] values = attr.getValues(); + for(String value : values){ + valueNode.add(node.getType().toJson(factory, value)); + } + } + else if(arrayElement instanceof ObjectArrayElement){ + throw new UnsupportedOperationException("Object ArrayField type is not currently supported."); + } + else{ + throw new UnsupportedOperationException("ArrayElement type is not supported: " + node.getClass().getName()); } fieldCursor.parent(); diff --git a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/SortTranslator.java b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/SortTranslator.java index ecf2745..b4d5993 100644 --- a/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/SortTranslator.java +++ b/lightblue-ldap-crud/src/main/java/com/redhat/lightblue/crud/ldap/translator/SortTranslator.java @@ -58,7 +58,7 @@ private void doTranslate(Sort sort, List data() { }); } + @After + public void after(){ + Error.reset(); + } + private final String fieldName = "testfield"; private final String metadataType; private final String crudValue; diff --git a/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/LdapCrudUtilTest.java b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/LdapCrudUtilTest.java new file mode 100644 index 0000000..78435fb --- /dev/null +++ b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/LdapCrudUtilTest.java @@ -0,0 +1,88 @@ +/* + Copyright 2015 Red Hat, Inc. and/or its affiliates. + + This file is part of lightblue. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package com.redhat.lightblue.crud.ldap; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.redhat.lightblue.common.ldap.LdapConstant; +import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; +import com.redhat.lightblue.crud.ldap.model.TrivialLdapFieldNameTranslator; +import com.redhat.lightblue.metadata.EntityMetadata; +import com.redhat.lightblue.util.Path; + +public class LdapCrudUtilTest { + + public static EntityMetadata createTestEntityMetadataWithLdapProperty(Object property){ + EntityMetadata md = new EntityMetadata("fake"); + md.getEntityInfo().getProperties().put(LdapConstant.BACKEND, property); + + return md; + } + + /** + * Should return an instance of {@link TrivialLdapFieldNameTranslator} + * because the "ldap" property does not exist, ergo no translations. + */ + @Test + public void testGetLdapFieldNameTranslator_NoProperties(){ + EntityMetadata md = new EntityMetadata("fake"); + + LdapFieldNameTranslator translator = LdapCrudUtil.getLdapFieldNameTranslator(md); + assertNotNull(translator); + assertTrue(translator instanceof TrivialLdapFieldNameTranslator); + } + + /** + * An "ldap" property does exist, but the specific implementation is not visible here. + */ + @Test + public void testGetLdapFieldNameTranslator(){ + EntityMetadata md = createTestEntityMetadataWithLdapProperty(new FakeLdapFieldNameTranslator()); + + LdapFieldNameTranslator translator = LdapCrudUtil.getLdapFieldNameTranslator(md); + assertNotNull(translator); + assertTrue(LdapCrudUtil.getLdapFieldNameTranslator(md) instanceof FakeLdapFieldNameTranslator); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetLdapFieldNameTranslator_NotInstanceofLdapFieldNameTranslator(){ + EntityMetadata md = createTestEntityMetadataWithLdapProperty(new Object()); + + LdapCrudUtil.getLdapFieldNameTranslator(md); + } + + /** Fake implementation of {@link LdapFieldNameTranslator} for testing purposes. */ + private static final class FakeLdapFieldNameTranslator implements LdapFieldNameTranslator{ + + @Override + public String translateFieldName(Path path) { + throw new UnsupportedOperationException("method does nothing."); + } + + @Override + public Path translateAttributeName(String attributeName) { + throw new UnsupportedOperationException("method does nothing."); + } + + } + +} diff --git a/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/LdapMetadataListenerTest.java b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/LdapMetadataListenerTest.java new file mode 100644 index 0000000..351ab05 --- /dev/null +++ b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/LdapMetadataListenerTest.java @@ -0,0 +1,232 @@ +/* + Copyright 2015 Red Hat, Inc. and/or its affiliates. + + This file is part of lightblue. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package com.redhat.lightblue.crud.ldap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.redhat.lightblue.common.ldap.LdapConstant; +import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; +import com.redhat.lightblue.metadata.ArrayField; +import com.redhat.lightblue.metadata.EntityMetadata; +import com.redhat.lightblue.metadata.Fields; +import com.redhat.lightblue.metadata.ObjectArrayElement; +import com.redhat.lightblue.metadata.ObjectField; +import com.redhat.lightblue.metadata.SimpleArrayElement; +import com.redhat.lightblue.metadata.SimpleField; +import com.redhat.lightblue.metadata.types.IntegerType; +import com.redhat.lightblue.metadata.types.StringType; +import com.redhat.lightblue.util.Error; +import com.redhat.lightblue.util.Path; + +public class LdapMetadataListenerTest { + + /** + * No fields are defined, so ensure the fields are automatically created. + */ + @Test + public void testBeforeCreateNewSchema(){ + EntityMetadata md = new EntityMetadata("fake"); + + LdapMetadataListener listener = new LdapMetadataListener(); + listener.beforeCreateNewSchema(null, md); + + assertTrue(md.getFields().has(LdapConstant.ATTRIBUTE_DN)); + assertTrue(md.getFields().has(LdapConstant.ATTRIBUTE_OBJECT_CLASS)); + assertTrue(md.getFields().has(LdapConstant.ATTRIBUTE_OBJECT_CLASS + "#")); + } + + /** + * The fields that were defined should remain. + */ + @Test + public void testBeforeCreateNewSchema_alreadyHasDefinedDnAndObjectClass(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + SimpleField dnField = new SimpleField(LdapConstant.ATTRIBUTE_DN, StringType.TYPE); + fields.addNew(dnField); + ArrayField objectClassField = new ArrayField(LdapConstant.ATTRIBUTE_OBJECT_CLASS, new SimpleArrayElement(StringType.TYPE)); + fields.addNew(objectClassField); + SimpleField objectClassCountField = new SimpleField(LdapConstant.ATTRIBUTE_OBJECT_CLASS + "#", IntegerType.TYPE); + fields.addNew(objectClassCountField); + + LdapMetadataListener listener = new LdapMetadataListener(); + listener.beforeCreateNewSchema(null, md); + + assertTrue(md.getFields().has(LdapConstant.ATTRIBUTE_DN)); + assertTrue(md.getFields().has(LdapConstant.ATTRIBUTE_OBJECT_CLASS)); + assertTrue(md.getFields().has(LdapConstant.ATTRIBUTE_OBJECT_CLASS + "#")); + + assertEquals(dnField, md.getFields().getField(LdapConstant.ATTRIBUTE_DN)); + assertEquals(objectClassField, md.getFields().getField(LdapConstant.ATTRIBUTE_OBJECT_CLASS)); + assertEquals(objectClassCountField, md.getFields().getField(LdapConstant.ATTRIBUTE_OBJECT_CLASS + "#")); + } + + /** + * This ensures that if the DN is defined within an object in the metadata that the method + * continues to work as expected. + */ + @Test + public void testBeforeCreateNewSchema_DnInObject(){ + String fieldName = "someobject." + LdapConstant.ATTRIBUTE_DN; + + EntityMetadata md = LdapCrudUtilTest.createTestEntityMetadataWithLdapProperty( + new FakeLdapFieldNameTranslator(fieldName, LdapConstant.ATTRIBUTE_DN)); + md.getFields().addNew(new ObjectField("someobject")); + + LdapMetadataListener listener = new LdapMetadataListener(); + listener.beforeCreateNewSchema(null, md); + + assertNotNull(md.resolve(new Path(fieldName))); + } + + /** + * This ensures that if the DN is defined within an object in the metadata that the method + * continues to work as expected. + */ + @Test + public void testBeforeCreateNewSchema_ObjectClassAndCountInObject(){ + String fieldName = "someobject." + LdapConstant.ATTRIBUTE_OBJECT_CLASS; + + EntityMetadata md = LdapCrudUtilTest.createTestEntityMetadataWithLdapProperty( + new FakeLdapFieldNameTranslator(fieldName, LdapConstant.ATTRIBUTE_OBJECT_CLASS)); + md.getFields().addNew(new ObjectField("someobject")); + + LdapMetadataListener listener = new LdapMetadataListener(); + listener.beforeCreateNewSchema(null, md); + + assertNotNull(md.resolve(new Path(fieldName))); + assertNotNull(md.resolve(new Path(fieldName + "# "))); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedDn_butAsWrongType(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new SimpleField(LdapConstant.ATTRIBUTE_DN, IntegerType.TYPE)); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedDn_butAsWrongField(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new ArrayField(LdapConstant.ATTRIBUTE_DN)); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedObjectClass_butAsWrongType(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new SimpleField(LdapConstant.ATTRIBUTE_OBJECT_CLASS, StringType.TYPE)); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedObjectClass_butAsWrongField(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new SimpleField(LdapConstant.ATTRIBUTE_OBJECT_CLASS)); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedObjectClass_butWithWrongElementType(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new ArrayField(LdapConstant.ATTRIBUTE_OBJECT_CLASS, new SimpleArrayElement(IntegerType.TYPE))); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedObjectClass_butWithWrongElementField(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + ArrayField field = new ArrayField(LdapConstant.ATTRIBUTE_OBJECT_CLASS, new ObjectArrayElement()); + fields.addNew(field); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedObjectClassCount_butAsWrongType(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new SimpleField(LdapConstant.ATTRIBUTE_OBJECT_CLASS + "#", StringType.TYPE)); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + @Test(expected = Error.class) + public void testBeforeCreateNewSchema_alreadyHasDefinedObjectClassCount_butAsWrongField(){ + EntityMetadata md = new EntityMetadata("fake"); + Fields fields = md.getEntitySchema().getFields(); + + fields.addNew(new ArrayField(LdapConstant.ATTRIBUTE_OBJECT_CLASS + "#")); + + new LdapMetadataListener().beforeCreateNewSchema(null, md); + } + + /** Fake implementation of {@link LdapFieldNameTranslator} for testing purposes. */ + private static final class FakeLdapFieldNameTranslator implements LdapFieldNameTranslator{ + + private final String fieldName; + private final String attributeName; + + public FakeLdapFieldNameTranslator(String fieldName, String attributeName){ + this.fieldName = fieldName; + this.attributeName = attributeName; + } + + @Override + public String translateFieldName(Path path) { + if(fieldName.equals(path.toString())){ + return attributeName; + } + return path.toString(); + } + + @Override + public Path translateAttributeName(String attributeName) { + if(this.attributeName.equals(attributeName)) { + return new Path(fieldName); + } + return new Path(attributeName); + } + + } + +} diff --git a/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslatorTest.java b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslatorTest.java index 34f96b9..397d2c5 100644 --- a/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslatorTest.java +++ b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/model/TrivialLdapFieldNameTranslatorTest.java @@ -22,18 +22,20 @@ import org.junit.Test; +import com.redhat.lightblue.util.Path; + public class TrivialLdapFieldNameTranslatorTest { @Test public void testTranslateFieldName(){ String fieldName = "fakeFieldName"; - assertEquals(fieldName, new TrivialLdapFieldNameTranslator().translateFieldName(fieldName)); + assertEquals(fieldName, new TrivialLdapFieldNameTranslator().translateFieldName(new Path(fieldName))); } @Test public void testTranslateAttributeName(){ String attributeName = "fakeAttributeName"; - assertEquals(attributeName, new TrivialLdapFieldNameTranslator().translateAttributeName(attributeName)); + assertEquals(attributeName, new TrivialLdapFieldNameTranslator().translateAttributeName(attributeName).toString()); } } diff --git a/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslatorTest.java b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslatorTest.java index 4fe3c1a..7c5f2b0 100644 --- a/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslatorTest.java +++ b/lightblue-ldap-crud/src/test/java/com/redhat/lightblue/crud/ldap/translator/ResultTranslatorTest.java @@ -378,15 +378,23 @@ public FieldTreeNode resolve(Path p, int level) { new ResultTranslator(factory, md, new TrivialLdapFieldNameTranslator()).translate(result); } - @Test(expected = UnsupportedOperationException.class) + @Test public void testTranslate_ObjectField() throws JSONException{ - SearchResultEntry result = new SearchResultEntry(-1, "uid=john.doe,dc=example,dc=com", new Attribute[]{new Attribute("fake")}); + SearchResultEntry result = new SearchResultEntry(-1, "uid=john.doe,dc=example,dc=com", new Attribute[]{new Attribute("key", "value")}); - EntityMetadata md = fakeEntityMetadata("fakeMetadata", - new ObjectField("fake") - ); + ObjectField objectField = new ObjectField("fakeObject"); + objectField.getFields().addNew(new SimpleField("key", StringType.TYPE)); - new ResultTranslator(factory, md, new TrivialLdapFieldNameTranslator()).translate(result); + EntityMetadata md = fakeEntityMetadata("fakeMetadata", objectField); + + DocCtx document = new ResultTranslator(factory, md, new TrivialLdapFieldNameTranslator()).translate(result); + + assertNotNull(document); + + JSONAssert.assertEquals( + "{\"dn\":\"uid=john.doe,dc=example,dc=com\",\"fakeObject\":{\"key\":\"value\"}}", + document.getOutputDocument().toString(), + true); } @Test(expected = UnsupportedOperationException.class) diff --git a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/AbstractLdapCRUDController.java b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/AbstractLdapCRUDController.java index 4297863..564b988 100644 --- a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/AbstractLdapCRUDController.java +++ b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/AbstractLdapCRUDController.java @@ -22,10 +22,11 @@ import static com.redhat.lightblue.util.test.AbstractJsonNodeTest.loadJsonNode; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.ErrorCollector; +import org.junit.runners.model.MultipleFailureException; import com.fasterxml.jackson.databind.JsonNode; import com.redhat.lightblue.DataError; @@ -41,22 +42,29 @@ public abstract class AbstractLdapCRUDController extends AbstractCRUDController{ @ClassRule public static LdapServerExternalResource ldapServer = LdapServerExternalResource.createDefaultInstance(); - @Rule - public ErrorCollector errorCollector = new ErrorCollector(); - - protected void assertNoErrors(Response response){ + protected void assertNoErrors(Response response) throws MultipleFailureException{ + List errors = new ArrayList(); for(Error error : response.getErrors()){ Exception e = new Exception(error.getMessage(), error); e.printStackTrace(); - errorCollector.addError(e); + errors.add(e); + } + + if(!errors.isEmpty()){ + throw new MultipleFailureException(errors); } } - protected void assertNoDataErrors(Response response){ + protected void assertNoDataErrors(Response response) throws MultipleFailureException{ + List errors = new ArrayList(); for(DataError error : response.getDataErrors()){ Exception e = new Exception("DataError: " + error.toJson().toString()); e.printStackTrace(); - errorCollector.addError(e); + errors.add(e); + } + + if(!errors.isEmpty()){ + throw new MultipleFailureException(errors); } } diff --git a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDControllerTest.java b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDControllerTest.java index dcd41d8..cd47dba 100644 --- a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDControllerTest.java +++ b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDControllerTest.java @@ -55,11 +55,11 @@ public class ITCaseLdapCRUDControllerTest extends AbstractLdapCRUDController{ @BeforeClass public static void beforeClass() throws Exception { - ldapServer.add("ou=Users,dc=example,dc=com", new Attribute[]{ + ldapServer.add(BASEDB_USERS, new Attribute[]{ new Attribute("objectClass", "top"), new Attribute("objectClass", "organizationalUnit"), new Attribute("ou", "Users")}); - ldapServer.add("ou=Departments,dc=example,dc=com", new Attribute[]{ + ldapServer.add(BASEDB_DEPARTMENTS, new Attribute[]{ new Attribute("objectClass", "top"), new Attribute("objectClass", "organizationalUnit"), new Attribute("ou", "Departments")}); diff --git a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_Objects_Test.java b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_Objects_Test.java new file mode 100644 index 0000000..a1c4653 --- /dev/null +++ b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_Objects_Test.java @@ -0,0 +1,96 @@ +/* + Copyright 2015 Red Hat, Inc. and/or its affiliates. + + This file is part of lightblue. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package com.redhat.lightblue.crud.ldap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.skyscreamer.jsonassert.JSONAssert; + +import com.fasterxml.jackson.databind.JsonNode; +import com.redhat.lightblue.Response; +import com.redhat.lightblue.crud.FindRequest; +import com.redhat.lightblue.crud.InsertionRequest; +import com.redhat.lightblue.ldap.test.LdapServerExternalResource; +import com.redhat.lightblue.mongo.test.MongoServerExternalResource; +import com.unboundid.ldap.sdk.Attribute; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ITCaseLdapCRUDController_Objects_Test extends AbstractLdapCRUDController{ + + private static final String BASEDB_USERS = "ou=Users,dc=example,dc=com"; + + @BeforeClass + public static void beforeClass() throws Exception { + ldapServer.add(BASEDB_USERS, new Attribute[]{ + new Attribute("objectClass", "top"), + new Attribute("objectClass", "organizationalUnit"), + new Attribute("ou", "Users")}); + + System.setProperty("ldap.host", "localhost"); + System.setProperty("ldap.port", String.valueOf(LdapServerExternalResource.DEFAULT_PORT)); + System.setProperty("ldap.database", "test"); + System.setProperty("ldap.personWithAddress.basedn", BASEDB_USERS); + + System.setProperty("mongo.host", "localhost"); + System.setProperty("mongo.port", String.valueOf(MongoServerExternalResource.DEFAULT_PORT)); + System.setProperty("mongo.database", "lightblue"); + + initLightblueFactory("./datasources.json", "./metadata/person-with-address-metadata.json"); + } + + @Test + public void test1PersonWithAddress_Insert() throws Exception{ + Response response = lightblueFactory.getMediator().insert( + createRequest_FromResource(InsertionRequest.class, "./crud/insert/person-with-address-insert-single.json")); + + assertNotNull(response); + assertNoErrors(response); + assertNoDataErrors(response); + assertEquals(1, response.getModifiedCount()); + + JsonNode entityData = response.getEntityData(); + assertNotNull(entityData); + JSONAssert.assertEquals( + "[{\"dn\":\"uid=john.doe," + BASEDB_USERS + "\"}]", + entityData.toString(), false); + } + + @Test + public void test2PersonWithAddress_Find() throws Exception{ + Response response = lightblueFactory.getMediator().find( + createRequest_FromResource(FindRequest.class, "./crud/find/person-with-address-find-single.json")); + + assertNotNull(response); + assertNoErrors(response); + assertNoDataErrors(response); + assertEquals(1, response.getMatchCount()); + + JsonNode entityData = response.getEntityData(); + assertNotNull(entityData); + JSONAssert.assertEquals( + "[{\"dn\":\"uid=john.doe," + BASEDB_USERS + "\",\"address\":{\"street\":\"123 Some St.\",\"postalCode\":12345,\"state\":\"NC\"}}]", + entityData.toString(), true); + } + +} diff --git a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_WithProperties_Test.java b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_WithProperties_Test.java index 8f27c99..b0786de 100644 --- a/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_WithProperties_Test.java +++ b/lightblue-ldap-integration-test/src/test/java/com/redhat/lightblue/crud/ldap/ITCaseLdapCRUDController_WithProperties_Test.java @@ -47,7 +47,7 @@ public class ITCaseLdapCRUDController_WithProperties_Test extends AbstractLdapCR @BeforeClass public static void beforeClass() throws Exception { - ldapServer.add("ou=Customers,dc=example,dc=com", new Attribute[]{ + ldapServer.add(BASEDB_CUSTOMERS, new Attribute[]{ new Attribute("objectClass", "top"), new Attribute("objectClass", "organizationalUnit"), new Attribute("ou", "Customers")}); diff --git a/lightblue-ldap-integration-test/src/test/resources/crud/find/person-with-address-find-single.json b/lightblue-ldap-integration-test/src/test/resources/crud/find/person-with-address-find-single.json new file mode 100644 index 0000000..5c4c987 --- /dev/null +++ b/lightblue-ldap-integration-test/src/test/resources/crud/find/person-with-address-find-single.json @@ -0,0 +1,13 @@ +{ + "entity": "personWithAddress", + "entityVersion": "1.0.0", + "projection": [ + {"field": "dn"}, + {"field": "address.*"} + ], + "query": { + "field": "uid", + "op": "$eq", + "rvalue": "john.doe" + } +} diff --git a/lightblue-ldap-integration-test/src/test/resources/crud/insert/person-with-address-insert-single.json b/lightblue-ldap-integration-test/src/test/resources/crud/insert/person-with-address-insert-single.json new file mode 100644 index 0000000..3124e2a --- /dev/null +++ b/lightblue-ldap-integration-test/src/test/resources/crud/insert/person-with-address-insert-single.json @@ -0,0 +1,19 @@ +{ + "entity": "personWithAddress", + "entityVersion": "1.0.0", + "projection": { + "field": "dn" + }, + "data": { + "objectClass": ["top", "person", "organizationalPerson", "inetOrgPerson"], + "uid": "john.doe", + "givenName": "John", + "sn": "Doe", + "cn": "John Doe", + "address": { + "street": "123 Some St.", + "postalCode": 12345, + "state": "NC" + } + } +} \ No newline at end of file diff --git a/lightblue-ldap-integration-test/src/test/resources/metadata/person-with-address-metadata.json b/lightblue-ldap-integration-test/src/test/resources/metadata/person-with-address-metadata.json new file mode 100644 index 0000000..78f16f0 --- /dev/null +++ b/lightblue-ldap-integration-test/src/test/resources/metadata/person-with-address-metadata.json @@ -0,0 +1,49 @@ +{ + "entityInfo": { + "name": "personWithAddress", + "datastore": { + "backend":"ldap", + "database": "${ldap.database}", + "basedn": "${ldap.personWithAddress.basedn}", + "uniqueattr": "uid" + }, + "ldap": { + "fieldsToAttributes": [ + { + "field": "address.state", + "attribute": "st" + } + ] + } + }, + "schema": { + "name": "personWithAddress", + "version": { + "value": "1.0.0", + "changelog": "blahblah" + }, + "status": { + "value": "active" + }, + "access" : { + "insert": ["anyone"], + "update": ["anyone"], + "delete": ["anyone"], + "find": ["anyone"] + }, + "fields": { + "uid": {"type": "string"}, + "givenName": {"type": "string"}, + "sn": {"type": "string"}, + "cn": {"type": "string"}, + "address": { + "type": "object", + "fields": { + "street": {"type": "string"}, + "postalCode": {"type": "integer"}, + "state": {"type": "string"} + } + } + } + } +} diff --git a/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadata.java b/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadata.java index 7f5fe9b..f5fc0f6 100644 --- a/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadata.java +++ b/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadata.java @@ -20,9 +20,11 @@ import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.redhat.lightblue.common.ldap.LdapFieldNameTranslator; +import com.redhat.lightblue.util.Path; /** * Container for special ldap properties parsed from the metadata.json file. @@ -33,42 +35,49 @@ */ public class LdapMetadata implements LdapFieldNameTranslator{ - private final Map fieldsToAttributes = new HashMap(); + private final BiMap fieldsToAttributes = HashBiMap.create(); /** * Returns an immutable copy of the internal collection of {@link FieldAttributeMapping}s. * @return a collection of {@link FieldAttributeMapping}s. */ - public Map getFieldsToAttributes(){ - return new HashMap(fieldsToAttributes); + public Map getFieldsToAttributes(){ + return new HashMap(fieldsToAttributes); } @Override - public String translateFieldName(String fieldName){ - String attributeName = fieldsToAttributes.get(fieldName); - if(attributeName == null){ - return fieldName; + public String translateFieldName(Path path){ + String attributeName = fieldsToAttributes.get(path); + if(attributeName != null){ + return attributeName; } - return attributeName; + Path last = path.suffix(1); + attributeName = fieldsToAttributes.get(last); + if(attributeName != null){ + return attributeName; + } + + return last.toString(); } @Override - public String translateAttributeName(String attributeName){ - for(Entry f2a : fieldsToAttributes.entrySet()){ - if(f2a.getValue().equalsIgnoreCase(attributeName)){ - return f2a.getKey(); - } + public Path translateAttributeName(String attributeName){ + Path fieldPath = fieldsToAttributes.inverse().get(attributeName); + + if(fieldPath == null){ + return new Path(attributeName); } - return attributeName; + + return fieldPath; } /** * Adds a {@link FieldAttributeMapping} to this {@link LdapMetadata}. * @param fieldAttributeMapping - {@link FieldAttributeMapping} */ - public void addFieldToAttribute(String fieldName, String attributeName){ - fieldsToAttributes.put(fieldName, attributeName); + public void addFieldToAttribute(Path fieldPath, String attributeName){ + fieldsToAttributes.put(fieldPath, attributeName); } } diff --git a/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParser.java b/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParser.java index 5ff8124..382fee4 100644 --- a/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParser.java +++ b/lightblue-ldap-metadata/src/main/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParser.java @@ -27,6 +27,7 @@ import com.redhat.lightblue.metadata.parser.MetadataParser; import com.redhat.lightblue.metadata.parser.PropertyParser; import com.redhat.lightblue.util.Error; +import com.redhat.lightblue.util.Path; /** * {@link PropertyParser} implementation for LDAP. @@ -51,7 +52,7 @@ public com.redhat.lightblue.metadata.ldap.model.LdapMetadata parse(String name, if(fieldsToAttributesNode != null){ for(T fieldToAttributeNode : fieldsToAttributesNode){ ldapMetadata.addFieldToAttribute( - p.getRequiredStringProperty(fieldToAttributeNode, FIELD), + new Path(p.getRequiredStringProperty(fieldToAttributeNode, FIELD)), p.getRequiredStringProperty(fieldToAttributeNode, ATTRIBUTE)); } } @@ -70,9 +71,9 @@ public void convert(MetadataParser p, T emptyNode, Object object) { if(!ldapMetadata.getFieldsToAttributes().isEmpty()){ Object fieldsToAttributesNode = p.newArrayField(emptyNode, FIELDS_TO_ATTRIBUTES); - for(Entry entry : ldapMetadata.getFieldsToAttributes().entrySet()){ + for(Entry entry : ldapMetadata.getFieldsToAttributes().entrySet()){ T fieldToAttributeNode = p.newNode(); - p.putString(fieldToAttributeNode, FIELD, entry.getKey()); + p.putString(fieldToAttributeNode, FIELD, entry.getKey().toString()); p.putString(fieldToAttributeNode, ATTRIBUTE, entry.getValue()); p.addObjectToArray(fieldsToAttributesNode, fieldToAttributeNode); diff --git a/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadataTest.java b/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadataTest.java index 9ee1f1b..29bc71a 100644 --- a/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadataTest.java +++ b/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/model/LdapMetadataTest.java @@ -27,55 +27,92 @@ import org.junit.Test; +import com.redhat.lightblue.util.Path; + public class LdapMetadataTest { @Test public void testTranslateFieldName(){ - String fieldName = "fakeFieldName"; + Path fieldName = new Path("fakeFieldName"); String attributeName = "fakeAttributeName"; - LdapMetadata property = new LdapMetadata(); - property.addFieldToAttribute(fieldName, attributeName); - property.addFieldToAttribute("anotherField", "anotherAttribute"); + LdapMetadata metadata = new LdapMetadata(); + metadata.addFieldToAttribute(fieldName, attributeName); + metadata.addFieldToAttribute(new Path("anotherField"), "anotherAttribute"); - assertEquals(attributeName, property.translateFieldName(fieldName)); + assertEquals(attributeName, metadata.translateFieldName(new Path(fieldName))); + } + + @Test + public void testTranslateFieldName_WithPath(){ + Path fieldName = new Path("fakePath.fakeFieldName"); + String attributeName = "fakeAttributeName"; + + LdapMetadata metadata = new LdapMetadata(); + metadata.addFieldToAttribute(fieldName, attributeName); + + assertEquals(attributeName, metadata.translateFieldName(new Path(fieldName))); + } + + @Test + public void testTranslateFieldName_WithPath_MatchesOnTail(){ + Path fieldName = new Path("fakeFieldName"); + String pathedFieldName = "fakePath." + fieldName; + String attributeName = "fakeAttributeName"; + + LdapMetadata metadata = new LdapMetadata(); + metadata.addFieldToAttribute(fieldName, attributeName); + + assertEquals(attributeName, metadata.translateFieldName(new Path(pathedFieldName))); } @Test public void testTranslateFieldName_ValueNotPresent(){ String fieldName = "fakeFieldName"; - assertEquals(fieldName, new LdapMetadata().translateFieldName(fieldName)); + assertEquals(fieldName, new LdapMetadata().translateFieldName(new Path(fieldName))); } @Test public void testTranslateAttributeName(){ - String fieldName = "fakeFieldName"; + Path fieldName = new Path("fakeFieldName"); + String attributeName = "fakeAttributeName"; + + LdapMetadata metadata = new LdapMetadata(); + metadata.addFieldToAttribute(fieldName, attributeName); + metadata.addFieldToAttribute(new Path("anotherField"), "anotherAttribute"); + + assertEquals(fieldName, metadata.translateAttributeName(attributeName)); + } + + @Test + public void testTranslateAttributeName_WithPath(){ + Path fieldName = new Path("somePath.fakeFieldName"); String attributeName = "fakeAttributeName"; - LdapMetadata property = new LdapMetadata(); - property.addFieldToAttribute(fieldName, attributeName); - property.addFieldToAttribute("anotherField", "anotherAttribute"); + LdapMetadata metadata = new LdapMetadata(); + metadata.addFieldToAttribute(fieldName, attributeName); + metadata.addFieldToAttribute(new Path("anotherField"), "anotherAttribute"); - assertEquals(fieldName, property.translateAttributeName(attributeName)); + assertEquals(fieldName, metadata.translateAttributeName(attributeName)); } @Test public void testTranslateAttributeName_ValueNotPresent(){ String attributeName = "fakeAttributeName"; - assertEquals(attributeName, new LdapMetadata().translateAttributeName(attributeName)); + assertEquals(attributeName, new LdapMetadata().translateAttributeName(attributeName).toString()); } @Test public void testGetFieldsToAttributes_AssertImmutable(){ - LdapMetadata property = new LdapMetadata(); - property.addFieldToAttribute("anotherField", "anotherAttribute"); + LdapMetadata metadata = new LdapMetadata(); + metadata.addFieldToAttribute(new Path("anotherField"), "anotherAttribute"); - Map fieldsToAttributes = property.getFieldsToAttributes(); + Map fieldsToAttributes = metadata.getFieldsToAttributes(); assertNotNull(fieldsToAttributes); - assertMapEquivalent(fieldsToAttributes, property.getFieldsToAttributes()); - assertNotSame(fieldsToAttributes, property.getFieldsToAttributes()); + assertMapEquivalent(fieldsToAttributes, metadata.getFieldsToAttributes()); + assertNotSame(fieldsToAttributes, metadata.getFieldsToAttributes()); } } diff --git a/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParserTest.java b/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParserTest.java index 34b46ee..9a947ab 100644 --- a/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParserTest.java +++ b/lightblue-ldap-metadata/src/test/java/com/redhat/lightblue/metadata/ldap/parser/LdapPropertyParserTest.java @@ -39,6 +39,7 @@ import com.redhat.lightblue.common.ldap.LdapConstant; import com.redhat.lightblue.metadata.ldap.model.LdapMetadata; import com.redhat.lightblue.test.MetadataUtil; +import com.redhat.lightblue.util.Path; public class LdapPropertyParserTest { @@ -54,13 +55,13 @@ public void testParse() throws IOException{ assertNotNull(ldapMetadata); - Map fieldsToAttributes = ldapMetadata.getFieldsToAttributes(); + Map fieldsToAttributes = ldapMetadata.getFieldsToAttributes(); assertNotNull(fieldsToAttributes); assertEquals(2, fieldsToAttributes.size()); - Map expected = new HashMap(); - expected.put("firstName", "givenName"); - expected.put("lastName", "sn"); + Map expected = new HashMap(); + expected.put(new Path("firstName"), "givenName"); + expected.put(new Path("lastName"), "sn"); assertMapEquivalent(expected, fieldsToAttributes); } @@ -87,8 +88,8 @@ public void testParse_IncorrectBackend(){ @Test public void testConvert() throws IOException, JSONException{ LdapMetadata ldapMetadata = new LdapMetadata(); - ldapMetadata.addFieldToAttribute("firstName", "givenName"); - ldapMetadata.addFieldToAttribute("lastName", "sn"); + ldapMetadata.addFieldToAttribute(new Path("firstName"), "givenName"); + ldapMetadata.addFieldToAttribute(new Path("lastName"), "sn"); JsonNode node = json("{}");