--- ./src/java/org/apache/wiki/auth/AuthenticationManager.java 2009-03-04 15:08:50.503427000 +1100 +++ new/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java 2009-03-04 14:06:33.000000000 +1100 @@ -110,21 +110,30 @@ /** If this jspwiki.properties property is true, allow cookies to be used to assert identities. */ protected static final String PROP_ALLOW_COOKIE_ASSERTIONS = "jspwiki.cookieAssertions"; /** The {@link javax.security.auth.spi.LoginModule} to use for custom authentication. */ protected static final String PROP_LOGIN_MODULE = "jspwiki.loginModule.class"; + /** Property specifying whether the LoginModule can change login names or passwords. */ + protected static final String PROP_CHANGE_LOGIN = "jspwiki.loginModule.canChangeLoginName"; + protected static final String PROP_CHANGE_PASSWORD = "jspwiki.loginModule.canChangePassword"; + /** Empty Map passed to JAAS {@link #doJAASLogin(Class, CallbackHandler, Map)} method. */ protected static final Map EMPTY_MAP = Collections.unmodifiableMap( new HashMap() ); /** Class (of type LoginModule) to use for custom authentication. */ protected Class m_loginModuleClass = UserDatabaseLoginModule.class; /** Options passed to {@link javax.security.auth.spi.LoginModule#initialize(Subject, CallbackHandler, Map, Map)}; * initialized by {@link #initialize(WikiEngine, Properties)}. */ protected Map m_loginModuleOptions = new HashMap(); + /** Can this LoginModule change the username or password? + * initialized by {@link #initialize(WikiEngine, Properties)}. */ + protected boolean m_canChangeLoginName = true; + protected boolean m_canChangePassword = true; + /** Just to provide compatibility with the old versions. The same * as SECURITY_OFF. * * @deprecated use {@link #SECURITY_OFF} instead */ @@ -149,20 +158,52 @@ /** If true, logs the IP address of the editor */ private boolean m_storeIPAddress = true; private boolean m_useJAAS = true; /** Keeps a list of the usernames who have attempted a login recently. */ private TimedCounterList m_lastLoginAttempts = new TimedCounterList(); /** + * Returns true if the current Authentication manager is capable of + * changing the login name. Defaults to true for most LoginModules + * except when using container based authentication. + * + * @return true if it is possible to change the password, + * false otherwise. + */ + public final boolean canChangeLoginName() + { + if (isContainerAuthenticated()) + return false; + else + return m_canChangeLoginName; + } + + /** + * Returns true if the current Authentication manager is capable of + * changing the password. Defaults to true for most LoginModules + * except when using container based authentication. + * + * @return true if it is possible to change the password, + * false otherwise. + */ + public final boolean canChangePassword() + { + if (isContainerAuthenticated()) + return false; + else + return m_canChangePassword; + } + + /** * Creates an AuthenticationManager instance for the given WikiEngine and * the specified set of properties. All initialization for the modules is * done here. * @param engine the wiki engine * @param props the properties used to initialize the wiki engine * @throws WikiException if the AuthenticationManager cannot be initialized */ @SuppressWarnings("unchecked") public final void initialize( WikiEngine engine, Properties props ) throws WikiException { @@ -192,18 +233,25 @@ try { m_loginModuleClass = (Class) Class.forName( loginModuleClassName ); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new WikiException(e.getMessage()); } + // Can this LoginModule change names or passwords? + m_canChangeLoginName = TextUtil.getBooleanProperty( props, + PROP_CHANGE_LOGIN, + true ); + m_canChangePassword = TextUtil.getBooleanProperty( props, + PROP_CHANGE_PASSWORD, + true ); // Initialize the LoginModule options initLoginModuleOptions( props ); } /** * Returns true if this WikiEngine uses container-managed authentication. * This method is used primarily for cosmetic purposes in the JSP tier, and * performs no meaningful security function per se. Delegates to @@ -637,21 +624,21 @@ // Try creating an absolute path first File defaultFile = null; if( engine.getRootPath() != null ) { defaultFile = new File( engine.getRootPath() + "/WEB-INF/" + name ); } if ( defaultFile != null && defaultFile.exists() ) { try { - return defaultFile.toURL(); + return defaultFile.toURI().toURL(); } catch ( MalformedURLException e) { // Shouldn't happen, but log it if it does log.warn( "Malformed URL: " + e.getMessage() ); } } // Ok, the absolute path didn't work; try other methods --- ./src/java/org/apache/wiki/auth/UserManager.java 2009-03-04 15:08:50.487395000 +1100 +++ new/src/com/ecyrd/jspwiki/auth/UserManager.java 2009-03-04 14:22:51.000000000 +1100 @@ -444,12 +441,12 @@ loginName = InputValidator.isBlank( loginName ) ? null : loginName; password = InputValidator.isBlank( password ) ? null : password; fullname = InputValidator.isBlank( fullname ) ? null : fullname; email = InputValidator.isBlank( email ) ? null : email; - // A special case if we have container authentication - if ( m_engine.getAuthenticationManager().isContainerAuthenticated() ) + // A special case if we can't rename accounts + if ( !m_engine.getAuthenticationManager().canChangeLoginName() ) { // If authenticated, login name is always taken from container if ( context.getWikiSession().isAuthenticated() ) { loginName = context.getWikiSession().getLoginPrincipal().getName(); @@ -500,23 +497,24 @@ } break; } } - // If container-managed auth and user not logged in, throw an error - if ( m_engine.getAuthenticationManager().isContainerAuthenticated() + // If we can't change or create loginnames and user not logged in, + // throw an error + if ( !m_engine.getAuthenticationManager().canChangeLoginName() && !context.getWikiSession().isAuthenticated() ) { session.addMessage( SESSION_MESSAGES, rb.getString("security.error.createprofilebeforelogin") ); } validator.validateNotNull( profile.getLoginName(), rb.getString("security.user.loginname") ); validator.validateNotNull( profile.getFullname(), rb.getString("security.user.fullname") ); validator.validate( profile.getEmail(), rb.getString("security.user.email"), InputValidator.EMAIL ); // If new profile, passwords must match and can't be null - if ( !m_engine.getAuthenticationManager().isContainerAuthenticated() ) + if ( m_engine.getAuthenticationManager().canChangePassword() ) { String password = profile.getPassword(); if ( password == null ) { if ( isNew ) --- ./src/java/org/apache/wiki/tags/UserProfileTag.java 2009-03-04 15:08:46.789924000 +1100 +++ new/src/com/ecyrd/jspwiki/tags/UserProfileTag.java 2009-03-04 13:28:48.000000000 +1100 @@ -181,22 +180,34 @@ { result = user.getName(); } } } - else if ( CHANGE_PASSWORD.equals( m_prop ) || CHANGE_LOGIN_NAME.equals( m_prop ) ) + else if ( CHANGE_LOGIN_NAME.equals( m_prop ) || NOT_CHANGE_LOGIN_NAME.equals( m_prop ) ) { - AuthenticationManager authMgr = m_wikiContext.getEngine().getAuthenticationManager(); - if ( !authMgr.isContainerAuthenticated() ) + boolean canChange = m_wikiContext.getEngine().getAuthenticationManager().canChangeLoginName(); + + // Cheap and nasty trick to check for boolean NOT + if ( m_prop.charAt(0) == '!' ) + { + canChange = !canChange; + } + if ( canChange ) { return EVAL_BODY_INCLUDE; } } - else if ( NOT_CHANGE_PASSWORD.equals( m_prop ) || NOT_CHANGE_LOGIN_NAME.equals( m_prop ) ) + else if ( CHANGE_PASSWORD.equals( m_prop ) || NOT_CHANGE_PASSWORD.equals( m_prop ) ) { - AuthenticationManager authMgr = m_wikiContext.getEngine().getAuthenticationManager(); - if ( authMgr.isContainerAuthenticated() ) + boolean canChange = m_wikiContext.getEngine().getAuthenticationManager().canChangePassword(); + + // Cheap and nasty trick to check for boolean NOT + if ( m_prop.charAt(0) == '!' ) + { + canChange = !canChange; + } + if ( canChange ) { return EVAL_BODY_INCLUDE; } } --- ./etc/jspwiki.properties.tmpl 2009-03-04 15:09:52.481215000 +1100 +++ new/etc/jspwiki.properties.tmpl 2009-03-04 15:46:44.678536000 +1100 @@ -441,10 +441,17 @@ # The implementation MUST have a zero-argument constructor (as noted in the # javax.security.auth.spi.LoginModule Javadocs). jspwiki.loginModule.class = org.apache.wiki.auth.login.UserDatabaseLoginModule # +# Not all LoginModules allow you to change login names or passwords. Set these +# options to false if you use a custom LoginModule which doesn't allow these +# changes. +#jspwiki.loginModule.canChangeLoginName = true +#jspwiki.loginModule.canChangePassword = true + +# # JAAS LoginContext parameters used to initialize the LoginModule. Note that 'param1' # etc. should be replaced with the actual parameter names. The parameter names and # values will be loaded to a Map and passed to the LoginModule as the 'options' parameter # when its initialize() method is called. The default UserDatabaseLoginModule class does # not need any options. --- ./UPGRADING 2009-03-04 15:09:52.916210000 +1100 +++ new/UPGRADING 2009-03-04 15:50:52.712754000 +1100 @@ -111,10 +111,17 @@ in jspwiki.properties as follows: jspwiki.loginModule.class = com.foo.login.MyLoginModule If this property is not set, JSPWiki will default (as before) to the UserDatabaseLoginModule implementation. + + Unlike the default UserDatabaseLoginModule, some custom Login Modules do + not allow for login name or password changes. If that is the case with + your custom module, set the following properties to false (defualt + is true): + jspwiki.loginModule.canChangeLoginName = true + jspwiki.loginModule.canChangePassword = true Note that parameters passed to your custom LoginModule are now expressed in jspwiki.properties also, as key/value pairs, rather than in the JAAS config file. The parameter names and values will be loaded to a Map and passed to the LoginModule as the 'options' parameter when its initialize() method @@ -317,6 +324,6 @@ -> For security reasons, class com.ecyrd.jspwiki.auth.user.DefaultUserProfile was made final. -> Class com.ecyrd.jspwiki.rpc.RPCManager's constructor is now protected. - -> Class com.ecyrd.jspwiki.plugin.BugReportHandler had it's public parameter names changed to PARAM_xxx \ No newline at end of file + -> Class com.ecyrd.jspwiki.plugin.BugReportHandler had it's public parameter names changed to PARAM_xxx