/** * Call beforeModification/Creation/Deletion methods. * * @param modificationQueue * @param securityContext * @param errorBuffer * @param doValidation * @return valid * @throws FrameworkException */ public boolean doValidationAndIndexing( ModificationQueue modificationQueue, SecurityContext securityContext, ErrorBuffer errorBuffer, boolean doValidation) throws FrameworkException { boolean valid = true; // examine only the last 4 bits here switch (status & 0x000f) { case 6: // created, modified => only creation callback will be called case 4: // created => creation callback case 2: // modified => modification callback if (doValidation) { valid &= validate(securityContext, errorBuffer); } object.indexPassiveProperties(); break; case 1: // deleted => deletion callback object.removeFromIndex(); break; default: break; } return valid; }
@Override public void processMessage(final WebSocketMessage webSocketData) { final GraphObject obj = getGraphObject(webSocketData.getId()); String key = (String) webSocketData.getNodeData().get("key"); if (obj != null) { PropertyKey propertyKey = StructrApp.getConfiguration().getPropertyKeyForJSONName(obj.getClass(), key); PropertyConverter converter = propertyKey.inputConverter(getWebSocket().getSecurityContext()); Object value = obj.getProperty(propertyKey); if (converter != null) { try { value = converter.revert(value); } catch (FrameworkException ex) { getWebSocket() .send(MessageBuilder.status().code(400).message(ex.getMessage()).build(), true); } } webSocketData.setNodeData(key, value); // send only over local connection (no broadcast) getWebSocket().send(webSocketData, true); } else { getWebSocket().send(MessageBuilder.status().code(404).build(), true); } }
@Override public List<GraphObject> getProperty( SecurityContext securityContext, GraphObject obj, boolean applyConverter, Predicate<GraphObject> predicate) { if (obj instanceof AbstractNode) { try { final String query = Scripting.replaceVariables(new ActionContext(securityContext), obj, this.format); final Map<String, Object> parameters = new LinkedHashMap<>(); parameters.put("id", obj.getUuid()); parameters.put("type", obj.getType()); return StructrApp.getInstance(securityContext) .command(CypherQueryCommand.class) .execute(query, parameters); } catch (Throwable t) { t.printStackTrace(); } } return null; }
/** * Call validators. This must be synchronized globally * * @param securityContext * @param errorBuffer * @return valid */ private boolean validate(SecurityContext securityContext, ErrorBuffer errorBuffer) { boolean valid = true; for (PropertyKey key : removedProperties.keySet()) { List<PropertyValidator> validators = key.getValidators(); for (PropertyValidator validator : validators) { Object value = object.getProperty(key); valid &= validator.isValid(securityContext, object, key, value, errorBuffer); } } // explicitly call isValid() return valid && object.isValid(errorBuffer); }
public Object transformOutput(final ActionContext actionContext, final GraphObject source) throws FrameworkException { if (outputFunction == null) { return source.getProperty(sourceProperty); } // output transformation requested actionContext.setConstant("input", source); return Scripting.evaluate(actionContext, null, "${" + outputFunction + "}"); }
@Override public boolean isValid(GraphObject object, String key, T value, ErrorBuffer errorBuffer) { if (value == null) { errorBuffer.add(object.getType(), new EmptyPropertyToken(key)); } else { if (values.contains(value)) { return true; } else { errorBuffer.add(object.getType(), new ValueToken(key, values.toArray())); } } return false; }
public void delete(boolean passive) { int statusBefore = status; if (passive) { status |= STATE_DELETED_PASSIVELY; } status |= STATE_DELETED; if (status != statusBefore) { // copy all properties on deletion for (final PropertyKey key : object.getPropertyKeys(PropertyView.Public)) { removedProperties.put(key, object.getProperty(key)); } modified = true; } updateCache(); }
@Override public boolean isValid( GraphObject object, PropertyKey<String> key, String value, ErrorBuffer errorBuffer) { if (key == null) { return false; } if (StringUtils.isBlank(value)) { errorBuffer.add(object.getType(), new EmptyPropertyToken(key)); return false; } // FIXME: search should be case-sensitive! List<SearchAttribute> attrs = new LinkedList<SearchAttribute>(); attrs.add(Search.andExactName(value)); attrs.add(Search.andType(type)); // just check for existance try { Result nodes = Services.command(securityContext, SearchNodeCommand.class).execute(attrs); if (nodes != null && !nodes.isEmpty()) { return true; } else { errorBuffer.add(object.getType(), new PropertyNotFoundToken(key, value)); return false; } } catch (FrameworkException fex) { // handle error } return false; }
@Override public List<GraphObject> doGet(List<VetoableGraphObjectListener> listeners) throws PathException { List<? extends GraphObject> results = relationshipConstraint.doGet(listeners); long desiredId = idConstraint.getId(); GraphObject desiredObject = null; for (GraphObject obj : results) { if (obj.getId() == desiredId) { desiredObject = obj; break; } } // if object was found, return it if (desiredObject != null) { List<GraphObject> resultList = new LinkedList<GraphObject>(); resultList.add(desiredObject); return resultList; } throw new NotFoundException(); }
@Override public T getProperty( final SecurityContext securityContext, final GraphObject obj, final boolean applyConverter, final Predicate<GraphObject> predicate) { Object value = null; final PropertyContainer propertyContainer = obj.getPropertyContainer(); if (propertyContainer != null) { // this may throw a java.lang.IllegalStateException: Relationship[<id>] has been deleted in // this tx if (propertyContainer.hasProperty(dbName())) { value = propertyContainer.getProperty(dbName()); } } if (applyConverter) { // apply property converters PropertyConverter converter = databaseConverter(securityContext, obj); if (converter != null) { try { value = converter.revert(value); } catch (Throwable t) { logger.warn( "Unable to convert property {} of type {}: {}", new Object[] {dbName(), getClass().getSimpleName(), t}); logger.warn("", t); } } } // no value found, use schema default if (value == null) { value = defaultValue(); } return (T) value; }
public GraphObjectModificationState(GraphObject object) { this.object = object; this.isNode = (object instanceof NodeInterface); if (!isNode) { this.relType = ((RelationshipInterface) object).getRelType(); } // store uuid for later use this.uuid = object.getUuid(); if (changeLogEnabled) { // create on demand changeLog = new StringBuilder(); } }
@Override public Map<String, Object> getData(final SecurityContext securityContext) throws FrameworkException { return PropertyMap.javaTypeToInputType(securityContext, object.getClass(), modifiedProperties); }
@Override public int compare(GraphObject n1, GraphObject n2) { if (n1 == null || n2 == null) { logger.log(Level.WARNING, "Cannot compare null objects!"); return -1; // FIXME: this should throw a NPE! } try { Comparable c1 = n1.getComparableProperty(sortKey); Comparable c2 = n2.getComparableProperty(sortKey); if (c1 == null || c2 == null) { try { logger.log( Level.WARNING, "Cannot compare {0} of type {1} to {2} of type {3}, sort key {4} not found.", new Object[] { n1.getProperty(AbstractNode.uuid), n1.getProperty(AbstractNode.type), n2.getProperty(AbstractNode.uuid), n2.getProperty(AbstractNode.type), sortKey }); } catch (Throwable t) { logger.log(Level.SEVERE, "Error in comparator", t); } return -1; } if (DESCENDING.equalsIgnoreCase(sortOrder)) { return (c2.compareTo(c1)); } else { return (c1.compareTo(c2)); } } catch (Throwable t) { t.printStackTrace(); logger.log( Level.WARNING, "Cannot compare properties {0} of type {1} to {2} of type {3}, property {4} error.", new Object[] { n1.getProperty(AbstractNode.uuid), n1.getProperty(AbstractNode.type), n2.getProperty(AbstractNode.uuid), n2.getProperty(AbstractNode.type), sortKey }); } return 0; }
@Override public String toString() { return object.getClass().getSimpleName() + "(" + object + "); " + status; }
/** * Call beforeModification/Creation/Deletion methods. * * @param modificationQueue * @param securityContext * @param errorBuffer * @return valid * @throws FrameworkException */ public boolean doInnerCallback( ModificationQueue modificationQueue, SecurityContext securityContext, ErrorBuffer errorBuffer) throws FrameworkException { boolean valid = true; // check for modification propagation along the relationships if ((status & STATE_PROPAGATING_MODIFICATION) == STATE_PROPAGATING_MODIFICATION && object instanceof AbstractNode) { Set<AbstractNode> nodes = ((AbstractNode) object).getNodesForModificationPropagation(); if (nodes != null) { for (AbstractNode node : nodes) { modificationQueue.propagatedModification(node); } } } // examine only the last 4 bits here switch (status & 0x000f) { case 15: case 14: case 13: case 12: case 11: case 10: case 9: case 8: // since all values >= 8 mean that the object was passively deleted, no action has to // be taken // (no callback for passive deletion!) break; case 7: // created, modified, deleted, poor guy => no callback break; case 6: // created, modified => only creation callback will be called valid &= object.onCreation(securityContext, errorBuffer); break; case 5: // created, deleted => no callback break; case 4: // created => creation callback valid &= object.onCreation(securityContext, errorBuffer); break; case 3: // modified, deleted => deletion callback valid &= object.onDeletion(securityContext, errorBuffer, removedProperties); break; case 2: // modified => modification callback valid &= object.onModification(securityContext, errorBuffer); break; case 1: // deleted => deletion callback valid &= object.onDeletion(securityContext, errorBuffer, removedProperties); break; case 0: // no action, no callback break; default: break; } // mark as finished modified = false; return valid; }
public Object evaluate( final GraphObject entity, final String key, final Object data, final String defaultValue, final int depth) throws FrameworkException { Object value = constants.get(key); if (value == null) { // special HttpServletRequest handling if (data instanceof HttpServletRequest) { value = ((HttpServletRequest) data).getParameter(key); } // special handling of maps.. if (data instanceof Map) { value = ((Map) data).get(key); } if (data != null) { if (data instanceof GraphObject) { value = ((GraphObject) data).evaluate(securityContext, key, defaultValue); } else { switch (key) { case "size": if (data instanceof Collection) { return ((Collection) data).size(); } if (data.getClass().isArray()) { return ((Object[]) data).length; } break; } } } else { // "data-less" keywords to start the evaluation chain switch (key) { case "request": return securityContext.getRequest(); case "host": return securityContext.getRequest().getServerName(); case "port": return securityContext.getRequest().getServerPort(); case "pathInfo": case "path_info": return securityContext.getRequest().getPathInfo(); case "parameterMap": case "parameter_map": return securityContext.getRequest().getParameterMap(); case "remoteAddress": case "remote_address": final String remoteAddress = securityContext.getRequest().getHeader("X-FORWARDED-FOR"); if (remoteAddress == null) { return securityContext.getRequest().getRemoteAddr(); } return remoteAddress; case "response": if (securityContext != null) { final HttpServletResponse response = securityContext.getResponse(); if (response != null) { try { // return output stream of HTTP response for streaming return response.getOutputStream(); } catch (IOException ioex) { logger.warn("", ioex); } } } return null; case "now": return DatePropertyParser.format(new Date(), DateProperty.DEFAULT_FORMAT); case "me": return securityContext.getUser(false); case "element": logger.warn( "The \"element\" keyword is deprecated! Please use \"this\" instead. Used in {}", entity.getProperty(GraphObject.id)); case "this": return entity; case "locale": return locale != null ? locale.toString() : null; } } } if (value == null && defaultValue != null) { return Function.numberOrString(defaultValue); } return value; }
@Override public Object setProperty( final SecurityContext securityContext, final GraphObject obj, final T value) throws FrameworkException { final PropertyConverter converter = databaseConverter(securityContext, obj); final Object convertedValue; if (converter != null) { convertedValue = converter.convert(value); } else { convertedValue = value; } final PropertyContainer propertyContainer = obj.getPropertyContainer(); if (propertyContainer != null) { if (!TransactionCommand.inTransaction()) { throw new NotInTransactionException("setProperty outside of transaction"); } boolean internalSystemPropertiesUnlocked = (obj instanceof CreationContainer); // notify only non-system properties // collect modified properties if (obj instanceof AbstractNode) { if (!unvalidated) { TransactionCommand.nodeModified( securityContext.getCachedUser(), (AbstractNode) obj, AbstractPrimitiveProperty.this, propertyContainer.hasProperty(dbName()) ? propertyContainer.getProperty(dbName()) : null, value); } internalSystemPropertiesUnlocked = ((AbstractNode) obj).internalSystemPropertiesUnlocked; } else if (obj instanceof AbstractRelationship) { if (!unvalidated) { TransactionCommand.relationshipModified( securityContext.getCachedUser(), (AbstractRelationship) obj, AbstractPrimitiveProperty.this, propertyContainer.hasProperty(dbName()) ? propertyContainer.getProperty(dbName()) : null, value); } internalSystemPropertiesUnlocked = ((AbstractRelationship) obj).internalSystemPropertiesUnlocked; } // catch all sorts of errors and wrap them in a FrameworkException try { // save space if (convertedValue == null) { propertyContainer.removeProperty(dbName()); } else { if (!isSystemInternal() || internalSystemPropertiesUnlocked) { propertyContainer.setProperty(dbName(), convertedValue); } else { logger.warn( "Tried to set internal system property {} to {}. Action was denied.", new Object[] {dbName(), convertedValue}); } } updateAccessInformation(securityContext, propertyContainer); } catch (Throwable t) { // throw FrameworkException with the given cause final FrameworkException fex = new FrameworkException( 500, "Unable to set property " + jsonName() + " on entity with ID " + obj.getUuid() + ": " + t.toString()); fex.initCause(t); throw fex; } if (isIndexed()) { // do indexing, needs to be done after // setProperty to make spatial index // work if (!isPassivelyIndexed()) { index(obj, convertedValue); } } } return null; }
/** * Call afterModification/Creation/Deletion methods. * * @param securityContext */ public void doOuterCallback(SecurityContext securityContext) { if ((status & (STATE_DELETED | STATE_DELETED_PASSIVELY)) == 0) { if ((status & STATE_PROPAGATED_MODIFICATION) == STATE_PROPAGATED_MODIFICATION) { object.propagatedModification(securityContext); } if ((status & STATE_LOCATION_MODIFIED) == STATE_LOCATION_MODIFIED) { object.locationModified(securityContext); } if ((status & STATE_SECURITY_MODIFIED) == STATE_SECURITY_MODIFIED) { object.securityModified(securityContext); } if ((status & STATE_OWNER_MODIFIED) == STATE_OWNER_MODIFIED) { object.ownerModified(securityContext); } } // examine only the last 4 bits here switch (status & 0x000f) { case 15: case 14: case 13: case 12: case 11: case 10: case 9: case 8: // since all values >= 8 mean that the object was passively deleted, no action has to // be taken // (no callback for passive deletion!) break; case 7: // created, modified, deleted, poor guy => no callback break; case 6: // created, modified => only creation callback will be called object.afterCreation(securityContext); break; case 5: // created, deleted => no callback break; case 4: // created => creation callback object.afterCreation(securityContext); break; case 3: // modified, deleted => deletion callback object.afterDeletion(securityContext, removedProperties); break; case 2: // modified => modification callback object.afterModification(securityContext); break; case 1: // deleted => deletion callback object.afterDeletion(securityContext, removedProperties); break; case 0: // no action, no callback break; default: break; } }
// ~--- methods -------------------------------------------------------- @Override public void processMessage(final WebSocketMessage webSocketData) { final String keyString = (String) webSocketData.getNodeData().get("key"); if (keyString == null) { logger.error("Unable to remove given object from collection: key is null"); getWebSocket().send(MessageBuilder.status().code(400).build(), true); } final String idToRemove = (String) webSocketData.getNodeData().get("idToRemove"); if (idToRemove == null) { logger.error("Unable to remove given object from collection: idToRemove is null"); getWebSocket().send(MessageBuilder.status().code(400).build(), true); } GraphObject obj = getNode(webSocketData.getId()); if (obj != null) { if (!((AbstractNode) obj).isGranted(Permission.write, getWebSocket().getSecurityContext())) { getWebSocket() .send(MessageBuilder.status().message("No write permission").code(400).build(), true); logger.warn( "No write permission for {} on {}", new Object[] {getWebSocket().getCurrentUser().toString(), obj.toString()}); return; } } if (obj == null) { // No node? Try to find relationship obj = getRelationship(webSocketData.getId()); } GraphObject objToRemove = getNode(idToRemove); if (obj != null && objToRemove != null) { try { PropertyKey key = StructrApp.getConfiguration().getPropertyKeyForJSONName(obj.getClass(), keyString); if (key != null) { List collection = (List) obj.getProperty(key); collection.remove(objToRemove); obj.setProperties(obj.getSecurityContext(), new PropertyMap(key, collection)); if (obj instanceof NodeInterface) { TransactionCommand.registerNodeCallback((NodeInterface) obj, callback); } else if (obj instanceof RelationshipInterface) { TransactionCommand.registerRelCallback((RelationshipInterface) obj, callback); } } } catch (FrameworkException ex) { logger.error("Unable to set properties: {}", ((FrameworkException) ex).toString()); getWebSocket().send(MessageBuilder.status().code(400).build(), true); } } else { logger.warn("Graph object with uuid {} not found.", webSocketData.getId()); getWebSocket().send(MessageBuilder.status().code(404).build(), true); } }