You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by ma...@apache.org on 2015/11/17 17:02:31 UTC

[1/2] syncope git commit: resolved conflict

Repository: syncope
Updated Branches:
  refs/heads/master e034afcd5 -> 3cf1df3f9


resolved conflict


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

Branch: refs/heads/master
Commit: bd910fbee38daf9f1477e08a7aeca1a25dd25620
Parents: e034afc
Author: massi <ma...@tirasa.net>
Authored: Mon Nov 16 15:34:13 2015 +0100
Committer: massi <ma...@tirasa.net>
Committed: Tue Nov 17 17:02:09 2015 +0100

----------------------------------------------------------------------
 client/cli/src/main/resources/messages.properties | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/bd910fbe/client/cli/src/main/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/cli/src/main/resources/messages.properties b/client/cli/src/main/resources/messages.properties
index 2497301..6e6cf72 100644
--- a/client/cli/src/main/resources/messages.properties
+++ b/client/cli/src/main/resources/messages.properties
@@ -30,5 +30,5 @@ resource.help.message=\nUsage: resource [options]\n  Options:\n    --help \n
 role.help.message=\nUsage: role [options]\n  Options:\n    --help \n    --details \n    --list \n    --read \n       Syntax: --read {ROLE-ID} {ROLE-ID} [...]\n    --delete \n       Syntax: --delete {ROLE-ID} {ROLE-ID} [...]\n
 schema.help.message=\nUsage: schema [options]\n  Options:\n    --help \n    --details \n    --list-all\n    --list-plain\n    --list-derived\n    --list-virtual\n    --read {SCHEMA-TYPE} {SCHEMA-KEY}\n        Schema type: PLAIN / DERIVED / VIRTUAL\n    --delete {SCHEMA-TYPE} {SCHEMA-KEY}\n        Schema type: PLAIN / DERIVED / VIRTUAL\n
 task.help.message=\nUsage: task [options]\n  Options:\n    --help \n    --details\n    --list\n       Syntax: --list {TASK-TYPE} \n          Task type: NOTIFICATION / PROPAGATION / PUSH / SCHEDULED / SYNCHRONIZATION\n    --list-running-jobs \n    --list-scheduled-jobs \n    --read \n       Syntax: --read {TASK-ID} {TASK-ID} [...]\n    --read-execution \n       Syntax: --read-execution {TASK-EXEC-ID} {TASK-EXEC-ID} [...]\n    --delete \n       Syntax: --delete {TASK-ID} {TASK-ID} [...]\n    --delete-execution \n       Syntax: --delete-execution {TASK-EXEC-ID} {TASK-EXEC-ID} [...]\n    --execute \n       Syntax: --execute {TASK-ID} {DRY-RUN}\n          Dry run: true / false\n
-user.help.message=\nUsage: user [options]\n  Options:\n    --help \n    --list \n    --details \n    --get-user-key\n       Syntax: --get-user-key {USERNAME} {USERNAME} [...]\n    --get-username\n       Syntax: --get-username {USER-ID} {USER-ID} [...]\n    --read \n       Syntax: --read {USER-ID} {USER-ID} [...]\n    --search-by-attribute \n       Syntax: --search-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n    --search-by-role \n       Syntax: --search-by-role {REALM} {ROLE-ID}\n    --search-by-resource \n       Syntax: --search-by-resource {REALM} {RESOURCE-NAME}\n    --delete \n       Syntax: --delete {USER-ID} {USER-ID} [...]\n
+user.help.message=\nUsage: user [options]\n  Options:\n    --help \n    --list \n    --details \n    --get-user-key\n       Syntax: --get-user-key {USERNAME} {USERNAME} [...]\n    --get-username\n       Syntax: --get-username {USER-ID} {USER-ID} [...]\n    --read \n       Syntax: --read {USER-ID} {USER-ID} [...]\n    --search-by-attribute \n       Syntax: --search-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n    --search-by-role \n       Syntax: --search-by-role {REALM} {ROLE-ID}\n    --search-by-resource \n       Syntax: --search-by-resource {REALM} {RESOURCE-NAME}\n    --delete-all\n    --delete \n       Syntax: --delete {USER-ID} {USER-ID} [...]\n
 workflow.help.message=\nUsage: workflow [options]\n  Options:\n    --help \n    --export-diagram {ANY-TYPE-KIND}\n        Any type kind: ANY_OBJECT / USER / GROUP\n    --export-definition {ANY-TYPE-KIND}\n        Any type kind: ANY_OBJECT / USER / GROUP\n


[2/2] syncope git commit: Fixed SYNCOPE-728, added debug setup option. SYNCOPE-158

Posted by ma...@apache.org.
Fixed SYNCOPE-728, added debug setup option. SYNCOPE-158


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

Branch: refs/heads/master
Commit: 3cf1df3f94dda26e259cd89dc900800c6484afa4
Parents: bd910fb
Author: massi <ma...@tirasa.net>
Authored: Tue Nov 17 17:01:51 2015 +0100
Committer: massi <ma...@tirasa.net>
Committed: Tue Nov 17 17:02:10 2015 +0100

----------------------------------------------------------------------
 .../apache/syncope/client/cli/SyncopeAdm.java   |  4 +-
 .../syncope/client/cli/SyncopeServices.java     | 13 +++
 .../cli/commands/install/InstallCommand.java    | 17 +++-
 .../commands/install/InstallSetupForDebug.java  | 80 +++++++++++++++++
 .../cli/commands/user/AbstractUserCommand.java  |  7 ++
 .../client/cli/commands/user/UserCommand.java   | 10 ++-
 .../client/cli/commands/user/UserDeleteAll.java | 94 ++++++++++++++++++++
 .../commands/user/UserDeleteByAttribute.java    | 84 +++++++++++++++++
 .../client/cli/commands/user/UserList.java      |  2 +-
 .../cli/commands/user/UserResultManager.java    | 23 ++---
 .../commands/user/UserSearchByAttribute.java    |  7 +-
 .../cli/commands/user/UserSearchByResource.java |  6 +-
 .../cli/commands/user/UserSearchByRole.java     |  4 +-
 .../commands/user/UserSyncopeOperations.java    | 47 +++++++++-
 .../cli/src/main/resources/messages.properties  |  2 +-
 15 files changed, 367 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
index 5c7ef20..17abce8 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeAdm.java
@@ -52,9 +52,11 @@ public final class SyncopeAdm {
             System.out.println(helpMessage());
         } catch (final IllegalArgumentException ex) {
             LOG.error("Error in main", ex);
-            RESULT_MANAGER.genericError(ex.getMessage());
             if (!ex.getMessage().startsWith("It seems you")) {
+                System.out.println("");
                 System.out.println(helpMessage());
+            } else {
+                RESULT_MANAGER.genericError(ex.getMessage());
             }
         } catch (final ProcessingException e) {
             LOG.error("Error in main", e);

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
index 5cfcefa..c3e7062 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
@@ -25,6 +25,7 @@ import org.apache.syncope.client.cli.commands.install.InstallConfigFileTemplate;
 import org.apache.syncope.client.cli.util.JasyptUtils;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,6 +58,18 @@ public final class SyncopeServices {
         return SYNCOPE_ADDRESS;
     }
 
+    public static void testUsernameAndPassword(final String username, final String password) {
+        final Properties properties = new Properties();
+        try {
+            properties.load(new FileInputStream(InstallConfigFileTemplate.configurationFilePath()));
+        } catch (final IOException e) {
+            LOG.error("Error opening properties file", e);
+        }
+        final SyncopeClient syncopeClient = new SyncopeClientFactoryBean()
+                .setAddress(properties.getProperty("syncope.rest.services")).create(username, password);
+        syncopeClient.getService(SyncopeService.class).info();
+    }
+
     private SyncopeServices() {
         // private constructor for static utility class
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
index b71eddd..21a5b40 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallCommand.java
@@ -36,7 +36,8 @@ public class InstallCommand extends AbstractCommand {
     private static final String HELP_MESSAGE = "\nUsage: install [options]\n"
             + "  Options:\n"
             + "    --help \n"
-            + "    --setup\n";
+            + "    --setup\n"
+            + "    --setup-debug\n";
 
     @Override
     public void execute(final Input input) {
@@ -45,7 +46,7 @@ public class InstallCommand extends AbstractCommand {
         }
 
         switch (Options.fromName(input.getOption())) {
-            case INSTALL:
+            case SETUP:
                 try {
                     new InstallSetup().setup();
                 } catch (final FileNotFoundException | IllegalAccessException ex) {
@@ -54,6 +55,15 @@ public class InstallCommand extends AbstractCommand {
                     break;
                 }
                 break;
+            case SETUP_DEBUG:
+                try {
+                    new InstallSetupForDebug().setup();
+                } catch (final FileNotFoundException | IllegalAccessException ex) {
+                    LOG.error("Error installing CLI", ex);
+                    installResultManager.genericError(ex.getMessage());
+                    break;
+                }
+                break;
             case HELP:
                 System.out.println(HELP_MESSAGE);
                 break;
@@ -70,7 +80,8 @@ public class InstallCommand extends AbstractCommand {
     private enum Options {
 
         HELP("--help"),
-        INSTALL("--setup");
+        SETUP("--setup"),
+        SETUP_DEBUG("--setup-debug");
 
         private final String optionName;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallSetupForDebug.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallSetupForDebug.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallSetupForDebug.java
new file mode 100644
index 0000000..cb35032
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/install/InstallSetupForDebug.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.install;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import javax.ws.rs.ProcessingException;
+import org.apache.syncope.client.cli.SyncopeServices;
+import org.apache.syncope.client.cli.util.FileSystemUtils;
+import org.apache.syncope.client.cli.util.JasyptUtils;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InstallSetupForDebug {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InstallSetupForDebug.class);
+
+    private final InstallResultManager installResultManager = new InstallResultManager();
+
+    public void setup() throws FileNotFoundException, IllegalAccessException {
+        installResultManager.printWelcome();
+
+        System.out.println("Path to config files of Syncope CLI client will be: "
+                + InstallConfigFileTemplate.dirPath());
+
+        if (!FileSystemUtils.exists(InstallConfigFileTemplate.dirPath())) {
+            throw new FileNotFoundException("Directory: " + InstallConfigFileTemplate.dirPath() + " does not exists!");
+        }
+
+        if (!FileSystemUtils.canWrite(InstallConfigFileTemplate.dirPath())) {
+            throw new IllegalAccessException("Permission denied on " + InstallConfigFileTemplate.dirPath());
+        }
+        System.out.println("- File system permission checked");
+        System.out.println("");
+
+        final JasyptUtils jasyptUtils = JasyptUtils.getJasyptUtils();
+        try {
+
+            final String contentCliPropertiesFile = InstallConfigFileTemplate.cliPropertiesFile(
+                    "http",
+                    "localhost",
+                    "9080",
+                    "/syncope/rest",
+                    "admin",
+                    jasyptUtils.encrypt("password"));
+            FileSystemUtils.createFileWith(InstallConfigFileTemplate.configurationFilePath(), contentCliPropertiesFile);
+        } catch (final IOException ex) {
+            System.out.println(ex.getMessage());
+        }
+
+        try {
+            final SyncopeService syncopeService = SyncopeServices.get(SyncopeService.class);
+            final String syncopeVersion = syncopeService.info().getVersion();
+            installResultManager.installationSuccessful(syncopeVersion);
+        } catch (final ProcessingException ex) {
+            LOG.error("Error installing CLI", ex);
+            installResultManager.manageProcessingException(ex);
+        } catch (final Exception e) {
+            LOG.error("Error installing CLI", e);
+            installResultManager.manageException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/AbstractUserCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/AbstractUserCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/AbstractUserCommand.java
index 5a28a04..eb53ab1 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/AbstractUserCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/AbstractUserCommand.java
@@ -18,8 +18,15 @@
  */
 package org.apache.syncope.client.cli.commands.user;
 
+import org.apache.syncope.client.cli.commands.realm.RealmSyncopeOperations;
+import org.apache.syncope.client.cli.commands.resource.ResourceSyncopeOperations;
+
 public abstract class AbstractUserCommand {
 
+    protected final RealmSyncopeOperations realmSyncopeOperations = new RealmSyncopeOperations();
+    
+    protected final ResourceSyncopeOperations resourceSyncopeOperations = new ResourceSyncopeOperations();
+    
     protected final UserSyncopeOperations userSyncopeOperations = new UserSyncopeOperations();
 
     protected final UserResultManager userResultManager = new UserResultManager();

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserCommand.java
index 7339f25..3832625 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserCommand.java
@@ -64,6 +64,12 @@ public class UserCommand extends AbstractCommand {
             case DELETE:
                 new UserDelete(input).delete();
                 break;
+            case DELETE_ALL:
+                new UserDeleteAll(input).delete();
+                break;
+            case DELETE_BY_ATTRIBUTE:
+                new UserDeleteByAttribute(input).delete();
+                break;
             case HELP:
                 System.out.println(getHelpMessage());
                 break;
@@ -88,7 +94,9 @@ public class UserCommand extends AbstractCommand {
         SEARCH_BY_ATTRIBUTE("--search-by-attribute"),
         SEARCH_BY_ROLE("--search-by-role"),
         SEARCH_BY_RESOURCE("--search-by-resource"),
-        DELETE("--delete");
+        DELETE("--delete"),
+        DELETE_ALL("--delete-all"),
+        DELETE_BY_ATTRIBUTE("--delete-by-attribute");
 
         private final String optionName;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java
new file mode 100644
index 0000000..2bde176
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java
@@ -0,0 +1,94 @@
+/*
+ * 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.user;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UserDeleteAll extends AbstractUserCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(UserDeleteAll.class);
+
+    private static final String DELETE_ALL_HELP_MESSAGE = "user --delete-all {REALM}";
+
+    private final Input input;
+
+    public UserDeleteAll(final Input input) {
+        this.input = input;
+    }
+
+    public void delete() {
+        if (input.parameterNumber() == 1) {
+            try {
+                final Scanner scanIn = new Scanner(System.in);
+                System.out.println(
+                        "\nRunning this operation you will delete all the realm users managed by Syncope, "
+                        + "are you sure? [yes/no]");
+                final String answer = scanIn.nextLine();
+                if ("yes".equalsIgnoreCase(answer)) {
+                    System.out.println("\nUsername:");
+                    final String username = scanIn.nextLine();
+                    System.out.println("\nPassword:");
+                    final String password = scanIn.nextLine();
+                    if (userSyncopeOperations.auth(username, password)) {
+                        System.out.println("Deleting process started");
+                        final String realm = input.firstParameter();
+                        if (!realmSyncopeOperations.exists(realm)) {
+                            userResultManager.notFoundError("Realm", realm);
+                            return;
+                        }
+                        final Map<String, BulkActionResult.Status> results = userSyncopeOperations.deleteAll(realm);
+                        final Map<String, String> users = new HashMap<>();
+                        int deletedUsers = 0;
+                        for (final Map.Entry<String, BulkActionResult.Status> entrySet : results.entrySet()) {
+                            final String userId = entrySet.getKey();
+                            final BulkActionResult.Status status = entrySet.getValue();
+                            if (!BulkActionResult.Status.SUCCESS.equals(status)) {
+                                users.put(userId, status.name());
+                            } else {
+                                deletedUsers++;
+                            }
+                        }
+                        userResultManager.genericMessage("Deleted users: " + deletedUsers);
+                        if (!users.isEmpty()) {
+                            userResultManager.printUndeletedUsers(users);
+                        }
+                    } else {
+                        userResultManager.genericError("Authentication error");
+                    }
+                } else if ("no".equalsIgnoreCase(answer)) {
+                    userResultManager.genericError("Delete all operation skipped");
+                } else {
+                    userResultManager.genericError("Invalid parameter, please use [yes/no]");
+                }
+            } catch (final SyncopeClientException ex) {
+                LOG.error("Error deleting user", ex);
+                userResultManager.genericError(ex.getMessage());
+            }
+        } else {
+            userResultManager.commandOptionError(DELETE_ALL_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java
new file mode 100644
index 0000000..b559672
--- /dev/null
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java
@@ -0,0 +1,84 @@
+/*
+ * 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.user;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.ws.WebServiceException;
+import org.apache.syncope.client.cli.Input;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UserDeleteByAttribute extends AbstractUserCommand {
+
+    private static final Logger LOG = LoggerFactory.getLogger(UserDeleteByAttribute.class);
+
+    private static final String SEARCH_HELP_MESSAGE = "user --delete-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}";
+
+    private final Input input;
+
+    public UserDeleteByAttribute(final Input input) {
+        this.input = input;
+    }
+
+    public void delete() {
+        if (input.parameterNumber() == 2) {
+            final String realm = input.firstParameter();
+            final Input.PairParameter pairParameter = input.toPairParameter(input.secondParameter());
+            try {
+                if (!realmSyncopeOperations.exists(realm)) {
+                    userResultManager.notFoundError("Realm", realm);
+                    return;
+                }
+                final Map<String, BulkActionResult.Status> results = userSyncopeOperations.deleteByAttribute(
+                        realm, pairParameter.getKey(), pairParameter.getValue());
+                final Map<String, String> users = new HashMap<>();
+                int deletedUsers = 0;
+                for (final Map.Entry<String, BulkActionResult.Status> entrySet : results.entrySet()) {
+                    final String userId = entrySet.getKey();
+                    final BulkActionResult.Status status = entrySet.getValue();
+                    if (!BulkActionResult.Status.SUCCESS.equals(status)) {
+                        users.put(userId, status.name());
+                    } else {
+                        deletedUsers++;
+                    }
+                }
+                userResultManager.genericMessage("Deleted users: " + deletedUsers);
+                if (!users.isEmpty()) {
+                    userResultManager.printUndeletedUsers(users);
+                }
+            } catch (final WebServiceException | SyncopeClientException ex) {
+                LOG.error("Error searching user", ex);
+                if (ex.getMessage().startsWith("NotFound")) {
+                    userResultManager.notFoundError("User with " + pairParameter.getKey(), pairParameter.getValue());
+                } else {
+                    userResultManager.genericError(ex.getMessage());
+                }
+            } catch (final IllegalArgumentException ex) {
+                LOG.error("Error searching user", ex);
+                userResultManager.genericError(ex.getMessage());
+                userResultManager.genericError(SEARCH_HELP_MESSAGE);
+            }
+        } else {
+            userResultManager.commandOptionError(SEARCH_HELP_MESSAGE);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserList.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserList.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserList.java
index e498747..42b5a23 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserList.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserList.java
@@ -44,7 +44,7 @@ public class UserList extends AbstractUserCommand {
             try {
                 final Scanner scanIn = new Scanner(System.in);
                 System.out.println(
-                        "This operation could be print a lot of information "
+                        "\nThis operation could be print a lot of information "
                         + "on your screen. Do you want to continue? [yes/no]");
                 final String answer = scanIn.nextLine();
                 if ("yes".equalsIgnoreCase(answer)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
index 3efc68b..ccc41df 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
@@ -18,12 +18,14 @@
  */
 package org.apache.syncope.client.cli.commands.user;
 
+import java.util.Arrays;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.apache.syncope.client.cli.commands.CommonsResultManager;
+import org.apache.syncope.client.cli.view.Table;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.to.UserTO;
 
@@ -92,20 +94,21 @@ public class UserResultManager extends CommonsResultManager {
             System.out.println(attributeSentence);
         }
     }
-
-    private void printPropagationStatus(final List<PropagationStatus> propagationStatuses) {
-        for (final PropagationStatus propagationStatus : propagationStatuses) {
-            System.out.println("       status: " + propagationStatus.getStatus());
-            System.out.println("       resource: " + propagationStatus.getResource());
-            System.out.println("       failure reason: " + propagationStatus.getFailureReason());
-        }
-    }
-
+    
     private void printRelationships(final List<RelationshipTO> relationshipTOs) {
         for (final RelationshipTO relationshipTO : relationshipTOs) {
             System.out.println("       type: " + relationshipTO.getType());
         }
     }
+    
+    public void printUndeletedUsers(final Map<String, String> users) {
+        final Table.TableBuilder tableBuilder
+                = new Table.TableBuilder("Users not deleted").header("user id").header("cause");
+        for (final Map.Entry<String, String> entrySet : users.entrySet()) {
+            tableBuilder.rowValues(new LinkedList(Arrays.asList(entrySet.getKey(), entrySet.getValue())));
+        }
+        tableBuilder.build().print();
+    }
 
     public void printDetails(final Map<String, String> details) {
         printDetails("users details", details);

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByAttribute.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByAttribute.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByAttribute.java
index 874c6a4..95ad127 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByAttribute.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByAttribute.java
@@ -21,7 +21,6 @@ package org.apache.syncope.client.cli.commands.user;
 import java.util.List;
 import javax.xml.ws.WebServiceException;
 import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.commands.realm.RealmSyncopeOperations;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.slf4j.Logger;
@@ -40,15 +39,14 @@ public class UserSearchByAttribute extends AbstractUserCommand {
     }
 
     public void search() {
-        if (input.parameterNumber() >= 2) {
+        if (input.parameterNumber() == 2) {
             final String realm = input.firstParameter();
             final Input.PairParameter pairParameter = input.toPairParameter(input.secondParameter());
-            final RealmSyncopeOperations realmSyncopeOperations = new RealmSyncopeOperations();
             try {
                 List<UserTO> userTOs;
                 if (!realmSyncopeOperations.exists(realm)) {
                     userResultManager.genericMessage("Operation performed on root realm because " + realm
-                            + "does not exists");
+                            + " does not exists");
                 }
                 userTOs = userSyncopeOperations.searchByAttribute(
                         realm, pairParameter.getKey(), pairParameter.getValue());
@@ -70,6 +68,7 @@ public class UserSearchByAttribute extends AbstractUserCommand {
                 userResultManager.genericError(ex.getMessage());
                 userResultManager.genericError(SEARCH_HELP_MESSAGE);
             }
+        } else {
             userResultManager.commandOptionError(SEARCH_HELP_MESSAGE);
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByResource.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByResource.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByResource.java
index 0fb7b9f..f258bfd 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByResource.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByResource.java
@@ -21,8 +21,6 @@ package org.apache.syncope.client.cli.commands.user;
 import java.util.List;
 import javax.xml.ws.WebServiceException;
 import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.commands.realm.RealmSyncopeOperations;
-import org.apache.syncope.client.cli.commands.resource.ResourceSyncopeOperations;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.slf4j.Logger;
@@ -44,13 +42,11 @@ public class UserSearchByResource extends AbstractUserCommand {
         if (input.parameterNumber() == 2) {
             final String realm = input.firstParameter();
             final String resource = input.secondParameter();
-            final RealmSyncopeOperations realmSyncopeOperations = new RealmSyncopeOperations();
-            final ResourceSyncopeOperations resourceSyncopeOperations = new ResourceSyncopeOperations();
             try {
                 List<UserTO> userTOs = null;
                 if (!realmSyncopeOperations.exists(realm)) {
                     userResultManager.genericMessage("Operation performed on root realm because " + realm
-                            + "does not exists");
+                            + " does not exists");
                 }
                 if (!resourceSyncopeOperations.exists(resource)) {
                     userResultManager.notFoundError("Resource", resource);

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByRole.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByRole.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByRole.java
index 861c9e9..6a0a523 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByRole.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSearchByRole.java
@@ -21,7 +21,6 @@ package org.apache.syncope.client.cli.commands.user;
 import java.util.List;
 import javax.xml.ws.WebServiceException;
 import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.commands.realm.RealmSyncopeOperations;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.slf4j.Logger;
@@ -43,12 +42,11 @@ public class UserSearchByRole extends AbstractUserCommand {
         if (input.parameterNumber() == 2) {
             final String realm = input.firstParameter();
             final String role = input.secondParameter();
-            final RealmSyncopeOperations realmSyncopeOperations = new RealmSyncopeOperations();
             try {
                 List<UserTO> userTOs;
                 if (!realmSyncopeOperations.exists(realm)) {
                     userResultManager.genericMessage("Operation performed on root realm because "
-                            + realm + "does not exists");
+                            + realm + " does not exists");
                 }
                 userTOs = userSyncopeOperations.searchByRole(realm, input.secondParameter());
                 if (userTOs == null || userTOs.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
index 8df901f..aec9271 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
@@ -18,9 +18,13 @@
  */
 package org.apache.syncope.client.cli.commands.user;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.client.lib.SyncopeClient;
+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.to.UserTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
@@ -31,6 +35,15 @@ public class UserSyncopeOperations {
 
     private final UserService userService = SyncopeServices.get(UserService.class);
 
+    public boolean auth(final String username, final String password) {
+        try {
+            SyncopeServices.testUsernameAndPassword(username, password);
+            return true;
+        } catch (final Exception e) {
+            return false;
+        }
+    }
+
     public List<UserTO> searchByRole(final String realm, final String role) {
         return userService.search(
                 SyncopeClient.getAnySearchQueryBuilder().realm(realm).
@@ -60,10 +73,6 @@ public class UserSyncopeOperations {
         return userService.read(Long.valueOf(userId));
     }
 
-    public void delete(final String userId) {
-        userService.delete(Long.valueOf(userId));
-    }
-
     public String getUsernameFromId(final String userId) {
         return userService.getUsername(Long.valueOf(userId)).getHeaderString(RESTHeaders.USERNAME);
     }
@@ -71,4 +80,34 @@ public class UserSyncopeOperations {
     public String getIdFromUsername(final String username) {
         return userService.getUserKey(username).getHeaderString(RESTHeaders.USER_KEY);
     }
+
+    public void delete(final String userId) {
+        userService.delete(Long.valueOf(userId));
+    }
+
+    public Map<String, BulkActionResult.Status> deleteByAttribute(
+            final String realm, final String attributeName, final String attributeValue) {
+        final List<UserTO> users = userService.search(
+                SyncopeClient.getAnySearchQueryBuilder().realm(realm).
+                fiql(SyncopeClient.getUserSearchConditionBuilder().is(attributeName).equalTo(attributeValue)
+                        .query()).build()).getResult();
+        return deleteBulk(users);
+    }
+
+    public Map<String, BulkActionResult.Status> deleteAll(final String realm) {
+        final AnyListQuery anyListQuery = new AnyListQuery();
+        anyListQuery.setDetails(false);
+        anyListQuery.setRealms(Arrays.asList(realm));
+        return deleteBulk(userService.list(anyListQuery).getResult());
+    }
+
+    private Map<String, BulkActionResult.Status> deleteBulk(final List<UserTO> users) {
+        final BulkAction bulkAction = new BulkAction();
+        bulkAction.setType(BulkAction.Type.DELETE);
+        for (UserTO user : users) {
+            bulkAction.getTargets().add(String.valueOf(user.getKey()));
+        }
+        final BulkActionResult bulkResult = userService.bulk(bulkAction);
+        return bulkResult.getResults();
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/3cf1df3f/client/cli/src/main/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/cli/src/main/resources/messages.properties b/client/cli/src/main/resources/messages.properties
index 6e6cf72..0e0700d 100644
--- a/client/cli/src/main/resources/messages.properties
+++ b/client/cli/src/main/resources/messages.properties
@@ -30,5 +30,5 @@ resource.help.message=\nUsage: resource [options]\n  Options:\n    --help \n
 role.help.message=\nUsage: role [options]\n  Options:\n    --help \n    --details \n    --list \n    --read \n       Syntax: --read {ROLE-ID} {ROLE-ID} [...]\n    --delete \n       Syntax: --delete {ROLE-ID} {ROLE-ID} [...]\n
 schema.help.message=\nUsage: schema [options]\n  Options:\n    --help \n    --details \n    --list-all\n    --list-plain\n    --list-derived\n    --list-virtual\n    --read {SCHEMA-TYPE} {SCHEMA-KEY}\n        Schema type: PLAIN / DERIVED / VIRTUAL\n    --delete {SCHEMA-TYPE} {SCHEMA-KEY}\n        Schema type: PLAIN / DERIVED / VIRTUAL\n
 task.help.message=\nUsage: task [options]\n  Options:\n    --help \n    --details\n    --list\n       Syntax: --list {TASK-TYPE} \n          Task type: NOTIFICATION / PROPAGATION / PUSH / SCHEDULED / SYNCHRONIZATION\n    --list-running-jobs \n    --list-scheduled-jobs \n    --read \n       Syntax: --read {TASK-ID} {TASK-ID} [...]\n    --read-execution \n       Syntax: --read-execution {TASK-EXEC-ID} {TASK-EXEC-ID} [...]\n    --delete \n       Syntax: --delete {TASK-ID} {TASK-ID} [...]\n    --delete-execution \n       Syntax: --delete-execution {TASK-EXEC-ID} {TASK-EXEC-ID} [...]\n    --execute \n       Syntax: --execute {TASK-ID} {DRY-RUN}\n          Dry run: true / false\n
-user.help.message=\nUsage: user [options]\n  Options:\n    --help \n    --list \n    --details \n    --get-user-key\n       Syntax: --get-user-key {USERNAME} {USERNAME} [...]\n    --get-username\n       Syntax: --get-username {USER-ID} {USER-ID} [...]\n    --read \n       Syntax: --read {USER-ID} {USER-ID} [...]\n    --search-by-attribute \n       Syntax: --search-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n    --search-by-role \n       Syntax: --search-by-role {REALM} {ROLE-ID}\n    --search-by-resource \n       Syntax: --search-by-resource {REALM} {RESOURCE-NAME}\n    --delete-all\n    --delete \n       Syntax: --delete {USER-ID} {USER-ID} [...]\n
+user.help.message=\nUsage: user [options]\n  Options:\n    --help \n    --list \n    --details \n    --get-user-key\n       Syntax: --get-user-key {USERNAME} {USERNAME} [...]\n    --get-username\n       Syntax: --get-username {USER-ID} {USER-ID} [...]\n    --read \n       Syntax: --read {USER-ID} {USER-ID} [...]\n    --search-by-attribute \n       Syntax: --search-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n    --search-by-role \n       Syntax: --search-by-role {REALM} {ROLE-ID}\n    --search-by-resource \n       Syntax: --search-by-resource {REALM} {RESOURCE-NAME}\n    --delete \n       Syntax: --delete {USER-ID} {USER-ID} [...]\n    --delete-all \n       Syntax: --delete-all {REALM}\n    --delete-by-attribute \n       Syntax: --delete-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n
 workflow.help.message=\nUsage: workflow [options]\n  Options:\n    --help \n    --export-diagram {ANY-TYPE-KIND}\n        Any type kind: ANY_OBJECT / USER / GROUP\n    --export-definition {ANY-TYPE-KIND}\n        Any type kind: ANY_OBJECT / USER / GROUP\n