| /* |
| 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.wiki.api.core.Engine; |
| import org.apache.wiki.api.core.Session; |
| import org.apache.wiki.api.exceptions.WikiException; |
| import org.apache.wiki.auth.authorize.Role; |
| import org.apache.wiki.event.WikiEventListener; |
| import org.apache.wiki.event.WikiEventManager; |
| import org.apache.wiki.event.WikiSecurityEvent; |
| |
| import javax.security.auth.Subject; |
| import javax.security.auth.callback.CallbackHandler; |
| import javax.security.auth.spi.LoginModule; |
| import javax.servlet.http.HttpServletRequest; |
| import java.security.Principal; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| |
| /** |
| * Manages authentication activities for a Engine: user login, logout, and credential refreshes. This class uses JAAS to determine how |
| * users log in. |
| * <p> |
| * The login procedure is protected in addition by a mechanism which prevents a hacker to try and force-guess passwords by slowing down |
| * attempts to log in into the same account. Every login attempt is recorded, and stored for a while (currently ten minutes), and each |
| * login attempt during that time incurs a penalty of 2^login attempts milliseconds - that is, 10 login attempts incur a login penalty |
| * of 1.024 seconds. The delay is currently capped to 20 seconds. |
| * |
| * @since 2.3 |
| */ |
| public interface AuthenticationManager { |
| |
| /** If this jspwiki.properties property is <code>true</code>, logs the IP address of the editor on saving. */ |
| String PROP_STOREIPADDRESS = "jspwiki.storeIPAddress"; |
| |
| /** If this jspwiki.properties property is <code>true</code>, allow cookies to be used for authentication. */ |
| String PROP_ALLOW_COOKIE_AUTH = "jspwiki.cookieAuthentication"; |
| |
| /** Whether logins should be throttled to limit brute-forcing attempts. Defaults to true. */ |
| String PROP_LOGIN_THROTTLING = "jspwiki.login.throttling"; |
| |
| /** Prefix for LoginModule options key/value pairs. */ |
| String PREFIX_LOGIN_MODULE_OPTIONS = "jspwiki.loginModule.options."; |
| |
| /** If this jspwiki.properties property is <code>true</code>, allow cookies to be used to assert identities. */ |
| String PROP_ALLOW_COOKIE_ASSERTIONS = "jspwiki.cookieAssertions"; |
| |
| /** The {@link LoginModule} to use for custom authentication. */ |
| String PROP_LOGIN_MODULE = "jspwiki.loginModule.class"; |
| |
| /** |
| * Creates an AuthenticationManager instance for the given Engine 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 |
| */ |
| void initialize( Engine engine, Properties props ) throws WikiException; |
| |
| /** |
| * Returns true if this Engine 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 |
| * {@link org.apache.wiki.auth.authorize.WebContainerAuthorizer#isContainerAuthorized()}, |
| * if used as the external authorizer; otherwise, returns <code>false</code>. |
| * |
| * @return <code>true</code> if the wiki's authentication is managed by the container, <code>false</code> otherwise |
| */ |
| boolean isContainerAuthenticated(); |
| |
| /** |
| * <p>Logs in the user by attempting to populate a Session Subject from a web servlet request by examining the request |
| * for the presence of container credentials and user cookies. The processing logic is as follows: |
| * </p> |
| * <ul> |
| * <li>If the Session had previously been unauthenticated, check to see if user has subsequently authenticated. To be considered |
| * "authenticated," the request must supply one of the following (in order of preference): the container <code>userPrincipal</code>, |
| * container <code>remoteUser</code>, or authentication cookie. If the user is authenticated, this method fires event |
| * {@link org.apache.wiki.event.WikiSecurityEvent#LOGIN_AUTHENTICATED} with two parameters: a Principal representing the login principal, |
| * and the current Session. In addition, if the authorizer is of type WebContainerAuthorizer, this method iterates through the |
| * container roles returned by {@link org.apache.wiki.auth.authorize.WebContainerAuthorizer#getRoles()}, tests for membership in each |
| * one, and adds those that pass to the Subject's principal set.</li> |
| * <li>If, after checking for authentication, the Session is still Anonymous, this method next checks to see if the user has |
| * "asserted" an identity by supplying an assertion cookie. If the user is found to be asserted, this method fires event |
| * {@link org.apache.wiki.event.WikiSecurityEvent#LOGIN_ASSERTED} with two parameters: <code>WikiPrincipal(<em>cookievalue</em>)</code>, |
| * and the current Session.</li> |
| * <li>If, after checking for authenticated and asserted status, the Session is <em>still</em> anonymous, this method fires event |
| * {@link org.apache.wiki.event.WikiSecurityEvent#LOGIN_ANONYMOUS} with two parameters: <code>WikiPrincipal(<em>remoteAddress</em>)</code>, |
| * and the current Session </li> |
| * </ul> |
| * |
| * @param request servlet request for this user |
| * @return always returns <code>true</code> (because anonymous login, at least, will always succeed) |
| * @throws org.apache.wiki.auth.WikiSecurityException if the user cannot be logged in for any reason |
| * @since 2.3 |
| */ |
| boolean login( HttpServletRequest request ) throws WikiSecurityException; |
| |
| /** |
| * Attempts to perform a Session login for the given username/password combination using JSPWiki's custom authentication mode. In |
| * order to log in, the JAAS LoginModule supplied by the Engine property {@link #PROP_LOGIN_MODULE} will be instantiated, and its |
| * {@link javax.security.auth.spi.LoginModule#initialize(Subject, CallbackHandler, Map, Map)} method will be invoked. By default, |
| * the {@link org.apache.wiki.auth.login.UserDatabaseLoginModule} class will be used. When the LoginModule's <code>initialize</code> |
| * method is invoked, an options Map populated by properties keys prefixed by {@link #PREFIX_LOGIN_MODULE_OPTIONS} will be passed as a |
| * parameter. |
| * |
| * @param session the current wiki session; may not be <code>null</code>. |
| * @param request the user's HTTP request. This parameter may be <code>null</code>, but the configured LoginModule will not have access |
| * to the HTTP request in this case. |
| * @param username The user name. This is a login name, not a WikiName. In most cases they are the same, but in some cases, they might not be. |
| * @param password the password |
| * @return true, if the username/password is valid |
| * @throws org.apache.wiki.auth.WikiSecurityException if the Authorizer or UserManager cannot be obtained |
| */ |
| boolean login( Session session, HttpServletRequest request, String username, String password ) throws WikiSecurityException; |
| |
| /** |
| * Logs the user out by retrieving the Session associated with the HttpServletRequest and unbinding all of the Subject's Principals, |
| * except for {@link Role#ALL}, {@link Role#ANONYMOUS}. is a cheap-and-cheerful way to do it without invoking JAAS LoginModules. |
| * The logout operation will also flush the JSESSIONID cookie from the user's browser session, if it was set. |
| * |
| * @param request the current HTTP request |
| */ |
| void logout( HttpServletRequest request ); |
| |
| /** |
| * Determines whether this Engine allows users to assert identities using cookies instead of passwords. This is determined by inspecting |
| * the Engine property {@link #PROP_ALLOW_COOKIE_ASSERTIONS}. |
| * |
| * @return <code>true</code> if cookies are allowed |
| */ |
| boolean allowsCookieAssertions(); |
| |
| /** |
| * Determines whether this Engine allows users to authenticate using cookies instead of passwords. This is determined by inspecting |
| * the Engine property {@link #PROP_ALLOW_COOKIE_AUTH}. |
| * |
| * @return <code>true</code> if cookies are allowed for authentication |
| * @since 2.5.62 |
| */ |
| boolean allowsCookieAuthentication(); |
| |
| /** |
| * Instantiates and executes a single JAAS {@link LoginModule}, and returns a Set of Principals that results from a successful login. |
| * The LoginModule is instantiated, then its {@link LoginModule#initialize(Subject, CallbackHandler, Map, Map)} method is called. The |
| * parameters passed to <code>initialize</code> is a dummy Subject, an empty shared-state Map, and an options Map the caller supplies. |
| * |
| * @param clazz the LoginModule class to instantiate |
| * @param handler the callback handler to supply to the LoginModule |
| * @param options a Map of key/value strings for initializing the LoginModule |
| * @return the set of Principals returned by the JAAS method {@link Subject#getPrincipals()} |
| * @throws WikiSecurityException if the LoginModule could not be instantiated for any reason |
| */ |
| Set< Principal > doJAASLogin( Class< ? extends LoginModule > clazz, CallbackHandler handler, Map< String, String > options) throws WikiSecurityException; |
| |
| /** |
| * Determines whether the supplied Principal is a "role principal". |
| * |
| * @param principal the principal to test |
| * @return {@code true} if the Principal is of type {@link GroupPrincipal} or {@link Role}, {@code false} otherwise. |
| */ |
| static boolean isRolePrincipal( final Principal principal ) { |
| return principal instanceof Role || principal instanceof GroupPrincipal; |
| } |
| |
| /** |
| * Determines whether the supplied Principal is a "user principal". |
| * |
| * @param principal the principal to test |
| * @return {@code false} if the Principal is of type {@link GroupPrincipal} or {@link Role}, {@code true} otherwise. |
| */ |
| static boolean isUserPrincipal( final Principal principal ) { |
| return !isRolePrincipal( principal ); |
| } |
| |
| /** |
| * Returns the first Principal in a set that isn't a {@link Role} or {@link GroupPrincipal}. |
| * |
| * @param principals the principal set |
| * @return the login principal |
| */ |
| default Principal getLoginPrincipal( final Set< Principal > principals ) { |
| for( final Principal principal : principals ) { |
| if ( isUserPrincipal( principal ) ) { |
| return principal; |
| } |
| } |
| return null; |
| } |
| |
| // events processing ....................................................... |
| |
| /** |
| * Registers a WikiEventListener with this instance. This is a convenience method. |
| * |
| * @param listener the event listener |
| */ |
| void addWikiEventListener( WikiEventListener listener ); |
| |
| /** |
| * Un-registers a WikiEventListener with this instance. This is a convenience method. |
| * |
| * @param listener the event listener |
| */ |
| void removeWikiEventListener( final WikiEventListener listener ); |
| |
| /** |
| * Fires a WikiSecurityEvent of the provided type, Principal and target Object to all registered listeners. |
| * |
| * @see org.apache.wiki.event.WikiSecurityEvent |
| * @param type the event type to be fired |
| * @param principal the subject of the event, which may be <code>null</code> |
| * @param target the changed Object, which may be <code>null</code> |
| */ |
| default void fireEvent( final int type, final Principal principal, final Object target ) { |
| if ( WikiEventManager.isListening( this ) ) { |
| WikiEventManager.fireEvent( this, new WikiSecurityEvent( this, type, principal, target ) ); |
| } |
| } |
| |
| } |