Java tutorial
/* * ***** BEGIN LICENSE BLOCK ***** * Zimbra Collaboration Suite Server * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software Foundation, * version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with this program. * If not, see <https://www.gnu.org/licenses/>. * ***** END LICENSE BLOCK ***** */ package com.zimbra.qa.unittest.prov.ldap; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.junit.*; import static org.junit.Assert.*; import com.zimbra.common.account.Key; import com.zimbra.common.account.ProvisioningConstants; import com.zimbra.common.httpclient.HttpClientUtil; import com.zimbra.common.service.ServiceException; import com.zimbra.cs.account.Account; import com.zimbra.cs.account.AccountServiceException; import com.zimbra.cs.account.AttributeManager.IDNType; import com.zimbra.cs.account.ldap.LdapProv; import com.zimbra.cs.account.CalendarResource; import com.zimbra.cs.account.Config; import com.zimbra.cs.account.DistributionList; import com.zimbra.cs.account.IDNUtil; import com.zimbra.cs.account.Domain; import com.zimbra.cs.account.NamedEntry; import com.zimbra.cs.account.Provisioning; import com.zimbra.cs.service.UserServlet; import com.zimbra.qa.unittest.prov.Names; /* * * SOAP: UTF-8 (multi bytes) * LDAP: UTF-8 (multi bytes) * Java: UTF-16 (2 bytes Unicode) * The primitive data type char in the Java programming language is an unsigned 16-bit * integer that can represent a Unicode code point in the range U+0000 to U+FFFF, * or the code units of UTF-16. */ public class TestProvIDN extends LdapTest { private static String UNICODESTR = "\u4e2d\u6587"; // private static String UNICODESTR = "\u5f35\u611b\u73b2"; private static String PASSWORD = "test123"; private static LdapProvTestUtil provUtil; private static Provisioning prov; private static String BASE_DOMAIN_NAME; @BeforeClass public static void init() throws Exception { provUtil = new LdapProvTestUtil(); prov = provUtil.getProv(); BASE_DOMAIN_NAME = baseDomainName(); } @AfterClass public static void cleanup() throws Exception { Cleanup.deleteAll(BASE_DOMAIN_NAME); } private String makeTestDomainName(String prefix) { return prefix + UNICODESTR + "." + BASE_DOMAIN_NAME; } private Domain createDomain(String domainName, String description) throws Exception { Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put(Provisioning.A_description, "=====" + description + "====="); Domain domain = prov.createDomain(domainName, attrs); assertNotNull(domain); return domain; } private Account createAccount(String email, String description) throws Exception { Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put(Provisioning.A_description, "=====" + description + "====="); Account acct = prov.createAccount(email, PASSWORD, attrs); assertNotNull(acct); return acct; } private CalendarResource createCalendarResource(String email, String description) throws Exception { Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put(Provisioning.A_description, "=====" + description + "====="); attrs.put(Provisioning.A_displayName, email); attrs.put(Provisioning.A_zimbraCalResType, "Equipment"); CalendarResource cr = prov.createCalendarResource(email, PASSWORD, attrs); assertNotNull(cr); return cr; } private DistributionList createDistributionList(String email, String description) throws Exception { Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put(Provisioning.A_description, "=====" + description + "====="); DistributionList dl = prov.createDistributionList(email, attrs); assertNotNull(dl); return dl; } static enum EntryType { DOMAIN, ACCOUNT, CR, DL } static enum NameType { UNAME, ANAME, } private NamedEntry createTest(EntryType entryType, NameType by, Names.IDNName name) throws Exception { NamedEntry created = null; NamedEntry notCreated = null; String firstCreateBy = null; String thenCreateBy = null; if (by == NameType.UNAME) { firstCreateBy = name.uName(); thenCreateBy = name.aName(); } else { firstCreateBy = name.aName(); thenCreateBy = name.uName(); } String desc = name.uName(); switch (entryType) { case DOMAIN: created = createDomain(firstCreateBy, desc); break; case ACCOUNT: created = createAccount(firstCreateBy, desc); break; case CR: created = createCalendarResource(firstCreateBy, desc); break; case DL: created = createDistributionList(firstCreateBy, desc); break; } assertNotNull(created); try { // entry should have existed switch (entryType) { case DOMAIN: notCreated = createDomain(thenCreateBy, desc); break; case ACCOUNT: notCreated = createAccount(thenCreateBy, desc); break; case CR: notCreated = createCalendarResource(thenCreateBy, desc); break; case DL: notCreated = createDistributionList(thenCreateBy, desc); break; } fail(); } catch (ServiceException e) { String expectedExceptionCode = null; switch (entryType) { case DOMAIN: expectedExceptionCode = AccountServiceException.DOMAIN_EXISTS; break; case ACCOUNT: expectedExceptionCode = AccountServiceException.ACCOUNT_EXISTS; break; case CR: expectedExceptionCode = AccountServiceException.ACCOUNT_EXISTS; break; case DL: expectedExceptionCode = AccountServiceException.DISTRIBUTION_LIST_EXISTS; break; } if (!e.getCode().equals(expectedExceptionCode)) fail(); } return created; } private void getTest(EntryType entryType, NamedEntry expectedEntry, Names.IDNName name) throws Exception { NamedEntry gotByUName = null; NamedEntry gotByAName = null; switch (entryType) { case DOMAIN: gotByUName = prov.get(Key.DomainBy.name, name.uName()); gotByAName = prov.get(Key.DomainBy.name, name.aName()); break; case ACCOUNT: gotByUName = prov.get(Key.AccountBy.name, name.uName()); gotByAName = prov.get(Key.AccountBy.name, name.aName()); break; case CR: gotByUName = prov.get(Key.CalendarResourceBy.name, name.uName()); gotByAName = prov.get(Key.CalendarResourceBy.name, name.aName()); break; case DL: gotByUName = prov.get(Key.DistributionListBy.name, name.uName()); gotByAName = prov.get(Key.DistributionListBy.name, name.aName()); break; } if (expectedEntry == null) { assertNull(gotByUName); assertNull(gotByAName); } else { assertNotNull(gotByUName); assertNotNull(gotByAName); assertEquals(expectedEntry.getId(), gotByUName.getId()); assertEquals(expectedEntry.getId(), gotByAName.getId()); } } private void renameTest(EntryType entryType, NamedEntry entry, Names.IDNName name) throws Exception { switch (entryType) { case DOMAIN: assertTrue(prov instanceof LdapProv); ((LdapProv) prov).renameDomain(entry.getId(), name.uName()); break; case ACCOUNT: prov.renameAccount(entry.getId(), name.uName()); break; case CR: prov.renameCalendarResource(entry.getId(), name.uName()); break; case DL: prov.renameDistributionList(entry.getId(), name.uName()); break; } } private void setAddressAttrsTest(EntryType entryType, NamedEntry entry, Names.IDNName name) throws Exception { Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put(Provisioning.A_zimbraMailCanonicalAddress, "canonical-" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailForwardingAddress, "extra-1-forwarding-" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailForwardingAddress, "extra-2-forwarding-" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailDeliveryAddress, "extra-1-delivery-" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailDeliveryAddress, "extra-2-delivery-" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailCatchAllAddress, "@" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailCatchAllCanonicalAddress, "@" + name.uName()); attrs.put("+" + Provisioning.A_zimbraMailCatchAllForwardingAddress, "@" + name.uName()); prov.modifyAttrs(entry, attrs); } @Test public void testDomain() throws Exception { Names.IDNName d1Name = new Names.IDNName(makeTestDomainName("domain-1.")); Names.IDNName d2Name = new Names.IDNName(makeTestDomainName("domain-2.")); Names.IDNName d2RenamedName = new Names.IDNName(makeTestDomainName("domain-2-renamed.")); // create domain with unicode name Domain domain1 = (Domain) createTest(EntryType.DOMAIN, NameType.UNAME, d1Name); Domain domain2 = (Domain) createTest(EntryType.DOMAIN, NameType.ANAME, d2Name); // get by name test getTest(EntryType.DOMAIN, domain1, d1Name); getTest(EntryType.DOMAIN, domain2, d2Name); // rename test renameTest(EntryType.DOMAIN, domain2, d2RenamedName); /* skip this test for now, because domain id cache still contains the id of the new domain when it was created, not the id of the old domain, which was written to the new domain. This is OK, because Provisioning.renameDomain is only available through -l, and the program (zmprov) exits after Provisioning.renameDomain is called. */ // getTest(EntryType.DOMAIN, domain2, d2RenamedName); } private void doTestInvalidNames(String domainName, boolean expectedValid) throws Exception { boolean actualValid; try { Domain domain = prov.createDomain(domainName, new HashMap<String, Object>()); actualValid = (domain != null); } catch (ServiceException e) { actualValid = false; } assertEquals(expectedValid, actualValid); } @Test public void testDomainInvalidNames() throws Exception { Config config = prov.getConfig(); // save he current value of zimbraAllowNonLDHCharsInDomain String curAllowNonLDH = config.getAttr(Provisioning.A_zimbraAllowNonLDHCharsInDomain); // test values String goodEnglish = "good" + "." + BASE_DOMAIN_NAME; String goodIDN = makeTestDomainName("good"); String LDHEnglish_comma = "ldh'ldh" + "." + BASE_DOMAIN_NAME; String LDHIDN_comma = makeTestDomainName("ldh'ldh"); // when zimbraAllowNonLDHCharsInDomain is TRUE Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put(Provisioning.A_zimbraAllowNonLDHCharsInDomain, ProvisioningConstants.TRUE); prov.modifyAttrs(config, attrs); String prefix = "allowtest."; // so that we don't run into domain exist problem doTestInvalidNames(prefix + goodEnglish, true); doTestInvalidNames(prefix + goodIDN, true); doTestInvalidNames(prefix + LDHEnglish_comma, true); // test failed. javamail does not allow it anymore, fix? bug 41123 doTestInvalidNames(prefix + LDHIDN_comma, true); // test failed. javamail does not allow it anymore. fix? bug 41123 // when zimbraAllowNonLDHCharsInDomain is FALSE attrs.clear(); attrs.put(Provisioning.A_zimbraAllowNonLDHCharsInDomain, ProvisioningConstants.FALSE); prov.modifyAttrs(config, attrs); prefix = "notallowtest."; doTestInvalidNames(prefix + goodEnglish, true); doTestInvalidNames(prefix + goodIDN, true); doTestInvalidNames(prefix + LDHEnglish_comma, false); doTestInvalidNames(prefix + LDHIDN_comma, false); // restore the orig config value back attrs.clear(); attrs.put(Provisioning.A_zimbraAllowNonLDHCharsInDomain, curAllowNonLDH); prov.modifyAttrs(config, attrs); } @Test public void testAccount() throws Exception { Names.IDNName domainName = new Names.IDNName(makeTestDomainName("domain-acct-test.")); Domain domain = createDomain(domainName.uName(), domainName.uName()); Names.IDNName acct1Name = new Names.IDNName("acct-1", domainName.uName()); Names.IDNName acct2Name = new Names.IDNName("acct-2", domainName.uName()); Names.IDNName acct2RenamedName = new Names.IDNName("acct-2-renamed", domainName.uName()); Names.IDNName alias1Name = new Names.IDNName("alias-1-of-acct-1", domainName.uName()); Names.IDNName alias2Name = new Names.IDNName("alias-2-of-acct-1", domainName.uName()); Names.IDNName cr1Name = new Names.IDNName("cr-1", domainName.uName()); Names.IDNName cr2Name = new Names.IDNName("cr-2", domainName.uName()); Names.IDNName cr2RenamedName = new Names.IDNName("cr-2-renamed", domainName.uName()); /* * account */ // create test Account acct1 = (Account) createTest(EntryType.ACCOUNT, NameType.UNAME, acct1Name); Account acct2 = (Account) createTest(EntryType.ACCOUNT, NameType.ANAME, acct2Name); // get by name test getTest(EntryType.ACCOUNT, acct1, acct1Name); getTest(EntryType.ACCOUNT, acct2, acct2Name); // set address attrs tets setAddressAttrsTest(EntryType.ACCOUNT, acct1, acct1Name); // add aliases prov.addAlias(acct1, alias1Name.uName()); // add alias by uname prov.addAlias(acct1, alias2Name.aName()); // add alias by aname // get by alias name test getTest(EntryType.ACCOUNT, acct1, alias1Name); getTest(EntryType.ACCOUNT, acct1, alias2Name); // remove aliases prov.removeAlias(acct1, alias1Name.aName()); prov.removeAlias(acct1, alias2Name.uName()); getTest(EntryType.ACCOUNT, null, alias1Name); getTest(EntryType.ACCOUNT, null, alias2Name); // add aliases back so we can view them after the test prov.addAlias(acct1, alias1Name.uName()); // add alias by uname prov.addAlias(acct1, alias2Name.aName()); // add alias by aname // rename test renameTest(EntryType.ACCOUNT, acct2, acct2RenamedName); getTest(EntryType.ACCOUNT, acct2, acct2RenamedName); /* * cr */ // create test CalendarResource cr1 = (CalendarResource) createTest(EntryType.CR, NameType.UNAME, cr1Name); CalendarResource cr2 = (CalendarResource) createTest(EntryType.CR, NameType.ANAME, cr2Name); // get by name test getTest(EntryType.CR, cr1, cr1Name); getTest(EntryType.CR, cr2, cr2Name); // rename test renameTest(EntryType.CR, cr2, cr2RenamedName); getTest(EntryType.CR, cr2, cr2RenamedName); } @Test public void testDistributionList() throws Exception { Names.IDNName domainName = new Names.IDNName(makeTestDomainName("domain-dl-test.")); Domain domain = createDomain(domainName.uName(), domainName.uName()); Names.IDNName acct1Name = new Names.IDNName("acct-1", domainName.uName()); Names.IDNName acct2Name = new Names.IDNName("acct-2", domainName.uName()); Names.IDNName dl1Name = new Names.IDNName("dl-1", domainName.uName()); Names.IDNName dl2Name = new Names.IDNName("dl-2", domainName.uName()); Names.IDNName dl2RenamedName = new Names.IDNName("dl-2-renamed", domainName.uName()); Names.IDNName nestedDl1Name = new Names.IDNName("nested-dl-1", domainName.uName()); Names.IDNName nestedDl2Name = new Names.IDNName("nested-dl-2", domainName.uName()); /* * dl */ // create test DistributionList dl1 = (DistributionList) createTest(EntryType.DL, NameType.UNAME, dl1Name); DistributionList dl2 = (DistributionList) createTest(EntryType.DL, NameType.ANAME, dl2Name); // get by name test getTest(EntryType.DL, dl1, dl1Name); getTest(EntryType.DL, dl2, dl2Name); // rename test renameTest(EntryType.DL, dl2, dl2RenamedName); getTest(EntryType.DL, dl2, dl2RenamedName); /* * member test */ Account a1 = createAccount(acct1Name.uName(), acct1Name.uName()); Account a2 = createAccount(acct2Name.uName(), acct2Name.uName()); DistributionList nestedDl1 = createDistributionList(nestedDl1Name.aName(), nestedDl1Name.uName()); DistributionList nestedDl2 = createDistributionList(nestedDl2Name.aName(), nestedDl2Name.uName()); prov.addMembers(dl1, new String[] { acct1Name.uName(), acct2Name.aName(), nestedDl1Name.uName(), nestedDl2Name.aName() }); boolean inList; inList = prov.inDistributionList(a1, dl1.getId()); assertTrue(inList); inList = prov.inDistributionList(a2, dl1.getId()); assertTrue(inList); HashMap<String, String> via = new HashMap<String, String>(); String[] members = dl1.getAllMembers(); List<String> memberIds = Arrays.asList(members); assertTrue(memberIds.contains(acct1Name.aName().toLowerCase())); assertTrue(memberIds.contains(acct2Name.aName().toLowerCase())); assertTrue(memberIds.contains(nestedDl1Name.aName().toLowerCase())); assertTrue(memberIds.contains(nestedDl2Name.aName().toLowerCase())); prov.removeMembers(dl1, new String[] { acct1Name.uName(), acct2Name.aName(), nestedDl1Name.uName(), nestedDl2Name.aName() }); members = dl1.getAllMembers(); assertEquals(0, members.length); } @Test public void testBasicAuth() throws Exception { Names.IDNName domainName = new Names.IDNName(makeTestDomainName("basicAuthTest.")); Domain domain = createDomain(domainName.uName(), domainName.uName()); Names.IDNName acctName = new Names.IDNName("acct", domainName.uName()); Account acct = (Account) createTest(EntryType.ACCOUNT, NameType.UNAME, acctName); HttpState initialState = new HttpState(); /* Cookie authCookie = new Cookie(restURL.getURL().getHost(), "ZM_AUTH_TOKEN", mAuthToken, "/", null, false); Cookie sessionCookie = new Cookie(restURL.getURL().getHost(), "JSESSIONID", mSessionId, "/zimbra", null, false); initialState.addCookie(authCookie); initialState.addCookie(sessionCookie); */ String guestName = acct.getUnicodeName(); String guestPassword = "test123"; Credentials loginCredentials = new UsernamePasswordCredentials(guestName, guestPassword); initialState.setCredentials(AuthScope.ANY, loginCredentials); HttpClient client = new HttpClient(); client.setState(initialState); String url = UserServlet.getRestUrl(acct) + "/Calendar"; System.out.println("REST URL: " + url); HttpMethod method = new GetMethod(url); HttpMethodParams methodParams = method.getParams(); methodParams.setCredentialCharset("UTF-8"); try { int respCode = HttpClientUtil.executeMethod(client, method); if (respCode != HttpStatus.SC_OK) { System.out.println("failed, respCode=" + respCode); } else { boolean chunked = false; boolean textContent = false; /* System.out.println("Headers:"); System.out.println("--------"); for (Header header : method.getRequestHeaders()) { System.out.print(" " + header.toString()); } System.out.println(); System.out.println("Body:"); System.out.println("-----"); String respBody = method.getResponseBodyAsString(); System.out.println(respBody); */ } } finally { // Release the connection. method.releaseConnection(); } } private void IDNUtilTest(String u1, String expectedAscii) { boolean verbose = true; String a1 = IDNUtil.toAsciiDomainName(u1); assertTrue(expectedAscii.equals(a1)); printOutput(verbose, "u1: " + u1); printOutput(verbose, "a1: " + a1); String u2 = IDNUtil.toUnicodeDomainName(u1); String a2 = IDNUtil.toAsciiDomainName(u2); printOutput(verbose, "u2: " + u2); printOutput(verbose, "a2: " + a2); assertTrue(a1.equals(a2) && u1.equals(u2)); // now try the java.net.IDN, should get same result // JDK1.6 only /* int flags = 0; String a1_java = IDN.toASCII(u1, flags); printOutput(verbose, "u1: " + u1); printOutput(verbose, "a1_java: " + a1_java); String u2_java = IDN.toUnicode(u1, flags); String a2_java = IDN.toASCII(u2_java, flags); printOutput(verbose, "u2_java: " + u2_java); printOutput(verbose, "a2_java: " + a2_java); assertTrue(a1_java.equals(a2_java) && u1.equals(u2_java)); // also., make sure the two libs produce same result assertTrue(a1.equals(a1_java) && u2.equals(u2_java)); */ printOutput(verbose, ""); } /* * run this test with both JRE1.5 and 1.6, should get same result * */ @Test public void testIDNUtil() throws Exception { IDNUtilTest("foobar.com", "foobar.com"); IDNUtilTest("\u4e2d\u6587.xyz\u4e2d\u6587abc.com", "xn--fiq228c.xn--xyzabc-dw7i870n.com"); // domain labels containing LDH characters IDNUtilTest("foo'bar.com", "foo'bar.com"); IDNUtilTest("\u4e2d'\u6587.xyz\u4e2d\u6587abc.com", "xn--'-kq6a506e.xn--xyzabc-dw7i870n.com"); } private void emailpTest(String unicode, IDNType idnType) { boolean verbose = false; printOutput(verbose, "\nTesting email with personal part, idn type = " + idnType + "\n"); String emailp_u1 = unicode; String emailp_a1 = IDNUtil.toAscii(emailp_u1, idnType); printOutput(verbose, "emailp_u1: " + emailp_u1); printOutput(verbose, "emailp_a1: " + emailp_a1); String emailp_u2 = IDNUtil.toUnicode(emailp_a1, idnType); String emailp_a2 = IDNUtil.toAscii(emailp_u2, idnType); printOutput(verbose, "emailp_u2: " + emailp_u2); printOutput(verbose, "emailp_a2: " + emailp_a2); assertEquals(emailp_u1, emailp_u2); assertEquals(emailp_a1, emailp_a2); } @Test public void testEmailp() throws Exception { // with personal name emailpTest("\"foo bar\" <test@\u4e2d\u6587.xyz\u4e2d\u6587abc.com>", IDNType.emailp); emailpTest("\"\u4e2d\u6587\" <test@\u4e2d\u6587.xyz\u4e2d\u6587abc.com>", IDNType.emailp); emailpTest("\"foo bar\" <test@\u4e2d\u6587.xyz\u4e2d\u6587abc.com>", IDNType.cs_emailp); emailpTest( "\"foo bar\" <test@\u4e2d\u6587.xyz\u4e2d\u6587abc.com>, \"cat dog\" <test@xyz\u4e2d\u6587abc.com>", IDNType.cs_emailp); } private void printOutput(boolean verbose, String text) { if (!verbose) return; PrintStream ps = System.out; try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(ps, "UTF-8")); writer.write(text + "\n"); writer.flush(); } catch (UnsupportedEncodingException e) { ps.println(text); } catch (IOException e) { ps.println(text); } } }