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 2018/03/16 16:51:00 UTC

[3/4] syncope git commit: [SYNCOPE-1281] Console support implemented

[SYNCOPE-1281] Console support implemented


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

Branch: refs/heads/master
Commit: 72c5885d989528188c0d7f7c27185f4ea5939fff
Parents: 3b884e0
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Mar 16 17:34:34 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 16 17:34:34 2018 +0100

----------------------------------------------------------------------
 .../client/console/commons/Constants.java       |   4 +
 .../client/console/pages/Administration.java    |  83 ------
 .../syncope/client/console/pages/BasePage.java  |  19 +-
 .../syncope/client/console/pages/Security.java  | 110 ++++++++
 .../client/console/pages/SecurityQuestions.java |  45 ----
 .../panels/ApplicationDirectoryPanel.java       | 266 +++++++++++++++++++
 .../console/panels/ApplicationModalPanel.java   |  91 +++++++
 .../console/panels/DynRealmDirectoryPanel.java  |  28 +-
 .../console/panels/DynRealmModalPanel.java      |   1 -
 .../console/panels/PrivilegeDirectoryPanel.java | 184 +++++++++++++
 .../console/panels/PrivilegeWizardBuilder.java  | 101 +++++++
 .../console/panels/RoleDirectoryPanel.java      |   2 +
 .../console/panels/SecurityQuestionsPanel.java  |   1 +
 .../panels/search/AbstractSearchPanel.java      |  12 +-
 .../console/panels/search/SearchClause.java     |   1 +
 .../panels/search/SearchClausePanel.java        |  23 +-
 .../console/panels/search/SearchUtils.java      |  20 ++
 .../console/panels/search/UserSearchPanel.java  |  16 ++
 .../policies/PolicyRuleDirectoryPanel.java      |   2 +-
 .../console/rest/ApplicationRestClient.java     |  49 ++++
 .../console/widgets/CompletenessWidget.java     |  11 +-
 .../client/console/widgets/NumberWidget.java    |   4 +-
 .../console/wizards/role/RoleWizardBuilder.java |  17 ++
 .../src/main/resources/console.properties       |   3 +-
 .../SyncopeConsoleApplication.properties        |   3 +-
 .../SyncopeConsoleApplication_it.properties     |   3 +-
 .../SyncopeConsoleApplication_ja.properties     |   3 +-
 .../SyncopeConsoleApplication_pt_BR.properties  |   3 +-
 .../SyncopeConsoleApplication_ru.properties     |   3 +-
 .../client/console/pages/Administration.html    |  36 ---
 .../console/pages/Administration.properties     |  18 --
 .../console/pages/Administration_it.properties  |  18 --
 .../console/pages/Administration_ja.properties  |  18 --
 .../pages/Administration_pt_BR.properties       |  18 --
 .../console/pages/Administration_ru.properties  |  20 --
 .../syncope/client/console/pages/BasePage.html  |   3 +-
 .../syncope/client/console/pages/Security.html  |  36 +++
 .../client/console/pages/Security.properties    |  21 ++
 .../client/console/pages/SecurityQuestions.html |  36 ---
 .../console/pages/SecurityQuestions.properties  |  18 --
 .../pages/SecurityQuestions_it.properties       |  18 --
 .../pages/SecurityQuestions_ja.properties       |  18 --
 .../pages/SecurityQuestions_pt_BR.properties    |  18 --
 .../pages/SecurityQuestions_ru.properties       |  20 --
 .../client/console/pages/Security_it.properties |  21 ++
 .../client/console/pages/Security_ja.properties |  21 ++
 .../console/pages/Security_pt_BR.properties     |  21 ++
 .../client/console/pages/Security_ru.properties |  22 ++
 .../panels/ApplicationDirectoryPanel.properties |  21 ++
 .../ApplicationDirectoryPanel_it.properties     |  21 ++
 .../ApplicationDirectoryPanel_ja.properties     |  21 ++
 .../ApplicationDirectoryPanel_pt_BR.properties  |  21 ++
 .../ApplicationDirectoryPanel_ru.properties     |  22 ++
 .../console/panels/ApplicationModalPanel.html   |  26 ++
 .../panels/PrivilegeDirectoryPanel.properties   |  18 ++
 .../PrivilegeDirectoryPanel_it.properties       |  18 ++
 .../PrivilegeDirectoryPanel_ja.properties       |  18 ++
 .../PrivilegeDirectoryPanel_pt_BR.properties    |  18 ++
 .../PrivilegeDirectoryPanel_ru.properties       |  18 ++
 .../panels/PrivilegeWizardBuilder$Profile.html  |  26 ++
 .../panels/PrivilegeWizardBuilder$Spec.html     |  25 ++
 .../panels/SecurityQuestionsPanel.properties    |   1 +
 .../panels/SecurityQuestionsPanel_it.properties |   1 +
 .../panels/SecurityQuestionsPanel_ja.properties |   1 +
 .../SecurityQuestionsPanel_pt_BR.properties     |   1 +
 .../panels/SecurityQuestionsPanel_ru.properties |   3 +-
 .../wizards/role/RoleWizardBuilder$Details.html |  15 +-
 .../role/RoleWizardBuilder$DynRealms.html       |  13 +-
 .../role/RoleWizardBuilder$Entitlements.html    |  13 +-
 .../role/RoleWizardBuilder$Privileges.html      |  25 ++
 .../wizards/role/RoleWizardBuilder$Realms.html  |  13 +-
 .../syncope/common/lib/to/PrivilegeTO.java      |  10 -
 .../core/persistence/api/entity/Privilege.java  |   8 +-
 .../persistence/jpa/entity/JPAPrivilege.java    |  23 +-
 .../persistence/jpa/inner/ApplicationTest.java  |   9 +-
 .../test/resources/domains/MasterContent.xml    |   6 +-
 .../java/data/ApplicationDataBinderImpl.java    |   7 +-
 .../src/main/resources/console.properties       |   4 +-
 .../syncope/fit/console/AjaxBrowseITCase.java   |  10 +-
 .../apache/syncope/fit/console/RolesITCase.java |  11 +-
 .../fit/console/SecurityQuestionsITCase.java    |  40 +--
 .../syncope/fit/core/ApplicationITCase.java     |  10 +-
 .../src/test/resources/console.properties       |   4 +-
 pom.xml                                         |   6 +-
 84 files changed, 1455 insertions(+), 561 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index c71fdbc..e0f70c4 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -109,6 +109,10 @@ public final class Constants {
 
     public static final String PREF_ROLE_PAGINATOR_ROWS = "role.paginator.rows";
 
+    public static final String PREF_APPLICATION_PAGINATOR_ROWS = "application.paginator.rows";
+
+    public static final String PREF_PRIVILEGE_PAGINATOR_ROWS = "privilege.paginator.rows";
+
     public static final String PREF_DYNREALM_PAGINATOR_ROWS = "dynRealm.paginator.rows";
 
     public static final String PREF_ACCESS_TOKEN_PAGINATOR_ROWS = "accessToken.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/pages/Administration.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Administration.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Administration.java
deleted file mode 100644
index 6f4423f..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/Administration.java
+++ /dev/null
@@ -1,83 +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.console.pages;
-
-import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
-import org.apache.syncope.client.console.panels.DynRealmDirectoryPanel;
-import org.apache.syncope.client.console.panels.RoleDirectoryPanel;
-import org.apache.syncope.client.console.wizards.role.RoleWizardBuilder;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
-import org.apache.wicket.extensions.markup.html.tabs.ITab;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-
-public class Administration extends BasePage {
-
-    private static final long serialVersionUID = -1100228004207271271L;
-
-    public Administration(final PageParameters parameters) {
-        super(parameters);
-
-        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
-        WebMarkupContainer content = new WebMarkupContainer("content");
-        content.setOutputMarkupId(true);
-        content.setMarkupId("administration");
-        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
-        body.add(content);
-    }
-
-    private List<ITab> buildTabList() {
-        final List<ITab> tabs = new ArrayList<>();
-
-        tabs.add(new AbstractTab(new ResourceModel("roles")) {
-
-            private static final long serialVersionUID = -6815067322125799251L;
-
-            @Override
-            public Panel getPanel(final String panelId) {
-                return new RoleDirectoryPanel.Builder(getPageReference()) {
-
-                    private static final long serialVersionUID = -5960765294082359003L;
-
-                }.addNewItemPanelBuilder(new RoleWizardBuilder(new RoleTO(), getPageReference()), true).build(panelId);
-            }
-        });
-
-        tabs.add(new AbstractTab(new ResourceModel("dynRealms")) {
-
-            private static final long serialVersionUID = -6815067322125799251L;
-
-            @Override
-            public Panel getPanel(final String panelId) {
-                return new DynRealmDirectoryPanel.Builder(getPageReference()) {
-
-                    private static final long serialVersionUID = -5960765294082359003L;
-
-                }.build(panelId);
-            }
-        });
-        return tabs;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
index ebb14f2..9aa8fa1 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
@@ -207,26 +207,19 @@ public class BasePage extends WebPage implements IAjaxIndicatorAware {
         MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, StandardEntitlement.LOG_LIST);
         liContainer.add(link);
 
-        liContainer = new WebMarkupContainer(getLIContainerId("securityquestions"));
-        confULContainer.add(liContainer);
-        bld = new StringBuilder();
-        bld.append(StandardEntitlement.SECURITY_QUESTION_CREATE).append(",").
-                append(StandardEntitlement.SECURITY_QUESTION_DELETE).append(",").
-                append(StandardEntitlement.SECURITY_QUESTION_UPDATE);
-        link = BookmarkablePageLinkBuilder.build("securityquestions", SecurityQuestions.class);
-        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, bld.toString());
-        liContainer.add(link);
-
         liContainer = new WebMarkupContainer(getLIContainerId("types"));
         confULContainer.add(liContainer);
         link = BookmarkablePageLinkBuilder.build("types", Types.class);
         MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, StandardEntitlement.SCHEMA_LIST);
         liContainer.add(link);
 
-        liContainer = new WebMarkupContainer(getLIContainerId("administration"));
+        liContainer = new WebMarkupContainer(getLIContainerId("security"));
         confULContainer.add(liContainer);
-        link = BookmarkablePageLinkBuilder.build("administration", Administration.class);
-        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, StandardEntitlement.ROLE_LIST);
+        link = BookmarkablePageLinkBuilder.build("security", Security.class);
+        bld = new StringBuilder();
+        bld.append(StandardEntitlement.ROLE_LIST).append(",").
+                append(StandardEntitlement.APPLICATION_LIST);
+        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, bld.toString());
         liContainer.add(link);
 
         liContainer = new WebMarkupContainer(getLIContainerId("policies"));

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/pages/Security.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Security.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Security.java
new file mode 100644
index 0000000..56efb31
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Security.java
@@ -0,0 +1,110 @@
+/*
+ * 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.console.pages;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.panels.ApplicationDirectoryPanel;
+import org.apache.syncope.client.console.panels.DynRealmDirectoryPanel;
+import org.apache.syncope.client.console.panels.RoleDirectoryPanel;
+import org.apache.syncope.client.console.panels.SecurityQuestionsPanel;
+import org.apache.syncope.client.console.wizards.role.RoleWizardBuilder;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class Security extends BasePage {
+
+    private static final long serialVersionUID = -1100228004207271271L;
+
+    public Security(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        content.setMarkupId("security");
+        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
+        body.add(content);
+    }
+
+    private List<ITab> buildTabList() {
+        final List<ITab> tabs = new ArrayList<>();
+
+        tabs.add(new AbstractTab(new ResourceModel("roles")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new RoleDirectoryPanel.Builder(getPageReference()) {
+
+                    private static final long serialVersionUID = -5960765294082359003L;
+
+                }.addNewItemPanelBuilder(new RoleWizardBuilder(new RoleTO(), getPageReference()), true).build(panelId);
+            }
+        });
+
+        tabs.add(new AbstractTab(new ResourceModel("dynRealms")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new DynRealmDirectoryPanel.Builder(getPageReference()) {
+
+                    private static final long serialVersionUID = -5960765294082359003L;
+
+                }.build(panelId);
+            }
+        });
+
+        tabs.add(new AbstractTab(new ResourceModel("applications")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new ApplicationDirectoryPanel.Builder(getPageReference()) {
+
+                    private static final long serialVersionUID = -5960765294082359003L;
+
+                }.build(panelId);
+            }
+        });
+
+        tabs.add(new AbstractTab(new ResourceModel("securityQuestions")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new SecurityQuestionsPanel(panelId, getPageReference());
+            }
+        });
+
+        return tabs;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestions.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestions.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestions.java
deleted file mode 100644
index dd34c1b..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestions.java
+++ /dev/null
@@ -1,45 +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.console.pages;
-
-import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
-import org.apache.syncope.client.console.panels.SecurityQuestionsPanel;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-
-public class SecurityQuestions extends BasePage {
-
-    private static final long serialVersionUID = 931085006718655535L;
-
-    public SecurityQuestions(final PageParameters parameters) {
-        super(parameters);
-
-        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
-
-        WebMarkupContainer content = new WebMarkupContainer("content");
-        content.setOutputMarkupId(true);
-        body.add(content);
-
-        SecurityQuestionsPanel securityQuestionsPanel =
-                new SecurityQuestionsPanel("securityQuestionPanel", getPageReference());
-        securityQuestionsPanel.setOutputMarkupId(true);
-
-        content.add(securityQuestionsPanel);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationDirectoryPanel.java
new file mode 100644
index 0000000..ba9e033
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationDirectoryPanel.java
@@ -0,0 +1,266 @@
+/*
+ * 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.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.ApplicationDirectoryPanel.ApplicationDataProvider;
+import org.apache.syncope.client.console.rest.ApplicationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class ApplicationDirectoryPanel extends
+        DirectoryPanel<ApplicationTO, ApplicationTO, ApplicationDataProvider, ApplicationRestClient> {
+
+    private static final long serialVersionUID = -5491515010207202168L;
+
+    protected final BaseModal<PrivilegeTO> privilegeModal = new BaseModal<PrivilegeTO>("outer") {
+
+        private static final long serialVersionUID = 389935548143327858L;
+
+        @Override
+        protected void onConfigure() {
+            super.onConfigure();
+            setFooterVisible(false);
+        }
+
+    };
+
+    protected ApplicationDirectoryPanel(final String id, final Builder builder) {
+        super(id, builder);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.APPLICATION_CREATE);
+        setReadOnly(!SyncopeConsoleSession.get().owns(StandardEntitlement.APPLICATION_UPDATE));
+
+        disableCheckBoxes();
+        setShowResultPage(true);
+
+        modal.size(Modal.Size.Medium);
+        modal.addSubmitButton();
+        setFooterVisibility(true);
+        modal.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+                updateResultTable(target);
+                modal.show(false);
+            }
+        });
+
+        privilegeModal.size(Modal.Size.Large);
+        setWindowClosedReloadCallback(privilegeModal);
+        addOuterObject(privilegeModal);
+
+        AjaxLink<Void> newApplLink = new AjaxLink<Void>("add") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                modal.header(new StringResourceModel("any.new"));
+                modal.setContent(new ApplicationModalPanel(new ApplicationTO(), true, modal, pageRef));
+                modal.show(true);
+                target.add(modal);
+            }
+        };
+        ((WebMarkupContainer) get("container:content")).addOrReplace(newApplLink);
+        MetaDataRoleAuthorizationStrategy.authorize(newApplLink, RENDER, StandardEntitlement.APPLICATION_CREATE);
+
+        initResultTable();
+    }
+
+    @Override
+    protected ApplicationDataProvider dataProvider() {
+        return new ApplicationDataProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_APPLICATION_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected List<IColumn<ApplicationTO, String>> getColumns() {
+        final List<IColumn<ApplicationTO, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<>(new ResourceModel("key"), "key", "key"));
+        columns.add(new PropertyColumn<>(new ResourceModel("description"), "description", "description"));
+        columns.add(new AbstractColumn<ApplicationTO, String>(new ResourceModel("privileges")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public void populateItem(
+                    final Item<ICellPopulator<ApplicationTO>> item,
+                    final String componentId,
+                    final IModel<ApplicationTO> rowModel) {
+
+                item.add(new Label(componentId, "[" + rowModel.getObject().getPrivileges().stream().
+                        map(EntityTO::getKey).collect(Collectors.joining(", ")) + "]"));
+            }
+        });
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<ApplicationTO> getActions(final IModel<ApplicationTO> model) {
+        final ActionsPanel<ApplicationTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<ApplicationTO>() {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ApplicationTO ignore) {
+                modal.header(new StringResourceModel("any.edit", model));
+                modal.setContent(new ApplicationModalPanel(model.getObject(), false, modal, pageRef));
+                modal.show(true);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.EDIT, StandardEntitlement.APPLICATION_UPDATE);
+
+        panel.add(new ActionLink<ApplicationTO>() {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ApplicationTO ignore) {
+                target.add(privilegeModal.setContent(new PrivilegeDirectoryPanel(
+                        privilegeModal, model.getObject(), pageRef)));
+
+                privilegeModal.header(new StringResourceModel(
+                        "application.privileges", ApplicationDirectoryPanel.this, Model.of(model.getObject())));
+
+                MetaDataRoleAuthorizationStrategy.authorize(
+                        privilegeModal.getForm(), ENABLE, StandardEntitlement.APPLICATION_UPDATE);
+
+                privilegeModal.show(true);
+            }
+        }, ActionLink.ActionType.COMPOSE, StandardEntitlement.APPLICATION_UPDATE);
+
+        panel.add(new ActionLink<ApplicationTO>() {
+
+            private static final long serialVersionUID = 3766262567901552032L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ApplicationTO ignore) {
+                try {
+                    restClient.delete(model.getObject().getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting application {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.APPLICATION_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    public abstract static class Builder
+            extends DirectoryPanel.Builder<ApplicationTO, ApplicationTO, ApplicationRestClient> {
+
+        private static final long serialVersionUID = 5530948153889495221L;
+
+        public Builder(final PageReference pageRef) {
+            super(new ApplicationRestClient(), pageRef);
+        }
+
+        @Override
+        protected WizardMgtPanel<ApplicationTO> newInstance(final String id, final boolean wizardInModal) {
+            return new ApplicationDirectoryPanel(id, this);
+        }
+    }
+
+    protected class ApplicationDataProvider extends DirectoryDataProvider<ApplicationTO> {
+
+        private static final long serialVersionUID = 3124431855954382273L;
+
+        private final SortableDataProviderComparator<ApplicationTO> comparator;
+
+        private final ApplicationRestClient restClient = new ApplicationRestClient();
+
+        public ApplicationDataProvider(final int paginatorRows) {
+            super(paginatorRows);
+            this.comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ApplicationTO> iterator(final long first, final long count) {
+            List<ApplicationTO> result = restClient.list();
+            Collections.sort(result, comparator);
+            return result.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.list().size();
+        }
+
+        @Override
+        public IModel<ApplicationTO> model(final ApplicationTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationModalPanel.java
new file mode 100644
index 0000000..6f9c1f2
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ApplicationModalPanel.java
@@ -0,0 +1,91 @@
+/*
+ * 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.console.panels;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ApplicationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.PropertyModel;
+
+public class ApplicationModalPanel extends AbstractModalPanel<ApplicationTO> {
+
+    private static final long serialVersionUID = 4575264480736377795L;
+
+    private final ApplicationRestClient restClient = new ApplicationRestClient();
+
+    private final ApplicationTO application;
+
+    private final boolean create;
+
+    public ApplicationModalPanel(
+            final ApplicationTO application,
+            final boolean create,
+            final BaseModal<ApplicationTO> modal,
+            final PageReference pageRef) {
+
+        super(modal, pageRef);
+        this.application = application;
+        this.create = create;
+
+        modal.setFormModel(application);
+
+        AjaxTextFieldPanel key = new AjaxTextFieldPanel(
+                "key", "key", new PropertyModel<>(application, "key"), false);
+        key.setReadOnly(!create);
+        key.setRequired(true);
+        add(key);
+
+        AjaxTextFieldPanel description = new AjaxTextFieldPanel(
+                "description", "description", new PropertyModel<>(application, "description"), false);
+        description.setRequired(false);
+        add(description);
+    }
+
+    @Override
+    public ApplicationTO getItem() {
+        return application;
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        try {
+            if (create) {
+                restClient.create(application);
+            } else {
+                restClient.update(application);
+            }
+            SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+            this.modal.close(target);
+        } catch (Exception e) {
+            LOG.error("While creating/updating application", e);
+            SyncopeConsoleSession.get().error(
+                    StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
+        }
+        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmDirectoryPanel.java
index d1c15e6..ebbacd4 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmDirectoryPanel.java
@@ -24,16 +24,19 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.DirectoryDataProvider;
 import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.DynRealmDirectoryPanel.DynRealmDataProvider;
 import org.apache.syncope.client.console.rest.DynRealmRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.client.console.wizards.DynRealmWrapper;
 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.DynRealmTO;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.wicket.PageReference;
@@ -76,7 +79,7 @@ public class DynRealmDirectoryPanel extends
             }
         });
 
-        AjaxLink<Void> newDynReamlLink = new AjaxLink<Void>("add") {
+        AjaxLink<Void> newDynRealmlLink = new AjaxLink<Void>("add") {
 
             private static final long serialVersionUID = -7978723352517770644L;
 
@@ -88,8 +91,8 @@ public class DynRealmDirectoryPanel extends
                 target.add(modal);
             }
         };
-        ((WebMarkupContainer) get("container:content")).addOrReplace(newDynReamlLink);
-        MetaDataRoleAuthorizationStrategy.authorize(newDynReamlLink, RENDER, StandardEntitlement.DYNREALM_CREATE);
+        ((WebMarkupContainer) get("container:content")).addOrReplace(newDynRealmlLink);
+        MetaDataRoleAuthorizationStrategy.authorize(newDynRealmlLink, RENDER, StandardEntitlement.DYNREALM_CREATE);
 
         initResultTable();
     }
@@ -130,6 +133,25 @@ public class DynRealmDirectoryPanel extends
             }
         }, ActionLink.ActionType.EDIT, StandardEntitlement.DYNREALM_UPDATE);
 
+        panel.add(new ActionLink<DynRealmTO>() {
+
+            private static final long serialVersionUID = 3766262567901552032L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final DynRealmTO ignore) {
+                try {
+                    restClient.delete(model.getObject().getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting dynamic realm {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.DYNREALM_DELETE, true);
+
         return panel;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
index f1a0b69..b373274 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
@@ -147,7 +147,6 @@ public class DynRealmModalPanel extends AbstractModalPanel<DynRealmWrapper> {
                     StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
         }
         ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
-
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeDirectoryPanel.java
new file mode 100644
index 0000000..2444812
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeDirectoryPanel.java
@@ -0,0 +1,184 @@
+/*
+ * 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.console.panels;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ApplicationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+
+public class PrivilegeDirectoryPanel extends DirectoryPanel<
+        PrivilegeTO, PrivilegeTO, DirectoryDataProvider<PrivilegeTO>, ApplicationRestClient>
+        implements ModalPanel {
+
+    private static final long serialVersionUID = 6524374026036584897L;
+
+    private final ApplicationTO application;
+
+    private final BaseModal<PrivilegeTO> baseModal;
+
+    protected PrivilegeDirectoryPanel(
+            final BaseModal<PrivilegeTO> baseModal, final ApplicationTO application, final PageReference pageRef) {
+
+        super(BaseModal.CONTENT_ID, pageRef, false);
+        this.baseModal = baseModal;
+        this.application = application;
+
+        disableCheckBoxes();
+        enableExitButton();
+
+        this.addNewItemPanelBuilder(new PrivilegeWizardBuilder(application, new PrivilegeTO(), pageRef), true);
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.APPLICATION_UPDATE);
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<PrivilegeTO, String>> getColumns() {
+        final List<IColumn<PrivilegeTO, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<>(new ResourceModel("key"), "key", "key"));
+        columns.add(new PropertyColumn<>(new ResourceModel("description"), "description", "description"));
+
+        return columns;
+    }
+
+    @Override
+    protected ActionsPanel<PrivilegeTO> getActions(final IModel<PrivilegeTO> model) {
+        ActionsPanel<PrivilegeTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<PrivilegeTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final PrivilegeTO ignore) {
+                PrivilegeDirectoryPanel.this.getTogglePanel().close(target);
+                send(PrivilegeDirectoryPanel.this, Broadcast.EXACT,
+                        new AjaxWizard.EditItemActionEvent<>(model.getObject(), target));
+            }
+        }, ActionLink.ActionType.EDIT, StandardEntitlement.APPLICATION_UPDATE);
+
+        panel.add(new ActionLink<PrivilegeTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final PrivilegeTO ignore) {
+                try {
+                    application.getPrivileges().remove(model.getObject());
+                    restClient.update(application);
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    customActionOnFinishCallback(target);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.APPLICATION_UPDATE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionType> getBulkActions() {
+        return Collections.<ActionType>emptyList();
+    }
+
+    @Override
+    protected PrivilegeDataProvider dataProvider() {
+        return new PrivilegeDataProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_PRIVILEGE_PAGINATOR_ROWS;
+    }
+
+    protected class PrivilegeDataProvider extends DirectoryDataProvider<PrivilegeTO> {
+
+        private static final long serialVersionUID = 4725679400450513556L;
+
+        private final SortableDataProviderComparator<PrivilegeTO> comparator;
+
+        public PrivilegeDataProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("description", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<PrivilegeTO> iterator(final long first, final long count) {
+            List<PrivilegeTO> list = application.getPrivileges();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return application.getPrivileges().size();
+        }
+
+        @Override
+        public IModel<PrivilegeTO> model(final PrivilegeTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+        if (event.getPayload() instanceof ExitEvent && modal != null) {
+            final AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
+            baseModal.show(false);
+            baseModal.close(target);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeWizardBuilder.java
new file mode 100644
index 0000000..e1c4f5d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PrivilegeWizardBuilder.java
@@ -0,0 +1,101 @@
+/*
+ * 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.console.panels;
+
+import java.io.Serializable;
+import org.apache.syncope.client.console.rest.ApplicationRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class PrivilegeWizardBuilder extends AjaxWizardBuilder<PrivilegeTO> {
+
+    private static final long serialVersionUID = -1817419622749405208L;
+
+    private final ApplicationRestClient restClient = new ApplicationRestClient();
+
+    private final ApplicationTO application;
+
+    public PrivilegeWizardBuilder(
+            final ApplicationTO application, final PrivilegeTO privilege, final PageReference pageRef) {
+
+        super(privilege, pageRef);
+        this.application = application;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final PrivilegeTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new Profile(modelObject));
+        wizardModel.add(new Spec(modelObject));
+        return wizardModel;
+    }
+
+    @Override
+    protected Serializable onApplyInternal(final PrivilegeTO modelObject) {
+        application.getPrivileges().removeIf(privilege -> privilege.getKey().equals(modelObject.getKey()));
+        application.getPrivileges().add(modelObject);
+
+        restClient.update(application);
+
+        return modelObject;
+    }
+
+    public class Profile extends WizardStep {
+
+        private static final long serialVersionUID = 11881843064077955L;
+
+        public Profile(final PrivilegeTO privilege) {
+            setTitleModel(privilege.getKey() == null
+                    ? new StringResourceModel("privilege.new")
+                    : new StringResourceModel("privilege.edit", Model.of(privilege)));
+
+            AjaxTextFieldPanel key = new AjaxTextFieldPanel(
+                    "key", "key", new PropertyModel<>(privilege, "key"), false);
+            key.setReadOnly(privilege.getKey() != null);
+            key.setRequired(true);
+            add(key);
+
+            AjaxTextFieldPanel description = new AjaxTextFieldPanel(
+                    "description", "description", new PropertyModel<>(privilege, "description"), false);
+            description.setRequired(false);
+            add(description);
+        }
+    }
+
+    public class Spec extends WizardStep {
+
+        private static final long serialVersionUID = -3237113253888332643L;
+
+        public Spec(final PrivilegeTO privilege) {
+            setTitleModel(privilege.getKey() == null
+                    ? new StringResourceModel("privilege.new")
+                    : new StringResourceModel("privilege.edit", Model.of(privilege)));
+
+            add(new JsonEditorPanel(new PropertyModel<>(privilege, "spec")));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
index 4768d16..a410833 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
@@ -117,6 +117,8 @@ public class RoleDirectoryPanel extends DirectoryPanel<RoleTO, RoleWrapper, Role
                 new ResourceModel("realms"), null, "realms"));
         columns.add(new PropertyColumn<>(
                 new ResourceModel("dynRealms"), null, "dynRealms"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("privileges"), null, "privileges"));
 
         return columns;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
index 3a12a82..7660153 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/SecurityQuestionsPanel.java
@@ -105,6 +105,7 @@ public class SecurityQuestionsPanel extends DirectoryPanel<
             final Builder<SecurityQuestionTO, SecurityQuestionTO, SecurityQuestionRestClient> builder) {
 
         super(id, builder);
+        setOutputMarkupId(true);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
index 7cdda85..3d44dec 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
@@ -19,9 +19,9 @@
 package org.apache.syncope.client.console.panels.search;
 
 import java.io.Serializable;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
@@ -68,6 +68,8 @@ public abstract class AbstractSearchPanel extends Panel {
 
     protected IModel<List<String>> roleNames;
 
+    protected IModel<List<String>> privilegeNames;
+
     protected IModel<List<SearchClause>> model;
 
     protected WebMarkupContainer searchFormContainer;
@@ -147,7 +149,7 @@ public abstract class AbstractSearchPanel extends Panel {
         final SearchClausePanel searchClausePanel = new SearchClausePanel("panel", "panel",
                 Model.of(new SearchClause()),
                 required,
-                types, anames, dnames, groupInfo, roleNames, resourceNames);
+                types, anames, dnames, groupInfo, roleNames, privilegeNames, resourceNames);
 
         if (enableSearch) {
             searchClausePanel.enableSearch(builder.resultContainer);
@@ -186,11 +188,7 @@ public abstract class AbstractSearchPanel extends Panel {
                 final List<PlainSchemaTO> schemas = schemaRestClient.<PlainSchemaTO>getSchemas(
                         SchemaType.PLAIN, null, anyTypeRestClient.read(type).getClasses().toArray(new String[] {}));
 
-                final Map<String, PlainSchemaTO> res = new HashMap<>();
-                for (PlainSchemaTO schema : schemas) {
-                    res.put(schema.getKey(), schema);
-                }
-                return res;
+                return schemas.stream().collect(Collectors.toMap(schema -> schema.getKey(), Function.identity()));
             }
         };
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
index 415abba..8d20abc 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
@@ -41,6 +41,7 @@ public final class SearchClause implements Serializable {
         GROUP_MEMBERSHIP,
         GROUP_MEMBER,
         ROLE_MEMBERSHIP,
+        PRIVILEGE,
         RESOURCE,
         RELATIONSHIP;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
index 745688e..0dc3196 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
@@ -86,6 +86,8 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
     private final IModel<List<String>> roleNames;
 
+    private final IModel<List<String>> privilegeNames;
+
     private final IModel<List<String>> resourceNames;
 
     private IModel<SearchClause> clause;
@@ -114,6 +116,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
             final IModel<List<String>> dnames,
             final Pair<IModel<Map<String, String>>, Integer> groupInfo,
             final IModel<List<String>> roleNames,
+            final IModel<List<String>> privilegeNames,
             final IModel<List<String>> resourceNames) {
 
         super(id, name, clause);
@@ -126,6 +129,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         this.dnames = dnames;
         this.groupInfo = groupInfo;
         this.roleNames = roleNames;
+        this.privilegeNames = privilegeNames;
         this.resourceNames = resourceNames;
 
         searchButton = new AjaxSubmitLink("search") {
@@ -170,6 +174,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         return Arrays.asList(SearchClause.Comparator.values());
 
                     case ROLE_MEMBERSHIP:
+                    case PRIVILEGE:
                     case GROUP_MEMBERSHIP:
                     case GROUP_MEMBER:
                     case RESOURCE:
@@ -219,6 +224,11 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         Collections.sort(roles);
                         return roles;
 
+                    case PRIVILEGE:
+                        final List<String> privileges = new ArrayList<>(privilegeNames.getObject());
+                        Collections.sort(privileges);
+                        return privileges;
+
                     case RESOURCE:
                         final List<String> resources = new ArrayList<>(resourceNames.getObject());
                         Collections.sort(resources);
@@ -596,6 +606,15 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                     property.setChoices(properties.getObject());
                     break;
 
+                case PRIVILEGE:
+                    value.setEnabled(false);
+                    value.setModelObject(StringUtils.EMPTY);
+
+                    // reload properties list
+                    properties.detach();
+                    property.setChoices(properties.getObject());
+                    break;
+                    
                 case GROUP_MEMBERSHIP:
                     value.setEnabled(false);
                     value.setModelObject(StringUtils.EMPTY);
@@ -718,6 +737,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         }
                         break;
                     case ROLE_MEMBERSHIP:
+                    case PRIVILEGE:
                     case RESOURCE:
                         switch (object) {
                             case EQUALS:
@@ -824,7 +844,8 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
     @Override
     public FieldPanel<SearchClause> clone() {
         final SearchClausePanel panel = new SearchClausePanel(
-                getId(), name, null, required, types, anames, dnames, groupInfo, roleNames, resourceNames);
+                getId(), name, null, required, types, anames, dnames, groupInfo,
+                roleNames, privilegeNames, resourceNames);
         panel.setReadOnly(this.isReadOnly());
         panel.setRequired(this.isRequired());
         if (searchButton.isEnabled()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
index eaecc2f..9bef61c 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
@@ -123,6 +123,9 @@ public final class SearchUtils implements Serializable {
         if (SpecialAttr.ROLES.toString().equals(property)) {
             clause.setType(SearchClause.Type.ROLE_MEMBERSHIP);
             clause.setProperty(value);
+        } else if (SpecialAttr.PRIVILEGES.toString().equals(property)) {
+            clause.setType(SearchClause.Type.PRIVILEGE);
+            clause.setProperty(value);
         } else if (SpecialAttr.RELATIONSHIPS.toString().equals(property)) {
             clause.setType(SearchClause.Type.RELATIONSHIP);
             clause.setProperty(value);
@@ -345,6 +348,23 @@ public final class SearchUtils implements Serializable {
                         }
                         break;
 
+                    case PRIVILEGE:
+                        if (StringUtils.isNotBlank(clause.getProperty())) {
+                            switch (clause.getComparator()) {
+                                case EQUALS:
+                                    condition = ((UserFiqlSearchConditionBuilder) builder).
+                                            withPrivileges(clause.getProperty());
+                                    break;
+                                case NOT_EQUALS:
+                                    condition = ((UserFiqlSearchConditionBuilder) builder).
+                                            withoutPrivileges(clause.getProperty());
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                        break;
+
                     case RELATIONSHIP:
                         if (StringUtils.isNotBlank(clause.getProperty())) {
                             if (builder instanceof UserFiqlSearchConditionBuilder) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
index efc9de8..7dd81b3 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
@@ -21,6 +21,7 @@ package org.apache.syncope.client.console.panels.search;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
+import org.apache.syncope.client.console.rest.ApplicationRestClient;
 import org.apache.syncope.client.console.rest.RoleRestClient;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -33,6 +34,8 @@ public final class UserSearchPanel extends AnyObjectSearchPanel {
 
     private final RoleRestClient roleRestClient = new RoleRestClient();
 
+    private final ApplicationRestClient applicationRestClient = new ApplicationRestClient();
+
     public static class Builder extends AnyObjectSearchPanel.Builder {
 
         private static final long serialVersionUID = 6308997285778809578L;
@@ -64,6 +67,18 @@ public final class UserSearchPanel extends AnyObjectSearchPanel {
                 return roleRestClient.list().stream().map(EntityTO::getKey).collect(Collectors.toList());
             }
         };
+
+        this.privilegeNames = new LoadableDetachableModel<List<String>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<String> load() {
+                return applicationRestClient.list().stream().
+                        flatMap(application -> application.getPrivileges().stream()).
+                        map(EntityTO::getKey).collect(Collectors.toList());
+            }
+        };
     }
 
     @Override
@@ -71,6 +86,7 @@ public final class UserSearchPanel extends AnyObjectSearchPanel {
         List<SearchClause.Type> result = new ArrayList<>();
         result.add(SearchClause.Type.ATTRIBUTE);
         result.add(SearchClause.Type.ROLE_MEMBERSHIP);
+        result.add(SearchClause.Type.PRIVILEGE);
         result.add(SearchClause.Type.GROUP_MEMBERSHIP);
         result.add(SearchClause.Type.RESOURCE);
         result.add(SearchClause.Type.RELATIONSHIP);

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
index 600ebd8..2b89ee4 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleDirectoryPanel.java
@@ -200,7 +200,7 @@ public class PolicyRuleDirectoryPanel<T extends PolicyTO> extends DirectoryPanel
                     customActionOnFinishCallback(target);
                 }
             }
-        }, ActionLink.ActionType.RELOAD, StandardEntitlement.TASK_LIST).hideLabel();
+        }, ActionLink.ActionType.RELOAD, StandardEntitlement.POLICY_LIST).hideLabel();
         return panel;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/rest/ApplicationRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ApplicationRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ApplicationRestClient.java
new file mode 100644
index 0000000..0c765f8
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ApplicationRestClient.java
@@ -0,0 +1,49 @@
+/*
+ * 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.console.rest;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.rest.api.service.ApplicationService;
+
+public class ApplicationRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -381814125643246243L;
+
+    public void delete(final String key) {
+        getService(ApplicationService.class).delete(key);
+    }
+
+    public ApplicationTO read(final String key) {
+        return getService(ApplicationService.class).read(key);
+    }
+
+    public void update(final ApplicationTO applicationTO) {
+        getService(ApplicationService.class).update(applicationTO);
+    }
+
+    public void create(final ApplicationTO applicationTO) {
+        getService(ApplicationService.class).create(applicationTO);
+    }
+
+    public List<ApplicationTO> list() {
+        return getService(ApplicationService.class).list();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
index d45e34f..aee979f 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
@@ -26,8 +26,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.pages.Notifications;
 import org.apache.syncope.client.console.pages.Policies;
-import org.apache.syncope.client.console.pages.Administration;
-import org.apache.syncope.client.console.pages.SecurityQuestions;
+import org.apache.syncope.client.console.pages.Security;
 import org.apache.syncope.client.console.pages.Types;
 import org.apache.syncope.client.console.topology.Topology;
 import org.apache.syncope.common.lib.info.NumbersInfo;
@@ -56,9 +55,9 @@ public class CompletenessWidget extends BaseWidget {
 
     private final BookmarkablePageLink<Types> types;
 
-    private final BookmarkablePageLink<SecurityQuestions> securityquestions;
+    private final BookmarkablePageLink<Security> securityquestions;
 
-    private final BookmarkablePageLink<Administration> roles;
+    private final BookmarkablePageLink<Security> roles;
 
     public CompletenessWidget(final String id, final Map<String, Boolean> confCompleteness) {
         super(id);
@@ -108,12 +107,12 @@ public class CompletenessWidget extends BaseWidget {
                 !confCompleteness.get(NumbersInfo.ConfItem.VIR_SCHEMA.name())
                 || !confCompleteness.get(NumbersInfo.ConfItem.ANY_TYPE.name()));
 
-        securityquestions = BookmarkablePageLinkBuilder.build("securityquestions", SecurityQuestions.class);
+        securityquestions = BookmarkablePageLinkBuilder.build("securityquestions", Security.class);
         securityquestions.setOutputMarkupPlaceholderTag(true);
         actions.add(securityquestions);
         securityquestions.setVisible(!confCompleteness.get(NumbersInfo.ConfItem.SECURITY_QUESTION.name()));
 
-        roles = BookmarkablePageLinkBuilder.build("roles", Administration.class);
+        roles = BookmarkablePageLinkBuilder.build("roles", Security.class);
         roles.setOutputMarkupPlaceholderTag(true);
         MetaDataRoleAuthorizationStrategy.authorize(roles, WebPage.ENABLE, StandardEntitlement.ROLE_LIST);
         actions.add(roles);

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
index c7e71a7..36cf21b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
@@ -27,7 +27,7 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.syncope.client.console.pages.Realms;
-import org.apache.syncope.client.console.pages.Administration;
+import org.apache.syncope.client.console.pages.Security;
 import org.apache.syncope.client.console.topology.Topology;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.wicket.request.component.IRequestablePage;
@@ -78,7 +78,7 @@ public class NumberWidget extends BaseWidget {
                     responsePage = Realms.class;
                     isAuthorized = SyncopeConsoleSession.get().owns(label + "_SEARCH");
                 } else {
-                    responsePage = Administration.class;
+                    responsePage = Security.class;
                     isAuthorized = SyncopeConsoleSession.get().owns(StandardEntitlement.ROLE_LIST);
                 }
                 break;

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
index 8ea6aa2..1f3ecd3 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.panels.search.UserSearchPanel;
+import org.apache.syncope.client.console.rest.ApplicationRestClient;
 import org.apache.syncope.client.console.rest.DynRealmRestClient;
 import org.apache.syncope.client.console.rest.RealmRestClient;
 import org.apache.syncope.client.console.rest.RoleRestClient;
@@ -94,6 +95,7 @@ public class RoleWizardBuilder extends AjaxWizardBuilder<RoleWrapper> {
         wizardModel.add(new Entitlements(modelObject.getInnerObject()));
         wizardModel.add(new Realms(modelObject.getInnerObject()));
         wizardModel.add(new DynRealms(modelObject.getInnerObject()));
+        wizardModel.add(new Privileges(modelObject.getInnerObject()));
         return wizardModel;
     }
 
@@ -179,4 +181,19 @@ public class RoleWizardBuilder extends AjaxWizardBuilder<RoleWrapper> {
         }
     }
 
+    public static class Privileges extends WizardStep {
+
+        private static final long serialVersionUID = 6896014330702958579L;
+
+        public Privileges(final RoleTO modelObject) {
+            setTitleModel(new ResourceModel("privileges"));
+            add(new AjaxPalettePanel.Builder<>().build("privileges",
+                    new PropertyModel<>(modelObject, "privileges"),
+                    new ListModel<>(new ApplicationRestClient().list().stream().
+                            flatMap(application -> application.getPrivileges().stream()).
+                            map(EntityTO::getKey).collect(Collectors.toList()))).
+                    hideLabel().setOutputMarkupId(true));
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/resources/console.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/console.properties b/client/console/src/main/resources/console.properties
index 9c46f7a..3139c1c 100644
--- a/client/console/src/main/resources/console.properties
+++ b/client/console/src/main/resources/console.properties
@@ -37,7 +37,6 @@ flowableModelerDirectory=${flowable-modeler.directory}
 
 reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
 
-page.administration=org.apache.syncope.client.console.pages.Administration
 page.dashboard=org.apache.syncope.client.console.pages.Dashboard
 page.realms=org.apache.syncope.client.console.pages.Realms
 page.topology=org.apache.syncope.client.console.topology.Topology
@@ -46,7 +45,7 @@ page.workflow=org.apache.syncope.client.console.pages.Workflow
 page.audit=org.apache.syncope.client.console.pages.Audit
 page.implementations=org.apache.syncope.client.console.pages.Implementations
 page.logs=org.apache.syncope.client.console.pages.Logs
-page.securityquestions=org.apache.syncope.client.console.pages.SecurityQuestions
+page.security=org.apache.syncope.client.console.pages.Security
 page.types=org.apache.syncope.client.console.pages.Types
 page.policies=org.apache.syncope.client.console.pages.Policies
 page.notifications=org.apache.syncope.client.console.pages.Notifications

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
index f668185..2a36516 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
@@ -53,7 +53,6 @@ realms=Realms
 topology=Topology
 roles=Roles
 policies=Policies
-securityQuestions=Security Questions
 workflow=Workflow
 logs=Logs
 layouts=Layouts
@@ -70,7 +69,7 @@ audit=Audit
 connectors.confirm.reload=This request is potentially dangerous for running operations, continue?
 intAttrNameInfo.help=Besides auto-completed attributes, you can also refer to groups, any objects or memberships  (if applicable); for example:
 confirmGlobalLogout=Do you really want to perform global logout?
-administration=Administration
 implementations=Implementations
 
 timeout=Operation is taking to long: it will be executed in background. Please check later for the result (errors won't be triggered).
+security=Security

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
index fa983f7..f1b3985 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
@@ -53,7 +53,6 @@ realms=Realm
 topology=Topologia
 roles=Ruoli
 policies=Politiche
-securityQuestions=Domande di sicurezza
 workflow=Workflow
 logs=Log
 layouts=Layout
@@ -70,7 +69,7 @@ audit=Audit
 connectors.confirm.reload=Questa richiesta \u00e8 potenzialmente dannosa per le operazioni in corso, proseguire?
 intAttrNameInfo.help=Oltre agli attributi auto-completati, \u00e8 possibile fare riferimento anche a gruppi, any object e membership (se applicabile); ad esempio:
 confirmGlobalLogout=Vuoi davvero procedere al logout globale?
-administration=Amministrazione
 implementations=Implementazioni
 
 timeout=L'operazione sta durando troppo: sar\u00e0 eseguita in background. Verifica il risultato pi\u00f9 tardi (gli errori non saranno notificati).
+security=Sicurezza

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
index 9c66b44..c512754 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
@@ -52,7 +52,6 @@ realms=\u30ec\u30eb\u30e0
 topology=\u30c8\u30dd\u30ed\u30b8\u30fc
 roles=\u30ed\u30fc\u30eb
 policies=\u30dd\u30ea\u30b7\u30fc
-securityQuestions=\u79d8\u5bc6\u306e\u8cea\u554f
 workflow=\u30ef\u30fc\u30af\u30d5\u30ed\u30fc
 logs=\u30ed\u30b0
 layouts=\u30ec\u30a4\u30a2\u30a6\u30c8
@@ -69,6 +68,6 @@ audit=\u76e3\u67fb
 connectors.confirm.reload=\u3053\u306e\u8981\u6c42\u306f\u3001\u5b9f\u884c\u4e2d\u306e\u64cd\u4f5c\u306b\u5bfe\u3057\u3066\u5371\u967a\u306a\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u7d9a\u884c\u3057\u307e\u3059\u304b?
 intAttrNameInfo.help=\u30aa\u30fc\u30c8\u30b3\u30f3\u30d7\u30ea\u30fc\u30c8\u306e\u5c5e\u6027\u4ee5\u5916\u306b\u3001\u30b0\u30eb\u30fc\u30d7\u3001\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3001\u30e1\u30f3\u30d0\u30fc\u30b7\u30c3\u30d7\u3082\u53c2\u7167\u53ef\u80fd\u3067\u3059 (\u8a72\u5f53\u3059\u308b\u5834\u5408)\u3002\u4f8b\:
 confirmGlobalLogout=\u30b0\u30ed\u30fc\u30d0\u30eb\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b?
-administration=\u7ba1\u7406
 implementations=\u5c0e\u5165
 timeout=\u64cd\u4f5c\u306b\u9577\u6642\u9593\u304b\u304b\u3063\u3066\u3044\u307e\u3059\: \u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u3067\u5b9f\u884c\u3055\u308c\u307e\u3059\u3002 \u7d50\u679c\u3092\u5f8c\u3067\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044 (\u30a8\u30e9\u30fc\u306f\u5f15\u304d\u8d77\u3053\u3057\u307e\u305b\u3093)\u3002
+security=Security

http://git-wip-us.apache.org/repos/asf/syncope/blob/72c5885d/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
index 8676cdb..9516f2b 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
@@ -53,7 +53,6 @@ realms=Realm
 topology=Topologia
 roles=\t\nFun\u00e7\u00e3o\n\t
 policies=Pol\u00edticas
-securityQuestions=Perguntas de seguran\u00e7a
 workflow=Fluxo de trabalho
 logs=Log
 layouts=Layout
@@ -70,7 +69,7 @@ audit=Audit
 connectors.confirm.reload=Esta requis\u00e7\u00e3o \u00e9 potencialmente perigosa para opera\u00e7\u00f5es em execu\u00e7\u00e3o, prosseguir?
 intAttrNameInfo.help=Besides auto-completed attributes, you can also refer to groups, any objects or memberships  (if applicable); for example:
 confirmGlobalLogout=Do you really want to perform global logout?
-administration=Administra\u00e7\u00e3o
 implementations=Implementa\u00e7\u00f5es
 
 timeout=Operation is taking to long: it will be executed in background. Please check later for the result (errors won't be triggered).
+security=Security