Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/provisioning-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ under the License.
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,4 @@ public Class<?> loadClass(final String name) throws ClassNotFoundException {
protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
throw new ClassNotFoundException("This classloader won't attemp to load " + name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,17 @@ public final class JexlUtils {
private static final String[] IGNORE_FIELDS = { "password", "clearPassword", "serialVersionUID", "class" };

private static final Map<Class<?>, Set<Pair<PropertyDescriptor, Field>>> FIELD_CACHE =
Collections.<Class<?>, Set<Pair<PropertyDescriptor, Field>>>synchronizedMap(
new HashMap<Class<?>, Set<Pair<PropertyDescriptor, Field>>>());
Collections.synchronizedMap(new HashMap<Class<?>, Set<Pair<PropertyDescriptor, Field>>>());

private static JexlEngine JEXL_ENGINE;

private static JexlEngine getEngine() {
synchronized (LOG) {
if (JEXL_ENGINE == null) {
JEXL_ENGINE = new JexlBuilder().
uberspect(new ClassFreeUberspect()).
uberspect(new SandboxUberspect()).
loader(new EmptyClassLoader()).
namespaces(Collections.<String, Object>singletonMap("syncope", new SyncopeJexlFunctions())).
namespaces(Collections.singletonMap("syncope", new SyncopeJexlFunctions())).
cache(512).
silent(false).
strict(false).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* 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.provisioning.api.jexl;

import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlPropertySet;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.logging.LogFactory;
import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.to.AttrTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.Membership;
import org.apache.syncope.core.persistence.api.entity.PlainAttr;
import org.apache.syncope.core.persistence.api.entity.Realm;

class SandboxUberspect extends Uberspect {

private static final Set<String> COLLECTION_METHODS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("contains", "containsAll", "isEmpty", "size", "iterator", "toString")));

private static final Set<String> LIST_METHODS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("get", "indexOf", "lastIndexOf", "toString")));

private static final Set<String> MAP_METHODS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("get", "getOrDefault", "containsKey", "containsValue", "toString")));

SandboxUberspect() {
super(LogFactory.getLog(JexlEngine.class), JexlUberspect.JEXL_STRATEGY);
}

@Override
public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
return null;
}

@Override
public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
if (obj instanceof AnyTO || obj instanceof Any
|| obj instanceof PlainAttr || obj instanceof AttrTO
|| obj instanceof MembershipTO || obj instanceof Membership
|| obj instanceof Realm || obj instanceof RealmTO) {

return super.getMethod(obj, method, args);
} else if (obj instanceof SyncopeJexlFunctions) {
return super.getMethod(obj, method, args);
} else if (obj instanceof Optional) {
return super.getMethod(obj, method, args);
} else if (obj.getClass().isArray()) {
return super.getMethod(obj, method, args);
} else if (obj instanceof String) {
return super.getMethod(obj, method, args);
} else if (obj instanceof Date || obj instanceof Instant) {
return super.getMethod(obj, method, args);
} else if (obj instanceof Map && MAP_METHODS.contains(method)) {
return super.getMethod(obj, method, args);
} else if (obj instanceof List && (LIST_METHODS.contains(method) || COLLECTION_METHODS.contains(method))) {
return super.getMethod(obj, method, args);
} else if (obj instanceof Collection && COLLECTION_METHODS.contains(method)) {
return super.getMethod(obj, method, args);
}
return null;
}

@Override
public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) {
return null;
}

@Override
public JexlPropertySet getPropertySet(
final List<PropertyResolver> resolvers, final Object obj, final Object identifier, final Object arg) {

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.core.provisioning.java.jexl;

import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
package org.apache.syncope.core.provisioning.api.jexl;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand All @@ -37,17 +35,36 @@
import org.apache.syncope.common.lib.to.AttrTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
import org.apache.syncope.core.provisioning.java.AbstractTest;
import org.apache.syncope.core.provisioning.api.AbstractTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

@Transactional("Master")
public class MailTemplateTest extends AbstractTest {

@Autowired
private MailTemplateDAO mailTemplateDAO;
private static final String CONFIRM_PASSWORD_RESET_TEMPLATE =
"<html><body>"
+ "<p>Hi,<br/> we are happy to inform you that the password request was successfully executed for "
+ "your account.</p> <p>Best regards.</p> </body> </html>";

private static final String REQUEST_PASSWORD_RESET_TEMPLATE = "Hi, a password reset was request for "
+ "${user.getUsername()}. In order to complete this request, you need to visit this link: "
+ "http://localhost:9080/syncope-enduser/app/#!/confirmpasswordreset?token="
+ "${input.get(0).replaceAll(' ', '%20')}"
+ "If you did not request this reset, just ignore the present e-mail. Best regards.";

private static final String OPTIN_TEMPLATE =
"<html> <body> <h3>Hi ${user.getPlainAttr(\"firstname\").get().values[0]} "
+ "${user.getPlainAttr(\"surname\").get().values[0]}, welcome to Syncope!</h3>"
+ "<p> Your username is ${user.username}.<br/>"
+ "Your email address is ${user.getPlainAttr(\"email\").get().values[0]}."
+ "Your email address inside a <a href=\"http://localhost/?email="
+ "${user.getPlainAttr(\"email\").get().values[0].replace('@', '%40')}\">link</a>.</p>"
+ "<p>This message was sent to the following recipients: <ul>\n $$ for (recipient: recipients) {\n"
+ " <li>${recipient.getPlainAttr(\"email\").get().values[0]}</li>\n $$ }\n </ul>\n"
+ " because one of the following events occurred: <ul>\n $$ for (event: events) {\n"
+ " <li>${event}</li>\n $$ }\n </ul>\n </p> \n $$ if (!empty(user.memberships)) {\n"
+ " You have been provided with the following groups:\n <ul>\n"
+ " $$ for(membership : user.memberships) {\n <li>${membership.groupName}</li>\n $$ }\n"
+ " </ul>\n $$ }\n </body> </html>";

private String evaluate(final String template, final Map<String, Object> jexlVars) {
StringWriter writer = new StringWriter();
Expand All @@ -59,10 +76,7 @@ private String evaluate(final String template, final Map<String, Object> jexlVar

@Test
public void confirmPasswordReset() throws IOException {
String htmlBody = evaluate(
mailTemplateDAO.find("confirmPasswordReset").getHTMLTemplate(),
new HashMap<>());

String htmlBody = evaluate(CONFIRM_PASSWORD_RESET_TEMPLATE, new HashMap<>());
assertNotNull(htmlBody);
}

Expand All @@ -80,16 +94,14 @@ public void requestPasswordReset() throws IOException {
input.add(token);
ctx.put("input", input);

String htmlBody = evaluate(
mailTemplateDAO.find("requestPasswordReset").getHTMLTemplate(),
ctx);
String textBody = evaluate(REQUEST_PASSWORD_RESET_TEMPLATE, ctx);

assertNotNull(htmlBody);
assertTrue(htmlBody.contains("a password reset was request for " + username + "."));
assertFalse(htmlBody.contains(
assertNotNull(textBody);
assertTrue(textBody.contains("a password reset was request for " + username + "."));
assertFalse(textBody.contains(
"http://localhost:9080/syncope-enduser/app/#!/confirmpasswordreset?token="
+ token));
assertTrue(htmlBody.contains(
assertTrue(textBody.contains(
"http://localhost:9080/syncope-enduser/app/#!/confirmpasswordreset?token="
+ token.replaceAll(" ", "%20")));
}
Expand All @@ -116,15 +128,16 @@ public void optin() throws IOException {
recipient.getPlainAttr("email").get().getValues().set(0, "another@syncope.apache.org");
ctx.put("recipients", Collections.singletonList(recipient));

String htmlBody = evaluate(
mailTemplateDAO.find("optin").getHTMLTemplate(),
ctx);
ctx.put("events", Collections.singletonList("event1"));

String htmlBody = evaluate(OPTIN_TEMPLATE, ctx);

assertNotNull(htmlBody);

assertTrue(htmlBody.contains("Hi John Doe,"));
assertTrue(htmlBody.contains("Your email address is john.doe@syncope.apache.org."));
assertTrue(htmlBody.contains("<li>another@syncope.apache.org</li>"));
assertTrue(htmlBody.contains("<li>a group</li>"));
assertTrue(htmlBody.contains("<li>event1</li>"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,71 +16,46 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.core.provisioning.java.jexl;

import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
package org.apache.syncope.core.provisioning.api.jexl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.MapContext;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
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.entity.Realm;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.java.AbstractTest;
import org.apache.syncope.core.provisioning.api.AbstractTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

@Transactional("Master")
public class MappingTest extends AbstractTest {

@Autowired
private ExternalResourceDAO resourceDAO;

@Autowired
private AnyTypeDAO anyTypeDAO;

@Autowired
private RealmDAO realmDAO;

@Autowired
private UserDAO userDAO;

@Test
public void anyConnObjectLink() {
ExternalResource ldap = resourceDAO.find("resource-ldap");
assertNotNull(ldap);

Provision provision = ldap.getProvision(anyTypeDAO.findUser()).get();
assertNotNull(provision);
assertNotNull(provision.getMapping());
assertNotNull(provision.getMapping().getConnObjectLink());
Realm realm = mock(Realm.class);
when(realm.getFullPath()).thenReturn("/even");

User user = userDAO.findByUsername("rossini");
User user = mock(User.class);
when(user.getUsername()).thenReturn("rossini");
when(user.getRealm()).thenReturn(realm);
assertNotNull(user);

JexlContext jexlContext = new MapContext();
JexlUtils.addFieldsToContext(user, jexlContext);
JexlUtils.addPlainAttrsToContext(user.getPlainAttrs(), jexlContext);

assertEquals(
"uid=rossini,ou=people,o=isp",
JexlUtils.evaluate(provision.getMapping().getConnObjectLink(), jexlContext));
String connObjectLink = "'uid=' + username + ',ou=people,o=isp'";
assertEquals("uid=rossini,ou=people,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));

String connObjectLink = "'uid=' + username + realm.replaceAll('/', ',o=') + ',ou=people,o=isp'";
connObjectLink = "'uid=' + username + realm.replaceAll('/', ',o=') + ',ou=people,o=isp'";
assertEquals("uid=rossini,o=even,ou=people,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));
}

@Test
public void realmConnObjectLink() {
Realm realm = realmDAO.findByFullPath("/even/two");
Realm realm = mock(Realm.class);
when(realm.getFullPath()).thenReturn("/even/two");
assertNotNull(realm);

JexlContext jexlContext = new MapContext();
Expand All @@ -89,7 +64,7 @@ public void realmConnObjectLink() {
String connObjectLink = "syncope:fullPath2Dn(fullPath, 'ou') + ',o=isp'";
assertEquals("ou=two,ou=even,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));

realm = realmDAO.findByFullPath("/even");
when(realm.getFullPath()).thenReturn("/even");
assertNotNull(realm);

jexlContext = new MapContext();
Expand Down
Loading