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/20 13:44:15 UTC

[1/2] syncope git commit: [SYNCOPE-1281] Added the possibility to map privileges for propagation

Repository: syncope
Updated Branches:
  refs/heads/2_0_X fa079531e -> 93ef03e69
  refs/heads/master a413dbca5 -> fb3a30e6e


[SYNCOPE-1281] Added the possibility to map privileges for propagation


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

Branch: refs/heads/master
Commit: fb3a30e6ec8440354546804396504178e94201ce
Parents: a413dbc
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Mar 20 14:38:05 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Mar 20 14:38:05 2018 +0100

----------------------------------------------------------------------
 .../NotificationWizardBuilder.java              |  9 ++--
 .../console/wizards/AbstractMappingPanel.java   | 11 ++--
 .../SyncopeConsoleApplication.properties        |  2 +-
 .../SyncopeConsoleApplication_it.properties     |  2 +-
 .../SyncopeConsoleApplication_pt_BR.properties  |  2 +-
 .../core/persistence/api/entity/Role.java       |  2 +
 .../persistence/jpa/entity/JPAApplication.java  |  2 +
 .../persistence/jpa/entity/JPAPrivilege.java    |  2 +
 .../core/persistence/jpa/entity/JPARole.java    |  9 ++++
 .../jpa/validation/entity/ApplicationCheck.java | 41 +++++++++++++++
 .../validation/entity/ApplicationValidator.java | 40 +++++++++++++++
 .../jpa/validation/entity/PrivilegeCheck.java   | 41 +++++++++++++++
 .../validation/entity/PrivilegeValidator.java   | 40 +++++++++++++++
 .../core/provisioning/api/IntAttrName.java      | 10 ++++
 .../provisioning/java/IntAttrNameParser.java    | 16 ++++--
 .../provisioning/java/MappingManagerImpl.java   | 53 +++++++++++++++-----
 .../java/data/ResourceDataBinderImpl.java       | 16 +++++-
 .../java/IntAttrNameParserTest.java             | 22 ++++++++
 .../syncope/fit/core/PropagationTaskITCase.java | 38 ++++++++++++++
 .../concepts/externalresources.adoc             |  2 +
 20 files changed, 328 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
index e4442a4..471e549 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
@@ -352,11 +352,10 @@ public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationWra
             recipientAttrName.setChoices(getSchemas());
             recipientAttrName.addRequiredLabel();
             recipientAttrName.setTitle(getString("intAttrNameInfo.help")
-                    + "<div style=\"font-size: 10px;\">"
-                    + "<code>groups[groupName].attribute</code>\n"
-                    + "<code>anyObjects[anyObjectName].attribute</code>\n"
-                    + "<code>memberships[groupName].attribute</code>\n"
-                    + "</div>", true);
+                    + "<code>groups[groupName].attribute</code>, "
+                    + "<code>anyObjects[anyObjectName].attribute</code>, "
+                    + "<code>memberships[groupName].attribute</code> or "
+                    + "<code>privileges[applicationKey]</code>", true);
             add(recipientAttrName);
 
             AjaxTextFieldPanel staticRecipientsFieldPanel =

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
index f344917..c35be6a 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
@@ -151,12 +151,11 @@ public abstract class AbstractMappingPanel extends Panel {
         mappingContainer.add(new Label("intAttrNameInfo", Model.of()).add(new PopoverBehavior(
                 Model.<String>of(),
                 Model.of(getString("intAttrNameInfo.help")
-                        + "<div style=\"font-size: 10px;\">"
-                        + "<code>groups[groupName].attribute</code>\n"
-                        + "<code>anyObjects[anyObjectName].attribute</code>\n"
-                        + "<code>memberships[groupName].attribute</code>\n"
-                        + "</div>"),
-                new PopoverConfig().withHtml(true).withPlacement(TooltipConfig.Placement.bottom)) {
+                        + "<code>groups[groupName].attribute</code>, "
+                        + "<code>anyObjects[anyObjectName].attribute</code>, "
+                        + "<code>memberships[groupName].attribute</code> or "
+                        + "<code>privileges[applicationKey]</code>"),
+                new PopoverConfig().withHtml(true).withPlacement(TooltipConfig.Placement.right)) {
 
             private static final long serialVersionUID = -7867802555691605021L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/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 2a36516..1d093bc 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
@@ -67,7 +67,7 @@ OrderByLink.CSS.none=sorting
 entitlements=Entitlements
 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:
+intAttrNameInfo.help=Besides auto-completed attributes, you can also refer to groups, any objects, memberships or privileges; for example:
 confirmGlobalLogout=Do you really want to perform global logout?
 implementations=Implementations
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/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 f1b3985..a4514ff 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
@@ -67,7 +67,7 @@ OrderByLink.CSS.none=sorting
 entitlements=Entitlement
 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:
+intAttrNameInfo.help=Oltre agli attributi auto-completati, \u00e8 possibile fare riferimento anche a gruppi, any object, membership e privilegi; ad esempio:
 confirmGlobalLogout=Vuoi davvero procedere al logout globale?
 implementations=Implementazioni
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/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 9516f2b..3d40751 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
@@ -67,7 +67,7 @@ OrderByLink.CSS.none=sorting
 entitlements=Entitlement
 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:
+intAttrNameInfo.help=Besides auto-completed attributes, you can also refer to groups, any objects, memberships or privileges; for example:
 confirmGlobalLogout=Do you really want to perform global logout?
 implementations=Implementa\u00e7\u00f5es
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
index 619c228..e4cf8a2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
@@ -44,5 +44,7 @@ public interface Role extends ProvidedKeyEntity {
 
     boolean add(Privilege privilege);
 
+    Set<? extends Privilege> getPrivileges(Application application);
+
     Set<? extends Privilege> getPrivileges();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java
index 0b5f093..756ce12 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java
@@ -28,9 +28,11 @@ import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.Application;
 import org.apache.syncope.core.persistence.api.entity.Privilege;
+import org.apache.syncope.core.persistence.jpa.validation.entity.ApplicationCheck;
 
 @Entity
 @Table(name = JPAApplication.TABLE)
+@ApplicationCheck
 public class JPAApplication extends AbstractProvidedKeyEntity implements Application {
 
     private static final long serialVersionUID = -5951400197744722305L;

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java
index 0e1a3b5..d09bb1d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java
@@ -24,9 +24,11 @@ import javax.persistence.ManyToOne;
 import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.Application;
 import org.apache.syncope.core.persistence.api.entity.Privilege;
+import org.apache.syncope.core.persistence.jpa.validation.entity.PrivilegeCheck;
 
 @Entity
 @Table(name = JPAPrivilege.TABLE)
+@PrivilegeCheck
 public class JPAPrivilege extends AbstractProvidedKeyEntity implements Privilege {
 
     private static final long serialVersionUID = -6479069294944858456L;

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index 728c360..4dbddbf 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.persistence.Cacheable;
 import javax.persistence.CascadeType;
 import javax.persistence.CollectionTable;
@@ -36,6 +37,7 @@ import javax.persistence.ManyToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.validation.Valid;
+import org.apache.syncope.core.persistence.api.entity.Application;
 import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
@@ -147,6 +149,13 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     }
 
     @Override
+    public Set<? extends Privilege> getPrivileges(final Application application) {
+        return privileges.stream().
+                filter(privilege -> privilege.getApplication().equals(application)).
+                collect(Collectors.toSet());
+    }
+
+    @Override
     public Set<? extends Privilege> getPrivileges() {
         return privileges;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java
new file mode 100644
index 0000000..674a5b1
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = ApplicationValidator.class)
+@Documented
+public @interface ApplicationCheck {
+
+    String message() default "{org.apache.syncope.core.persistence.validation.application}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java
new file mode 100644
index 0000000..7360021
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import javax.validation.ConstraintValidatorContext;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.Application;
+
+public class ApplicationValidator extends AbstractValidator<ApplicationCheck, Application> {
+
+    @Override
+    public boolean isValid(final Application application, final ConstraintValidatorContext context) {
+        context.disableDefaultConstraintViolation();
+
+        if (application.getKey() == null || !KEY_PATTERN.matcher(application.getKey()).matches()) {
+            context.buildConstraintViolationWithTemplate(
+                    getTemplate(EntityViolationType.InvalidKey, "Invalid application key")).
+                    addPropertyNode("key").addConstraintViolation();
+            return false;
+        }
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java
new file mode 100644
index 0000000..aabc552
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = PrivilegeValidator.class)
+@Documented
+public @interface PrivilegeCheck {
+
+    String message() default "{org.apache.syncope.core.persistence.validation.privilege}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java
new file mode 100644
index 0000000..6f035d8
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import javax.validation.ConstraintValidatorContext;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+
+public class PrivilegeValidator extends AbstractValidator<PrivilegeCheck, Privilege> {
+
+    @Override
+    public boolean isValid(final Privilege privilege, final ConstraintValidatorContext context) {
+        context.disableDefaultConstraintViolation();
+
+        if (privilege.getKey() == null || !KEY_PATTERN.matcher(privilege.getKey()).matches()) {
+            context.buildConstraintViolationWithTemplate(
+                    getTemplate(EntityViolationType.InvalidKey, "Invalid privilege key")).
+                    addPropertyNode("key").addConstraintViolation();
+            return false;
+        }
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrName.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrName.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrName.java
index f46384e..1c42ec9 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrName.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrName.java
@@ -39,6 +39,8 @@ public class IntAttrName {
 
     private String membershipOfGroup;
 
+    private String privilegesOfApplication;
+
     public AnyTypeKind getAnyTypeKind() {
         return anyTypeKind;
     }
@@ -95,6 +97,14 @@ public class IntAttrName {
         this.membershipOfGroup = membershipOfGroup;
     }
 
+    public String getPrivilegesOfApplication() {
+        return privilegesOfApplication;
+    }
+
+    public void setPrivilegesOfApplication(final String privilegesOfApplication) {
+        this.privilegesOfApplication = privilegesOfApplication;
+    }
+
     @Override
     public String toString() {
         return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java
index 2b77a61..2d42609 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java
@@ -35,6 +35,9 @@ import org.springframework.transaction.annotation.Transactional;
 
 public class IntAttrNameParser {
 
+    private static final Pattern PRIVILEGE_PATTERN = Pattern.compile(
+            "^privileges\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]");
+
     private static final Pattern ENCLOSING_GROUP_PATTERN = Pattern.compile(
             "^groups\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]\\.(.+)");
 
@@ -92,11 +95,18 @@ public class IntAttrNameParser {
     public IntAttrName parse(final String intAttrName, final AnyTypeKind provisionAnyTypeKind) throws ParseException {
         IntAttrName result = new IntAttrName();
 
+        Matcher matcher;
         if (intAttrName.indexOf('.') == -1) {
-            result.setAnyTypeKind(provisionAnyTypeKind);
-            setFieldOrSchemaName(intAttrName, result.getAnyTypeKind(), result);
+            matcher = PRIVILEGE_PATTERN.matcher(intAttrName);
+            if (matcher.matches()) {
+                result.setAnyTypeKind(AnyTypeKind.USER);
+                result.setPrivilegesOfApplication(matcher.group(1));
+            } else {
+                result.setAnyTypeKind(provisionAnyTypeKind);
+                setFieldOrSchemaName(intAttrName, result.getAnyTypeKind(), result);
+            }
         } else {
-            Matcher matcher = ENCLOSING_GROUP_PATTERN.matcher(intAttrName);
+            matcher = ENCLOSING_GROUP_PATTERN.matcher(intAttrName);
             if (matcher.matches()) {
                 result.setAnyTypeKind(AnyTypeKind.GROUP);
                 result.setEnclosingGroup(matcher.group(1));

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
index 8685d39..aced103 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
@@ -43,14 +43,17 @@ import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.ApplicationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.Application;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
 import org.apache.syncope.core.persistence.api.entity.Membership;
@@ -112,6 +115,9 @@ public class MappingManagerImpl implements MappingManager {
     private VirSchemaDAO virSchemaDAO;
 
     @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
     private AnyObjectDAO anyObjectDAO;
 
     @Autowired
@@ -121,6 +127,9 @@ public class MappingManagerImpl implements MappingManager {
     private RealmDAO realmDAO;
 
     @Autowired
+    private ApplicationDAO applicationDAO;
+
+    @Autowired
     private DerAttrHandler derAttrHandler;
 
     @Autowired
@@ -526,7 +535,10 @@ public class MappingManagerImpl implements MappingManager {
                         attr = ((GroupableRelatable<?, ?, ?, ?, ?>) reference).getPlainAttr(
                                 intAttrName.getSchemaName(), membership).orElse(null);
                     }
-                    if (attr != null) {
+                    if (attr == null) {
+                        LOG.warn("Invalid PlainSchema {} or PlainAttr not found for {}",
+                                intAttrName.getSchemaName(), reference);
+                    } else {
                         if (attr.getUniqueValue() != null) {
                             values.add(anyUtils.clonePlainAttrValue(attr.getUniqueValue()));
                         } else if (attr.getValues() != null) {
@@ -537,13 +549,15 @@ public class MappingManagerImpl implements MappingManager {
 
                 case DERIVED:
                     DerSchema derSchema = derSchemaDAO.find(intAttrName.getSchemaName());
-                    if (derSchema != null) {
-                        String value = membership == null
+                    if (derSchema == null) {
+                        LOG.warn("Invalid DerSchema: {}", intAttrName.getSchemaName());
+                    } else {
+                        String derValue = membership == null
                                 ? derAttrHandler.getValue(reference, derSchema)
                                 : derAttrHandler.getValue(reference, membership, derSchema);
-                        if (value != null) {
+                        if (derValue != null) {
                             PlainAttrValue attrValue = anyUtils.newPlainAttrValue();
-                            attrValue.setStringValue(value);
+                            attrValue.setStringValue(derValue);
                             values.add(attrValue);
                         }
                     }
@@ -554,7 +568,9 @@ public class MappingManagerImpl implements MappingManager {
                     transform = false;
 
                     VirSchema virSchema = virSchemaDAO.find(intAttrName.getSchemaName());
-                    if (virSchema != null) {
+                    if (virSchema == null) {
+                        LOG.warn("Invalid VirSchema: {}", intAttrName.getSchemaName());
+                    } else {
                         LOG.debug("Expire entry cache {}-{}", reference, intAttrName.getSchemaName());
                         virAttrCache.expire(
                                 reference.getType().getKey(), reference.getKey(), intAttrName.getSchemaName());
@@ -562,18 +578,29 @@ public class MappingManagerImpl implements MappingManager {
                         List<String> virValues = membership == null
                                 ? virAttrHandler.getValues(reference, virSchema)
                                 : virAttrHandler.getValues(reference, membership, virSchema);
-                        virValues.stream().
-                                map(value -> {
-                                    PlainAttrValue attrValue = anyUtils.newPlainAttrValue();
-                                    attrValue.setStringValue(value);
-                                    return attrValue;
-                                }).
-                                forEachOrdered(attrValue -> values.add(attrValue));
+                        virValues.forEach(virValue -> {
+                            PlainAttrValue attrValue = anyUtils.newPlainAttrValue();
+                            attrValue.setStringValue(virValue);
+                            values.add(attrValue);
+                        });
                     }
                     break;
 
                 default:
             }
+        } else if (intAttrName.getPrivilegesOfApplication() != null && reference instanceof User) {
+            Application application = applicationDAO.find(intAttrName.getPrivilegesOfApplication());
+            if (application == null) {
+                LOG.warn("Invalid application: {}", intAttrName.getPrivilegesOfApplication());
+            } else {
+                userDAO.findAllRoles((User) reference).stream().
+                        flatMap(role -> role.getPrivileges(application).stream()).
+                        forEach(privilege -> {
+                            PlainAttrValue attrValue = anyUtils.newPlainAttrValue();
+                            attrValue.setStringValue(privilege.getKey());
+                            values.add(attrValue);
+                        });
+            }
         }
 
         LOG.debug("Internal values: {}", values);

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index 7e5cea6..1db8e56 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -410,14 +410,19 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
                     LOG.error("Invalid intAttrName '{}'", itemTO.getIntAttrName(), e);
                 }
 
-                if (intAttrName == null || intAttrName.getSchemaType() == null && intAttrName.getField() == null) {
+                if (intAttrName == null
+                        || intAttrName.getSchemaType() == null && intAttrName.getField() == null
+                        && intAttrName.getPrivilegesOfApplication() == null) {
+
                     LOG.error("'{}' not existing", itemTO.getIntAttrName());
                     invalidMapping.getElements().add("'" + itemTO.getIntAttrName() + "' not existing");
                 } else {
                     boolean allowed = true;
                     if (intAttrName.getSchemaType() != null
                             && intAttrName.getEnclosingGroup() == null
-                            && intAttrName.getRelatedAnyObject() == null) {
+                            && intAttrName.getRelatedAnyObject() == null
+                            && intAttrName.getPrivilegesOfApplication() == null) {
+
                         switch (intAttrName.getSchemaType()) {
                             case PLAIN:
                                 allowed = allowedSchemas.getPlainSchemas().contains(intAttrName.getSchemaName());
@@ -492,6 +497,13 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
                                     "Only " + MappingPurpose.PROPAGATION.name()
                                     + " allowed when referring to any objects");
                         }
+                        if (intAttrName.getPrivilegesOfApplication() != null
+                                && item.getPurpose() != MappingPurpose.PROPAGATION) {
+
+                            invalidMapping.getElements().add(
+                                    "Only " + MappingPurpose.PROPAGATION.name()
+                                    + " allowed when referring to privileges");
+                        }
                         if (intAttrName.getSchemaType() == SchemaType.DERIVED
                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java
index beeb7b0..0316a12 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java
@@ -49,6 +49,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
 
         intAttrName = intAttrNameParser.parse("name", AnyTypeKind.GROUP);
         assertNotNull(intAttrName);
@@ -60,6 +61,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
 
         intAttrName = intAttrNameParser.parse("userOwner", AnyTypeKind.GROUP);
         assertNotNull(intAttrName);
@@ -71,6 +73,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
 
         intAttrName = intAttrNameParser.parse("name", AnyTypeKind.USER);
         assertNotNull(intAttrName);
@@ -89,6 +92,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
 
         intAttrName = intAttrNameParser.parse("cn", AnyTypeKind.ANY_OBJECT);
         assertNotNull(intAttrName);
@@ -99,6 +103,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
 
         intAttrName = intAttrNameParser.parse("rvirtualdata", AnyTypeKind.ANY_OBJECT);
         assertNotNull(intAttrName);
@@ -109,6 +114,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
     }
 
     @Test
@@ -122,6 +128,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertEquals("readers", intAttrName.getEnclosingGroup());
         assertNull(intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
     }
 
     @Test
@@ -135,6 +142,7 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertEquals("hp", intAttrName.getRelatedAnyObject());
         assertNull(intAttrName.getMembershipOfGroup());
+        assertNull(intAttrName.getPrivilegesOfApplication());
     }
 
     @Test
@@ -148,6 +156,20 @@ public class IntAttrNameParserTest extends AbstractTest {
         assertNull(intAttrName.getEnclosingGroup());
         assertEquals("top", intAttrName.getMembershipOfGroup());
         assertNull(intAttrName.getRelatedAnyObject());
+        assertNull(intAttrName.getPrivilegesOfApplication());
+    }
+
+    @Test
+    public void privileges() throws ParseException {
+        IntAttrName intAttrName = intAttrNameParser.parse("privileges[mightyApp]", AnyTypeKind.USER);
+        assertNotNull(intAttrName);
+        assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind());
+        assertNull(intAttrName.getField());
+        assertNull(intAttrName.getSchemaName());
+        assertNull(intAttrName.getSchemaType());
+        assertNull(intAttrName.getEnclosingGroup());
+        assertNull(intAttrName.getRelatedAnyObject());
+        assertEquals("mightyApp", intAttrName.getPrivilegesOfApplication());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
index f74b224..834eb10 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
@@ -29,6 +29,7 @@ import java.util.Optional;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.common.lib.to.TaskTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.BulkAction;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.PagedResult;
@@ -36,9 +37,11 @@ import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.ItemTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 import org.apache.syncope.common.rest.api.beans.ExecQuery;
@@ -144,6 +147,41 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
     }
 
     @Test
+    public void privileges() {
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        ldap.setKey("ldapWithPrivileges");
+
+        ItemTO item = new ItemTO();
+        item.setIntAttrName("privileges[mightyApp]");
+        item.setExtAttrName("businessCategory");
+        item.setPurpose(MappingPurpose.PROPAGATION);
+
+        ProvisionTO provision = ldap.getProvision(AnyTypeKind.USER.name()).get();
+        provision.getVirSchemas().clear();
+        provision.getMapping().add(item);
+
+        ldap = createResource(ldap);
+
+        try {
+            UserTO user = UserITCase.getUniqueSampleTO("privilege@syncope.apache.org");
+            user.getResources().add(ldap.getKey());
+            user.getRoles().add("Other");
+
+            ProvisioningResult<UserTO> result = createUser(user);
+            assertEquals(1, result.getPropagationStatuses().size());
+            assertNotNull(result.getPropagationStatuses().get(0).getAfterObj());
+
+            AttrTO businessCategory =
+                    result.getPropagationStatuses().get(0).getAfterObj().getAttr("businessCategory").orElse(null);
+            assertNotNull(businessCategory);
+            assertEquals(1, businessCategory.getValues().size());
+            assertEquals("postMighty", businessCategory.getValues().get(0));
+        } finally {
+            resourceService.delete(ldap.getKey());
+        }
+    }
+
+    @Test
     public void issueSYNCOPE741() {
         for (int i = 0; i < 3; i++) {
             taskService.execute(new ExecuteQuery.Builder().

http://git-wip-us.apache.org/repos/asf/syncope/blob/fb3a30e6/src/main/asciidoc/reference-guide/concepts/externalresources.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/externalresources.adoc b/src/main/asciidoc/reference-guide/concepts/externalresources.adoc
index 32b71d2..3c54e42 100644
--- a/src/main/asciidoc/reference-guide/concepts/externalresources.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/externalresources.adoc
@@ -110,6 +110,8 @@ specified by an expression matching one of the following models:
 name `anyObjectName`, if a relationship with the mapped entity exists
 ** `memberships[groupName].schema` - resolves to the attribute for the given `schema`, owned by the membership for group
 `groupName` of the mapped entity (user, any object), if such a membership exists
+** `privileges[applicationKey]` - resolves to the list of privileges, related to the given <<application,applicaton>>,
+owned by the mapped entity (which can only be user, in this case)
 * external attribute - the name of the attribute on the Identity Store
 * transformers - http://commons.apache.org/proper/commons-jexl/[JEXL^] expression or Java class implementing
 ifeval::["{snapshotOrRelease}" == "release"]


[2/2] syncope git commit: Adjusting internal attribute's popover appearance

Posted by il...@apache.org.
Adjusting internal attribute's popover appearance


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

Branch: refs/heads/2_0_X
Commit: 93ef03e692fd745320fd86c149aa01ef56ee5950
Parents: fa07953
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Mar 20 14:44:07 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Mar 20 14:44:07 2018 +0100

----------------------------------------------------------------------
 .../console/notifications/NotificationWizardBuilder.java  |  8 +++-----
 .../client/console/wizards/AbstractMappingPanel.java      | 10 ++++------
 2 files changed, 7 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/93ef03e6/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
index aa34be9..3ccfab0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
@@ -357,11 +357,9 @@ public class NotificationWizardBuilder extends AjaxWizardBuilder<NotificationWra
             recipientAttrName.setChoices(getSchemas());
             recipientAttrName.addRequiredLabel();
             recipientAttrName.setTitle(getString("intAttrNameInfo.help")
-                    + "<div style=\"font-size: 10px;\">"
-                    + "<code>groups[groupName].attribute</code>\n"
-                    + "<code>anyObjects[anyObjectName].attribute</code>\n"
-                    + "<code>memberships[groupName].attribute</code>\n"
-                    + "</div>", true);
+                    + "<code>groups[groupName].attribute</code>, "
+                    + "<code>anyObjects[anyObjectName].attribute</code> or "
+                    + "<code>memberships[groupName].attribute</code>", true);
             add(recipientAttrName);
 
             AjaxTextFieldPanel staticRecipientsFieldPanel =

http://git-wip-us.apache.org/repos/asf/syncope/blob/93ef03e6/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
index 5532f86..42475c0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AbstractMappingPanel.java
@@ -152,12 +152,10 @@ public abstract class AbstractMappingPanel extends Panel {
         mappingContainer.add(new Label("intAttrNameInfo", Model.of()).add(new PopoverBehavior(
                 Model.<String>of(),
                 Model.of(getString("intAttrNameInfo.help")
-                        + "<div style=\"font-size: 10px;\">"
-                        + "<code>groups[groupName].attribute</code>\n"
-                        + "<code>anyObjects[anyObjectName].attribute</code>\n"
-                        + "<code>memberships[groupName].attribute</code>\n"
-                        + "</div>"),
-                new PopoverConfig().withHtml(true).withPlacement(TooltipConfig.Placement.bottom)) {
+                        + "<code>groups[groupName].attribute</code>, "
+                        + "<code>anyObjects[anyObjectName].attribute</code> or "
+                        + "<code>memberships[groupName].attribute</code>"),
+                new PopoverConfig().withHtml(true).withPlacement(TooltipConfig.Placement.right)) {
 
             private static final long serialVersionUID = -7867802555691605021L;