diff --git a/pom.xml b/pom.xml index cc0e27ce06..96b88c4bd9 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 2.12.4 3.4.4 1.0.0 - 8.22 + 8.23 1.69 3.11.2 1.10.19 diff --git a/remote-api/src/main/java/org/alfresco/repo/web/scripts/RepositoryContainer.java b/remote-api/src/main/java/org/alfresco/repo/web/scripts/RepositoryContainer.java index bbc5f018a9..dfc51fdfcc 100644 --- a/remote-api/src/main/java/org/alfresco/repo/web/scripts/RepositoryContainer.java +++ b/remote-api/src/main/java/org/alfresco/repo/web/scripts/RepositoryContainer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2019 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -30,6 +30,7 @@ import java.net.SocketException; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Supplier; import javax.servlet.http.HttpServletResponse; @@ -355,7 +356,10 @@ public class RepositoryContainer extends AbstractRuntimeContainer return; } - if ((required == RequiredAuthentication.user || required == RequiredAuthentication.admin) && isGuest) + // if the required authentication is not equal to guest, then it should be one of the following: + // user | sysadmin | admin (the 'none' authentication is handled above) + // in this case the guest user should not be able to execute those scripts. + if (required != RequiredAuthentication.guest && isGuest) { throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access."); } @@ -383,28 +387,9 @@ public class RepositoryContainer extends AbstractRuntimeContainer { return false; } - // The user will now have been authenticated, based on HTTP Auth, Ticket etc + // The user will now have been authenticated, based on HTTP Auth, Ticket, etc. // Check that the user they authenticated as has appropriate access to the script - - // Check to see if they supplied HTTP Auth or Ticket as guest, on a script that needs more - if (required == RequiredAuthentication.user || required == RequiredAuthentication.admin) - { - final String authenticatedUser = AuthenticationUtil.getFullyAuthenticatedUser(); - final String runAsUser = AuthenticationUtil.getRunAsUser(); - - if ( (authenticatedUser == null) || - (authenticatedUser.equals(runAsUser) && authorityService.hasGuestAuthority()) || - (!authenticatedUser.equals(runAsUser) && authorityService.isGuestAuthority(authenticatedUser)) ) - { - throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access."); - } - } - - // Check to see if they're admin or system on an Admin only script - if (required == RequiredAuthentication.admin && !(authorityService.hasAdminAuthority() || AuthenticationUtil.getFullyAuthenticatedUser().equals(AuthenticationUtil.getSystemUserName()))) - { - throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires admin authentication; however, a non-admin has attempted access."); - } + checkScriptAccess(required, desc.getId()); if (debug) { @@ -424,7 +409,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer // Execute Web Script if authentication passed // The Web Script has its own txn management with potential runAs() user - transactionedExecuteAs(script, scriptReq, scriptRes); + transactionedExecuteAs(script, scriptReq, scriptRes, required); } finally { @@ -441,6 +426,65 @@ public class RepositoryContainer extends AbstractRuntimeContainer } } + private boolean isSystemUser() + { + return Objects.equals(AuthenticationUtil.getFullyAuthenticatedUser(), AuthenticationUtil.getSystemUserName()); + } + + private boolean isSysAdminUser() + { + return authorityService.hasSysAdminAuthority(); + } + + private boolean isAdmin() + { + return authorityService.hasAdminAuthority(); + } + + public final boolean isAdminOrSystemUser() + { + return isAdmin() || isSystemUser(); + } + + /** + * Check to see if they supplied HTTP Auth or Ticket as guest, on a script that needs more + */ + private void checkGuestAccess(RequiredAuthentication required, String scriptDescriptorId) + { + if (required == RequiredAuthentication.user || required == RequiredAuthentication.admin + || required == RequiredAuthentication.sysadmin) + { + final String authenticatedUser = AuthenticationUtil.getFullyAuthenticatedUser(); + final String runAsUser = AuthenticationUtil.getRunAsUser(); + + if ((authenticatedUser == null) || (authenticatedUser.equals(runAsUser) + && authorityService.hasGuestAuthority()) || (!authenticatedUser.equals(runAsUser) + && authorityService.isGuestAuthority(authenticatedUser))) + { + throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + scriptDescriptorId + + " requires user authentication; however, a guest has attempted access."); + } + } + } + + private void checkScriptAccess(RequiredAuthentication required, String scriptDescriptorId) + { + // first, check guest access + checkGuestAccess(required, scriptDescriptorId); + + // Check to see if the user is sysAdmin, admin or system on a sysadmin scripts + if (required == RequiredAuthentication.sysadmin && !(isSysAdminUser() || isAdminOrSystemUser())) + { + throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + scriptDescriptorId + + " requires system-admin authentication; however, a non-system-admin has attempted access."); + } + else if (required == RequiredAuthentication.admin && !isAdminOrSystemUser()) + { + throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + scriptDescriptorId + + " requires admin authentication; however, a non-admin has attempted access."); + } + } + /** * Execute script within required level of transaction * @@ -626,6 +670,35 @@ public class RepositoryContainer extends AbstractRuntimeContainer }, runAs); } } + + /** + * Execute script within required level of transaction as required effective user. + * + * @param script WebScript + * @param scriptReq WebScriptRequest + * @param scriptRes WebScriptResponse + * @param requiredAuthentication Required authentication + * @throws IOException + */ + private void transactionedExecuteAs(final WebScript script, final WebScriptRequest scriptReq, + final WebScriptResponse scriptRes, RequiredAuthentication requiredAuthentication) throws IOException + { + // Execute as System if and only if, the current user is a member of System-Admin group, and he is not a super admin. + // E.g. if 'jdoe' is a member of ALFRESCO_SYSTEM_ADMINISTRATORS group, then the work should be executed as System to satisfy the ACL checks. + // But, if the current user is Admin (i.e. super admin, which by default he is a member fo the ALFRESCO_SYSTEM_ADMINISTRATORS group) + // then don't wrap the work as RunAs, since he can do anything! + if (requiredAuthentication == RequiredAuthentication.sysadmin && isSysAdminUser() && !isAdmin()) + { + AuthenticationUtil.runAs(() -> { + transactionedExecute(script, scriptReq, scriptRes); + return null; + }, AuthenticationUtil.SYSTEM_USER_NAME); + } + else + { + transactionedExecuteAs(script, scriptReq, scriptRes); + } + } /* (non-Javadoc) * @see org.alfresco.web.scripts.AbstractRuntimeContainer#onApplicationEvent(org.springframework.context.ApplicationEvent) diff --git a/remote-api/src/main/java/org/alfresco/web/scripts/WebScriptUtils.java b/remote-api/src/main/java/org/alfresco/web/scripts/WebScriptUtils.java index 866d7cf704..917b5fe81e 100644 --- a/remote-api/src/main/java/org/alfresco/web/scripts/WebScriptUtils.java +++ b/remote-api/src/main/java/org/alfresco/web/scripts/WebScriptUtils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -35,6 +35,7 @@ import org.alfresco.repo.jscript.ScriptUtils; import org.alfresco.repo.web.scripts.RepositoryContainer; import org.alfresco.service.cmr.admin.RepoUsage; import org.alfresco.service.cmr.repository.StoreRef; +import org.springframework.extensions.webscripts.Description.RequiredAuthentication; import org.springframework.extensions.webscripts.WebScript; /** @@ -65,27 +66,61 @@ public class WebScriptUtils extends ScriptUtils */ public Object[] findWebScripts(String family) { - List values = new ArrayList(); - + List values = new ArrayList<>(); + for (WebScript webscript : this.repositoryContainer.getRegistry().getWebScripts()) { - if (family != null) + addScriptDescription(family, values, webscript); + } + + return values.toArray(new Object[0]); + } + + /** + * Searches for webscript components with the given family name accessible to the current user. + * + * @param family the family + * + * @return An array of webscripts that match the given family name accessible to the current user + * + * @since 7.1 + */ + public Object[] findWebScriptsForCurrentUser(String family) + { + List values = new ArrayList<>(); + + final boolean isAdminOrSystemUser = repositoryContainer.isAdminOrSystemUser(); + for (WebScript webscript : this.repositoryContainer.getRegistry().getWebScripts()) + { + final RequiredAuthentication required = webscript.getDescription().getRequiredAuthentication(); + // Ignore admin webscripts if the current user is not an Admin or System + if (RequiredAuthentication.admin == required && !isAdminOrSystemUser) { - Set familys = webscript.getDescription().getFamilys(); - if (familys != null && familys.contains(family)) - { - values.add(webscript.getDescription()); - } + continue; } - else + + addScriptDescription(family, values, webscript); + } + + return values.toArray(new Object[0]); + } + + private void addScriptDescription(String family, List values, WebScript webscript) + { + if (family != null) + { + Set families = webscript.getDescription().getFamilys(); + if (families != null && families.contains(family)) { values.add(webscript.getDescription()); } } - - return values.toArray(new Object[0]); + else + { + values.add(webscript.getDescription()); + } } - + public String getHostAddress() { try diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js index c7610cae61..c9312b48c2 100644 --- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js +++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js @@ -40,7 +40,7 @@ var Admin = Admin || {}; var toolInfo = {}; // collect the tools required for the Admin Console - var tools = utils.findWebScripts("AdminConsole"); + var tools = utils.findWebScriptsForCurrentUser("AdminConsole"); // process each tool and generate the data so that a label+link can // be output by the component template for each tool required diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml index 55445b39d4..016f57de71 100644 --- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml +++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml @@ -7,7 +7,7 @@ AdminConsole:Edition:Community argument - admin + sysadmin internal required - \ No newline at end of file + diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml index 26816eeb20..2b7ae51945 100644 --- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml +++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml @@ -5,7 +5,7 @@ /admin/ AdminConsoleHelper argument - admin + sysadmin internal required - \ No newline at end of file + diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml index 39eceeb809..b1179e5ebe 100644 --- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml +++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml @@ -5,7 +5,7 @@ /api/admin/jmxdump AdminConsoleHelper - admin + sysadmin internal - \ No newline at end of file + diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml index e852951f6c..fb3fea15ae 100644 --- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml +++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml @@ -3,8 +3,8 @@ Update and retrieve repository usage /api/admin/usage - admin + sysadmin required Admin internal - \ No newline at end of file + diff --git a/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java b/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java index c0610d25e8..4a9d6fe556 100644 --- a/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java +++ b/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -31,10 +31,12 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.dictionary.Facetable; import org.alfresco.repo.dictionary.IndexTokenisationMode; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authority.AuthorityServiceImpl; import org.alfresco.repo.web.scripts.BaseWebScriptTest; import org.alfresco.service.cmr.admin.RepoAdminService; import org.alfresco.service.cmr.admin.RepoUsage; @@ -48,11 +50,18 @@ import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.i18n.MessageLookup; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.license.LicenseDescriptor; import org.alfresco.service.namespace.QName; import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.util.PropertyMap; +import org.apache.commons.lang3.RandomStringUtils; import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.context.ApplicationContext; @@ -74,29 +83,46 @@ import static org.mockito.Mockito.when; @Category(OwnJVMTestsCategory.class) public class AdminWebScriptTest extends BaseWebScriptTest { - private ApplicationContext ctx; - private RepoAdminService repoAdminService; - private DescriptorService descriptorService; + private RepoAdminService repoAdminService; + private DescriptorService descriptorService; + private PersonService personService; + private MutableAuthenticationService authenticationService; + private String admin; private String guest; + private String user1_sysAdmin; + private String user2; @Override protected void setUp() throws Exception { super.setUp(); - ctx = getServer().getApplicationContext(); - repoAdminService = (RepoAdminService) ctx.getBean("RepoAdminService"); - descriptorService = (DescriptorService) ctx.getBean("DescriptorService"); + ApplicationContext ctx = getServer().getApplicationContext(); + repoAdminService = ctx.getBean("RepoAdminService", RepoAdminService.class); + descriptorService = ctx.getBean("DescriptorService", DescriptorService.class); + personService = ctx.getBean("PersonService", PersonService.class); + authenticationService = ctx.getBean("AuthenticationService", MutableAuthenticationService.class); + AuthorityService authorityService = ctx.getBean("AuthorityService", AuthorityService.class); admin = AuthenticationUtil.getAdminUserName(); guest = AuthenticationUtil.getGuestUserName(); AuthenticationUtil.setFullyAuthenticatedUser(admin); + + user1_sysAdmin = RandomStringUtils.randomAlphabetic(10); + String user1_password = RandomStringUtils.randomAlphabetic(10); + createUser(user1_sysAdmin, user1_password); + authorityService.addAuthority(AuthorityServiceImpl.GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY, user1_sysAdmin); + + user2 = RandomStringUtils.randomAlphabetic(10); + String user2_password = RandomStringUtils.randomAlphabetic(10); + createUser(user2, user2_password); } @Override protected void tearDown() throws Exception { super.tearDown(); + AuthenticationUtil.clearCurrentSecurityContext(); } public void testGetRestrictions() throws Exception @@ -227,6 +253,129 @@ public class AdminWebScriptTest extends BaseWebScriptTest assertTrue(property.getResidual()); } + public void testSysAdminAccess() throws Exception + { + AuthenticationUtil.clearCurrentSecurityContext(); + + String url = "/admin/admin-communitysummary"; + TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url); + + Response response = sendRequest(req, Status.STATUS_OK, user1_sysAdmin); + Document doc = Jsoup.parse(response.getContentAsString()); + assertNotNull(doc.title()); + assertTrue(doc.title().contains("System Summary")); + + // Super Admin should still have access to all the scripts + response = sendRequest(req, Status.STATUS_OK, admin); + doc = Jsoup.parse(response.getContentAsString()); + assertNotNull(doc.title()); + assertTrue(doc.title().contains("System Summary")); + } + + public void testSysAdminAccess_nodeBrowser() throws Exception + { + AuthenticationUtil.clearCurrentSecurityContext(); + + String nodeBrowserUrl = "/admin/admin-nodebrowser"; + + // test the get webscript of the node browser + TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(nodeBrowserUrl); + // The node browser is only accessible to admins, not sysAdmins + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // test the post webscript of the node browser too + TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(nodeBrowserUrl, "", + "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA"); + // The node browser is only accessible to admins, not sysAdmins + sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // Normal user shouldn't have access either + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2); + + // Admin should have access to everything + Response response = sendRequest(getReq, Status.STATUS_OK, admin); + Document doc = Jsoup.parse(response.getContentAsString()); + assertNotNull(doc.title()); + assertTrue(doc.title().contains("Node Browser")); + } + + public void testSysAdminAccess_repoConsole() throws Exception + { + String repoConsoleUrl = "/admin/admin-repoconsole"; + + // test the get webscript of the repo console + TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(repoConsoleUrl); + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // test the post webscript of the repo console too + TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(repoConsoleUrl, "", + "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA"); + sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // Normal user shouldn't have access either + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2); + + // Admin should have access to everything + Response response = sendRequest(getReq, Status.STATUS_OK, admin); + Document doc = Jsoup.parse(response.getContentAsString()); + assertNotNull(doc.title()); + assertTrue(doc.title().contains("Model and Messages Console")); + } + + public void testSysAdminAccess_tenantConsole() throws Exception + { + String tenantConsoleUrl = "/admin/admin-tenantconsole"; + // test the get webscript of the tenant console + TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(tenantConsoleUrl); + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // test the post webscript of the tenant console too + TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(tenantConsoleUrl, "", + "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA"); + sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // Normal user shouldn't have access either + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2); + + // Admin should have access to everything + Response response = sendRequest(getReq, Status.STATUS_OK, admin); + Document doc = Jsoup.parse(response.getContentAsString()); + assertNotNull(doc.title()); + assertTrue(doc.title().contains("Tenant Admin Console")); + } + + public void testSysAdminAccess_workflowConsole() throws Exception + { + String workflowConsoleUrl = "/admin/admin-workflowconsole"; + // test the get webscript of the workflow console + TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(workflowConsoleUrl); + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // test the post webscript of the workflow console too + TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(workflowConsoleUrl, "", + "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA"); + sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin); + + // Normal user shouldn't have access either + sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2); + + // Admin should have access to everything + Response response = sendRequest(getReq, Status.STATUS_OK, admin); + Document doc = Jsoup.parse(response.getContentAsString()); + assertNotNull(doc.title()); + assertTrue(doc.title().contains("Workflow Admin Console")); + } + + public void testNonSysAdminAccess() throws Exception + { + AuthenticationUtil.clearCurrentSecurityContext(); + + String url = "/admin/admin-communitysummary"; + TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url); + + sendRequest(req, Status.STATUS_UNAUTHORIZED, user2); + } + private class SimplePropertyDefinition implements PropertyDefinition { private boolean isAspect; @@ -350,4 +499,19 @@ public class AdminWebScriptTest extends BaseWebScriptTest return null; } } + + private void createUser(String username, String password) + { + if (!personService.personExists(username)) + { + this.authenticationService.createAuthentication(username, password.toCharArray()); + + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, username); + personProps.put(ContentModel.PROP_FIRSTNAME, "testFirstName"); + personProps.put(ContentModel.PROP_LASTNAME, "testLastName"); + personProps.put(ContentModel.PROP_EMAIL, username + "@email.com"); + this.personService.createPerson(personProps); + } + } }