/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.scim.jdbc;
import org.cloudfoundry.identity.uaa.resources.SimpleAttributeNameMapper;
import org.cloudfoundry.identity.uaa.resources.jdbc.SearchQueryConverter.ProcessedFilter;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class ScimSearchQueryConverterTests {
private ScimSearchQueryConverter filterProcessor;
@Before
public void setUp() {
Map<String, String> replaceWith = new HashMap<>();
replaceWith.put("emails\\.value", "email");
replaceWith.put("groups\\.display", "authorities");
replaceWith.put("phoneNumbers\\.value", "phoneNumber");
filterProcessor = new ScimSearchQueryConverter();
filterProcessor.setAttributeNameMapper(new SimpleAttributeNameMapper(replaceWith));
}
@Test
public void canConvertValidFilters() throws Exception {
validate(filterProcessor.convert("username pr", null, false), "username IS NOT NULL", 0);
validate(filterProcessor.convert("username eq \"joe\"", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username eq \"'bar\"", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("displayName eq \"openid\"", null, false), "LOWER(displayName) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("USERNAME eq \"joe\"", null, false), "LOWER(USERNAME) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username EQ \"joe\"", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username eq \"Joe\"", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username eq \"Joe\"", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("displayName co \"write\"", null, false), "LOWER(displayName) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("displayName sw \"scim.\"", null, false), "LOWER(displayName) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username gt \"joe\"", null, false), "LOWER(username) > LOWER(:__value_0)", 1);
validate(filterProcessor.convert("userName eq \"joe\" and meta.version eq 0", null, false),"(LOWER(userName) = LOWER(:__value_0) AND version = :__value_1)", 2);
validate(filterProcessor.convert("meta.created gt \"1970-01-01T00:00:00.000Z\"", null, false),"created > :__value_0", 1);
validate(filterProcessor.convert("username pr and active eq true", null, false),"(username IS NOT NULL AND active = :__value_0)", 1);
validate(filterProcessor.convert("username pr", "username", true),"username IS NOT NULL ORDER BY username ASC", 0);
validate(filterProcessor.convert("displayName pr", "displayName", false),"displayName IS NOT NULL ORDER BY displayName DESC", 0);
validate(filterProcessor.convert("username pr and emails.value co \".com\"", null, false),"(username IS NOT NULL AND LOWER(email) LIKE LOWER(:__value_0))", 1);
validate(filterProcessor.convert("username eq \"joe\" or emails.value co \".com\"", null, false),"(LOWER(username) = LOWER(:__value_0) OR LOWER(email) LIKE LOWER(:__value_1))", 2);
validate(filterProcessor.convert("active eq true", null, false),"active = :__value_0", 1, Boolean.class);
validate(filterProcessor.convert("Version eq 1000000.45", null, false),"Version = :__value_0", 1, Double.class);
validate(filterProcessor.convert("meta.VerSion eq 1000000", null, false),"VerSion = :__value_0", 1, Double.class);
}
@Test
public void caseInsensitiveDbDoesNotInjectLower() throws Exception {
filterProcessor.setDbCaseInsensitive(true);
validate(filterProcessor.convert("username pr", null, false), "username IS NOT NULL", 0);
validate(filterProcessor.convert("username eq \"joe\"", null, false), "username = :__value_0", 1);
validate(filterProcessor.convert("username eq \"'bar\"", null, false), "username = :__value_0", 1);
validate(filterProcessor.convert("displayName eq \"openid\"", null, false), "displayName = :__value_0", 1);
validate(filterProcessor.convert("USERNAME eq \"joe\"", null, false), "USERNAME = :__value_0", 1);
validate(filterProcessor.convert("username EQ \"joe\"", null, false), "username = :__value_0", 1);
validate(filterProcessor.convert("username eq \"Joe\"", null, false), "username = :__value_0", 1);
validate(filterProcessor.convert("username eq \"Joe\"", null, false), "username = :__value_0", 1);
validate(filterProcessor.convert("displayName co \"write\"", null, false), "displayName LIKE :__value_0", 1);
validate(filterProcessor.convert("displayName sw \"scim.\"", null, false), "displayName LIKE :__value_0", 1);
validate(filterProcessor.convert("username gt \"joe\"", null, false), "username > :__value_0", 1);
validate(filterProcessor.convert("userName eq \"joe\" and meta.version eq 0", null, false),"(userName = :__value_0 AND version = :__value_1)", 2);
validate(filterProcessor.convert("meta.created gt \"1970-01-01T00:00:00.000Z\"", null, false),"created > :__value_0", 1);
validate(filterProcessor.convert("username pr and active eq true", null, false),"(username IS NOT NULL AND active = :__value_0)", 1);
validate(filterProcessor.convert("username pr", "username", true),"username IS NOT NULL ORDER BY username ASC", 0);
validate(filterProcessor.convert("displayName pr", "displayName", false),"displayName IS NOT NULL ORDER BY displayName DESC", 0);
validate(filterProcessor.convert("username pr and emails.value co \".com\"", null, false),"(username IS NOT NULL AND email LIKE :__value_0)", 1);
validate(filterProcessor.convert("username eq \"joe\" or emails.value co \".com\"", null, false),"(username = :__value_0 OR email LIKE :__value_1)", 2);
validate(filterProcessor.convert("active eq true", null, false),"active = :__value_0", 1, Boolean.class);
validate(filterProcessor.convert("Version eq 1000000.45", null, false),"Version = :__value_0", 1, Double.class);
validate(filterProcessor.convert("meta.VerSion eq 1000000", null, false),"VerSion = :__value_0", 1, Double.class);
}
@Test
public void canConvertWithReplacePatterns() {
validate(filterProcessor.convert("emails.value sw \"joe\"", null, false), "LOWER(email) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("groups.display co \"org.foo\"", null, false),"LOWER(authorities) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("phoneNumbers.value sw \"+1-222\"", null, false),"LOWER(phoneNumber) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username pr", "emails.value", true),"username IS NOT NULL ORDER BY email ASC", 0);
}
@Test
public void testFilterWithApostrophe() throws Exception {
validate(filterProcessor.convert("username eq \"marissa'@test.org\"", null, false),
"LOWER(username) = LOWER(:__value_0)", 1);
}
@Test
public void canConvertLegacyValidFilters() throws Exception {
validate(filterProcessor.convert("username pr", null, false), "username IS NOT NULL", 0);
validate(filterProcessor.convert("username eq 'joe'", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("displayName eq \"openid\"", null, false), "LOWER(displayName) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("USERNAME eq 'joe'", null, false), "LOWER(USERNAME) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username EQ 'joe'", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username eq 'Joe'", null, false), "LOWER(username) = LOWER(:__value_0)", 1);
validate(filterProcessor.convert("displayName co 'write'", null, false), "LOWER(displayName) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("displayName sw 'scim.'", null, false), "LOWER(displayName) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username gt 'joe'", null, false), "LOWER(username) > LOWER(:__value_0)", 1);
validate(filterProcessor.convert("userName eq 'joe' and meta.version eq 0", null, false), "(LOWER(userName) = LOWER(:__value_0) AND version = :__value_1)", 2);
validate(filterProcessor.convert("meta.created gt '1970-01-01T00:00:00.000Z'", null, false), "created > :__value_0", 1);
validate(filterProcessor.convert("username pr and active eq true", null, false), "(username IS NOT NULL AND active = :__value_0)", 1);
validate(filterProcessor.convert("username pr", "username", true), "username IS NOT NULL ORDER BY username ASC", 0);
validate(filterProcessor.convert("displayName pr", "displayName", false), "displayName IS NOT NULL ORDER BY displayName DESC", 0);
validate(filterProcessor.convert("username pr and emails.value co '.com'", null, false), "(username IS NOT NULL AND LOWER(email) LIKE LOWER(:__value_0))", 1);
validate(filterProcessor.convert("username eq 'joe' or emails.value co '.com'", null, false), "(LOWER(username) = LOWER(:__value_0) OR LOWER(email) LIKE LOWER(:__value_1))", 2);
}
@Test(expected = IllegalArgumentException.class)
public void testIllegalUnquotedValueInFilter() throws Exception {
filterProcessor.convert("username eq joe", null, false);
}
@Test
public void canConvertLegacyWithReplacePatterns() {
Map<String, String> replaceWith = new HashMap<>();
replaceWith.put("emails\\.value", "email");
replaceWith.put("groups\\.display", "authorities");
replaceWith.put("phoneNumbers\\.value", "phoneNumber");
filterProcessor.setAttributeNameMapper(new SimpleAttributeNameMapper(replaceWith));
validate(filterProcessor.convert("emails.value sw 'joe'", null, false), "LOWER(email) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("groups.display co 'org.foo'", null, false), "LOWER(authorities) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("phoneNumbers.value sw '+1-222'", null, false), "LOWER(phoneNumber) LIKE LOWER(:__value_0)", 1);
validate(filterProcessor.convert("username pr", "emails.value", true), "username IS NOT NULL ORDER BY email ASC", 0);
}
private void validate(ProcessedFilter filter, String expectedSql, int expectedParamCount, Class... types) {
assertNotNull(filter);
expectedSql = expectedSql.replaceAll("__value_", filter.getParamPrefix());
assertEquals(expectedSql, filter.getSql());
assertEquals(expectedParamCount, filter.getParams().size());
int count = 0;
for (Class type : types) {
String param = filter.getParamPrefix()+String.valueOf(count++);
Object value = filter.getParams().get(param);
assertEquals(type, value.getClass());
}
}
}