| /* |
| 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.wiki.auth; |
| |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.wiki.TestEngine; |
| import org.apache.wiki.WikiPage; |
| import org.apache.wiki.WikiSessionTest; |
| import org.apache.wiki.api.core.Session; |
| import org.apache.wiki.auth.authorize.Group; |
| import org.apache.wiki.auth.authorize.GroupManager; |
| import org.apache.wiki.auth.permissions.PermissionFactory; |
| import org.apache.wiki.auth.user.DuplicateUserException; |
| import org.apache.wiki.auth.user.UserDatabase; |
| import org.apache.wiki.auth.user.UserProfile; |
| import org.apache.wiki.auth.user.XMLUserDatabase; |
| import org.apache.wiki.pages.PageManager; |
| import org.apache.wiki.workflow.Decision; |
| import org.apache.wiki.workflow.DecisionQueue; |
| import org.apache.wiki.workflow.DecisionRequiredException; |
| import org.apache.wiki.workflow.Fact; |
| import org.apache.wiki.workflow.Outcome; |
| import org.apache.wiki.workflow.WorkflowManager; |
| import org.junit.jupiter.api.AfterEach; |
| import org.junit.jupiter.api.Assertions; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| |
| import java.security.Principal; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Properties; |
| |
| /** |
| */ |
| public class UserManagerTest { |
| |
| private TestEngine m_engine; |
| private UserManager m_mgr; |
| private UserDatabase m_db; |
| private String m_groupName; |
| |
| /** |
| * |
| */ |
| @BeforeEach |
| public void setUp() throws Exception { |
| final Properties props = TestEngine.getTestProperties(); |
| |
| // Make sure user profile save workflow is OFF |
| props.remove( "jspwiki.approver" + WorkflowManager.WF_UP_CREATE_SAVE_APPROVER ); |
| |
| // Make sure we are using the XML user database |
| props.put( XMLUserDatabase.PROP_USERDATABASE, "target/test-classes/userdatabase.xml" ); |
| m_engine = new TestEngine( props ); |
| m_mgr = m_engine.getUserManager(); |
| m_db = m_mgr.getUserDatabase(); |
| m_groupName = "Group" + System.currentTimeMillis(); |
| } |
| |
| @AfterEach |
| public void tearDown() throws Exception { |
| final GroupManager groupManager = m_engine.getGroupManager(); |
| if( groupManager.findRole( m_groupName ) != null ) { |
| groupManager.removeGroup( m_groupName ); |
| } |
| } |
| |
| /** Call this setup program to use the save-profile workflow. */ |
| protected void setUpWithWorkflow() throws Exception { |
| final Properties props = TestEngine.getTestProperties(); |
| |
| // Turn on user profile saves by the Admin group |
| props.put( "jspwiki.approver." + WorkflowManager.WF_UP_CREATE_SAVE_APPROVER, "Admin" ); |
| |
| // Make sure we are using the XML user database |
| props.put( XMLUserDatabase.PROP_USERDATABASE, "target/test-classes/userdatabase.xml" ); |
| m_engine = new TestEngine( props ); |
| m_mgr = m_engine.getUserManager(); |
| m_db = m_mgr.getUserDatabase(); |
| } |
| |
| @Test |
| public void testSetRenamedUserProfile() throws Exception { |
| // First, count the number of users, groups, and pages |
| final int oldUserCount = m_db.getWikiNames().length; |
| final GroupManager groupManager = m_engine.getGroupManager(); |
| final PageManager pageManager = m_engine.getManager( PageManager.class ); |
| final AuthorizationManager authManager = m_engine.getManager( AuthorizationManager.class ); |
| final int oldGroupCount = groupManager.getRoles().length; |
| final int oldPageCount = pageManager.getTotalPageCount(); |
| |
| // Setup Step 1: create a new user with random name |
| final Session session = m_engine.guestSession(); |
| final long now = System.currentTimeMillis(); |
| final String oldLogin = "TestLogin" + now; |
| final String oldName = "Test User " + now; |
| final String newLogin = "RenamedLogin" + now; |
| final String newName = "Renamed User " + now; |
| UserProfile profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( oldLogin ); |
| profile.setFullname( oldName ); |
| profile.setPassword( "password" ); |
| m_mgr.setUserProfile( session, profile ); |
| |
| // 1a. Make sure the profile saved successfully and that we're logged in |
| profile = m_mgr.getUserProfile( session ); |
| Assertions.assertEquals( oldLogin, profile.getLoginName() ); |
| Assertions.assertEquals( oldName, profile.getFullname() ); |
| Assertions.assertEquals( oldUserCount + 1, m_db.getWikiNames().length ); |
| Assertions.assertTrue( session.isAuthenticated() ); |
| |
| // Setup Step 2: create a new group with our test user in it |
| Group group = groupManager.parseGroup( m_groupName, "Alice \n Bob \n Charlie \n " + oldLogin + "\n" + oldName, true ); |
| groupManager.setGroup( session, group ); |
| |
| // 2a. Make sure the group is created with the user in it, and the role is added to the Subject |
| Assertions.assertEquals( oldGroupCount + 1, groupManager.getRoles().length ); |
| Assertions.assertTrue( group.isMember( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertTrue( group.isMember( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( newName ) ) ); |
| Assertions.assertTrue( groupManager.isUserInRole( session, group.getPrincipal() ) ); |
| |
| // Setup Step 3: create a new page with our test user in the ACL |
| String pageName = "TestPage" + now; |
| m_engine.saveText( pageName, "Test text. [{ALLOW view " + oldName + ", " + oldLogin + ", Alice}] More text." ); |
| |
| // 3a. Make sure the page got saved, and that ONLY our test user has permission to read it. |
| WikiPage p = ( WikiPage )m_engine.getManager( PageManager.class ).getPage( pageName ); |
| Assertions.assertEquals( oldPageCount + 1, pageManager.getTotalPageCount() ); |
| Assertions.assertNotNull( p.getAcl().getEntry( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertNotNull( p.getAcl().getEntry( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newName ) ) ); |
| Assertions.assertTrue( authManager.checkPermission( session, PermissionFactory.getPagePermission( p, "view" ) ), "Test User view page" ); |
| final Session bobSession = WikiSessionTest.authenticatedSession( m_engine, Users.BOB, Users.BOB_PASS ); |
| Assertions.assertFalse( authManager.checkPermission( bobSession, PermissionFactory.getPagePermission( p, "view" ) ), "Bob !view page" ); |
| |
| // Setup Step 4: change the user name in the profile and see what happens |
| profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( oldLogin ); |
| profile.setFullname( newName ); |
| profile.setPassword( "password" ); |
| m_mgr.setUserProfile( session, profile ); |
| |
| // Test 1: the wiki session should have the new wiki name in Subject |
| Principal[] principals = session.getPrincipals(); |
| Assertions.assertTrue( ArrayUtils.contains( principals, new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertFalse( ArrayUtils.contains( principals, new WikiPrincipal( oldName ) ) ); |
| Assertions.assertFalse( ArrayUtils.contains( principals, new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertTrue( ArrayUtils.contains( principals, new WikiPrincipal( newName ) ) ); |
| |
| // Test 2: our group should not contain the old name OR login name any more |
| // (the full name is always used) |
| group = groupManager.getGroup( m_groupName ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertTrue( group.isMember( new WikiPrincipal( newName ) ) ); |
| |
| // Test 3: our page should not contain the old wiki name OR login name |
| // in the ACL any more (the full name is always used) |
| p = ( WikiPage )m_engine.getManager( PageManager.class ).getPage( pageName ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertNotNull( p.getAcl().getEntry( new WikiPrincipal( newName ) ) ); |
| Assertions.assertTrue( authManager.checkPermission( session, PermissionFactory.getPagePermission( p, "view" ) ), "Test User view page" ); |
| Assertions.assertFalse( authManager.checkPermission( bobSession, PermissionFactory.getPagePermission( p, "view" ) ), "Bob !view page" ); |
| |
| // Test 4: our page text should have been re-written |
| // (The new full name should be in the ACL, but the login name should have been removed) |
| String expectedText = "[{ALLOW view Alice," + newName + "}]\nTest text. More text.\r\n"; |
| String actualText = m_engine.getManager( PageManager.class ).getText( pageName ); |
| Assertions.assertEquals( expectedText, actualText ); |
| |
| // Remove our test page |
| m_engine.getManager( PageManager.class ).deletePage( pageName ); |
| |
| // Setup Step 6: re-create the group with our old test user names in it |
| group = groupManager.parseGroup( m_groupName, "Alice \n Bob \n Charlie \n " + oldLogin + "\n" + oldName, true ); |
| groupManager.setGroup( session, group ); |
| |
| // Setup Step 7: Save a new page with the old login/wiki names in the ACL again |
| // The test user should still be able to see the page (because the login name matches...) |
| pageName = "TestPage2" + now; |
| m_engine.saveText( pageName, "More test text. [{ALLOW view " + oldName + ", " + oldLogin + ", Alice}] More text." ); |
| p = ( WikiPage )m_engine.getManager( PageManager.class ).getPage( pageName ); |
| Assertions.assertEquals( oldPageCount + 1, pageManager.getTotalPageCount() ); |
| Assertions.assertNotNull( p.getAcl().getEntry( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertNotNull( p.getAcl().getEntry( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newName ) ) ); |
| Assertions.assertTrue( authManager.checkPermission( session, PermissionFactory.getPagePermission( p, "view" ) ), "Test User view page" ); |
| Assertions.assertFalse( authManager.checkPermission( bobSession, PermissionFactory.getPagePermission( p, "view" ) ), "Bob !view page" ); |
| |
| // Setup Step 8: re-save the profile with the new login name |
| profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( newLogin ); |
| profile.setFullname( oldName ); |
| profile.setPassword( "password" ); |
| m_mgr.setUserProfile( session, profile ); |
| |
| // Test 5: the wiki session should have the new login name in Subject |
| principals = session.getPrincipals(); |
| Assertions.assertFalse( ArrayUtils.contains( principals, new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertTrue( ArrayUtils.contains( principals, new WikiPrincipal( oldName ) ) ); |
| Assertions.assertTrue( ArrayUtils.contains( principals, new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertFalse( ArrayUtils.contains( principals, new WikiPrincipal( newName ) ) ); |
| |
| // Test 6: our group should not contain the old name OR login name any more |
| // (the full name is always used) |
| group = groupManager.getGroup( m_groupName ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertTrue( group.isMember( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertFalse( group.isMember( new WikiPrincipal( newName ) ) ); |
| |
| // Test 7: our page should not contain the old wiki name OR login name |
| // in the ACL any more (the full name is always used) |
| p = ( WikiPage )m_engine.getManager( PageManager.class ).getPage( pageName ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( oldLogin ) ) ); |
| Assertions.assertNotNull( p.getAcl().getEntry( new WikiPrincipal( oldName ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newLogin ) ) ); |
| Assertions.assertNull( p.getAcl().getEntry( new WikiPrincipal( newName ) ) ); |
| Assertions.assertTrue( authManager.checkPermission( session, PermissionFactory.getPagePermission( p, "view" ) ), "Test User view page" ); |
| Assertions.assertFalse( authManager.checkPermission( bobSession, PermissionFactory.getPagePermission( p, "view" ) ), "Bob !view page" ); |
| |
| // Test 8: our page text should have been re-written |
| // (The new full name should be in the ACL, but the login name should have been removed) |
| expectedText = "[{ALLOW view Alice," + oldName + "}]\nMore test text. More text.\r\n"; |
| actualText = m_engine.getManager( PageManager.class ).getText( pageName ); |
| Assertions.assertEquals( expectedText, actualText ); |
| |
| // CLEANUP: delete the profile; user and page; should be back to old counts |
| m_db.deleteByLoginName( newLogin ); |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| |
| groupManager.removeGroup( group.getName() ); |
| Assertions.assertEquals( oldGroupCount, groupManager.getRoles().length ); |
| |
| m_engine.getManager( PageManager.class ).deletePage( pageName ); |
| Assertions.assertEquals( oldPageCount, pageManager.getTotalPageCount() ); |
| } |
| |
| @Test |
| public void testSetUserProfile() throws Exception { |
| // First, count the number of users in the db now. |
| final int oldUserCount = m_db.getWikiNames().length; |
| |
| // Create a new user with random name |
| final Session session = m_engine.guestSession(); |
| final String loginName = "TestUser" + String.valueOf( System.currentTimeMillis() ); |
| UserProfile profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( loginName ); |
| profile.setFullname( "FullName" + loginName ); |
| profile.setPassword( "password" ); |
| m_mgr.setUserProfile( session, profile ); |
| |
| // Make sure the profile saved successfully |
| profile = m_mgr.getUserProfile( session ); |
| Assertions.assertEquals( loginName, profile.getLoginName() ); |
| Assertions.assertEquals( oldUserCount + 1, m_db.getWikiNames().length ); |
| |
| // Now delete the profile; should be back to old count |
| m_db.deleteByLoginName( loginName ); |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| } |
| |
| @Test |
| public void testSetUserProfileWithApproval() throws Exception { |
| setUpWithWorkflow(); |
| |
| // First, count the number of users in the db now. |
| final int oldUserCount = m_db.getWikiNames().length; |
| |
| // Create a new user with random name |
| final Session session = m_engine.guestSession(); |
| final String loginName = "TestUser" + String.valueOf( System.currentTimeMillis() ); |
| final UserProfile profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( loginName ); |
| profile.setFullname( "FullName" + loginName ); |
| profile.setPassword( "password" ); |
| |
| // Because user profile saves require approvals, we will catch a Redirect |
| try { |
| m_mgr.setUserProfile( session, profile ); |
| Assertions.fail( "We should have caught a DecisionRequiredException caused by approval!" ); |
| } catch( final DecisionRequiredException e ) { |
| } |
| |
| // The user should NOT be saved yet |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| |
| // Now, look in Admin's queue, and verify there's a pending Decision there |
| final DecisionQueue dq = m_engine.getManager( WorkflowManager.class ).getDecisionQueue(); |
| final Collection< Decision > decisions = dq.getActorDecisions( m_engine.adminSession() ); |
| Assertions.assertEquals( 1, decisions.size() ); |
| |
| // Verify that the Decision has all the facts and attributes we need |
| final Decision d = decisions.iterator().next(); |
| final List< Fact > facts = d.getFacts(); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_FULL_NAME, profile.getFullname() ), facts.get( 0 ) ); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_LOGIN_NAME, profile.getLoginName() ), facts.get( 1 ) ); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_SUBMITTER, session.getUserPrincipal().getName() ), facts.get( 2 ) ); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_EMAIL, profile.getEmail() ), facts.get( 3 ) ); |
| Assertions.assertEquals( profile, d.getWorkflow().getAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ) ); |
| |
| // Approve the profile |
| d.decide( Outcome.DECISION_APPROVE ); |
| |
| // Make sure the profile saved successfully |
| Assertions.assertEquals( oldUserCount + 1, m_db.getWikiNames().length ); |
| |
| // Now delete the profile; should be back to old count |
| m_db.deleteByLoginName( loginName ); |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| } |
| |
| @Test |
| public void testSetUserProfileWithDenial() throws Exception { |
| setUpWithWorkflow(); |
| |
| // First, count the number of users in the db now. |
| final int oldUserCount = m_db.getWikiNames().length; |
| |
| // Create a new user with random name |
| final Session session = m_engine.guestSession(); |
| final String loginName = "TestUser" + String.valueOf( System.currentTimeMillis() ); |
| final UserProfile profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( loginName ); |
| profile.setFullname( "FullName" + loginName ); |
| profile.setPassword( "password" ); |
| |
| // Because user profile saves require approvals, we will catch a Redirect |
| try { |
| m_mgr.setUserProfile( session, profile ); |
| Assertions.fail( "We should have caught a DecisionRequiredException caused by approval!" ); |
| } catch( final DecisionRequiredException e ) { |
| } |
| |
| // The user should NOT be saved yet |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| |
| // Now, look in Admin's queue, and verify there's a pending Decision there |
| final DecisionQueue dq = m_engine.getManager( WorkflowManager.class ).getDecisionQueue(); |
| final Collection< Decision > decisions = dq.getActorDecisions( m_engine.adminSession() ); |
| Assertions.assertEquals( 1, decisions.size() ); |
| |
| // Verify that the Decision has all the facts and attributes we need |
| final Decision d = decisions.iterator().next(); |
| final List< Fact > facts = d.getFacts(); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_FULL_NAME, profile.getFullname() ), facts.get( 0 ) ); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_LOGIN_NAME, profile.getLoginName() ), facts.get( 1 ) ); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_SUBMITTER, session.getUserPrincipal().getName() ), facts.get( 2 ) ); |
| Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_EMAIL, profile.getEmail() ), facts.get( 3 ) ); |
| Assertions.assertEquals( profile, d.getWorkflow().getAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ) ); |
| |
| // Approve the profile |
| d.decide( Outcome.DECISION_DENY ); |
| |
| // Make sure the profile did NOT save |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| } |
| |
| @Test |
| public void testSetCollidingUserProfile() throws Exception { |
| // First, count the number of users in the db now. |
| final int oldUserCount = m_db.getWikiNames().length; |
| |
| // Create a new user with random name |
| final Session session = m_engine.guestSession(); |
| final String loginName = "TestUser" + String.valueOf( System.currentTimeMillis() ); |
| final UserProfile profile = m_db.newProfile(); |
| profile.setEmail( "jspwiki.tests@mailinator.com" ); |
| profile.setLoginName( loginName ); |
| profile.setFullname( "FullName" + loginName ); |
| profile.setPassword( "password" ); |
| |
| // Set the login name to collide with Janne's: should prohibit saving |
| profile.setLoginName( "janne" ); |
| try { |
| m_mgr.setUserProfile( session, profile ); |
| Assertions.fail( "UserManager allowed saving of user with login name 'janne', but it shouldn't have." ); |
| } catch( final DuplicateUserException e ) { |
| // Good! That's what we expected; reset for next test |
| profile.setLoginName( loginName ); |
| } |
| |
| // Set the login name to collide with Janne's: should prohibit saving |
| profile.setFullname( "Janne Jalkanen" ); |
| try { |
| m_mgr.setUserProfile( session, profile ); |
| Assertions.fail( "UserManager allowed saving of user with login name 'janne', but it shouldn't have." ); |
| } catch( final DuplicateUserException e ) { |
| // Good! That's what we expected |
| } |
| |
| // There shouldn't have been any users added |
| Assertions.assertEquals( oldUserCount, m_db.getWikiNames().length ); |
| } |
| |
| } |