Skip to content
Open
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
23 changes: 22 additions & 1 deletion UPGRADING
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Apache JSPWiki 2.12.0 - Upgrading Notes
Apache JSPWiki 3.0.0 - Upgrading Notes
==================================================

Licensed to the Apache Software Foundation (ASF) under one
Expand All @@ -21,6 +21,27 @@ Apache JSPWiki 2.12.0 - Upgrading Notes

The license file can be found in LICENSE.


Upgrading JSPWiki to 3.0.0
---------------------------
Please see https://jspwiki-wiki.apache.org/Wiki.jsp?page=NewIn3.0.0 for details

1. New requirements
* Java 17 needed to run JSPWiki

2. Backwards incompatible changes:
* Page level access controls (i.e. [ALLOW edit/view/etc User/Role] ) logic has been changed.
Prior to this release, wiki name, full name, login name, role name or group names were allowed.
With this release and moving forward, login name, role name and group names are utilitized only.
Wiki and Full names are no longer honored. see JSPWIKI-130 for additional details. This also includes
the IF plugin.

Review all wiki pages looking for the ALLOW/DENY and IF plugins and adjust as necessary.

3. Many new security features have been added and enabled by default. Please review the default
jspwiki.properties file


Upgrading JSPWiki to 2.12.0
---------------------------

Expand Down
5 changes: 5 additions & 0 deletions jspwiki-main/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jspwiki-util</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ public Principal getCurrentUser() {
// This shouldn't happen, really...
return WikiPrincipal.GUEST;
}
return m_session.getUserPrincipal();
return m_session.getLoginPrincipal();
}

/**
Expand Down
8 changes: 7 additions & 1 deletion jspwiki-main/src/main/java/org/apache/wiki/WikiSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ protected void injectUserProfilePrincipals() {
throw new IllegalStateException( "User database cannot be null." );
}
try {
final UserProfile profile = database.find( searchId );
final UserProfile profile = database.findByLoginName( searchId );
final Principal[] principals = database.getPrincipals( profile.getLoginName() );
for( final Principal principal : principals ) {
// Add the Principal to the Subject
Expand All @@ -426,6 +426,12 @@ protected void injectUserProfilePrincipals() {
// Set the user principal if needed; we prefer FullName, but the WikiName will also work
final boolean isFullNamePrincipal = ( principal instanceof WikiPrincipal &&
( ( WikiPrincipal )principal ).getType().equals( WikiPrincipal.FULL_NAME ) );
if (( principal instanceof WikiPrincipal &&
( ( WikiPrincipal )principal ).getType().equals( WikiPrincipal.LOGIN_NAME )) ){
m_loginPrincipal = principal;
}


if ( isFullNamePrincipal ) {
m_userPrincipal = principal;
} else if ( !( m_userPrincipal instanceof WikiPrincipal ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ default boolean hasAccess( final Context context, final HttpServletResponse resp
* <li>Finally, if a user cannot be found, manufacture and return a generic {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}</li>
* </ol>
*
* @param name the name of the Principal to resolve
* @param name the name of the Principal to resolve. Note: as of v3.0.0, the
* underlying behavior has changed. Principals can be resolved via login names only.
* @return the fully-resolved Principal
*/
Principal resolvePrincipal( final String name );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,9 @@ public Principal resolvePrincipal( final String name ) {

// Ok, no luck---this must be a user principal
final Principal[] principals;
final UserProfile profile;
final UserDatabase db = m_engine.getManager( UserManager.class ).getUserDatabase();
try {
profile = db.find( name );
principals = db.getPrincipals( profile.getLoginName() );
principals = db.getPrincipals( name );
for( final Principal value : principals ) {
principal = value;
if( principal.getName().equals( name ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public UserProfile getUserProfile( final Session session ) {
if ( session.isAuthenticated() ) {
user = session.getUserPrincipal();
try {
profile = getUserDatabase().find( user.getName() );
profile = getUserDatabase().findByWikiName( user.getName());
newProfile = false;
} catch( final NoSuchPrincipalException e ) {
LOG.debug(e.getMessage(), e);
Expand All @@ -164,6 +164,8 @@ public UserProfile getUserProfile( final Session session ) {
profile = getUserDatabase().newProfile();
if ( user != null ) {
profile.setLoginName( user.getName() );
} else {
LOG.warn("new profile however the user principal is null. this shouldn't happen");
}
if ( !profile.isNew() ) {
throw new IllegalStateException( "New profile should be marked 'new'. Check your UserProfile implementation." );
Expand Down Expand Up @@ -375,8 +377,10 @@ public void validateProfile( final Context context, final UserProfile profile )
final String email = profile.getEmail();

// It's illegal to use as a full name someone else's login name
//FIXME this check seems dubious at best. can't be two John Smith's in the same
//database?
try {
otherProfile = getUserDatabase().find( fullName );
otherProfile = getUserDatabase().findByFullName( fullName );
if( otherProfile != null && !profile.equals( otherProfile ) && !fullName.equals( otherProfile.getFullname() ) ) {
final Object[] args = { fullName };
session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString( "security.error.illegalfullname" ), args ) );
Expand All @@ -388,7 +392,7 @@ public void validateProfile( final Context context, final UserProfile profile )

// It's illegal to use as a login name someone else's full name
try {
otherProfile = getUserDatabase().find( loginName );
otherProfile = getUserDatabase().findByFullName(loginName );
if( otherProfile != null && !profile.equals( otherProfile ) && !loginName.equals( otherProfile.getLoginName() ) ) {
final Object[] args = { loginName };
session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString( "security.error.illegalloginname" ), args ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ public interface UserDatabase {
* that supplied the name is unknown.
*
* @param index the login name, full name, or wiki name
* @deprecated depending on the use case, this API's usage can be dangerous.
* Recommend using other APIs for more explicit lookup types. see JSPWIKI-130
* for additional details.
*/
@Deprecated
UserProfile find( String index ) throws NoSuchPrincipalException;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import javax.xml.parsers.ParserConfigurationException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
Expand All @@ -47,6 +48,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
Expand All @@ -55,6 +57,8 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.xml.XMLConstants;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;

/**
* <p>Manages {@link DefaultUserProfile} objects using XML files for persistence. Passwords are hashed using SHA1. User entries are simple
Expand Down Expand Up @@ -193,7 +197,36 @@ public void initialize( final Engine engine, final Properties props ) throws NoR
}

LOG.info( "XML user database at " + c_file.getAbsolutePath() );
File checkFile = new File(c_file.getParent(), c_file.getName() + ".check");
if (checkFile.exists()) {

FileInputStream fis = null;
byte[] computedHash = null;
byte[] storedHash = null;
try {
fis = new FileInputStream(c_file);
computedHash = DigestUtils.sha256(fis);
storedHash = FileUtils.readFileToByteArray(checkFile);
} catch (Exception ex) {
throw new RuntimeException("Failed to compute integrity check. ", ex);
} finally {
if (fis != null)
try {
fis.close();
} catch (IOException ex) {
LOG.debug(ex.getMessage());
}
}
if (Arrays.equals(computedHash, storedHash)) {
LOG.info("XML user database hash check passed. no modifications detected.");
} else {
throw new RuntimeException("XML user database has been modified outside of JSP Wiki. Refusing start up. An administrator will need to restore the file from backup");
}

} else {
LOG.info("XML user database check file does not exist. This is normal if JSPWIki was just installed.");
}

buildDOM();
sanitizeDOM();
}
Expand Down Expand Up @@ -303,6 +336,23 @@ private void saveDOM() throws WikiSecurityException {
}
LOG.error( "Could not save database: " + c_file + ". Check the file permissions" );
}
FileInputStream fis = null;
try {
fis = new FileInputStream(c_file);
byte[] hash = DigestUtils.sha256(fis);
File checkFile = new File(c_file.getParent(), c_file.getName() + ".check");
FileUtils.writeByteArrayToFile(checkFile, hash);
} catch (Exception ex) {
LOG.warn("Failed to recompute and/or save the check file", ex);
} finally {
if (fis!=null) {
try {
fis.close();
} catch (IOException ex) {
LOG.debug(ex.getMessage());
}
}
}
}

private long c_lastCheck;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,8 @@ public void testResolveUsers() throws WikiException
Assertions.fail( "Failed save: " + e.getLocalizedMessage() );
}
Assertions.assertEquals( new WikiPrincipal( "authmanagertest", WikiPrincipal.LOGIN_NAME ), m_auth.resolvePrincipal( "authmanagertest" ) );
Assertions.assertEquals( new WikiPrincipal( "AuthorizationManagerTest User", WikiPrincipal.FULL_NAME ), m_auth.resolvePrincipal( "AuthorizationManagerTest User" ) );
Assertions.assertEquals( new WikiPrincipal( "AuthorizationManagerTestUser", WikiPrincipal.WIKI_NAME ), m_auth.resolvePrincipal( "AuthorizationManagerTestUser" ) );
//Assertions.assertEquals( new WikiPrincipal( "AuthorizationManagerTest User", WikiPrincipal.FULL_NAME ), m_auth.resolvePrincipal( "AuthorizationManagerTest User" ) );
//Assertions.assertEquals( new WikiPrincipal( "AuthorizationManagerTestUser", WikiPrincipal.WIKI_NAME ), m_auth.resolvePrincipal( "AuthorizationManagerTestUser" ) );
try
{
m_engine.getManager( UserManager.class ).getUserDatabase().deleteByLoginName( "authmanagertest" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Licensed to the Apache Software Foundation (ASF) under one

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import java.io.File;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.wiki.HttpMockFactory;
import org.apache.wiki.TestEngine;
Expand Down Expand Up @@ -52,6 +53,9 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.apache.wiki.WikiEngine;


class UserManagerTest {
Expand All @@ -60,7 +64,7 @@ class UserManagerTest {
UserManager m_mgr;
UserDatabase m_db;
String m_groupName;

File target;
/**
*
*/
Expand All @@ -71,8 +75,11 @@ void setUp() throws Exception {
// Make sure user profile save workflow is OFF
props.remove( "jspwiki.approver" + WorkflowManager.WF_UP_CREATE_SAVE_APPROVER );

target = new File("target/XMLUserDatabaseTest" + UUID.randomUUID().toString() + ".xml");
FileUtils.copyFile(new File("src/test/resources/userdatabase.xml" ), target);
props.put( XMLUserDatabase.PROP_USERDATABASE, target.getAbsolutePath() );
// 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.getManager( UserManager.class );
m_db = m_mgr.getUserDatabase();
Expand Down Expand Up @@ -277,6 +284,7 @@ void testSetUserProfile() throws Exception {
// Create a new user with random name
final Context context = Wiki.context().create( m_engine, HttpMockFactory.createHttpRequest(), "" );
final String loginName = "TestUser" + System.currentTimeMillis();

UserProfile profile = m_db.newProfile();
profile.setEmail( "jspwiki.tests@mailinator.com" );
profile.setLoginName( loginName );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Licensed to the Apache Software Foundation (ASF) under one
*/
package org.apache.wiki.auth.user;

import java.io.File;
import java.io.IOException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.wiki.TestEngine;
import org.apache.wiki.WikiEngine;
Expand All @@ -28,13 +30,16 @@ Licensed to the Apache Software Foundation (ASF) under one
import org.apache.wiki.util.CryptoUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.apache.wiki.auth.authorize.XMLGroupDatabase;


public class XMLUserDatabaseTest {
Expand All @@ -44,7 +49,10 @@ public class XMLUserDatabaseTest {
@BeforeEach
public void setUp() throws Exception {
final Properties props = TestEngine.getTestProperties();
props.put( XMLUserDatabase.PROP_USERDATABASE, "target/test-classes/userdatabase.xml" );
File target = new File("target/XMLUserDatabaseTest" + UUID.randomUUID().toString() + ".xml");
FileUtils.copyFile(new File("src/test/resources/userdatabase.xml" ), target);
props.put( XMLUserDatabase.PROP_USERDATABASE, target.getAbsolutePath() );

final WikiEngine engine = new TestEngine( props );
m_db = new XMLUserDatabase();
m_db.initialize( engine, props );
Expand Down Expand Up @@ -313,4 +321,42 @@ public void testValidatePassword() {
Assertions.assertTrue( m_db.validatePassword( "user", "password" ) );
}


@Test
public void JSPWIKI_130() throws Exception {
final Properties props = TestEngine.getTestProperties();
File target = new File("target/JSPWIKI_130" + UUID.randomUUID().toString() + ".xml");
FileUtils.copyFile(new File("src/test/resources/userdatabase.xml"), target);
props.put(XMLUserDatabase.PROP_USERDATABASE, target.getAbsolutePath());
final WikiEngine engine = new TestEngine(props);
XMLUserDatabase m_db = new XMLUserDatabase();
m_db.initialize(engine, props);
//create a user and save it the changes.
// Create new user & verify it saved ok
UserProfile profile = m_db.newProfile();
profile.setEmail( "renamed@mailinator.com" );
profile.setFullname( "Renamed User" );
profile.setLoginName( "olduser" );
profile.setPassword( "password" );
m_db.save( profile );

//confirm the check file exists
File f = new File(target.getParent(), target.getName() + ".check");
Assertions.assertTrue(f.exists());

//shutdown the database manager

//change a bit or two in the check file.
FileUtils.writeStringToFile(f, "you've been hacked", StandardCharsets.UTF_8);


//start it back up again, expecting it to barf
try{
m_db.initialize(engine, props);
Assertions.fail("xml database check did not trigger");
}catch (RuntimeException ex) {
//this is expected
}

}
}
Loading
Loading