You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/11/25 12:26:04 UTC

[1/5] syncope git commit: Upgrading Activiti

Repository: syncope
Updated Branches:
  refs/heads/master 7b1cb89bb -> e9ff5c73e


Upgrading Activiti


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/560fc4df
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/560fc4df
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/560fc4df

Branch: refs/heads/master
Commit: 560fc4df106825c80520f941beb2e0e601cb056f
Parents: 1bee9d7
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Nov 18 14:54:17 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Nov 18 14:54:17 2015 +0100

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/560fc4df/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5e6921b..fc8a0b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -339,7 +339,7 @@ under the License.
     <commons-lang.version>3.3.2</commons-lang.version>
     <commons-codec.version>1.10</commons-codec.version>
 
-    <activiti.version>5.16.4</activiti.version>
+    <activiti.version>5.16.5</activiti.version>
 
     <aspectj.version>1.8.7</aspectj.version>
 


[4/5] syncope git commit: Cleaning up invalid AnyObjectService#list (with no params) method, and adjusting CLI accordingly

Posted by il...@apache.org.
Cleaning up invalid AnyObjectService#list (with no params) method, and adjusting CLI accordingly


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/0f938e72
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/0f938e72
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/0f938e72

Branch: refs/heads/master
Commit: 0f938e72d6723517fe451010ec3a2e398b3e4ecc
Parents: bbb051f
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Nov 25 12:07:53 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Nov 25 12:09:02 2015 +0100

----------------------------------------------------------------------
 .../cli/commands/any/AbstractAnyCommand.java    |  27 -----
 .../client/cli/commands/any/AnyCommand.java     | 111 -------------------
 .../client/cli/commands/any/AnyDelete.java      |  59 ----------
 .../client/cli/commands/any/AnyList.java        |  50 ---------
 .../client/cli/commands/any/AnyRead.java        |  58 ----------
 ...yReadAttributeBySchemaTypeAndSchemaName.java |  61 ----------
 .../any/AnyReadAttributesBySchemaType.java      |  64 -----------
 .../cli/commands/any/AnyResultManager.java      |  80 -------------
 .../cli/commands/any/AnySyncopeOperations.java  |  53 ---------
 .../anyobject/AbstractAnyObjectCommand.java     |  27 +++++
 .../commands/anyobject/AnyObjectCommand.java    | 111 +++++++++++++++++++
 .../cli/commands/anyobject/AnyObjectDelete.java |  59 ++++++++++
 .../cli/commands/anyobject/AnyObjectList.java   |  50 +++++++++
 .../cli/commands/anyobject/AnyObjectRead.java   |  58 ++++++++++
 ...tReadAttributeBySchemaTypeAndSchemaName.java |  61 ++++++++++
 .../AnyObjectReadAttributesBySchemaType.java    |  64 +++++++++++
 .../anyobject/AnyObjectResultManager.java       |  80 +++++++++++++
 .../anyobject/AnyObjectSyncopeOperations.java   |  53 +++++++++
 .../common/rest/api/service/AnyService.java     |  11 --
 .../common/rest/api/service/GroupService.java   |  13 +++
 .../common/rest/api/service/UserService.java    |  14 +++
 .../rest/cxf/service/AbstractAnyService.java    |  11 +-
 .../core/rest/cxf/service/GroupServiceImpl.java |   7 ++
 .../core/rest/cxf/service/UserServiceImpl.java  |   7 ++
 24 files changed, 609 insertions(+), 580 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AbstractAnyCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AbstractAnyCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AbstractAnyCommand.java
deleted file mode 100644
index dc62ba9..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AbstractAnyCommand.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-public abstract class AbstractAnyCommand {
-
-    protected final AnySyncopeOperations anySyncopeOperations = new AnySyncopeOperations();
-
-    protected final AnyResultManager anyResultManager = new AnyResultManager();
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyCommand.java
deleted file mode 100644
index c4b2159..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyCommand.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.cli.Command;
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.commands.AbstractCommand;
-
-@Command(name = "any")
-public class AnyCommand extends AbstractCommand {
-
-    private final AnyResultManager anyResultManager = new AnyResultManager();
-
-    @Override
-    public void execute(final Input input) {
-        if (StringUtils.isBlank(input.getOption())) {
-            input.setOption(AnyOptions.HELP.getOptionName());
-        }
-        switch (AnyOptions.fromName(input.getOption())) {
-            case DETAILS:
-                break;
-            case LIST:
-                new AnyList(input).list();
-                break;
-            case READ:
-                new AnyRead(input).read();
-                break;
-            case READ_ATTRIBUTES_BY_SCHEMA:
-                new AnyReadAttributeBySchemaTypeAndSchemaName(input).read();
-                break;
-            case READ_ATTRIBUTES_BY_SCHEMA_TYPE:
-                new AnyReadAttributesBySchemaType(input).read();
-                break;
-            case DELETE:
-                new AnyDelete(input).delete();
-                break;
-            case HELP:
-                System.out.println(getHelpMessage());
-                break;
-            default:
-                anyResultManager.defaultOptionMessage(input.getOption(), getHelpMessage());
-        }
-    }
-
-    @Override
-    public String getHelpMessage() {
-        return anyResultManager.commandHelpMessage(getClass());
-    }
-
-    private enum AnyOptions {
-
-        HELP("--help"),
-        DETAILS("--details"),
-        LIST("--list"),
-        READ("--read"),
-        READ_ATTRIBUTES_BY_SCHEMA("--read-attr-by-schema"),
-        READ_ATTRIBUTES_BY_SCHEMA_TYPE("--read-attr-by-schema-type"),
-        DELETE("--delete");
-
-        private final String optionName;
-
-        AnyOptions(final String optionName) {
-            this.optionName = optionName;
-        }
-
-        public String getOptionName() {
-            return optionName;
-        }
-
-        public boolean equalsOptionName(final String otherName) {
-            return (otherName == null) ? false : optionName.equals(otherName);
-        }
-
-        public static AnyOptions fromName(final String name) {
-            AnyOptions optionToReturn = HELP;
-            for (final AnyOptions option : AnyOptions.values()) {
-                if (option.equalsOptionName(name)) {
-                    optionToReturn = option;
-                }
-            }
-            return optionToReturn;
-        }
-
-        public static List<String> toList() {
-            final List<String> options = new ArrayList<>();
-            for (final AnyOptions value : values()) {
-                options.add(value.getOptionName());
-            }
-            return options;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyDelete.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyDelete.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyDelete.java
deleted file mode 100644
index 170cb57..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyDelete.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AnyDelete extends AbstractAnyCommand {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AnyDelete.class);
-
-    private static final String DELETE_HELP_MESSAGE = "any --delete {ANY-ID} {ANY-ID} [...]";
-
-    private final Input input;
-
-    public AnyDelete(final Input input) {
-        this.input = input;
-    }
-
-    public void delete() {
-        if (input.parameterNumber() >= 1) {
-            for (final String parameter : input.getParameters()) {
-                try {
-                    anySyncopeOperations.delete(parameter);
-                    anyResultManager.deletedMessage("Any", parameter);
-                } catch (final SyncopeClientException ex) {
-                    LOG.error("Error deleting group", ex);
-                    if (ex.getMessage().startsWith("NotFound")) {
-                        anyResultManager.notFoundError("any", parameter);
-                    } else {
-                        anyResultManager.genericError(ex.getMessage());
-                    }
-                } catch (final NumberFormatException ex) {
-                    anyResultManager.numberFormatException("any", parameter);
-                }
-            }
-        } else {
-            anyResultManager.commandOptionError(DELETE_HELP_MESSAGE);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyList.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyList.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyList.java
deleted file mode 100644
index 5b2ee56..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyList.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AnyList extends AbstractAnyCommand {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AnyList.class);
-
-    private static final String LIST_HELP_MESSAGE = "any --list";
-
-    private final Input input;
-
-    public AnyList(final Input input) {
-        this.input = input;
-    }
-
-    public void list() {
-        if (input.parameterNumber() == 0) {
-            try {
-                anyResultManager.printAnys(anySyncopeOperations.list());
-            } catch (final SyncopeClientException ex) {
-                LOG.error("Error listing any object", ex);
-                anyResultManager.genericError(ex.getMessage());
-            }
-        } else {
-            anyResultManager.unnecessaryParameters(input.listParameters(), LIST_HELP_MESSAGE);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyRead.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyRead.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyRead.java
deleted file mode 100644
index ca50b91..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyRead.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AnyRead extends AbstractAnyCommand {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AnyRead.class);
-
-    private static final String READ_HELP_MESSAGE = "any --read {ANY-ID} {ANY-ID} [...]";
-
-    private final Input input;
-
-    public AnyRead(final Input input) {
-        this.input = input;
-    }
-
-    public void read() {
-        if (input.parameterNumber() >= 1) {
-            for (final String parameter : input.getParameters()) {
-                try {
-                    anyResultManager.printGroup(anySyncopeOperations.read(parameter));
-                } catch (final SyncopeClientException ex) {
-                    LOG.error("Error reading group", ex);
-                    if (ex.getMessage().startsWith("NotFound")) {
-                        anyResultManager.notFoundError("Any object", parameter);
-                    } else {
-                        anyResultManager.genericError(ex.getMessage());
-                    }
-                } catch (final NumberFormatException ex) {
-                    anyResultManager.numberFormatException("any object", parameter);
-                }
-            }
-        } else {
-            anyResultManager.commandOptionError(READ_HELP_MESSAGE);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributeBySchemaTypeAndSchemaName.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributeBySchemaTypeAndSchemaName.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributeBySchemaTypeAndSchemaName.java
deleted file mode 100644
index b1cd414..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributeBySchemaTypeAndSchemaName.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.util.CommandUtils;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.types.SchemaType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AnyReadAttributeBySchemaTypeAndSchemaName extends AbstractAnyCommand {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AnyReadAttributeBySchemaTypeAndSchemaName.class);
-
-    private static final String READ_HELP_MESSAGE
-            = "any --read-attr-by-schema {ANY-ID} {SCHEMA-TYPE} {SCHEMA-NAME}\n"
-            + "   Schema type: PLAIN / DERIVED / VIRTUAL";
-
-    private final Input input;
-
-    public AnyReadAttributeBySchemaTypeAndSchemaName(final Input input) {
-        this.input = input;
-    }
-
-    public void read() {
-        if (input.parameterNumber() == 3) {
-            try {
-                anyResultManager.printAttribute(anySyncopeOperations.readAttribute(
-                        input.firstParameter(), input.secondParameter(), input.thirdParameter()));
-            } catch (final SyncopeClientException ex) {
-                LOG.error("Error reading any object", ex);
-                anyResultManager.genericError(ex.getMessage());
-            } catch (final NumberFormatException ex) {
-                anyResultManager.numberFormatException("any object", input.firstParameter());
-            } catch (final IllegalArgumentException ex) {
-                LOG.error("Error reading schema", ex);
-                anyResultManager.typeNotValidError(
-                        "schema", input.secondParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
-            }
-        } else {
-            anyResultManager.commandOptionError(READ_HELP_MESSAGE);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributesBySchemaType.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributesBySchemaType.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributesBySchemaType.java
deleted file mode 100644
index 602c68f..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyReadAttributesBySchemaType.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.util.CommandUtils;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.types.SchemaType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AnyReadAttributesBySchemaType extends AbstractAnyCommand {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AnyReadAttributesBySchemaType.class);
-
-    private static final String READ_HELP_MESSAGE = "any --read-attr-by-schema-type {ANY-ID} {SCHEMA-TYPE}\n"
-            + "   Schema type: PLAIN / DERIVED / VIRTUAL";
-
-    private final Input input;
-
-    public AnyReadAttributesBySchemaType(final Input input) {
-        this.input = input;
-    }
-
-    public void read() {
-        if (input.parameterNumber() == 2) {
-            try {
-                anyResultManager.printAttributes(anySyncopeOperations.readAttributes(
-                        input.firstParameter(), input.secondParameter()));
-            } catch (final SyncopeClientException ex) {
-                LOG.error("Error reading any", ex);
-                if (ex.getMessage().startsWith("NotFound")) {
-                    anyResultManager.notFoundError("Any", input.firstParameter());
-                } else {
-                    anyResultManager.genericError(ex.getMessage());
-                }
-            } catch (final NumberFormatException ex) {
-                anyResultManager.numberFormatException("any", input.firstParameter());
-            } catch (final IllegalArgumentException ex) {
-                LOG.error("Error reading schema", ex);
-                anyResultManager.typeNotValidError(
-                        "schema", input.secondParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
-            }
-        } else {
-            anyResultManager.commandOptionError(READ_HELP_MESSAGE);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyResultManager.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyResultManager.java
deleted file mode 100644
index 3b50406..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnyResultManager.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.apache.syncope.client.cli.commands.CommonsResultManager;
-import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-
-public class AnyResultManager extends CommonsResultManager {
-
-    public void printAnys(final List<AnyObjectTO> anyObjectTOs) {
-        System.out.println("");
-        for (final AnyObjectTO anyObjectTO : anyObjectTOs) {
-            printGroup(anyObjectTO);
-        }
-    }
-
-    public void printGroup(final AnyObjectTO anyObjectTO) {
-        System.out.println(" > ANY ID: " + anyObjectTO.getKey());
-        System.out.println("    type: " + anyObjectTO.getType());
-        System.out.println("    realm: " + anyObjectTO.getRealm());
-        System.out.println("    status: " + anyObjectTO.getStatus());
-        System.out.println("    RESOURCES: ");
-        printResources(anyObjectTO.getResources());
-        System.out.println("    PLAIN ATTRIBUTES: ");
-        printAttributes(anyObjectTO.getPlainAttrs());
-        System.out.println("    DERIVED ATTRIBUTES: ");
-        printAttributes(anyObjectTO.getDerAttrs());
-        System.out.println("    VIRTUAL ATTRIBUTES: ");
-        printAttributes(anyObjectTO.getVirAttrs());
-    }
-
-    private void printResources(final Set<String> resources) {
-        for (final String resource : resources) {
-            System.out.println("      - " + resource);
-        }
-    }
-
-    public void printAttributes(final Set<AttrTO> attributes) {
-        for (final AttrTO attribute : attributes) {
-            printAttribute(attribute);
-        }
-        System.out.println("");
-    }
-
-    public void printAttribute(final AttrTO attribute) {
-        final StringBuilder attributeMessageBuilder = new StringBuilder();
-        attributeMessageBuilder.append("     - ")
-                .append(attribute.getSchema())
-                .append(": ")
-                .append(attribute.getValues());
-        if (attribute.isReadonly()) {
-            attributeMessageBuilder.append(" - is readonly");
-        }
-        System.out.println(attributeMessageBuilder.toString());
-    }
-
-    public void printDetails(final Map<String, String> details) {
-        printDetails("groups details", details);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnySyncopeOperations.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnySyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnySyncopeOperations.java
deleted file mode 100644
index 5156952..0000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/any/AnySyncopeOperations.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.cli.commands.any;
-
-import java.util.List;
-import java.util.Set;
-import org.apache.syncope.client.cli.SyncopeServices;
-import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.types.SchemaType;
-import org.apache.syncope.common.rest.api.beans.AnyListQuery;
-import org.apache.syncope.common.rest.api.service.AnyObjectService;
-
-public class AnySyncopeOperations {
-
-    private final AnyObjectService anyObjectService = SyncopeServices.get(AnyObjectService.class);
-
-    public List<AnyObjectTO> list() {
-        return anyObjectService.list(new AnyListQuery()).getResult();
-    }
-
-    public AnyObjectTO read(final String anyId) {
-        return anyObjectService.read(Long.valueOf(anyId));
-    }
-
-    public Set<AttrTO> readAttributes(final String anyId, final String schemaType) {
-        return anyObjectService.read(Long.valueOf(anyId), SchemaType.valueOf(schemaType));
-    }
-
-    public AttrTO readAttribute(final String anyId, final String schemaType, final String schema) {
-        return anyObjectService.read(Long.valueOf(anyId), SchemaType.valueOf(schemaType), schema);
-    }
-
-    public void delete(final String anyId) {
-        anyObjectService.delete(Long.valueOf(anyId));
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AbstractAnyObjectCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AbstractAnyObjectCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AbstractAnyObjectCommand.java
new file mode 100644
index 0000000..b78b772
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AbstractAnyObjectCommand.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+public abstract class AbstractAnyObjectCommand {
+
+    protected final AnyObjectSyncopeOperations anySyncopeOperations = new AnyObjectSyncopeOperations();
+
+    protected final AnyObjectResultManager anyResultManager = new AnyObjectResultManager();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectCommand.java
new file mode 100644
index 0000000..6dc6ba7
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectCommand.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.cli.Command;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.commands.AbstractCommand;
+
+@Command(name = "anyObject")
+public class AnyObjectCommand extends AbstractCommand {
+
+    private final AnyObjectResultManager anyObjectResultManager = new AnyObjectResultManager();
+
+    @Override
+    public void execute(final Input input) {
+        if (StringUtils.isBlank(input.getOption())) {
+            input.setOption(AnyObjectOptions.HELP.getOptionName());
+        }
+        switch (AnyObjectOptions.fromName(input.getOption())) {
+            case DETAILS:
+                break;
+            case LIST:
+                new AnyObjectList(input).list();
+                break;
+            case READ:
+                new AnyObjectRead(input).read();
+                break;
+            case READ_ATTRIBUTES_BY_SCHEMA:
+                new AnyObjectReadAttributeBySchemaTypeAndSchemaName(input).read();
+                break;
+            case READ_ATTRIBUTES_BY_SCHEMA_TYPE:
+                new AnyObjectReadAttributesBySchemaType(input).read();
+                break;
+            case DELETE:
+                new AnyObjectDelete(input).delete();
+                break;
+            case HELP:
+                System.out.println(getHelpMessage());
+                break;
+            default:
+                anyObjectResultManager.defaultOptionMessage(input.getOption(), getHelpMessage());
+        }
+    }
+
+    @Override
+    public String getHelpMessage() {
+        return anyObjectResultManager.commandHelpMessage(getClass());
+    }
+
+    private enum AnyObjectOptions {
+
+        HELP("--help"),
+        DETAILS("--details"),
+        LIST("--list"),
+        READ("--read"),
+        READ_ATTRIBUTES_BY_SCHEMA("--read-attr-by-schema"),
+        READ_ATTRIBUTES_BY_SCHEMA_TYPE("--read-attr-by-schema-type"),
+        DELETE("--delete");
+
+        private final String optionName;
+
+        AnyObjectOptions(final String optionName) {
+            this.optionName = optionName;
+        }
+
+        public String getOptionName() {
+            return optionName;
+        }
+
+        public boolean equalsOptionName(final String otherName) {
+            return (otherName == null) ? false : optionName.equals(otherName);
+        }
+
+        public static AnyObjectOptions fromName(final String name) {
+            AnyObjectOptions optionToReturn = HELP;
+            for (final AnyObjectOptions option : AnyObjectOptions.values()) {
+                if (option.equalsOptionName(name)) {
+                    optionToReturn = option;
+                }
+            }
+            return optionToReturn;
+        }
+
+        public static List<String> toList() {
+            final List<String> options = new ArrayList<>();
+            for (final AnyObjectOptions value : values()) {
+                options.add(value.getOptionName());
+            }
+            return options;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectDelete.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectDelete.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectDelete.java
new file mode 100644
index 0000000..16ff80b
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectDelete.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnyObjectDelete extends AbstractAnyObjectCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyObjectDelete.class);
+
+    private static final String DELETE_HELP_MESSAGE = "any --delete {ANY-ID} {ANY-ID} [...]";
+
+    private final Input input;
+
+    public AnyObjectDelete(final Input input) {
+        this.input = input;
+    }
+
+    public void delete() {
+        if (input.parameterNumber() >= 1) {
+            for (final String parameter : input.getParameters()) {
+                try {
+                    anySyncopeOperations.delete(parameter);
+                    anyResultManager.deletedMessage("Any", parameter);
+                } catch (final SyncopeClientException ex) {
+                    LOG.error("Error deleting group", ex);
+                    if (ex.getMessage().startsWith("NotFound")) {
+                        anyResultManager.notFoundError("any", parameter);
+                    } else {
+                        anyResultManager.genericError(ex.getMessage());
+                    }
+                } catch (final NumberFormatException ex) {
+                    anyResultManager.numberFormatException("any", parameter);
+                }
+            }
+        } else {
+            anyResultManager.commandOptionError(DELETE_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectList.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectList.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectList.java
new file mode 100644
index 0000000..1e3e658
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectList.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnyObjectList extends AbstractAnyObjectCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyObjectList.class);
+
+    private static final String LIST_HELP_MESSAGE = "any --list {ANY-TYPE}";
+
+    private final Input input;
+
+    public AnyObjectList(final Input input) {
+        this.input = input;
+    }
+
+    public void list() {
+        if (input.parameterNumber() == 1) {
+            try {
+                anyResultManager.printAnys(anySyncopeOperations.list(input.firstParameter()));
+            } catch (final SyncopeClientException ex) {
+                LOG.error("Error listing any object", ex);
+                anyResultManager.genericError(ex.getMessage());
+            }
+        } else {
+            anyResultManager.unnecessaryParameters(input.listParameters(), LIST_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectRead.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectRead.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectRead.java
new file mode 100644
index 0000000..03137f7
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectRead.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnyObjectRead extends AbstractAnyObjectCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyObjectRead.class);
+
+    private static final String READ_HELP_MESSAGE = "any --read {ANY-ID} {ANY-ID} [...]";
+
+    private final Input input;
+
+    public AnyObjectRead(final Input input) {
+        this.input = input;
+    }
+
+    public void read() {
+        if (input.parameterNumber() >= 1) {
+            for (final String parameter : input.getParameters()) {
+                try {
+                    anyResultManager.printGroup(anySyncopeOperations.read(parameter));
+                } catch (final SyncopeClientException ex) {
+                    LOG.error("Error reading group", ex);
+                    if (ex.getMessage().startsWith("NotFound")) {
+                        anyResultManager.notFoundError("Any object", parameter);
+                    } else {
+                        anyResultManager.genericError(ex.getMessage());
+                    }
+                } catch (final NumberFormatException ex) {
+                    anyResultManager.numberFormatException("any object", parameter);
+                }
+            }
+        } else {
+            anyResultManager.commandOptionError(READ_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributeBySchemaTypeAndSchemaName.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributeBySchemaTypeAndSchemaName.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributeBySchemaTypeAndSchemaName.java
new file mode 100644
index 0000000..8db0e21
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributeBySchemaTypeAndSchemaName.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.util.CommandUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnyObjectReadAttributeBySchemaTypeAndSchemaName extends AbstractAnyObjectCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyObjectReadAttributeBySchemaTypeAndSchemaName.class);
+
+    private static final String READ_HELP_MESSAGE
+            = "any --read-attr-by-schema {ANY-ID} {SCHEMA-TYPE} {SCHEMA-NAME}\n"
+            + "   Schema type: PLAIN / DERIVED / VIRTUAL";
+
+    private final Input input;
+
+    public AnyObjectReadAttributeBySchemaTypeAndSchemaName(final Input input) {
+        this.input = input;
+    }
+
+    public void read() {
+        if (input.parameterNumber() == 3) {
+            try {
+                anyResultManager.printAttribute(anySyncopeOperations.readAttribute(
+                        input.firstParameter(), input.secondParameter(), input.thirdParameter()));
+            } catch (final SyncopeClientException ex) {
+                LOG.error("Error reading any object", ex);
+                anyResultManager.genericError(ex.getMessage());
+            } catch (final NumberFormatException ex) {
+                anyResultManager.numberFormatException("any object", input.firstParameter());
+            } catch (final IllegalArgumentException ex) {
+                LOG.error("Error reading schema", ex);
+                anyResultManager.typeNotValidError(
+                        "schema", input.secondParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
+            }
+        } else {
+            anyResultManager.commandOptionError(READ_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributesBySchemaType.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributesBySchemaType.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributesBySchemaType.java
new file mode 100644
index 0000000..ff42b0a
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectReadAttributesBySchemaType.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.client.cli.util.CommandUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnyObjectReadAttributesBySchemaType extends AbstractAnyObjectCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyObjectReadAttributesBySchemaType.class);
+
+    private static final String READ_HELP_MESSAGE = "any --read-attr-by-schema-type {ANY-ID} {SCHEMA-TYPE}\n"
+            + "   Schema type: PLAIN / DERIVED / VIRTUAL";
+
+    private final Input input;
+
+    public AnyObjectReadAttributesBySchemaType(final Input input) {
+        this.input = input;
+    }
+
+    public void read() {
+        if (input.parameterNumber() == 2) {
+            try {
+                anyResultManager.printAttributes(anySyncopeOperations.readAttributes(
+                        input.firstParameter(), input.secondParameter()));
+            } catch (final SyncopeClientException ex) {
+                LOG.error("Error reading any", ex);
+                if (ex.getMessage().startsWith("NotFound")) {
+                    anyResultManager.notFoundError("Any", input.firstParameter());
+                } else {
+                    anyResultManager.genericError(ex.getMessage());
+                }
+            } catch (final NumberFormatException ex) {
+                anyResultManager.numberFormatException("any", input.firstParameter());
+            } catch (final IllegalArgumentException ex) {
+                LOG.error("Error reading schema", ex);
+                anyResultManager.typeNotValidError(
+                        "schema", input.secondParameter(), CommandUtils.fromEnumToArray(SchemaType.class));
+            }
+        } else {
+            anyResultManager.commandOptionError(READ_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectResultManager.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectResultManager.java
new file mode 100644
index 0000000..4f89b23
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectResultManager.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.client.cli.commands.CommonsResultManager;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+
+public class AnyObjectResultManager extends CommonsResultManager {
+
+    public void printAnys(final List<AnyObjectTO> anyObjectTOs) {
+        System.out.println("");
+        for (final AnyObjectTO anyObjectTO : anyObjectTOs) {
+            printGroup(anyObjectTO);
+        }
+    }
+
+    public void printGroup(final AnyObjectTO anyObjectTO) {
+        System.out.println(" > ANY ID: " + anyObjectTO.getKey());
+        System.out.println("    type: " + anyObjectTO.getType());
+        System.out.println("    realm: " + anyObjectTO.getRealm());
+        System.out.println("    status: " + anyObjectTO.getStatus());
+        System.out.println("    RESOURCES: ");
+        printResources(anyObjectTO.getResources());
+        System.out.println("    PLAIN ATTRIBUTES: ");
+        printAttributes(anyObjectTO.getPlainAttrs());
+        System.out.println("    DERIVED ATTRIBUTES: ");
+        printAttributes(anyObjectTO.getDerAttrs());
+        System.out.println("    VIRTUAL ATTRIBUTES: ");
+        printAttributes(anyObjectTO.getVirAttrs());
+    }
+
+    private void printResources(final Set<String> resources) {
+        for (final String resource : resources) {
+            System.out.println("      - " + resource);
+        }
+    }
+
+    public void printAttributes(final Set<AttrTO> attributes) {
+        for (final AttrTO attribute : attributes) {
+            printAttribute(attribute);
+        }
+        System.out.println("");
+    }
+
+    public void printAttribute(final AttrTO attribute) {
+        final StringBuilder attributeMessageBuilder = new StringBuilder();
+        attributeMessageBuilder.append("     - ")
+                .append(attribute.getSchema())
+                .append(": ")
+                .append(attribute.getValues());
+        if (attribute.isReadonly()) {
+            attributeMessageBuilder.append(" - is readonly");
+        }
+        System.out.println(attributeMessageBuilder.toString());
+    }
+
+    public void printDetails(final Map<String, String> details) {
+        printDetails("groups details", details);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectSyncopeOperations.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectSyncopeOperations.java
new file mode 100644
index 0000000..c55499a
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/anyobject/AnyObjectSyncopeOperations.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.cli.commands.anyobject;
+
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.client.cli.SyncopeServices;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.rest.api.beans.AnyListQuery;
+import org.apache.syncope.common.rest.api.service.AnyObjectService;
+
+public class AnyObjectSyncopeOperations {
+
+    private final AnyObjectService anyObjectService = SyncopeServices.get(AnyObjectService.class);
+
+    public List<AnyObjectTO> list(final String type) {
+        return anyObjectService.list(type, new AnyListQuery()).getResult();
+    }
+
+    public AnyObjectTO read(final String anyId) {
+        return anyObjectService.read(Long.valueOf(anyId));
+    }
+
+    public Set<AttrTO> readAttributes(final String anyId, final String schemaType) {
+        return anyObjectService.read(Long.valueOf(anyId), SchemaType.valueOf(schemaType));
+    }
+
+    public AttrTO readAttribute(final String anyId, final String schemaType, final String schema) {
+        return anyObjectService.read(Long.valueOf(anyId), SchemaType.valueOf(schemaType), schema);
+    }
+
+    public void delete(final String anyId) {
+        anyObjectService.delete(Long.valueOf(anyId));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
index ec8495c..1d1bb0f 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
@@ -41,7 +41,6 @@ import org.apache.syncope.common.lib.to.BulkAction;
 import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.types.SchemaType;
-import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 import org.apache.syncope.common.rest.api.beans.AnySearchQuery;
 
 public interface AnyService<TO extends AnyTO, P extends AnyPatch> extends JAXRSService {
@@ -86,16 +85,6 @@ public interface AnyService<TO extends AnyTO, P extends AnyPatch> extends JAXRSS
     TO read(@NotNull @PathParam("key") Long key);
 
     /**
-     * Returns a paged list of existing any objects matching the given query.
-     *
-     * @param listQuery query conditions
-     * @return paged list of existing any objects matching the given query
-     */
-    @GET
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<TO> list(@BeanParam AnyListQuery listQuery);
-
-    /**
      * Returns a paged list of any objects matching the given query.
      *
      * @param searchQuery query conditions

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
index d1e556d..08535dc 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
@@ -19,12 +19,15 @@
 package org.apache.syncope.common.rest.api.service;
 
 import java.util.List;
+import javax.ws.rs.BeanParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 
 /**
  * REST operations for groups.
@@ -42,4 +45,14 @@ public interface GroupService extends AnyService<GroupTO, GroupPatch> {
     @Path("own")
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
     List<GroupTO> own();
+
+    /**
+     * Returns a paged list of existing groups matching the given query.
+     *
+     * @param listQuery query conditions
+     * @return paged list of existing groups matching the given query
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> list(@BeanParam AnyListQuery listQuery);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
index e23e6cd..1fc14f9 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
@@ -19,8 +19,10 @@
 package org.apache.syncope.common.rest.api.service;
 
 import javax.validation.constraints.NotNull;
+import javax.ws.rs.BeanParam;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
 import javax.ws.rs.OPTIONS;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
@@ -31,7 +33,9 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.patch.StatusPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 
 /**
  * REST operations for users.
@@ -60,6 +64,16 @@ public interface UserService extends AnyService<UserTO, UserPatch> {
     Response getUserKey(@NotNull @PathParam("username") String username);
 
     /**
+     * Returns a paged list of existing users matching the given query.
+     *
+     * @param listQuery query conditions
+     * @return paged list of existing users matching the given query
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> list(@BeanParam AnyListQuery listQuery);
+
+    /**
      * Creates a new user.
      *
      * @param userTO user to be created

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
index 07dba6e..e08c476 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
@@ -109,8 +109,7 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
         return getAnyLogic().read(key);
     }
 
-    @Override
-    public PagedResult<TO> list(final AnyListQuery listQuery) {
+    protected PagedResult<TO> list(final AnyListQuery listQuery) {
         CollectionUtils.transform(listQuery.getRealms(), new Transformer<String, String>() {
 
             @Override
@@ -260,8 +259,8 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
             for (String resource : patch.getResources()) {
                 result.getResults().put(resource,
                         updated.getAny().getResources().contains(resource)
-                                ? BulkActionResult.Status.FAILURE
-                                : BulkActionResult.Status.SUCCESS);
+                        ? BulkActionResult.Status.FAILURE
+                        : BulkActionResult.Status.SUCCESS);
             }
         } else {
             for (PropagationStatus propagationStatusTO : updated.getPropagationStatuses()) {
@@ -317,8 +316,8 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
             for (String resource : patch.getResources()) {
                 result.getResults().put(resource,
                         updated.getAny().getResources().contains(resource)
-                                ? BulkActionResult.Status.FAILURE
-                                : BulkActionResult.Status.SUCCESS);
+                        ? BulkActionResult.Status.FAILURE
+                        : BulkActionResult.Status.SUCCESS);
             }
         } else {
             for (PropagationStatus propagationStatusTO : updated.getPropagationStatuses()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
index f2915f7..cf65346 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java
@@ -21,6 +21,8 @@ package org.apache.syncope.core.rest.cxf.service;
 import java.util.List;
 import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 import org.apache.syncope.common.rest.api.service.GroupService;
 import org.apache.syncope.core.logic.AbstractAnyLogic;
 import org.apache.syncope.core.logic.GroupLogic;
@@ -50,4 +52,9 @@ public class GroupServiceImpl extends AbstractAnyService<GroupTO, GroupPatch> im
         return logic.own();
     }
 
+    @Override
+    public PagedResult<GroupTO> list(final AnyListQuery listQuery) {
+        return super.list(listQuery);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/0f938e72/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java
index 0f62925..1b57c38 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java
@@ -22,9 +22,11 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.patch.StatusPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 import org.apache.syncope.common.rest.api.service.UserService;
 import org.apache.syncope.core.logic.AbstractAnyLogic;
 import org.apache.syncope.core.logic.UserLogic;
@@ -64,6 +66,11 @@ public class UserServiceImpl extends AbstractAnyService<UserTO, UserPatch> imple
     }
 
     @Override
+    public PagedResult<UserTO> list(final AnyListQuery listQuery) {
+        return super.list(listQuery);
+    }
+
+    @Override
     public Response create(final UserTO userTO, final boolean storePassword) {
         ProvisioningResult<UserTO> created = logic.create(userTO, storePassword, isNullPriorityAsync());
         return createResponse(created);


[2/5] syncope git commit: [SYNCOPE-119] Added search options and checks to match assignable conditions, for usage with memberships and relationships

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index a8ebe0c..639f1b2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -41,8 +41,11 @@ import org.apache.syncope.common.lib.types.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.misc.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.RelationshipType;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
@@ -131,52 +134,77 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
 
         SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 
-        // relationships
-        for (RelationshipTO relationshipTO : anyObjectTO.getRelationships()) {
-            AnyObject otherEnd = anyObjectDAO.find(relationshipTO.getRightKey());
-
-            if (otherEnd == null) {
-                LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
-            } else {
-                RelationshipType relationshipType = relationshipTypeDAO.find(relationshipTO.getType());
-                ARelationship relationship = null;
-                if (anyObject.getKey() != null) {
-                    relationship = anyObject.getRelationship(relationshipType, anyObject.getKey());
-                }
-                if (relationship == null) {
-                    relationship = entityFactory.newEntity(ARelationship.class);
+        // realm
+        Realm realm = realmDAO.find(anyObjectTO.getRealm());
+        if (realm == null) {
+            SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+            noRealm.getElements().add("Invalid or null realm specified: " + anyObjectTO.getRealm());
+            scce.addException(noRealm);
+        }
+        anyObject.setRealm(realm);
+
+        if (anyObject.getRealm() != null) {
+            AssignableCond assignableCond = new AssignableCond();
+            assignableCond.setRealmFullPath(anyObject.getRealm().getFullPath());
+
+            // relationships
+            List<AnyObject> assignableAnyObjects =
+                    searchDAO.search(SearchCond.getLeafCond(assignableCond), AnyTypeKind.ANY_OBJECT);
+
+            for (RelationshipTO relationshipTO : anyObjectTO.getRelationships()) {
+                AnyObject otherEnd = anyObjectDAO.find(relationshipTO.getRightKey());
+                if (otherEnd == null) {
+                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
+                } else if (assignableAnyObjects.contains(otherEnd)) {
+                    RelationshipType relationshipType = relationshipTypeDAO.find(relationshipTO.getType());
+                    ARelationship relationship = entityFactory.newEntity(ARelationship.class);
                     relationship.setType(relationshipType);
                     relationship.setRightEnd(anyObject);
                     relationship.setLeftEnd(anyObject);
 
                     anyObject.add(relationship);
+                } else {
+                    LOG.error("{} cannot be assigned to {}", otherEnd, anyObject);
+
+                    SyncopeClientException unassignabled =
+                            SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+                    unassignabled.getElements().add("Cannot be assigned: " + otherEnd);
+                    scce.addException(unassignabled);
                 }
             }
-        }
 
-        // memberships
-        for (MembershipTO membershipTO : anyObjectTO.getMemberships()) {
-            Group group = groupDAO.find(membershipTO.getRightKey());
-
-            if (group == null) {
-                LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
-            } else {
-                AMembership membership = null;
-                if (anyObject.getKey() != null) {
-                    membership = anyObject.getMembership(group.getKey());
-                }
-                if (membership == null) {
-                    membership = entityFactory.newEntity(AMembership.class);
+            // memberships
+            List<Group> assignableGroups =
+                    searchDAO.search(SearchCond.getLeafCond(assignableCond), AnyTypeKind.GROUP);
+
+            for (MembershipTO membershipTO : anyObjectTO.getMemberships()) {
+                Group group = groupDAO.find(membershipTO.getRightKey());
+                if (group == null) {
+                    LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
+                } else if (assignableGroups.contains(group)) {
+                    AMembership membership = entityFactory.newEntity(AMembership.class);
                     membership.setRightEnd(group);
                     membership.setLeftEnd(anyObject);
 
                     anyObject.add(membership);
+                } else {
+                    LOG.error("{} cannot be assigned to {}", group, anyObject);
+
+                    SyncopeClientException unassignabled =
+                            SyncopeClientException.build(ClientExceptionType.InvalidMembership);
+                    unassignabled.getElements().add("Cannot be assigned: " + group);
+                    scce.addException(unassignabled);
                 }
             }
         }
 
-        // realm, attributes, derived attributes, virtual attributes and resources
+        // attributes, derived attributes, virtual attributes and resources
         fill(anyObject, anyObjectTO, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), scce);
+
+        // Throw composite exception if there is at least one element set in the composing exceptions
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
     }
 
     @Override
@@ -200,6 +228,9 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
         Set<String> toBeProvisioned = new HashSet<>();
 
         // relationships
+        List<AnyObject> assignableAnyObjects =
+                searchDAO.searchAssignable(anyObject.getRealm().getFullPath(), AnyTypeKind.ANY_OBJECT);
+
         for (RelationshipPatch patch : anyObjectPatch.getRelationships()) {
             if (patch.getRelationshipTO() != null) {
                 RelationshipType relationshipType = relationshipTypeDAO.find(patch.getRelationshipTO().getType());
@@ -214,8 +245,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
                     AnyObject otherEnd = anyObjectDAO.find(patch.getRelationshipTO().getRightKey());
                     if (otherEnd == null) {
                         LOG.debug("Ignoring invalid any object {}", patch.getRelationshipTO().getRightKey());
-                    } else {
-
+                    } else if (assignableAnyObjects.contains(otherEnd)) {
                         relationship = entityFactory.newEntity(ARelationship.class);
                         relationship.setType(relationshipType);
                         relationship.setRightEnd(otherEnd);
@@ -224,12 +254,22 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
                         anyObject.add(relationship);
 
                         toBeProvisioned.addAll(otherEnd.getResourceNames());
+                    } else {
+                        LOG.error("{} cannot be assigned to {}", otherEnd, anyObject);
+
+                        SyncopeClientException unassignabled =
+                                SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+                        unassignabled.getElements().add("Cannot be assigned: " + otherEnd);
+                        scce.addException(unassignabled);
                     }
                 }
             }
         }
 
         // memberships
+        List<Group> assignableGroups =
+                searchDAO.searchAssignable(anyObject.getRealm().getFullPath(), AnyTypeKind.GROUP);
+
         for (MembershipPatch patch : anyObjectPatch.getMemberships()) {
             if (patch.getMembershipTO() != null) {
                 AMembership membership = anyObject.getMembership(patch.getMembershipTO().getRightKey());
@@ -242,7 +282,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
                     Group group = groupDAO.find(patch.getMembershipTO().getRightKey());
                     if (group == null) {
                         LOG.debug("Ignoring invalid group {}", patch.getMembershipTO().getRightKey());
-                    } else {
+                    } else if (assignableGroups.contains(group)) {
                         membership = entityFactory.newEntity(AMembership.class);
                         membership.setRightEnd(group);
                         membership.setLeftEnd(anyObject);
@@ -250,6 +290,13 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
                         anyObject.add(membership);
 
                         toBeProvisioned.addAll(group.getResourceNames());
+                    } else {
+                        LOG.error("{} cannot be assigned to {}", group, anyObject);
+
+                        SyncopeClientException unassignabled =
+                                SyncopeClientException.build(ClientExceptionType.InvalidMembership);
+                        unassignabled.getElements().add("Cannot be assigned: " + group);
+                        scce.addException(unassignabled);
                     }
                 }
             }
@@ -278,6 +325,11 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
             }
         }
 
+        // Throw composite exception if there is at least one element set in the composing exceptions
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
+
         return propByRes;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index 9562ff7..c71fce5 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@ -44,6 +44,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.group.TypeExtension;
@@ -85,7 +86,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
     }
 
     @Override
-    public Group create(final Group group, final GroupTO groupTO) {
+    public void create(final Group group, final GroupTO groupTO) {
         SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 
         // name
@@ -98,7 +99,16 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
             group.setName(groupTO.getName());
         }
 
-        // realm, attributes, derived attributes, virtual attributes and resources
+        // realm
+        Realm realm = realmDAO.find(groupTO.getRealm());
+        if (realm == null) {
+            SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+            noRealm.getElements().add("Invalid or null realm specified: " + groupTO.getRealm());
+            scce.addException(noRealm);
+        }
+        group.setRealm(realm);
+
+        // attributes, derived attributes, virtual attributes and resources
         fill(group, groupTO, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), scce);
 
         // owner
@@ -153,7 +163,10 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
             }
         }
 
-        return group;
+        // Throw composite exception if there is at least one element set in the composing exceptions
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
     }
 
     @Override
@@ -268,6 +281,11 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
             }
         }
 
+        // Throw composite exception if there is at least one element set in the composing exceptions
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
+
         return propByRes;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index ca3448c..22d690a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -57,7 +57,10 @@ import org.apache.syncope.core.misc.security.Encryptor;
 import org.apache.syncope.core.misc.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.RelationshipType;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
@@ -173,50 +176,71 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
             }
         }
 
-        // relationships
-        for (RelationshipTO relationshipTO : userTO.getRelationships()) {
-            AnyObject anyObject = anyObjectDAO.find(relationshipTO.getRightKey());
-            if (anyObject == null) {
-                LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
-            } else {
-                RelationshipType relationshipType = relationshipTypeDAO.find(relationshipTO.getType());
-                URelationship relationship = null;
-                if (user.getKey() != null) {
-                    relationship = user.getRelationship(relationshipType, anyObject.getKey());
-                }
-                if (relationship == null) {
-                    relationship = entityFactory.newEntity(URelationship.class);
+        // realm
+        Realm realm = realmDAO.find(userTO.getRealm());
+        if (realm == null) {
+            SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+            noRealm.getElements().add("Invalid or null realm specified: " + userTO.getRealm());
+            scce.addException(noRealm);
+        }
+        user.setRealm(realm);
+
+        if (user.getRealm() != null) {
+            AssignableCond assignableCond = new AssignableCond();
+            assignableCond.setRealmFullPath(user.getRealm().getFullPath());
+
+            // relationships
+            List<AnyObject> assignableAnyObjects =
+                    searchDAO.search(SearchCond.getLeafCond(assignableCond), AnyTypeKind.ANY_OBJECT);
+
+            for (RelationshipTO relationshipTO : userTO.getRelationships()) {
+                AnyObject otherEnd = anyObjectDAO.find(relationshipTO.getRightKey());
+                if (otherEnd == null) {
+                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
+                } else if (assignableAnyObjects.contains(otherEnd)) {
+                    RelationshipType relationshipType = relationshipTypeDAO.find(relationshipTO.getType());
+                    URelationship relationship = entityFactory.newEntity(URelationship.class);
                     relationship.setType(relationshipType);
-                    relationship.setRightEnd(anyObject);
+                    relationship.setRightEnd(otherEnd);
                     relationship.setLeftEnd(user);
 
                     user.add(relationship);
+                } else {
+                    LOG.error("{} cannot be assigned to {}", otherEnd, user);
+
+                    SyncopeClientException unassignabled =
+                            SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+                    unassignabled.getElements().add("Cannot be assigned: " + otherEnd);
+                    scce.addException(unassignabled);
                 }
             }
-        }
-
-        // memberships
-        for (MembershipTO membershipTO : userTO.getMemberships()) {
-            Group group = groupDAO.find(membershipTO.getRightKey());
 
-            if (group == null) {
-                LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
-            } else {
-                UMembership membership = null;
-                if (user.getKey() != null) {
-                    membership = user.getMembership(group.getKey());
-                }
-                if (membership == null) {
-                    membership = entityFactory.newEntity(UMembership.class);
+            // memberships
+            List<Group> assignableGroups =
+                    searchDAO.search(SearchCond.getLeafCond(assignableCond), AnyTypeKind.GROUP);
+
+            for (MembershipTO membershipTO : userTO.getMemberships()) {
+                Group group = groupDAO.find(membershipTO.getRightKey());
+                if (group == null) {
+                    LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
+                } else if (assignableGroups.contains(group)) {
+                    UMembership membership = entityFactory.newEntity(UMembership.class);
                     membership.setRightEnd(group);
                     membership.setLeftEnd(user);
 
                     user.add(membership);
+                } else {
+                    LOG.error("{} cannot be assigned to {}", group, user);
+
+                    SyncopeClientException unassignabled =
+                            SyncopeClientException.build(ClientExceptionType.InvalidMembership);
+                    unassignabled.getElements().add("Cannot be assigned: " + group);
+                    scce.addException(unassignabled);
                 }
             }
         }
 
-        // realm, attributes, derived attributes, virtual attributes and resources
+        // attributes, derived attributes, virtual attributes and resources
         fill(user, userTO, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce);
 
         // set password
@@ -239,6 +263,11 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
         user.setSecurityAnswer(userTO.getSecurityAnswer());
 
         user.setMustChangePassword(userTO.isMustChangePassword());
+
+        // Throw composite exception if there is at least one element set in the composing exceptions
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
     }
 
     private boolean isPasswordMapped(final ExternalResource resource) {
@@ -341,6 +370,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
         Set<String> toBeProvisioned = new HashSet<>();
 
         // relationships
+        List<AnyObject> assignableAnyObjects =
+                searchDAO.searchAssignable(user.getRealm().getFullPath(), AnyTypeKind.ANY_OBJECT);
+
         for (RelationshipPatch patch : userPatch.getRelationships()) {
             if (patch.getRelationshipTO() != null) {
                 RelationshipType relationshipType = relationshipTypeDAO.find(patch.getRelationshipTO().getType());
@@ -355,7 +387,7 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                     AnyObject otherEnd = anyObjectDAO.find(patch.getRelationshipTO().getRightKey());
                     if (otherEnd == null) {
                         LOG.debug("Ignoring invalid any object {}", patch.getRelationshipTO().getRightKey());
-                    } else {
+                    } else if (assignableAnyObjects.contains(otherEnd)) {
                         relationship = entityFactory.newEntity(URelationship.class);
                         relationship.setType(relationshipType);
                         relationship.setRightEnd(otherEnd);
@@ -364,12 +396,22 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                         user.add(relationship);
 
                         toBeProvisioned.addAll(otherEnd.getResourceNames());
+                    } else {
+                        LOG.error("{} cannot be assigned to {}", otherEnd, user);
+
+                        SyncopeClientException unassignabled =
+                                SyncopeClientException.build(ClientExceptionType.InvalidRelationship);
+                        unassignabled.getElements().add("Cannot be assigned: " + otherEnd);
+                        scce.addException(unassignabled);
                     }
                 }
             }
         }
 
         // memberships
+        List<Group> assignableGroups =
+                searchDAO.searchAssignable(user.getRealm().getFullPath(), AnyTypeKind.GROUP);
+
         for (MembershipPatch patch : userPatch.getMemberships()) {
             if (patch.getMembershipTO() != null) {
                 UMembership membership = user.getMembership(patch.getMembershipTO().getRightKey());
@@ -382,7 +424,7 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                     Group group = groupDAO.find(patch.getMembershipTO().getRightKey());
                     if (group == null) {
                         LOG.debug("Ignoring invalid group {}", patch.getMembershipTO().getRightKey());
-                    } else {
+                    } else if (assignableGroups.contains(group)) {
                         membership = entityFactory.newEntity(UMembership.class);
                         membership.setRightEnd(group);
                         membership.setLeftEnd(user);
@@ -403,6 +445,13 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                                 }
                             }
                         }
+                    } else {
+                        LOG.error("{} cannot be assigned to {}", group, user);
+
+                        SyncopeClientException unassignabled =
+                                SyncopeClientException.build(ClientExceptionType.InvalidMembership);
+                        unassignabled.getElements().add("Cannot be assigned: " + group);
+                        scce.addException(unassignabled);
                     }
                 }
             }
@@ -429,6 +478,11 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
             }
         }
 
+        // Throw composite exception if there is at least one element set in the composing exceptions
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
+
         return propByRes;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
index 0be3f0d..82d8647 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
@@ -62,7 +62,7 @@ public class AnyObjectServiceImpl extends AbstractAnyService<AnyObjectTO, AnyObj
         }
 
         AnySearchQuery searchQuery = new AnySearchQuery();
-        searchQuery.setFiql(new AnyObjectFiqlSearchConditionBuilder().type(type).query());
+        searchQuery.setFiql(new AnyObjectFiqlSearchConditionBuilder(type).query());
         searchQuery.setDetails(listQuery.isDetails());
         searchQuery.setOrderBy(listQuery.getOrderBy());
         searchQuery.setPage(listQuery.getPage());

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AnyObjectITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AnyObjectITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AnyObjectITCase.java
index c044187..192fa76 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AnyObjectITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AnyObjectITCase.java
@@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.SchemaType;
@@ -68,6 +69,27 @@ public class AnyObjectITCase extends AbstractITCase {
     }
 
     @Test
+    public void createInvalidMembership() {
+        // 1. create anyObject in realm /odd and attempt to assign group 15, from realm /even => exception
+        AnyObjectTO anyObjectTO = getSampleTO("createInvalidMembership");
+        anyObjectTO.setRealm("/odd");
+        anyObjectTO.getMemberships().add(new MembershipTO.Builder().group(15L).build());
+
+        try {
+            createAnyObject(anyObjectTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidMembership, e.getType());
+        }
+
+        // 2. change anyObject's realm to /even/two, now it works
+        anyObjectTO.setRealm("/even/two");
+
+        anyObjectTO = createAnyObject(anyObjectTO).getAny();
+        assertTrue(anyObjectTO.getMembershipMap().containsKey(15L));
+    }
+
+    @Test
     public void delete() {
         try {
             anyObjectService.delete(0L);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SearchITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SearchITCase.java
index 38624b9..d7b8796 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SearchITCase.java
@@ -88,14 +88,14 @@ public class SearchITCase extends AbstractITCase {
 
     @Test
     public void searchByGroupNameAndKey() {
-        PagedResult<GroupTO> matchingGroups = groupService.search(
+        PagedResult<GroupTO> groups = groupService.search(
                 new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getUserSearchConditionBuilder().
+                fiql(SyncopeClient.getGroupSearchConditionBuilder().
                         is("name").equalTo("root").and("key").lessThan(2).query()).build());
-        assertNotNull(matchingGroups);
-        assertEquals(1, matchingGroups.getResult().size());
-        assertEquals("root", matchingGroups.getResult().iterator().next().getName());
-        assertEquals(1L, matchingGroups.getResult().iterator().next().getKey());
+        assertNotNull(groups);
+        assertEquals(1, groups.getResult().size());
+        assertEquals("root", groups.getResult().iterator().next().getName());
+        assertEquals(1L, groups.getResult().iterator().next().getKey());
     }
 
     @Test
@@ -225,21 +225,21 @@ public class SearchITCase extends AbstractITCase {
 
     @Test
     public void searchByBooleanAnyCond() {
-        PagedResult<GroupTO> matchingGroups = groupService.search(
+        PagedResult<GroupTO> groups = groupService.search(
                 new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
                 fiql(SyncopeClient.getGroupSearchConditionBuilder().is("show").equalTo("true").query()).build());
-        assertNotNull(matchingGroups);
-        assertFalse(matchingGroups.getResult().isEmpty());
+        assertNotNull(groups);
+        assertFalse(groups.getResult().isEmpty());
     }
 
     @Test
     public void searchByRelationshipAnyCond() {
-        PagedResult<GroupTO> matchingGroups = groupService.search(
+        PagedResult<GroupTO> groups = groupService.search(
                 new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
                 fiql(SyncopeClient.getGroupSearchConditionBuilder().is("userOwner").equalTo(5).query()).build());
-        assertNotNull(matchingGroups);
-        assertEquals(1, matchingGroups.getResult().size());
-        assertEquals(6L, matchingGroups.getResult().iterator().next().getKey());
+        assertNotNull(groups);
+        assertEquals(1, groups.getResult().size());
+        assertEquals(6L, groups.getResult().iterator().next().getKey());
     }
 
     @Test
@@ -259,7 +259,7 @@ public class SearchITCase extends AbstractITCase {
     public void searchByType() {
         PagedResult<AnyObjectTO> matching = anyObjectService.search(
                 new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").query()).build());
+                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").query()).build());
         assertNotNull(matching);
 
         assertFalse(matching.getResult().isEmpty());
@@ -269,13 +269,118 @@ public class SearchITCase extends AbstractITCase {
 
         matching = anyObjectService.search(
                 new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("UNEXISTING").query()).build());
+                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("UNEXISTING").query()).build());
         assertNotNull(matching);
 
         assertTrue(matching.getResult().isEmpty());
     }
 
     @Test
+    public void searchByRelationship() {
+        PagedResult<AnyObjectTO> anyObjects = anyObjectService.search(
+                new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
+                        inRelationships(2L).query()).
+                build());
+        assertNotNull(anyObjects);
+        assertTrue(CollectionUtils.exists(anyObjects.getResult(), new Predicate<AnyObjectTO>() {
+
+            @Override
+            public boolean evaluate(final AnyObjectTO anyObject) {
+                return anyObject.getKey() == 1L;
+            }
+        }));
+
+        PagedResult<UserTO> users = userService.search(
+                new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                fiql(SyncopeClient.getUserSearchConditionBuilder().inRelationships(1L).query()).
+                build());
+        assertNotNull(users);
+        assertTrue(CollectionUtils.exists(users.getResult(), new Predicate<UserTO>() {
+
+            @Override
+            public boolean evaluate(final UserTO user) {
+                return user.getKey() == 4L;
+            }
+        }));
+    }
+
+    @Test
+    public void searchByRelationshipType() {
+        PagedResult<AnyObjectTO> anyObjects = anyObjectService.search(
+                new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
+                        inRelationshipTypes("neighborhood").query()).
+                build());
+        assertNotNull(anyObjects);
+        assertTrue(CollectionUtils.exists(anyObjects.getResult(), new Predicate<AnyObjectTO>() {
+
+            @Override
+            public boolean evaluate(final AnyObjectTO anyObject) {
+                return anyObject.getKey() == 1L;
+            }
+        }));
+        assertTrue(CollectionUtils.exists(anyObjects.getResult(), new Predicate<AnyObjectTO>() {
+
+            @Override
+            public boolean evaluate(final AnyObjectTO anyObject) {
+                return anyObject.getKey() == 2L;
+            }
+        }));
+
+        PagedResult<UserTO> users = userService.search(
+                new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                fiql(SyncopeClient.getUserSearchConditionBuilder().inRelationshipTypes("neighborhood").query()).
+                build());
+        assertNotNull(users);
+        assertTrue(CollectionUtils.exists(users.getResult(), new Predicate<UserTO>() {
+
+            @Override
+            public boolean evaluate(final UserTO user) {
+                return user.getKey() == 4L;
+            }
+        }));
+    }
+
+    @Test
+    public void assignable() {
+        PagedResult<GroupTO> groups = groupService.search(
+                new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                fiql(SyncopeClient.getGroupSearchConditionBuilder().
+                        isAssignable("/even/two").query()).
+                build());
+        assertNotNull(groups);
+        assertTrue(CollectionUtils.exists(groups.getResult(), new Predicate<GroupTO>() {
+
+            @Override
+            public boolean evaluate(final GroupTO group) {
+                return group.getKey() == 15L;
+            }
+        }));
+        assertFalse(CollectionUtils.exists(groups.getResult(), new Predicate<GroupTO>() {
+
+            @Override
+            public boolean evaluate(final GroupTO group) {
+                return group.getKey() == 16L;
+            }
+        }));
+
+        PagedResult<AnyObjectTO> anyObjects = anyObjectService.search(
+                new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
+                        isAssignable("/odd").query()).
+                build());
+        assertNotNull(anyObjects);
+        assertFalse(CollectionUtils.exists(anyObjects.getResult(), new Predicate<AnyObjectTO>() {
+
+            @Override
+            public boolean evaluate(final AnyObjectTO anyObject) {
+                return anyObject.getKey() == 3L;
+            }
+        }));
+    }
+
+    @Test
     public void orderBy() {
         PagedResult<UserTO> matchingUsers = userService.search(
                 new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
index 75631e7..cea4752 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
@@ -416,7 +416,7 @@ public class SyncTaskITCase extends AbstractTaskITCase {
             // 3. unlink any existing printer and delete from Syncope (printer is now only on external resource)
             PagedResult<AnyObjectTO> matchingPrinters = anyObjectService.search(
                     new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
+                    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
                             is("location").equalTo("sync*").query()).build());
             assertTrue(matchingPrinters.getSize() > 0);
             for (AnyObjectTO printer : matchingPrinters.getResult()) {
@@ -435,7 +435,7 @@ public class SyncTaskITCase extends AbstractTaskITCase {
             // hence PrefixMappingItemTransformer was applied during sync)
             matchingPrinters = anyObjectService.search(
                     new AnySearchQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
+                    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
                             is("location").equalTo("sync*").query()).build());
             assertTrue(matchingPrinters.getSize() > 0);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
index 54589c6..21adebb 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
@@ -445,7 +445,7 @@ public class UserITCase extends AbstractITCase {
 
     @Test
     public void createWithRequiredValueMissing() {
-        UserTO userTO = getSampleTO("a.b@c.it");
+        UserTO userTO = getUniqueSampleTO("a.b@c.it");
 
         AttrTO type = userTO.getPlainAttrMap().get("type");
         userTO.getPlainAttrs().remove(type);


[5/5] syncope git commit: [SYNCOPE-735] Merge from 1_2_X

Posted by il...@apache.org.
[SYNCOPE-735] Merge from 1_2_X


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/e9ff5c73
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/e9ff5c73
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/e9ff5c73

Branch: refs/heads/master
Commit: e9ff5c73ed144337bf9dc44cbb96bbe01ea3e1d7
Parents: 0f938e7 560fc4d
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Nov 25 12:25:46 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Nov 25 12:25:46 2015 +0100

----------------------------------------------------------------------
 .../activiti/ActivitiUserWorkflowAdapter.java   | 53 ++++++++++++++++++--
 .../activiti/spring/DomainProcessEngine.java    |  6 +++
 2 files changed, 56 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/e9ff5c73/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --cc core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
index 48092ee,0000000..b26c9cd
mode 100644,000000..100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
@@@ -1,866 -1,0 +1,913 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.workflow.activiti;
 +
 +import com.fasterxml.jackson.databind.JsonNode;
 +import com.fasterxml.jackson.databind.ObjectMapper;
 +import com.fasterxml.jackson.databind.node.ObjectNode;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import javax.ws.rs.NotFoundException;
 +import org.activiti.bpmn.converter.BpmnXMLConverter;
 +import org.activiti.bpmn.model.BpmnModel;
 +import org.activiti.editor.constants.ModelDataJsonConstants;
 +import org.activiti.editor.language.json.converter.BpmnJsonConverter;
 +import org.activiti.engine.ActivitiException;
- import org.activiti.engine.ProcessEngine;
 +import org.activiti.engine.form.FormProperty;
 +import org.activiti.engine.form.FormType;
 +import org.activiti.engine.form.TaskFormData;
 +import org.activiti.engine.history.HistoricActivityInstance;
 +import org.activiti.engine.history.HistoricDetail;
 +import org.activiti.engine.history.HistoricTaskInstance;
 +import org.activiti.engine.impl.persistence.entity.HistoricFormPropertyEntity;
 +import org.activiti.engine.query.Query;
 +import org.activiti.engine.repository.Model;
 +import org.activiti.engine.repository.ProcessDefinition;
 +import org.activiti.engine.runtime.ProcessInstance;
 +import org.activiti.engine.task.Task;
 +import org.apache.commons.io.IOUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.patch.PasswordPatch;
 +import org.apache.syncope.common.lib.patch.UserPatch;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormTO;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.common.lib.types.WorkflowFormPropertyType;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
++import org.apache.syncope.core.workflow.activiti.spring.DomainProcessEngine;
 +import org.apache.syncope.core.workflow.api.WorkflowDefinitionFormat;
 +import org.apache.syncope.core.workflow.api.WorkflowException;
 +import org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter;
 +import org.springframework.beans.factory.annotation.Autowired;
++import org.springframework.jdbc.core.JdbcTemplate;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +/**
 + * Activiti {@link http://www.activiti.org/} based implementation.
 + */
 +public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
 +
 +    protected static final String[] PROPERTY_IGNORE_PROPS = { "type" };
 +
 +    public static final String WF_PROCESS_ID = "userWorkflow";
 +
 +    public static final String WF_PROCESS_RESOURCE = "userWorkflow.bpmn20.xml";
 +
 +    public static final String WF_DGRM_RESOURCE = "userWorkflow.userWorkflow.png";
 +
 +    public static final String USER = "user";
 +
 +    public static final String WF_EXECUTOR = "wfExecutor";
 +
 +    public static final String FORM_SUBMITTER = "formSubmitter";
 +
 +    public static final String USER_TO = "userTO";
 +
 +    public static final String ENABLED = "enabled";
 +
 +    public static final String USER_PATCH = "userPatch";
 +
 +    public static final String EMAIL_KIND = "emailKind";
 +
 +    public static final String TASK = "task";
 +
 +    public static final String TOKEN = "token";
 +
 +    public static final String PASSWORD = "password";
 +
 +    public static final String PROP_BY_RESOURCE = "propByResource";
 +
 +    public static final String PROPAGATE_ENABLE = "propagateEnable";
 +
 +    public static final String ENCRYPTED_PWD = "encryptedPwd";
 +
 +    public static final String TASK_IS_FORM = "taskIsForm";
 +
 +    public static final String MODEL_DATA_JSON_MODEL = "model";
 +
 +    public static final String STORE_PASSWORD = "storePassword";
 +
 +    public static final String EVENT = "event";
 +
 +    @Resource(name = "adminUser")
 +    protected String adminUser;
 +
 +    @Autowired
-     protected ProcessEngine engine;
++    protected DomainProcessEngine engine;
 +
 +    @Autowired
 +    protected UserDataBinder userDataBinder;
 +
 +    @Override
 +    public String getPrefix() {
 +        return "ACT_";
 +    }
 +
 +    protected void throwException(final ActivitiException e, final String defaultMessage) {
 +        if (e.getCause() != null) {
 +            if (e.getCause().getCause() instanceof SyncopeClientException) {
 +                throw (SyncopeClientException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof ParsingValidationException) {
 +                throw (ParsingValidationException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof InvalidEntityException) {
 +                throw (InvalidEntityException) e.getCause().getCause();
 +            }
 +        }
 +
 +        throw new WorkflowException(defaultMessage, e);
 +    }
 +
 +    protected void updateStatus(final User user) {
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While setting user status: unexpected task number ({})", tasks.size());
 +        } else {
 +            user.setStatus(tasks.get(0).getTaskDefinitionKey());
 +        }
 +    }
 +
 +    protected String getFormTask(final User user) {
 +        String result = null;
 +
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While checking if form task: unexpected task number ({})", tasks.size());
 +        } else {
 +            try {
 +                TaskFormData formData = engine.getFormService().getTaskFormData(tasks.get(0).getId());
 +                if (formData != null && !formData.getFormProperties().isEmpty()) {
 +                    result = tasks.get(0).getId();
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.warn("Could not get task form data", e);
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    protected Set<String> getPerformedTasks(final User user) {
 +        final Set<String> result = new HashSet<>();
 +
 +        for (HistoricActivityInstance task : engine.getHistoryService().createHistoricActivityInstanceQuery().
 +                executionId(user.getWorkflowId()).list()) {
 +
 +            result.add(task.getActivityId());
 +        }
 +
 +        return result;
 +    }
 +
++    protected void cleanupHistory(final User user) {
++        JdbcTemplate jdbcTemplate = new JdbcTemplate(engine.getDataSource());
++
++        List<String> taskIds = jdbcTemplate.queryForList(
++                "SELECT TASK_ID_ FROM ACT_HI_VARINST WHERE NAME_='" + TASK_IS_FORM
++                + "' AND LONG_=1 AND PROC_INST_ID_='" + user.getWorkflowId() + "'", String.class);
++
++        StringBuilder update = new StringBuilder();
++
++        update.append("DELETE FROM ACT_HI_VARINST WHERE PROC_INST_ID_='").append(user.getWorkflowId()).append("' ");
++        for (String taskId : taskIds) {
++            update.append("AND TASK_ID_<>'").append(taskId).append("' ");
++        }
++        jdbcTemplate.execute(update.toString());
++
++        update.setLength(0);
++        update.append("DELETE FROM ACT_HI_TASKINST WHERE PROC_INST_ID_='").append(user.getWorkflowId()).append("' ");
++        for (String taskId : taskIds) {
++            update.append("AND ID_<>'").append(taskId).append("' ");
++        }
++        jdbcTemplate.execute(update.toString());
++
++        update.setLength(0);
++        update.append("DELETE FROM ACT_HI_ACTINST WHERE PROC_INST_ID_='").append(user.getWorkflowId()).append("' ");
++        for (String taskId : taskIds) {
++            update.append("AND TASK_ID_<>'").append(taskId).append("' ");
++        }
++        jdbcTemplate.execute(update.toString());
++    }
++
 +    /**
 +     * Saves resources to be propagated and password for later - after form submission - propagation.
++     *
++     * @param user user
++     * @param password pasword
++     * @param propByRes current propagation actions against resources
 +     */
 +    protected void saveForFormSubmit(final User user, final String password, final PropagationByResource propByRes) {
 +        String formTaskId = getFormTask(user);
 +        if (formTaskId != null) {
 +            // SYNCOPE-238: This is needed to simplify the task query in this.getForms()
 +            engine.getTaskService().setVariableLocal(formTaskId, TASK_IS_FORM, Boolean.TRUE);
 +            engine.getRuntimeService().setVariable(user.getWorkflowId(), PROP_BY_RESOURCE, propByRes);
 +            if (propByRes != null) {
 +                propByRes.clear();
 +            }
 +
 +            if (StringUtils.isNotBlank(password)) {
 +                engine.getRuntimeService().setVariable(user.getWorkflowId(), ENCRYPTED_PWD, encrypt(password));
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final boolean storePassword) {
 +
 +        return create(userTO, disablePwdPolicyCheck, null, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean storePassword) {
 +        return create(userTO, false, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final Boolean enabled, final boolean storePassword) {
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
 +        variables.put(USER_TO, userTO);
 +        variables.put(ENABLED, enabled);
 +        variables.put(STORE_PASSWORD, storePassword);
 +
 +        ProcessInstance processInstance = null;
 +        try {
 +            processInstance = engine.getRuntimeService().startProcessInstanceByKey(WF_PROCESS_ID, variables);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While starting " + WF_PROCESS_ID + " instance");
 +        }
 +
 +        User user = engine.getRuntimeService().getVariable(processInstance.getProcessInstanceId(), USER, User.class);
 +
 +        Boolean updatedEnabled =
 +                engine.getRuntimeService().getVariable(processInstance.getProcessInstanceId(), ENABLED, Boolean.class);
 +        if (updatedEnabled != null) {
 +            user.setSuspended(!updatedEnabled);
 +        }
 +
 +        // this will make UserValidator not to consider password policies at all
 +        if (disablePwdPolicyCheck) {
 +            user.removeClearPassword();
 +        }
 +
 +        updateStatus(user);
 +        user = userDAO.save(user);
 +
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                processInstance.getProcessInstanceId(), PROPAGATE_ENABLE, Boolean.class);
 +        if (propagateEnable == null) {
 +            propagateEnable = enabled;
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.CREATE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, userTO.getPassword(), propByRes);
 +
++        Set<String> tasks = getPerformedTasks(user);
++
++        cleanupHistory(user);
++
 +        return new WorkflowResult<Pair<Long, Boolean>>(
-                 new ImmutablePair<>(user.getKey(), propagateEnable), propByRes, getPerformedTasks(user));
++                new ImmutablePair<>(user.getKey(), propagateEnable), propByRes, tasks);
 +    }
 +
 +    protected Set<String> doExecuteTask(final User user, final String task, final Map<String, Object> moreVariables) {
 +        Set<String> preTasks = getPerformedTasks(user);
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
 +        variables.put(TASK, task);
 +
 +        // using BeanUtils to access all user's properties and trigger lazy loading - we are about to
 +        // serialize a User instance for availability within workflow tasks, and this breaks transactions
 +        BeanUtils.copyProperties(user, entityFactory.newEntity(User.class));
 +        variables.put(USER, user);
 +
 +        if (moreVariables != null && !moreVariables.isEmpty()) {
 +            variables.putAll(moreVariables);
 +        }
 +
 +        if (StringUtils.isBlank(user.getWorkflowId())) {
 +            throw new WorkflowException(new NotFoundException("Empty workflow id for " + user));
 +        }
 +
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.size() == 1) {
 +            try {
 +                engine.getTaskService().complete(tasks.get(0).getId(), variables);
 +            } catch (ActivitiException e) {
 +                throwException(e, "While completing task '" + tasks.get(0).getName() + "' for " + user);
 +            }
 +        } else {
 +            LOG.warn("Expected a single task, found {}", tasks.size());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(task);
++
++        cleanupHistory(user);
++
 +        return postTasks;
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doActivate(final User user, final String token) {
 +        Set<String> tasks = doExecuteTask(user, "activate", Collections.singletonMap(TOKEN, (Object) token));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Pair<UserPatch, Boolean>> doUpdate(final User user, final UserPatch userPatch) {
 +        Set<String> tasks = doExecuteTask(user, "update", Collections.singletonMap(USER_PATCH, (Object) userPatch));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +        UserPatch updatedPatch = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), USER_PATCH, UserPatch.class);
 +
 +        saveForFormSubmit(
 +                updated, updatedPatch.getPassword() == null ? null : updatedPatch.getPassword().getValue(), propByRes);
 +
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class);
 +
 +        return new WorkflowResult<Pair<UserPatch, Boolean>>(
 +                new ImmutablePair<>(updatedPatch, propagateEnable), propByRes, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doSuspend(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "suspend", null);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doReactivate(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "reactivate", null);
 +        updateStatus(user);
 +
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected void doRequestPasswordReset(final User user) {
 +        Map<String, Object> variables = new HashMap<>(2);
 +        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "requestPasswordReset");
 +
 +        doExecuteTask(user, "requestPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Pair<UserPatch, Boolean>> doConfirmPasswordReset(
 +            final User user, final String token, final String password) {
 +
 +        Map<String, Object> variables = new HashMap<>(4);
 +        variables.put(TOKEN, token);
 +        variables.put(PASSWORD, password);
 +        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "confirmPasswordReset");
 +
 +        Set<String> tasks = doExecuteTask(user, "confirmPasswordReset", variables);
 +
 +        userDAO.save(user);
 +
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +        UserPatch updatedPatch = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), USER_PATCH, UserPatch.class);
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class);
 +
 +        return new WorkflowResult<Pair<UserPatch, Boolean>>(
 +                new ImmutablePair<>(updatedPatch, propagateEnable), propByRes, tasks);
 +    }
 +
 +    @Override
 +    protected void doDelete(final User user) {
 +        doExecuteTask(user, "delete", null);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, null, propByRes);
 +
 +        if (engine.getRuntimeService().createProcessInstanceQuery().
 +                processInstanceId(user.getWorkflowId()).active().list().isEmpty()) {
 +
 +            userDAO.delete(user.getKey());
 +
 +            if (!engine.getHistoryService().createHistoricProcessInstanceQuery().
 +                    processInstanceId(user.getWorkflowId()).list().isEmpty()) {
 +
 +                engine.getHistoryService().deleteHistoricProcessInstance(user.getWorkflowId());
 +            }
 +        } else {
 +            updateStatus(user);
 +            userDAO.save(user);
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Long> execute(final UserTO userTO, final String taskId) {
 +        User user = userDAO.authFind(userTO.getKey());
 +
 +        final Map<String, Object> variables = new HashMap<>();
 +        variables.put(USER_TO, userTO);
 +
 +        Set<String> performedTasks = doExecuteTask(user, taskId, variables);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    protected ProcessDefinition getProcessDefinition() {
 +        try {
 +            return engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(
 +                    ActivitiUserWorkflowAdapter.WF_PROCESS_ID).latestVersion().singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +
 +    }
 +
 +    protected Model getModel(final ProcessDefinition procDef) {
 +        try {
 +            Model model = engine.getRepositoryService().createModelQuery().
 +                    deploymentId(procDef.getDeploymentId()).singleResult();
 +            if (model == null) {
 +                throw new NotFoundException("Could not find Model for deployment " + procDef.getDeploymentId());
 +            }
 +            return model;
 +        } catch (Exception e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +    }
 +
 +    protected void exportProcessResource(final String resourceName, final OutputStream os) {
 +        ProcessDefinition procDef = getProcessDefinition();
 +
 +        InputStream procDefIS = engine.getRepositoryService().getResourceAsStream(procDef.getDeploymentId(),
 +                resourceName);
 +        try {
 +            IOUtils.copy(procDefIS, os);
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", procDef.getKey(), e);
 +        } finally {
 +            IOUtils.closeQuietly(procDefIS);
 +        }
 +    }
 +
 +    protected void exportProcessModel(final OutputStream os) {
 +        Model model = getModel(getProcessDefinition());
 +
 +        ObjectMapper objectMapper = new ObjectMapper();
 +        try {
 +            ObjectNode modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
 +            modelNode.put(ModelDataJsonConstants.MODEL_ID, model.getId());
 +            modelNode.replace(MODEL_DATA_JSON_MODEL,
 +                    objectMapper.readTree(engine.getRepositoryService().getModelEditorSource(model.getId())));
 +
 +            os.write(modelNode.toString().getBytes());
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", model.getId(), e);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDefinition(final WorkflowDefinitionFormat format, final OutputStream os) {
 +        switch (format) {
 +            case JSON:
 +                exportProcessModel(os);
 +                break;
 +
 +            case XML:
 +            default:
 +                exportProcessResource(WF_PROCESS_RESOURCE, os);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDiagram(final OutputStream os) {
 +        exportProcessResource(WF_DGRM_RESOURCE, os);
 +    }
 +
 +    @Override
 +    public void importDefinition(final WorkflowDefinitionFormat format, final String definition) {
 +        Model model = getModel(getProcessDefinition());
 +        switch (format) {
 +            case JSON:
 +                JsonNode definitionNode;
 +                try {
 +                    definitionNode = new ObjectMapper().readTree(definition);
 +                    if (definitionNode.has(MODEL_DATA_JSON_MODEL)) {
 +                        definitionNode = definitionNode.get(MODEL_DATA_JSON_MODEL);
 +                    }
 +                    if (!definitionNode.has(BpmnJsonConverter.EDITOR_CHILD_SHAPES)) {
 +                        throw new IllegalArgumentException(
 +                                "Could not find JSON node " + BpmnJsonConverter.EDITOR_CHILD_SHAPES);
 +                    }
 +
 +                    BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(definitionNode);
 +                    ActivitiImportUtils.fromXML(engine, new BpmnXMLConverter().convertToXML(bpmnModel));
 +                } catch (Exception e) {
 +                    throw new WorkflowException("While updating process "
 +                            + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
 +                }
 +
 +                ActivitiImportUtils.fromJSON(
 +                        engine, definitionNode.toString().getBytes(), getProcessDefinition(), model);
 +                break;
 +
 +            case XML:
 +            default:
 +                ActivitiImportUtils.fromXML(engine, definition.getBytes());
 +
 +                ActivitiImportUtils.fromJSON(engine, getProcessDefinition(), model);
 +        }
 +    }
 +
 +    protected WorkflowFormPropertyType fromActivitiFormType(final FormType activitiFormType) {
 +        WorkflowFormPropertyType result = WorkflowFormPropertyType.String;
 +
 +        if ("string".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.String;
 +        }
 +        if ("long".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Long;
 +        }
 +        if ("enum".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Enum;
 +        }
 +        if ("date".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Date;
 +        }
 +        if ("boolean".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Boolean;
 +        }
 +
 +        return result;
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final Task task) {
 +        return getFormTO(task, engine.getFormService().getTaskFormData(task.getId()));
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final Task task, final TaskFormData fd) {
 +        final WorkflowFormTO formTO =
 +                getFormTO(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
 +
 +        BeanUtils.copyProperties(task, formTO);
 +        return formTO;
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final HistoricTaskInstance task) {
 +        final List<HistoricFormPropertyEntity> props = new ArrayList<>();
 +
 +        for (HistoricDetail historicDetail
 +                : engine.getHistoryService().createHistoricDetailQuery().taskId(task.getId()).list()) {
 +
 +            if (historicDetail instanceof HistoricFormPropertyEntity) {
 +                props.add((HistoricFormPropertyEntity) historicDetail);
 +            }
 +        }
 +
 +        final WorkflowFormTO formTO = getHistoricFormTO(
 +                task.getProcessInstanceId(), task.getId(), task.getFormKey(), props);
 +        BeanUtils.copyProperties(task, formTO);
 +
 +        final HistoricActivityInstance historicActivityInstance = engine.getHistoryService().
 +                createHistoricActivityInstanceQuery().
 +                executionId(task.getExecutionId()).activityType("userTask").activityName(task.getName()).singleResult();
 +
 +        if (historicActivityInstance != null) {
 +            formTO.setCreateTime(historicActivityInstance.getStartTime());
 +            formTO.setDueDate(historicActivityInstance.getEndTime());
 +        }
 +
 +        return formTO;
 +    }
 +
 +    protected WorkflowFormTO getHistoricFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<HistoricFormPropertyEntity> props) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (HistoricFormPropertyEntity prop : props) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            propertyTO.setId(prop.getPropertyId());
 +            propertyTO.setName(prop.getPropertyId());
 +            propertyTO.setValue(prop.getPropertyValue());
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    protected WorkflowFormTO getFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<FormProperty> properties) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (FormProperty fProp : properties) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            BeanUtils.copyProperties(fProp, propertyTO, PROPERTY_IGNORE_PROPS);
 +            propertyTO.setType(fromActivitiFormType(fProp.getType()));
 +
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Date) {
 +                propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
 +            }
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Enum) {
 +                propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values"));
 +            }
 +
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<WorkflowFormTO> getForms() {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        String authUser = AuthContextUtils.getUsername();
 +        if (adminUser.equals(authUser)) {
 +            forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +        } else {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +
 +            forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                    taskCandidateOrAssigned(user.getKey().toString())));
 +
 +            List<String> candidateGroups = new ArrayList<>();
 +            for (Long groupId : userDAO.findAllGroupKeys(user)) {
 +                candidateGroups.add(groupId.toString());
 +            }
 +            if (!candidateGroups.isEmpty()) {
 +                forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                        taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                        taskCandidateGroupIn(candidateGroups)));
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public List<WorkflowFormTO> getForms(final String workflowId, final String name) {
 +        List<WorkflowFormTO> forms = getForms(
 +                engine.getTaskService().createTaskQuery().processInstanceId(workflowId).taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE));
 +
 +        forms.addAll(getForms(engine.getHistoryService().createHistoricTaskInstanceQuery().taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +
 +        return forms;
 +    }
 +
 +    protected <T extends Query<?, ?>, U extends Object> List<WorkflowFormTO> getForms(final Query<T, U> query) {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        for (U obj : query.list()) {
 +            try {
 +                if (obj instanceof HistoricTaskInstance) {
 +                    forms.add(getFormTO((HistoricTaskInstance) obj));
 +                } else if (obj instanceof Task) {
 +                    forms.add(getFormTO((Task) obj));
 +                } else {
 +                    throw new ActivitiException(
 +                            "Failure retrieving form", new IllegalArgumentException("Invalid task type"));
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.debug("No form found for task {}", obj, e);
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public WorkflowFormTO getForm(final String workflowId) {
 +        Task task;
 +        try {
 +            task = engine.getTaskService().createTaskQuery().processInstanceId(workflowId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading form for workflow instance " + workflowId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = engine.getFormService().getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            LOG.debug("No form found for task {}", task.getId(), e);
 +            formData = null;
 +        }
 +
 +        WorkflowFormTO result = null;
 +        if (formData != null && !formData.getFormProperties().isEmpty()) {
 +            result = getFormTO(task);
 +        }
 +
 +        return result;
 +    }
 +
 +    protected Pair<Task, TaskFormData> checkTask(final String taskId, final String authUser) {
 +        Task task;
 +        try {
 +            task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
++            if (task == null) {
++                throw new ActivitiException("NULL result");
++            }
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Activiti Task " + taskId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = engine.getFormService().getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Form for Activiti Task " + taskId, e);
 +        }
 +
 +        if (!adminUser.equals(authUser)) {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +        }
 +
 +        return new ImmutablePair<>(task, formData);
 +    }
 +
 +    @Override
 +    public WorkflowFormTO claimForm(final String taskId) {
 +        String authUser = AuthContextUtils.getUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(taskId, authUser);
 +
 +        if (!adminUser.equals(authUser)) {
 +            List<Task> tasksForUser = engine.getTaskService().createTaskQuery().taskId(taskId).taskCandidateUser(
 +                    authUser).list();
 +            if (tasksForUser.isEmpty()) {
 +                throw new WorkflowException(
 +                        new IllegalArgumentException(authUser + " is not candidate for task " + taskId));
 +            }
 +        }
 +
 +        Task task;
 +        try {
 +            engine.getTaskService().setOwner(taskId, authUser);
 +            task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading task " + taskId, e);
 +        }
 +
 +        return getFormTO(task, checked.getValue());
 +    }
 +
 +    @Override
 +    public WorkflowResult<UserPatch> submitForm(final WorkflowFormTO form) {
 +        String authUser = AuthContextUtils.getUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(form.getTaskId(), authUser);
 +
 +        if (!checked.getKey().getOwner().equals(authUser)) {
 +            throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
 +                    + checked.getKey().getOwner() + " but submitted by " + authUser));
 +        }
 +
 +        User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId());
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + checked.getKey().getProcessInstanceId());
 +        }
 +
 +        Set<String> preTasks = getPerformedTasks(user);
 +        try {
 +            engine.getFormService().submitTaskFormData(form.getTaskId(), form.getPropertiesForSubmit());
 +            engine.getRuntimeService().setVariable(user.getWorkflowId(), FORM_SUBMITTER, authUser);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While submitting form for task " + form.getTaskId());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(form.getTaskId());
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        // see if there is any propagation to be done
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +
 +        // fetch - if available - the encrypted password
 +        String clearPassword = null;
 +        String encryptedPwd = engine.getRuntimeService().getVariable(user.getWorkflowId(), ENCRYPTED_PWD, String.class);
 +        if (StringUtils.isNotBlank(encryptedPwd)) {
 +            clearPassword = decrypt(encryptedPwd);
 +        }
 +
 +        // supports approval chains
 +        saveForFormSubmit(user, clearPassword, propByRes);
 +
 +        UserPatch userPatch = engine.getRuntimeService().getVariable(user.getWorkflowId(), USER_PATCH, UserPatch.class);
 +        if (userPatch == null) {
 +            userPatch = new UserPatch();
 +            userPatch.setKey(updated.getKey());
 +            userPatch.setPassword(new PasswordPatch.Builder().onSyncope(true).value(clearPassword).build());
 +
 +            if (propByRes != null) {
 +                userPatch.getPassword().getResources().addAll(propByRes.get(ResourceOperation.CREATE));
 +            }
 +        }
 +
++        cleanupHistory(user);
++
 +        return new WorkflowResult<>(userPatch, propByRes, postTasks);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/e9ff5c73/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/spring/DomainProcessEngine.java
----------------------------------------------------------------------
diff --cc core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/spring/DomainProcessEngine.java
index 48eeb5c,0000000..f851e08
mode 100644,000000..100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/spring/DomainProcessEngine.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/spring/DomainProcessEngine.java
@@@ -1,108 -1,0 +1,114 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.workflow.activiti.spring;
 +
 +import java.util.Collections;
 +import java.util.Map;
++import javax.sql.DataSource;
 +import org.activiti.engine.DynamicBpmnService;
 +import org.activiti.engine.FormService;
 +import org.activiti.engine.HistoryService;
 +import org.activiti.engine.IdentityService;
 +import org.activiti.engine.ManagementService;
 +import org.activiti.engine.ProcessEngine;
 +import org.activiti.engine.ProcessEngineConfiguration;
 +import org.activiti.engine.RepositoryService;
 +import org.activiti.engine.RuntimeService;
 +import org.activiti.engine.TaskService;
++import org.activiti.engine.impl.ProcessEngineImpl;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +
 +/**
 + * {@link ProcessEngine} delegating actual method invocation to the inner map of {@link ProcessEngine} instances,
 + * one for each Syncope domain.
 + */
 +public class DomainProcessEngine implements ProcessEngine {
 +
 +    private final Map<String, ProcessEngine> engines;
 +
 +    public DomainProcessEngine(final Map<String, ProcessEngine> engines) {
 +        this.engines = Collections.synchronizedMap(engines);
 +    }
 +
 +    public Map<String, ProcessEngine> getEngines() {
 +        return engines;
 +    }
 +
 +    @Override
 +    public String getName() {
 +        return engines.get(AuthContextUtils.getDomain()).getName();
 +    }
 +
 +    @Override
 +    public void close() {
 +        for (ProcessEngine engine : engines.values()) {
 +            engine.close();
 +        }
 +    }
 +
 +    @Override
 +    public RepositoryService getRepositoryService() {
 +        return engines.get(AuthContextUtils.getDomain()).getRepositoryService();
 +    }
 +
 +    @Override
 +    public RuntimeService getRuntimeService() {
 +        return engines.get(AuthContextUtils.getDomain()).getRuntimeService();
 +    }
 +
 +    @Override
 +    public FormService getFormService() {
 +        return engines.get(AuthContextUtils.getDomain()).getFormService();
 +    }
 +
 +    @Override
 +    public TaskService getTaskService() {
 +        return engines.get(AuthContextUtils.getDomain()).getTaskService();
 +    }
 +
 +    @Override
 +    public HistoryService getHistoryService() {
 +        return engines.get(AuthContextUtils.getDomain()).getHistoryService();
 +    }
 +
 +    @Override
 +    public IdentityService getIdentityService() {
 +        return engines.get(AuthContextUtils.getDomain()).getIdentityService();
 +    }
 +
 +    @Override
 +    public ManagementService getManagementService() {
 +        return engines.get(AuthContextUtils.getDomain()).getManagementService();
 +    }
 +
 +    @Override
 +    public ProcessEngineConfiguration getProcessEngineConfiguration() {
 +        return engines.get(AuthContextUtils.getDomain()).getProcessEngineConfiguration();
 +    }
 +
 +    @Override
 +    public DynamicBpmnService getDynamicBpmnService() {
 +        return engines.get(AuthContextUtils.getDomain()).getDynamicBpmnService();
 +    }
 +
++    public DataSource getDataSource() {
++        ProcessEngineImpl engine = (ProcessEngineImpl) engines.get(AuthContextUtils.getDomain());
++        return engine.getProcessEngineConfiguration().getDataSource();
++    }
 +}


[3/5] syncope git commit: [SYNCOPE-119] Added search options and checks to match assignable conditions, for usage with memberships and relationships

Posted by il...@apache.org.
[SYNCOPE-119] Added search options and checks to match assignable conditions, for usage with memberships and relationships


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/bbb051fe
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/bbb051fe
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/bbb051fe

Branch: refs/heads/master
Commit: bbb051feb43291de74d7cec215c63d2d68cbca03
Parents: 7b1cb89
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Nov 25 11:43:25 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Nov 25 12:09:02 2015 +0100

----------------------------------------------------------------------
 .../syncope/client/lib/SyncopeClient.java       |   5 +-
 .../AnyObjectFiqlSearchConditionBuilder.java    | 110 ++++++++++++-
 .../common/lib/search/AnyObjectProperty.java    |  16 ++
 .../search/GroupFiqlSearchConditionBuilder.java |  21 ++-
 .../common/lib/search/GroupProperty.java        |   4 +
 .../syncope/common/lib/search/SpecialAttr.java  |  31 +++-
 .../search/UserFiqlSearchConditionBuilder.java  |  80 ++++++++--
 .../syncope/common/lib/search/UserProperty.java |   8 +
 .../common/lib/types/ClientExceptionType.java   |   2 +
 .../core/misc/search/SearchCondVisitor.java     |  29 +++-
 .../misc/search/SearchCondConverterTest.java    |  50 +++++-
 .../core/persistence/api/dao/AnySearchDAO.java  |   8 +
 .../core/persistence/api/dao/RealmDAO.java      |   3 +
 .../api/dao/search/AssignableCond.java          |  39 +++++
 .../api/dao/search/RelationshipTypeCond.java    |  39 +++++
 .../persistence/api/dao/search/SearchCond.java  |  42 ++++-
 .../persistence/jpa/dao/JPAAnySearchDAO.java    | 154 +++++++++++++------
 .../core/persistence/jpa/dao/JPARealmDAO.java   |  11 +-
 .../src/main/resources/views.xml                |   4 +-
 .../persistence/jpa/inner/AnySearchTest.java    | 116 +++++++++++++-
 .../core/persistence/jpa/inner/GroupTest.java   |   2 +-
 .../core/persistence/jpa/outer/RealmTest.java   |  11 ++
 .../test/resources/domains/MasterContent.xml    |   9 +-
 .../provisioning/api/data/GroupDataBinder.java  |   2 +-
 .../java/data/AbstractAnyDataBinder.java        |  24 +--
 .../java/data/AnyObjectDataBinderImpl.java      | 116 ++++++++++----
 .../java/data/GroupDataBinderImpl.java          |  24 ++-
 .../java/data/UserDataBinderImpl.java           | 116 ++++++++++----
 .../rest/cxf/service/AnyObjectServiceImpl.java  |   2 +-
 .../fit/core/reference/AnyObjectITCase.java     |  22 +++
 .../fit/core/reference/SearchITCase.java        | 135 ++++++++++++++--
 .../fit/core/reference/SyncTaskITCase.java      |   4 +-
 .../syncope/fit/core/reference/UserITCase.java  |   2 +-
 33 files changed, 1031 insertions(+), 210 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
index 198b585..c2d6250 100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
@@ -93,10 +93,11 @@ public class SyncopeClient {
     /**
      * Returns a new instance of {@link AnyObjectFiqlSearchConditionBuilder}, for assisted building of FIQL queries.
      *
+     * @param type any type
      * @return default instance of {@link AnyObjectFiqlSearchConditionBuilder}
      */
-    public static AnyObjectFiqlSearchConditionBuilder getAnyObjectSearchConditionBuilder() {
-        return new AnyObjectFiqlSearchConditionBuilder();
+    public static AnyObjectFiqlSearchConditionBuilder getAnyObjectSearchConditionBuilder(final String type) {
+        return new AnyObjectFiqlSearchConditionBuilder(type);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
index 67e7e15..0841de3 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
@@ -18,8 +18,11 @@
  */
 package org.apache.syncope.common.lib.search;
 
+import java.util.Collections;
 import java.util.Map;
 import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
+import org.apache.cxf.jaxrs.ext.search.client.FiqlSearchConditionBuilder;
+import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser;
 
 /**
  * Extends {@link AbstractFiqlSearchConditionBuilder} by providing some additional facilities for searching
@@ -27,12 +30,11 @@ import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
  */
 public class AnyObjectFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionBuilder {
 
-    public AnyObjectFiqlSearchConditionBuilder() {
-        super();
-    }
+    private final String type;
 
-    public AnyObjectFiqlSearchConditionBuilder(final Map<String, String> properties) {
-        super(properties);
+    public AnyObjectFiqlSearchConditionBuilder(final String type) {
+        super();
+        this.type = type;
     }
 
     @Override
@@ -41,15 +43,59 @@ public class AnyObjectFiqlSearchConditionBuilder extends AbstractFiqlSearchCondi
     }
 
     @Override
+    public String query() {
+        return new FiqlSearchConditionBuilder.Builder(Collections.<String, String>emptyMap()).
+                is(SpecialAttr.TYPE.toString()).equalTo(type).query();
+    }
+
+    @Override
     public AnyObjectProperty is(final String property) {
         return newBuilderInstance().is(property);
     }
 
-    public CompleteCondition type(final String type) {
-        return newBuilderInstance().is(SpecialAttr.TYPE.toString()).equalTo(type);
+    public CompleteCondition inGroups(final Long group, final Long... moreGroups) {
+        return newBuilderInstance().
+                is(SpecialAttr.GROUPS.toString()).
+                inGroups(group, moreGroups);
     }
 
-    protected static class Builder extends AbstractFiqlSearchConditionBuilder.Builder
+    public CompleteCondition notInGroups(final Long group, final Long... moreGroups) {
+        return newBuilderInstance().
+                is(SpecialAttr.GROUPS.toString()).
+                notInGroups(group, moreGroups);
+    }
+
+    public CompleteCondition inRelationships(final Long anyType, final Long... moreAnyTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIPS.toString()).
+                inRelationships(anyType, moreAnyTypes);
+    }
+
+    public CompleteCondition notInRelationships(final Long anyType, final Long... moreAnyTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIPS.toString()).
+                notInRelationships(anyType, moreAnyTypes);
+    }
+
+    public CompleteCondition inRelationshipTypes(final String type, final String... moreTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIP_TYPES.toString()).
+                inRelationshipTypes(type, moreTypes);
+    }
+
+    public CompleteCondition notInRelationshipTypes(final String type, final String... moreTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIP_TYPES.toString()).
+                notInRelationshipTypes(type, moreTypes);
+    }
+
+    public CompleteCondition isAssignable(final String realm, final String... moreRealms) {
+        return newBuilderInstance().
+                is(SpecialAttr.ASSIGNABLE.toString()).
+                isAssignable(realm, moreRealms);
+    }
+
+    protected class Builder extends AbstractFiqlSearchConditionBuilder.Builder
             implements AnyObjectProperty, CompleteCondition {
 
         public Builder(final Map<String, String> properties) {
@@ -61,11 +107,59 @@ public class AnyObjectFiqlSearchConditionBuilder extends AbstractFiqlSearchCondi
         }
 
         @Override
+        public String query() {
+            FiqlSearchConditionBuilder.Builder b = new FiqlSearchConditionBuilder.Builder(this);
+            return b.and(SpecialAttr.TYPE.toString()).equalTo(type).query();
+        }
+
+        @Override
         public AnyObjectProperty is(final String property) {
             Builder b = new Builder(this);
             b.result = property;
             return b;
         }
 
+        @Override
+        public CompleteCondition inGroups(final Long group, final Long... moreGroups) {
+            this.result = SpecialAttr.GROUPS.toString();
+            return condition(FiqlParser.EQ, group, (Object[]) moreGroups);
+        }
+
+        @Override
+        public CompleteCondition notInGroups(final Long group, final Long... moreGroups) {
+            this.result = SpecialAttr.GROUPS.toString();
+            return condition(FiqlParser.NEQ, group, (Object[]) moreGroups);
+        }
+
+        @Override
+        public CompleteCondition inRelationships(final Long anyObject, final Long... moreAnyObjects) {
+            this.result = SpecialAttr.RELATIONSHIPS.toString();
+            return condition(FiqlParser.EQ, anyObject, (Object[]) moreAnyObjects);
+        }
+
+        @Override
+        public CompleteCondition notInRelationships(final Long group, final Long... moreRelationships) {
+            this.result = SpecialAttr.RELATIONSHIPS.toString();
+            return condition(FiqlParser.NEQ, group, (Object[]) moreRelationships);
+        }
+
+        @Override
+        public CompleteCondition inRelationshipTypes(final String type, final String... moreTypes) {
+            this.result = SpecialAttr.RELATIONSHIP_TYPES.toString();
+            return condition(FiqlParser.EQ, type, (Object[]) moreTypes);
+        }
+
+        @Override
+        public CompleteCondition notInRelationshipTypes(final String type, final String... moreTypes) {
+            this.result = SpecialAttr.RELATIONSHIP_TYPES.toString();
+            return condition(FiqlParser.NEQ, type, (Object[]) moreTypes);
+        }
+
+        @Override
+        public CompleteCondition isAssignable(final String realm, final String... moreRealms) {
+            this.result = SpecialAttr.ASSIGNABLE.toString();
+            return condition(FiqlParser.EQ, realm, (Object[]) moreRealms);
+        }
+
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectProperty.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectProperty.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectProperty.java
index c675712..47de383 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectProperty.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectProperty.java
@@ -18,6 +18,22 @@
  */
 package org.apache.syncope.common.lib.search;
 
+import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
+
 public interface AnyObjectProperty extends SyncopeProperty {
 
+    CompleteCondition inGroups(Long group, Long... moreGroups);
+
+    CompleteCondition notInGroups(Long group, Long... moreGroups);
+
+    CompleteCondition inRelationships(Long anyObject, Long... moreAnyObjects);
+
+    CompleteCondition notInRelationships(Long anyObject, Long... moreAnyObjects);
+
+    CompleteCondition inRelationshipTypes(String type, String... moreTypes);
+
+    CompleteCondition notInRelationshipTypes(String type, String... moreTypes);
+
+    CompleteCondition isAssignable(String realm, String... moreRealms);
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
index aaa56d7..9a2bc3f 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
@@ -20,6 +20,7 @@ package org.apache.syncope.common.lib.search;
 
 import java.util.Map;
 import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
+import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser;
 
 /**
  * Extends {@link AbstractFiqlSearchConditionBuilder} by providing some additional facilities for searching
@@ -27,14 +28,6 @@ import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
  */
 public class GroupFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionBuilder {
 
-    public GroupFiqlSearchConditionBuilder() {
-        super();
-    }
-
-    public GroupFiqlSearchConditionBuilder(final Map<String, String> properties) {
-        super(properties);
-    }
-
     @Override
     protected Builder newBuilderInstance() {
         return new Builder(properties);
@@ -45,6 +38,12 @@ public class GroupFiqlSearchConditionBuilder extends AbstractFiqlSearchCondition
         return newBuilderInstance().is(property);
     }
 
+    public CompleteCondition isAssignable(final String realm, final String... moreRealms) {
+        return newBuilderInstance().
+                is(SpecialAttr.ASSIGNABLE.toString()).
+                isAssignable(realm, moreRealms);
+    }
+
     protected static class Builder extends AbstractFiqlSearchConditionBuilder.Builder
             implements GroupProperty, CompleteCondition {
 
@@ -63,5 +62,11 @@ public class GroupFiqlSearchConditionBuilder extends AbstractFiqlSearchCondition
             return b;
         }
 
+        @Override
+        public CompleteCondition isAssignable(final String realm, final String... moreRealms) {
+            this.result = SpecialAttr.ASSIGNABLE.toString();
+            return condition(FiqlParser.EQ, realm, (Object[]) moreRealms);
+        }
+
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupProperty.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupProperty.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupProperty.java
index 0eb7480..8dc375e 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupProperty.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/GroupProperty.java
@@ -18,6 +18,10 @@
  */
 package org.apache.syncope.common.lib.search;
 
+import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
+
 public interface GroupProperty extends SyncopeProperty {
 
+    CompleteCondition isAssignable(String realm, String... moreRealms);
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
index 551a0ad..1b1f484 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
@@ -24,11 +24,40 @@ import org.apache.commons.collections4.Predicate;
 
 public enum SpecialAttr {
 
+    /**
+     * Applies to users, groups and any objects.
+     *//**
+     * Applies to users, groups and any objects.
+     */
     NULL("$null"),
+    /**
+     * Applies to any objects.
+     */
     TYPE("$type"),
+    /**
+     * Applies to users, groups and any objects.
+     */
     RESOURCES("$resources"),
+    /**
+     * Applies to users and any objects.
+     */
     GROUPS("$groups"),
-    ROLES("$roles");
+    /**
+     * Applies to users and any objects.
+     */
+    RELATIONSHIPS("$relationships"),
+    /**
+     * Applies to users and any objects.
+     */
+    RELATIONSHIP_TYPES("$relationshipTypes"),
+    /**
+     * Applies to users.
+     */
+    ROLES("$roles"),
+    /**
+     * Applies to groups and any objects.
+     */
+    ASSIGNABLE("$assignable");
 
     private final String literal;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
index 323e38b..30e89dd 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
@@ -28,14 +28,6 @@ import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser;
  */
 public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionBuilder {
 
-    public UserFiqlSearchConditionBuilder() {
-        super();
-    }
-
-    public UserFiqlSearchConditionBuilder(final Map<String, String> properties) {
-        super(properties);
-    }
-
     @Override
     protected Builder newBuilderInstance() {
         return new Builder(properties);
@@ -47,20 +39,58 @@ public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionB
     }
 
     public CompleteCondition inGroups(final Long group, final Long... moreGroups) {
-        return newBuilderInstance().is(SpecialAttr.GROUPS.toString()).inGroups(group, moreGroups);
+        return newBuilderInstance().
+                is(SpecialAttr.GROUPS.toString()).
+                inGroups(group, moreGroups);
+    }
+
+    public CompleteCondition notInGroups(final Long group, final Long... moreGroups) {
+        return newBuilderInstance().
+                is(SpecialAttr.GROUPS.toString()).
+                notInGroups(group, moreGroups);
+    }
+
+    public CompleteCondition inRelationships(final Long anyType, final Long... moreAnyTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIPS.toString()).
+                inRelationships(anyType, moreAnyTypes);
+    }
+
+    public CompleteCondition notInRelationships(final Long anyType, final Long... moreAnyTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIPS.toString()).
+                notInRelationships(anyType, moreAnyTypes);
+    }
+
+    public CompleteCondition inRelationshipTypes(final String type, final String... moreTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIP_TYPES.toString()).
+                inRelationshipTypes(type, moreTypes);
+    }
+
+    public CompleteCondition notInRelationshipTypes(final String type, final String... moreTypes) {
+        return newBuilderInstance().
+                is(SpecialAttr.RELATIONSHIP_TYPES.toString()).
+                notInRelationshipTypes(type, moreTypes);
     }
 
     public CompleteCondition inRoles(final String role, final String... moreRoles) {
-        return newBuilderInstance().is(SpecialAttr.ROLES.toString()).inRoles(role, moreRoles);
+        return newBuilderInstance().
+                is(SpecialAttr.ROLES.toString()).
+                inRoles(role, moreRoles);
     }
 
-    public CompleteCondition notInGroups(final Long group, final Long... moreGroups) {
-        return newBuilderInstance().is(SpecialAttr.GROUPS.toString()).notInGroups(group, moreGroups);
+    public CompleteCondition notInRoles(final String role, final String... moreRoles) {
+        return newBuilderInstance().
+                is(SpecialAttr.ROLES.toString()).
+                notInRoles(role, moreRoles);
     }
 
     @Override
     public CompleteCondition hasResources(final String resource, final String... moreResources) {
-        return newBuilderInstance().is(SpecialAttr.RESOURCES.toString()).hasResources(resource, moreResources);
+        return newBuilderInstance().
+                is(SpecialAttr.RESOURCES.toString()).
+                hasResources(resource, moreResources);
     }
 
     @Override
@@ -99,6 +129,30 @@ public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionB
         }
 
         @Override
+        public CompleteCondition inRelationships(final Long anyObject, final Long... moreAnyObjects) {
+            this.result = SpecialAttr.RELATIONSHIPS.toString();
+            return condition(FiqlParser.EQ, anyObject, (Object[]) moreAnyObjects);
+        }
+
+        @Override
+        public CompleteCondition notInRelationships(final Long anyObject, final Long... moreAnyObjects) {
+            this.result = SpecialAttr.RELATIONSHIPS.toString();
+            return condition(FiqlParser.NEQ, anyObject, (Object[]) moreAnyObjects);
+        }
+
+        @Override
+        public CompleteCondition inRelationshipTypes(final String type, final String... moreTypes) {
+            this.result = SpecialAttr.RELATIONSHIP_TYPES.toString();
+            return condition(FiqlParser.EQ, type, (Object[]) moreTypes);
+        }
+
+        @Override
+        public CompleteCondition notInRelationshipTypes(final String type, final String... moreTypes) {
+            this.result = SpecialAttr.RELATIONSHIP_TYPES.toString();
+            return condition(FiqlParser.NEQ, type, (Object[]) moreTypes);
+        }
+
+        @Override
         public CompleteCondition inRoles(final String role, final String... moreRoles) {
             this.result = SpecialAttr.ROLES.toString();
             return condition(FiqlParser.EQ, role, (Object[]) moreRoles);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
index 210274c..9294ab0 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
@@ -26,6 +26,14 @@ public interface UserProperty extends SyncopeProperty {
 
     CompleteCondition notInGroups(Long group, Long... moreGroups);
 
+    CompleteCondition inRelationships(Long anyObject, Long... moreAnyObjects);
+
+    CompleteCondition notInRelationships(Long anyObject, Long... moreAnyObjects);
+
+    CompleteCondition inRelationshipTypes(String type, String... moreTypes);
+
+    CompleteCondition notInRelationshipTypes(String type, String... moreTypes);
+
     CompleteCondition inRoles(String role, String... moreRoles);
 
     CompleteCondition notInRoles(String role, String... moreRoles);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 68ad199..dfc5fe2 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -40,6 +40,7 @@ public enum ClientExceptionType {
     InvalidProvision(Response.Status.BAD_REQUEST),
     InvalidReport(Response.Status.BAD_REQUEST),
     InvalidReportExec(Response.Status.BAD_REQUEST),
+    InvalidRelationship(Response.Status.BAD_REQUEST),
     InvalidRelationshipType(Response.Status.BAD_REQUEST),
     InvalidAnyType(Response.Status.BAD_REQUEST),
     InvalidAnyObject(Response.Status.BAD_REQUEST),
@@ -52,6 +53,7 @@ public enum ClientExceptionType {
     InvalidDerSchema(Response.Status.BAD_REQUEST),
     InvalidVirSchema(Response.Status.BAD_REQUEST),
     InvalidMapping(Response.Status.BAD_REQUEST),
+    InvalidMembership(Response.Status.BAD_REQUEST),
     InvalidRealm(Response.Status.BAD_REQUEST),
     InvalidRole(Response.Status.BAD_REQUEST),
     InvalidUser(Response.Status.BAD_REQUEST),

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java b/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
index 6b067ca..1fe03d2 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
@@ -38,6 +38,9 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 
 /**
  * Converts CXF's <tt>SearchCondition</tt> into internal <tt>SearchCond</tt>.
@@ -105,22 +108,40 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
                             leaf = SearchCond.getLeafCond(typeCond);
                             break;
 
+                        case RESOURCES:
+                            ResourceCond resourceCond = new ResourceCond();
+                            resourceCond.setResourceName(value);
+                            leaf = SearchCond.getLeafCond(resourceCond);
+                            break;
+
                         case GROUPS:
                             MembershipCond groupCond = new MembershipCond();
                             groupCond.setGroupKey(Long.valueOf(value));
                             leaf = SearchCond.getLeafCond(groupCond);
                             break;
 
+                        case RELATIONSHIPS:
+                            RelationshipCond relationshipCond = new RelationshipCond();
+                            relationshipCond.setAnyObjectKey(Long.valueOf(value));
+                            leaf = SearchCond.getLeafCond(relationshipCond);
+                            break;
+
+                        case RELATIONSHIP_TYPES:
+                            RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond();
+                            relationshipTypeCond.setRelationshipTypeKey(value);
+                            leaf = SearchCond.getLeafCond(relationshipTypeCond);
+                            break;
+
                         case ROLES:
                             RoleCond roleCond = new RoleCond();
                             roleCond.setRoleKey(value);
                             leaf = SearchCond.getLeafCond(roleCond);
                             break;
 
-                        case RESOURCES:
-                            ResourceCond resourceCond = new ResourceCond();
-                            resourceCond.setResourceName(value);
-                            leaf = SearchCond.getLeafCond(resourceCond);
+                        case ASSIGNABLE:
+                            AssignableCond assignableCond = new AssignableCond();
+                            assignableCond.setRealmFullPath(value);
+                            leaf = SearchCond.getLeafCond(assignableCond);
                             break;
 
                         default:

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
----------------------------------------------------------------------
diff --git a/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java b/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
index 29f3c64..e081ffa 100644
--- a/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
+++ b/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.misc.search;
 import static org.junit.Assert.assertEquals;
 
 import org.apache.syncope.common.lib.search.AnyObjectFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.search.GroupFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.SpecialAttr;
 import org.apache.syncope.common.lib.search.UserFiqlSearchConditionBuilder;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
@@ -30,6 +31,9 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 import org.junit.Test;
 
 public class SearchCondConverterTest {
@@ -85,6 +89,35 @@ public class SearchCondConverterTest {
     }
 
     @Test
+    public void relationships() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().inRelationships(1L).query();
+        assertEquals(SpecialAttr.RELATIONSHIPS + "==1", fiqlExpression);
+
+        RelationshipCond relationshipCond = new RelationshipCond();
+        relationshipCond.setAnyObjectKey(1L);
+        SearchCond simpleCond = SearchCond.getLeafCond(relationshipCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void relationshipTypes() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().inRelationshipTypes("type1").query();
+        assertEquals(SpecialAttr.RELATIONSHIP_TYPES + "==type1", fiqlExpression);
+
+        RelationshipTypeCond relationshipCond = new RelationshipTypeCond();
+        relationshipCond.setRelationshipTypeKey("type1");
+        SearchCond simpleCond = SearchCond.getLeafCond(relationshipCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+
+        fiqlExpression = new AnyObjectFiqlSearchConditionBuilder("PRINTER").inRelationshipTypes("neighborhood").query();
+        assertEquals(
+                SpecialAttr.RELATIONSHIP_TYPES + "==neighborhood;" + SpecialAttr.TYPE + "==PRINTER",
+                fiqlExpression);
+    }
+
+    @Test
     public void groups() {
         String fiqlExpression = new UserFiqlSearchConditionBuilder().inGroups(1L).query();
         assertEquals(SpecialAttr.GROUPS + "==1", fiqlExpression);
@@ -121,8 +154,20 @@ public class SearchCondConverterTest {
     }
 
     @Test
+    public void assignable() {
+        String fiqlExpression = new GroupFiqlSearchConditionBuilder().isAssignable("/even/two").query();
+        assertEquals(SpecialAttr.ASSIGNABLE + "==/even/two", fiqlExpression);
+
+        AssignableCond assignableCond = new AssignableCond();
+        assignableCond.setRealmFullPath("/even/two");
+        SearchCond simpleCond = SearchCond.getLeafCond(assignableCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
     public void type() {
-        String fiqlExpression = new AnyObjectFiqlSearchConditionBuilder().type("PRINTER").query();
+        String fiqlExpression = new AnyObjectFiqlSearchConditionBuilder("PRINTER").query();
         assertEquals(SpecialAttr.TYPE + "==PRINTER", fiqlExpression);
 
         AnyTypeCond acond = new AnyTypeCond();
@@ -156,6 +201,9 @@ public class SearchCondConverterTest {
         String fiqlExpression = new UserFiqlSearchConditionBuilder().
                 is("fullname").equalTo("*o*", "*i*", "*ini").query();
         assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", fiqlExpression);
+        fiqlExpression = new UserFiqlSearchConditionBuilder().
+                is("fullname").equalTo("*o*").or("fullname").equalTo("*i*").or("fullname").equalTo("*ini").query();
+        assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", fiqlExpression);
 
         AttributeCond fullnameLeafCond1 = new AttributeCond(AttributeCond.Type.LIKE);
         fullnameLeafCond1.setSchema("fullname");

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java
index 401d957..67e85f2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java
@@ -36,6 +36,14 @@ public interface AnySearchDAO extends DAO<Any<?>, Long> {
     int count(Set<String> adminRealms, SearchCond searchCondition, AnyTypeKind kind);
 
     /**
+     * @param realmFullPath for use with {@link org.apache.syncope.core.persistence.api.dao.search.AssignableCond}
+     * @param kind any object
+     * @param <T> any
+     * @return the list of any objects matching the given search condition
+     */
+    <T extends Any<?>> List<T> searchAssignable(String realmFullPath, AnyTypeKind kind);
+
+    /**
      * @param searchCondition the search condition
      * @param kind any object
      * @param <T> any

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java
index 515779e..f8c94b6 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java
@@ -19,11 +19,14 @@
 package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
+import java.util.regex.Pattern;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 
 public interface RealmDAO extends DAO<Realm, Long> {
 
+    Pattern PATH_PATTERN = Pattern.compile("^(/[A-Za-z0-9]+)+");
+
     Realm getRoot();
 
     Realm find(Long key);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AssignableCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AssignableCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AssignableCond.java
new file mode 100644
index 0000000..0ece01b
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AssignableCond.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.search;
+
+public class AssignableCond extends AbstractSearchCond {
+
+    private static final long serialVersionUID = 1237627275756159522L;
+
+    private String realmFullPath;
+
+    public String getRealmFullPath() {
+        return realmFullPath;
+    }
+
+    public void setRealmFullPath(final String realmFullPath) {
+        this.realmFullPath = realmFullPath;
+    }
+
+    @Override
+    public final boolean isValid() {
+        return realmFullPath != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RelationshipTypeCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RelationshipTypeCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RelationshipTypeCond.java
new file mode 100644
index 0000000..d120e62
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RelationshipTypeCond.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.search;
+
+public class RelationshipTypeCond extends AbstractSearchCond {
+
+    private static final long serialVersionUID = 906241367466433419L;
+
+    private String relationshipTypeKey;
+
+    public String getRelationshipTypeKey() {
+        return relationshipTypeKey;
+    }
+
+    public void setRelationshipTypeKey(final String relationshipTypeKey) {
+        this.relationshipTypeKey = relationshipTypeKey;
+    }
+
+    @Override
+    public final boolean isValid() {
+        return relationshipTypeKey != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
index 14641c1..a33ccf3 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
@@ -43,12 +43,16 @@ public class SearchCond extends AbstractSearchCond {
 
     private RelationshipCond relationshipCond;
 
+    private RelationshipTypeCond relationshipTypeCond;
+
     private MembershipCond membershipCond;
 
     private RoleCond roleCond;
 
     private ResourceCond resourceCond;
 
+    private AssignableCond assignableCond;
+
     private SearchCond leftNodeCond;
 
     private SearchCond rightNodeCond;
@@ -84,6 +88,15 @@ public class SearchCond extends AbstractSearchCond {
         return nodeCond;
     }
 
+    public static SearchCond getLeafCond(final RelationshipTypeCond relationshipTypeCond) {
+        SearchCond nodeCond = new SearchCond();
+
+        nodeCond.type = Type.LEAF;
+        nodeCond.relationshipTypeCond = relationshipTypeCond;
+
+        return nodeCond;
+    }
+
     public static SearchCond getLeafCond(final MembershipCond membershipCond) {
         SearchCond nodeCond = new SearchCond();
 
@@ -111,6 +124,15 @@ public class SearchCond extends AbstractSearchCond {
         return nodeCond;
     }
 
+    public static SearchCond getLeafCond(final AssignableCond assignableCond) {
+        SearchCond nodeCond = new SearchCond();
+
+        nodeCond.type = Type.LEAF;
+        nodeCond.assignableCond = assignableCond;
+
+        return nodeCond;
+    }
+
     public static SearchCond getNotLeafCond(final AttributeCond attributeCond) {
         SearchCond nodeCond = getLeafCond(attributeCond);
         nodeCond.type = Type.NOT_LEAF;
@@ -216,6 +238,14 @@ public class SearchCond extends AbstractSearchCond {
         this.relationshipCond = relationshipCond;
     }
 
+    public RelationshipTypeCond getRelationshipTypeCond() {
+        return relationshipTypeCond;
+    }
+
+    public void setRelationshipTypeCond(final RelationshipTypeCond relationshipTypeCond) {
+        this.relationshipTypeCond = relationshipTypeCond;
+    }
+
     public MembershipCond getMembershipCond() {
         return membershipCond;
     }
@@ -240,6 +270,14 @@ public class SearchCond extends AbstractSearchCond {
         this.resourceCond = resourceCond;
     }
 
+    public AssignableCond getAssignableCond() {
+        return assignableCond;
+    }
+
+    public void setAssignableCond(final AssignableCond assignableCond) {
+        this.assignableCond = assignableCond;
+    }
+
     public SearchCond getLeftNodeCond() {
         return leftNodeCond;
     }
@@ -307,8 +345,8 @@ public class SearchCond extends AbstractSearchCond {
             case LEAF:
             case NOT_LEAF:
                 isValid = (anyTypeCond != null || anyCond != null || attributeCond != null
-                        || relationshipCond != null || membershipCond != null
-                        || roleCond != null || resourceCond != null)
+                        || relationshipCond != null || relationshipTypeCond != null || membershipCond != null
+                        || roleCond != null || resourceCond != null || assignableCond != null)
                         && (anyTypeCond == null || anyTypeCond.isValid())
                         && (anyCond == null || anyCond.isValid())
                         && (attributeCond == null || attributeCond.isValid())

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 64c850b..c1f6fe2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -55,7 +55,9 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
@@ -65,12 +67,14 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ReflectionUtils;
 
 @Repository
 public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySearchDAO {
 
-    private static final String EMPTY_ATTR_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
+    private static final String EMPTY_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
 
     @Autowired
     private RealmDAO realmDAO;
@@ -150,6 +154,14 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
         return ((Number) countQuery.getSingleResult()).intValue();
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public <T extends Any<?>> List<T> searchAssignable(final String realmFullPath, final AnyTypeKind kind) {
+        AssignableCond assignableCond = new AssignableCond();
+        assignableCond.setRealmFullPath(realmFullPath);
+        return search(SearchCond.getLeafCond(assignableCond), kind);
+    }
+
     @Override
     public <T extends Any<?>> List<T> search(final SearchCond searchCondition, final AnyTypeKind typeKind) {
         return search(searchCondition, Collections.<OrderByClause>emptyList(), typeKind);
@@ -406,7 +418,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
     }
 
     private StringBuilder getQuery(final SearchCond nodeCond, final List<Object> parameters,
-            final AnyTypeKind type, final SearchSupport svs) {
+            final AnyTypeKind anyTypeKind, final SearchSupport svs) {
 
         StringBuilder query = new StringBuilder();
 
@@ -414,51 +426,55 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
 
             case LEAF:
             case NOT_LEAF:
-                if (nodeCond.getAnyTypeCond() != null && AnyTypeKind.ANY_OBJECT == type) {
+                if (nodeCond.getAnyTypeCond() != null && AnyTypeKind.ANY_OBJECT == anyTypeKind) {
                     query.append(getQuery(nodeCond.getAnyTypeCond(),
                             nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
-                }
-                if (nodeCond.getRelationshipCond() != null
-                        && (AnyTypeKind.USER == type || AnyTypeKind.ANY_OBJECT == type)) {
+                } else if (nodeCond.getRelationshipTypeCond() != null
+                        && (AnyTypeKind.USER == anyTypeKind || AnyTypeKind.ANY_OBJECT == anyTypeKind)) {
+
+                    query.append(getQuery(nodeCond.getRelationshipTypeCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+                } else if (nodeCond.getRelationshipCond() != null
+                        && (AnyTypeKind.USER == anyTypeKind || AnyTypeKind.ANY_OBJECT == anyTypeKind)) {
 
                     query.append(getQuery(nodeCond.getRelationshipCond(),
                             nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
-                }
-                if (nodeCond.getMembershipCond() != null
-                        && (AnyTypeKind.USER == type || AnyTypeKind.ANY_OBJECT == type)) {
+                } else if (nodeCond.getMembershipCond() != null
+                        && (AnyTypeKind.USER == anyTypeKind || AnyTypeKind.ANY_OBJECT == anyTypeKind)) {
 
                     query.append(getQuery(nodeCond.getMembershipCond(),
                             nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
-                }
-                if (nodeCond.getRoleCond() != null && AnyTypeKind.USER == type) {
+                } else if (nodeCond.getAssignableCond() != null
+                        && (AnyTypeKind.GROUP == anyTypeKind || AnyTypeKind.ANY_OBJECT == anyTypeKind)) {
+
+                    query.append(getQuery(nodeCond.getAssignableCond(), parameters, anyTypeKind, svs));
+                } else if (nodeCond.getRoleCond() != null && AnyTypeKind.USER == anyTypeKind) {
                     query.append(getQuery(nodeCond.getRoleCond(),
                             nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
-                }
-                if (nodeCond.getResourceCond() != null) {
+                } else if (nodeCond.getResourceCond() != null) {
                     query.append(getQuery(nodeCond.getResourceCond(),
-                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, type, svs));
-                }
-                if (nodeCond.getAttributeCond() != null) {
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, anyTypeKind, svs));
+                } else if (nodeCond.getAttributeCond() != null) {
                     query.append(getQuery(nodeCond.getAttributeCond(),
-                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, type, svs));
-                }
-                if (nodeCond.getAnyCond() != null) {
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, anyTypeKind, svs));
+                } else if (nodeCond.getAnyCond() != null) {
                     query.append(getQuery(nodeCond.getAnyCond(),
-                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, type, svs));
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, anyTypeKind,
+                            svs));
                 }
                 break;
 
             case AND:
-                query.append(getQuery(nodeCond.getLeftNodeCond(), parameters, type, svs)).
+                query.append(getQuery(nodeCond.getLeftNodeCond(), parameters, anyTypeKind, svs)).
                         append(" AND any_id IN ( ").
-                        append(getQuery(nodeCond.getRightNodeCond(), parameters, type, svs)).
+                        append(getQuery(nodeCond.getRightNodeCond(), parameters, anyTypeKind, svs)).
                         append(")");
                 break;
 
             case OR:
-                query.append(getQuery(nodeCond.getLeftNodeCond(), parameters, type, svs)).
+                query.append(getQuery(nodeCond.getLeftNodeCond(), parameters, anyTypeKind, svs)).
                         append(" OR any_id IN ( ").
-                        append(getQuery(nodeCond.getRightNodeCond(), parameters, type, svs)).
+                        append(getQuery(nodeCond.getRightNodeCond(), parameters, anyTypeKind, svs)).
                         append(")");
                 break;
 
@@ -485,6 +501,29 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
         return query.toString();
     }
 
+    private String getQuery(final RelationshipTypeCond cond, final boolean not, final List<Object> parameters,
+            final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT any_id ").append("FROM ").
+                append(svs.relationship().name).
+                append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())).
+                append(" UNION SELECT right_any_id AS any_id FROM ").
+                append(svs.relationship().name).
+                append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())).
+                append(')');
+
+        return query.toString();
+    }
+
     private String getQuery(final RelationshipCond cond, final boolean not, final List<Object> parameters,
             final SearchSupport svs) {
 
@@ -499,7 +538,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
 
         query.append("SELECT DISTINCT any_id ").append("FROM ").
                 append(svs.relationship().name).append(" WHERE ").
-                append("right_anyObject_id=?").append(setParameter(parameters, cond.getAnyObjectKey())).
+                append("right_any_id=?").append(setParameter(parameters, cond.getAnyObjectKey())).
                 append(')');
 
         return query.toString();
@@ -570,7 +609,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
     private String getQuery(final ResourceCond cond, final boolean not, final List<Object> parameters,
             final AnyTypeKind typeKind, final SearchSupport svs) {
 
-        final StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
                 append(svs.field().name).append(" WHERE ");
 
         if (not) {
@@ -596,6 +635,24 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
         return query.toString();
     }
 
+    private String getQuery(final AssignableCond cond, final List<Object> parameters, final AnyTypeKind typeKind,
+            final SearchSupport svs) {
+
+        Realm realm = realmDAO.find(cond.getRealmFullPath());
+        if (realm == null) {
+            return EMPTY_QUERY;
+        }
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+        for (Realm current = realm; current.getParent() != null; current = current.getParent()) {
+            query.append("realm_id=?").append(setParameter(parameters, current.getKey())).append(" OR ");
+        }
+        query.setLength(query.length() - 4);
+
+        return query.toString();
+    }
+
     private void fillAttributeQuery(final StringBuilder query, final PlainAttrValue attrValue,
             final PlainSchema schema, final AttributeCond cond, final boolean not,
             final List<Object> parameters, final SearchSupport svs) {
@@ -696,7 +753,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
         PlainSchema schema = schemaDAO.find(cond.getSchema());
         if (schema == null) {
             LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
-            return EMPTY_ATTR_QUERY;
+            return EMPTY_QUERY;
         }
 
         PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
@@ -708,27 +765,31 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
             }
         } catch (ValidationException e) {
             LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
-            return EMPTY_ATTR_QUERY;
+            return EMPTY_QUERY;
         }
 
         StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ");
-        if (cond.getType() == AttributeCond.Type.ISNOTNULL) {
-            query.append(svs.field().name).
-                    append(" WHERE any_id NOT IN (SELECT any_id FROM ").
-                    append(svs.nullAttr().name).
-                    append(" WHERE schema_name='").append(schema.getKey()).append("')");
-        } else if (cond.getType() == AttributeCond.Type.ISNULL) {
-            query.append(svs.nullAttr().name).
-                    append(" WHERE schema_name='").append(schema.getKey()).append("'");
-        } else {
-            if (schema.isUniqueConstraint()) {
-                query.append(svs.uniqueAttr().name);
-            } else {
-                query.append(svs.attr().name);
-            }
-            query.append(" WHERE schema_name='").append(schema.getKey());
+        switch (cond.getType()) {
+            case ISNOTNULL:
+                query.append(svs.field().name).
+                        append(" WHERE any_id NOT IN (SELECT any_id FROM ").
+                        append(svs.nullAttr().name).
+                        append(" WHERE schema_name='").append(schema.getKey()).append("')");
+                break;
 
-            fillAttributeQuery(query, attrValue, schema, cond, not, parameters, svs);
+            case ISNULL:
+                query.append(svs.nullAttr().name).
+                        append(" WHERE schema_name='").append(schema.getKey()).append("'");
+                break;
+
+            default:
+                if (schema.isUniqueConstraint()) {
+                    query.append(svs.uniqueAttr().name);
+                } else {
+                    query.append(svs.attr().name);
+                }
+                query.append(" WHERE schema_name='").append(schema.getKey());
+                fillAttributeQuery(query, attrValue, schema, cond, not, parameters, svs);
         }
 
         return query.toString();
@@ -748,7 +809,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
         Field anyField = ReflectionUtils.findField(attrUtils.anyClass(), cond.getSchema());
         if (anyField == null) {
             LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
-            return EMPTY_ATTR_QUERY;
+            return EMPTY_QUERY;
         }
 
         PlainSchema schema = new JPAPlainSchema();
@@ -760,7 +821,6 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
         }
 
         // Deal with any Integer fields logically mapping to boolean values
-        // (JPAGroup.inheritPlainAttrs, for example)
         boolean foundBooleanMin = false;
         boolean foundBooleanMax = false;
         if (Integer.class.equals(anyField.getType())) {
@@ -806,7 +866,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>, Long> implements AnySea
                 schema.getValidator().validate(cond.getExpression(), attrValue);
             } catch (ValidationException e) {
                 LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
-                return EMPTY_ATTR_QUERY;
+                return EMPTY_QUERY;
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
index 2733388..1261a5f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.jpa.dao;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.regex.Pattern;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.CollectionUtils;
@@ -44,8 +43,6 @@ import org.springframework.transaction.annotation.Transactional;
 @Repository
 public class JPARealmDAO extends AbstractDAO<Realm, Long> implements RealmDAO {
 
-    private static final Pattern PATH_PATTERN = Pattern.compile("^(/[A-Za-z0-9]+)+");
-
     @Autowired
     private RoleDAO roleDAO;
 
@@ -124,11 +121,9 @@ public class JPARealmDAO extends AbstractDAO<Realm, Long> implements RealmDAO {
             return Collections.<Realm>emptyList();
         }
 
-        StringBuilder queryString = new StringBuilder("SELECT e FROM ").
-                append(JPARealm.class.getSimpleName()).append(" e WHERE e.").
-                append(policy instanceof AccountPolicy ? "accountPolicy" : "passwordPolicy").append("=:policy");
-
-        TypedQuery<Realm> query = entityManager().createQuery(queryString.toString(), Realm.class);
+        TypedQuery<Realm> query = entityManager().createQuery(
+                "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e."
+                + (policy instanceof AccountPolicy ? "accountPolicy" : "passwordPolicy") + "=:policy", Realm.class);
         query.setParameter("policy", policy);
 
         List<Realm> result = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/main/resources/views.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/views.xml b/core/persistence-jpa/src/main/resources/views.xml
index fd0bf9c..48c801e 100644
--- a/core/persistence-jpa/src/main/resources/views.xml
+++ b/core/persistence-jpa/src/main/resources/views.xml
@@ -70,7 +70,7 @@ under the License.
   <entry key="user_search_urelationship">
     CREATE VIEW user_search_urelationship AS
 
-    SELECT m.user_id AS any_id, m.anyObject_id AS anyObject_id
+    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_name AS type
     FROM URelationship m
   </entry>
   <entry key="user_search_umembership">
@@ -163,7 +163,7 @@ under the License.
   <entry key="anyObject_search_arelationship">
     CREATE VIEW anyObject_search_arelationship AS
 
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_anyObject_id
+    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_name AS type
     FROM ARelationship m
   </entry>
   <entry key="anyObject_search_amembership">

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
index 48c09d6..4045a76 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
@@ -44,7 +44,10 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -73,9 +76,13 @@ public class AnySearchTest extends AbstractTest {
         AnyObject anyObject = anyObjectDAO.find(1L);
         assertNotNull(anyObject);
 
-        RelationshipCond cond = new RelationshipCond();
-        cond.setAnyObjectKey(2L);
-        assertTrue(searchDAO.matches(anyObject, SearchCond.getLeafCond(cond), AnyTypeKind.ANY_OBJECT));
+        RelationshipCond relationshipCond = new RelationshipCond();
+        relationshipCond.setAnyObjectKey(2L);
+        assertTrue(searchDAO.matches(anyObject, SearchCond.getLeafCond(relationshipCond), AnyTypeKind.ANY_OBJECT));
+
+        RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond();
+        relationshipTypeCond.setRelationshipTypeKey("neighborhood");
+        assertTrue(searchDAO.matches(anyObject, SearchCond.getLeafCond(relationshipTypeCond), AnyTypeKind.ANY_OBJECT));
     }
 
     @Test
@@ -93,6 +100,17 @@ public class AnySearchTest extends AbstractTest {
         RoleCond roleCond = new RoleCond();
         roleCond.setRoleKey("Other");
         assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(roleCond), AnyTypeKind.USER));
+
+        user = userDAO.find(4L);
+        assertNotNull(user);
+
+        RelationshipCond relationshipCond = new RelationshipCond();
+        relationshipCond.setAnyObjectKey(1L);
+        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(relationshipCond), AnyTypeKind.USER));
+
+        RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond();
+        relationshipTypeCond.setRelationshipTypeKey("neighborhood");
+        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(relationshipTypeCond), AnyTypeKind.USER));
     }
 
     @Test
@@ -384,7 +402,7 @@ public class AnySearchTest extends AbstractTest {
 
         List<AnyObject> printers = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT);
         assertNotNull(printers);
-        assertEquals(2, printers.size());
+        assertEquals(3, printers.size());
 
         tcond.setAnyTypeName("UNEXISTING");
         printers = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT);
@@ -393,6 +411,51 @@ public class AnySearchTest extends AbstractTest {
     }
 
     @Test
+    public void searchByRelationshipType() {
+        // 1. first search for printers involved in "neighborhood" relationship
+        RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond();
+        relationshipTypeCond.setRelationshipTypeKey("neighborhood");
+
+        AnyTypeCond tcond = new AnyTypeCond();
+        tcond.setAnyTypeName("PRINTER");
+
+        SearchCond searchCondition = SearchCond.getAndCond(
+                SearchCond.getLeafCond(relationshipTypeCond), SearchCond.getLeafCond(tcond));
+        assertTrue(searchCondition.isValid());
+
+        List<Any<?>> matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT);
+        assertNotNull(matching);
+        assertEquals(2, matching.size());
+        assertTrue(CollectionUtils.exists(matching, new Predicate<Any<?>>() {
+
+            @Override
+            public boolean evaluate(final Any<?> any) {
+                return any.getKey() == 1L;
+            }
+        }));
+        assertTrue(CollectionUtils.exists(matching, new Predicate<Any<?>>() {
+
+            @Override
+            public boolean evaluate(final Any<?> any) {
+                return any.getKey() == 2L;
+            }
+        }));
+
+        // 2. search for users involved in "neighborhood" relationship
+        searchCondition = SearchCond.getLeafCond(relationshipTypeCond);
+        matching = searchDAO.search(searchCondition, AnyTypeKind.USER);
+        assertNotNull(matching);
+        assertEquals(2, matching.size());
+        assertTrue(CollectionUtils.exists(matching, new Predicate<Any<?>>() {
+
+            @Override
+            public boolean evaluate(final Any<?> any) {
+                return any.getKey() == 4L;
+            }
+        }));
+    }
+
+    @Test
     public void userOrderBy() {
         AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ);
         usernameLeafCond.setSchema("username");
@@ -414,7 +477,8 @@ public class AnySearchTest extends AbstractTest {
         orderByClauses.add(orderByClause);
 
         List<User> users = searchDAO.search(searchCondition, orderByClauses, AnyTypeKind.USER);
-        assertEquals(searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, AnyTypeKind.USER),
+        assertEquals(
+                searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, AnyTypeKind.USER),
                 users.size());
     }
 
@@ -431,12 +495,50 @@ public class AnySearchTest extends AbstractTest {
 
         List<Group> groups = searchDAO.search(
                 searchCondition, Collections.singletonList(orderByClause), AnyTypeKind.GROUP);
-        assertEquals(searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
-                searchCondition, AnyTypeKind.GROUP),
+        assertEquals(
+                searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, AnyTypeKind.GROUP),
                 groups.size());
     }
 
     @Test
+    public void assignable() {
+        AssignableCond assignableCond = new AssignableCond();
+        assignableCond.setRealmFullPath("/even/two");
+        SearchCond searchCondition = SearchCond.getLeafCond(assignableCond);
+        assertTrue(searchCondition.isValid());
+
+        List<Group> groups = searchDAO.search(searchCondition, AnyTypeKind.GROUP);
+        assertTrue(CollectionUtils.exists(groups, new Predicate<Group>() {
+
+            @Override
+            public boolean evaluate(final Group group) {
+                return group.getKey().equals(15L);
+            }
+        }));
+        assertFalse(CollectionUtils.exists(groups, new Predicate<Group>() {
+
+            @Override
+            public boolean evaluate(final Group group) {
+                return group.getKey().equals(16L);
+            }
+        }));
+
+        assignableCond = new AssignableCond();
+        assignableCond.setRealmFullPath("/odd");
+        searchCondition = SearchCond.getLeafCond(assignableCond);
+        assertTrue(searchCondition.isValid());
+
+        List<AnyObject> anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT);
+        assertFalse(CollectionUtils.exists(anyObjects, new Predicate<AnyObject>() {
+
+            @Override
+            public boolean evaluate(final AnyObject anyObject) {
+                return anyObject.getKey().equals(3L);
+            }
+        }));
+    }
+
+    @Test
     public void issue202() {
         ResourceCond ws2 = new ResourceCond();
         ws2.setResourceName("ws-target-resource-2");

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
index 9d449be..c4dba81 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
@@ -48,7 +48,7 @@ public class GroupTest extends AbstractTest {
     @Test
     public void findAll() {
         List<Group> list = groupDAO.findAll();
-        assertEquals("did not get expected number of groups ", 15, list.size());
+        assertEquals("did not get expected number of groups ", 16, list.size());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java
index 2b67e6a..d1bb773 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java
@@ -22,10 +22,12 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -40,11 +42,20 @@ public class RealmTest extends AbstractTest {
     @Autowired
     private RoleDAO roleDAO;
 
+    @Autowired
+    private GroupDAO groupDAO;
+
     @Test
     public void test() {
         Realm realm = realmDAO.find("/odd");
         assertNotNull(realm);
 
+        // need to remove this group in order to remove the realm, which is otherwise empty
+        Group group = groupDAO.find(16L);
+        assertNotNull(group);
+        assertEquals(realm, group.getRealm());
+        groupDAO.delete(group);
+
         Role role = roleDAO.find("User reviewer");
         assertTrue(role.getRealms().contains(realm));
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index b4c5a97..44335eb 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -167,6 +167,9 @@ under the License.
   <AnyObject id="2" realm_id="1" type_name="PRINTER"
              creator="admin" lastModifier="admin" 
              creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>
+  <AnyObject id="3" realm_id="4" type_name="PRINTER"
+             creator="admin" lastModifier="admin" 
+             creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>
   
   <ARelationship id="1" left_anyObject_id="1" right_anyObject_id="2" type_name="neighborhood"/>
   
@@ -280,12 +283,16 @@ under the License.
                 creator="admin" lastModifier="admin" 
                 creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>  
   <SyncopeGroup id="15" name="additional"
-                realm_id="1"
+                realm_id="3"
                 creator="admin" lastModifier="admin" 
                 creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>
   <TypeExtension id="1" group_id="15" anyType_name="USER"/>
   <TypeExtension_AnyTypeClass typeExtension_id="1" anyTypeClass_name="csv"/>
   <TypeExtension_AnyTypeClass typeExtension_id="1" anyTypeClass_name="other"/>
+  <SyncopeGroup id="16" name="fake"
+                realm_id="2"
+                creator="admin" lastModifier="admin" 
+                creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>
   
   <URelationship id="1" user_id="4" anyObject_id="1" type_name="neighborhood"/>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
index d2ab1fa..62753f0 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
@@ -29,7 +29,7 @@ public interface GroupDataBinder {
 
     GroupTO getGroupTO(Group group, boolean details);
 
-    Group create(Group group, GroupTO groupTO);
+    void create(Group group, GroupTO groupTO);
 
     PropagationByResource update(Group group, GroupPatch groupPatch);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bbb051fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index 281fc69..3db4fc8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -63,6 +63,7 @@ import org.apache.syncope.core.misc.utils.ConnObjectUtils;
 import org.apache.syncope.core.misc.utils.MappingUtils;
 import org.apache.syncope.core.misc.jexl.JexlUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
@@ -135,6 +136,9 @@ abstract class AbstractAnyDataBinder {
     protected RelationshipTypeDAO relationshipTypeDAO;
 
     @Autowired
+    protected AnySearchDAO searchDAO;
+
+    @Autowired
     protected EntityFactory entityFactory;
 
     @Autowired
@@ -431,11 +435,6 @@ abstract class AbstractAnyDataBinder {
             scce.addException(requiredValuesMissing);
         }
 
-        // Throw composite exception if there is at least one element set in the composing exceptions
-        if (scce.hasExceptions()) {
-            throw scce;
-        }
-
         return propByRes;
     }
 
@@ -488,15 +487,7 @@ abstract class AbstractAnyDataBinder {
             scce.addException(requiredValuesMissing);
         }
 
-        // 2. realm & resources
-        Realm realm = realmDAO.find(anyTO.getRealm());
-        if (realm == null) {
-            SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
-            noRealm.getElements().add("Invalid or null realm specified: " + anyTO.getRealm());
-            scce.addException(noRealm);
-        }
-        any.setRealm(realm);
-
+        // 2. resources
         for (String resourceName : anyTO.getResources()) {
             ExternalResource resource = resourceDAO.find(resourceName);
             if (resource == null) {
@@ -510,11 +501,6 @@ abstract class AbstractAnyDataBinder {
         if (!requiredValuesMissing.isEmpty()) {
             scce.addException(requiredValuesMissing);
         }
-
-        // Throw composite exception if there is at least one element set in the composing exceptions
-        if (scce.hasExceptions()) {
-            throw scce;
-        }
     }
 
     protected void fillTO(final AnyTO anyTO,