This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,487 @@
/*
* 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.catalina.realm;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.management.ObjectName;
import org.apache.catalina.Container;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Realm;
import org.apache.catalina.Wrapper;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
/**
* Realm implementation that contains one or more realms. Authentication is
* attempted for each realm in the order they were configured. If any realm
* authenticates the user then the authentication succeeds. When combining
* realms usernames should be unique across all combined realms.
*/
public class CombinedRealm extends RealmBase {
private static final Log log = LogFactory.getLog(CombinedRealm.class);
/**
* The list of Realms contained by this Realm.
*/
protected final List<Realm> realms = new LinkedList<>();
/**
* Descriptive information about this Realm implementation.
* @deprecated This will be removed in Tomcat 9 onwards.
*/
@Deprecated
protected static final String name = "CombinedRealm";
/**
* Add a realm to the list of realms that will be used to authenticate
* users.
* @param theRealm realm which should be wrapped by the combined realm
*/
public void addRealm(Realm theRealm) {
realms.add(theRealm);
if (log.isDebugEnabled()) {
sm.getString("combinedRealm.addRealm",
theRealm.getClass().getName(),
Integer.toString(realms.size()));
}
}
/**
* @return the set of Realms that this Realm is wrapping
*/
public ObjectName[] getRealms() {
ObjectName[] result = new ObjectName[realms.size()];
for (Realm realm : realms) {
if (realm instanceof RealmBase) {
result[realms.indexOf(realm)] =
((RealmBase) realm).getObjectName();
}
}
return result;
}
/**
* @return the list of Realms contained by this Realm.
*/
public Realm[] getNestedRealms() {
return realms.toArray(new Realm[0]);
}
/**
* Return the Principal associated with the specified username, which
* matches the digest calculated using the given parameters using the
* method described in RFC 2069; otherwise return <code>null</code>.
*
* @param username Username of the Principal to look up
* @param clientDigest Digest which has been submitted by the client
* @param nonce Unique (or supposedly unique) token which has been used
* for this request
* @param realmName Realm name
* @param md5a2 Second MD5 digest used to calculate the digest :
* MD5(Method + ":" + uri)
*/
@Override
public Principal authenticate(String username, String clientDigest,
String nonce, String nc, String cnonce, String qop,
String realmName, String md5a2) {
Principal authenticatedUser = null;
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart", username,
realm.getClass().getName()));
}
authenticatedUser = realm.authenticate(username, clientDigest, nonce,
nc, cnonce, qop, realmName, md5a2);
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail", username,
realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
username, realm.getClass().getName()));
}
break;
}
}
return authenticatedUser;
}
/**
* Return the Principal associated with the specified user name otherwise
* return <code>null</code>.
*
* @param username User name of the Principal to look up
*/
@Override
public Principal authenticate(String username) {
Principal authenticatedUser = null;
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart", username,
realm.getClass().getName()));
}
authenticatedUser = realm.authenticate(username);
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail", username,
realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
username, realm.getClass().getName()));
}
break;
}
}
return authenticatedUser;
}
/**
* Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in
* authenticating this username
*/
@Override
public Principal authenticate(String username, String credentials) {
Principal authenticatedUser = null;
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart", username,
realm.getClass().getName()));
}
authenticatedUser = realm.authenticate(username, credentials);
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail", username,
realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
username, realm.getClass().getName()));
}
break;
}
}
return authenticatedUser;
}
/**
* Set the Container with which this Realm has been associated.
*
* @param container The associated Container
*/
@Override
public void setContainer(Container container) {
for(Realm realm : realms) {
// Set the realmPath for JMX naming
if (realm instanceof RealmBase) {
((RealmBase) realm).setRealmPath(
getRealmPath() + "/realm" + realms.indexOf(realm));
}
// Set the container for sub-realms. Mainly so logging works.
realm.setContainer(container);
}
super.setContainer(container);
}
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
// Start 'sub-realms' then this one
Iterator<Realm> iter = realms.iterator();
while (iter.hasNext()) {
Realm realm = iter.next();
if (realm instanceof Lifecycle) {
try {
((Lifecycle) realm).start();
} catch (LifecycleException e) {
// If realm doesn't start can't authenticate against it
iter.remove();
log.error(sm.getString("combinedRealm.realmStartFail",
realm.getClass().getName()), e);
}
}
}
super.startInternal();
}
/**
* Gracefully terminate the active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
@Override
protected void stopInternal() throws LifecycleException {
// Stop this realm, then the sub-realms (reverse order to start)
super.stopInternal();
for (Realm realm : realms) {
if (realm instanceof Lifecycle) {
((Lifecycle) realm).stop();
}
}
}
/**
* Ensure child Realms are destroyed when this Realm is destroyed.
*/
@Override
protected void destroyInternal() throws LifecycleException {
for (Realm realm : realms) {
if (realm instanceof Lifecycle) {
((Lifecycle) realm).destroy();
}
}
super.destroyInternal();
}
/**
* Delegate the backgroundProcess call to all sub-realms.
*/
@Override
public void backgroundProcess() {
super.backgroundProcess();
for (Realm r : realms) {
r.backgroundProcess();
}
}
/**
* Return the Principal associated with the specified chain of X509
* client certificates. If there is none, return <code>null</code>.
*
* @param certs Array of client certificates, with the first one in
* the array being the certificate of the client itself.
*/
@Override
public Principal authenticate(X509Certificate[] certs) {
Principal authenticatedUser = null;
String username = null;
if (certs != null && certs.length >0) {
username = certs[0].getSubjectDN().getName();
}
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart", username,
realm.getClass().getName()));
}
authenticatedUser = realm.authenticate(certs);
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail", username,
realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
username, realm.getClass().getName()));
}
break;
}
}
return authenticatedUser;
}
/**
* {@inheritDoc}
*/
@Override
public Principal authenticate(GSSContext gssContext, boolean storeCred) {
if (gssContext.isEstablished()) {
Principal authenticatedUser = null;
GSSName gssName = null;
try {
gssName = gssContext.getSrcName();
} catch (GSSException e) {
log.warn(sm.getString("realmBase.gssNameFail"), e);
return null;
}
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart",
gssName, realm.getClass().getName()));
}
authenticatedUser = realm.authenticate(gssContext, storeCred);
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail",
gssName, realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
gssName, realm.getClass().getName()));
}
break;
}
}
return authenticatedUser;
}
// Fail in all other cases
return null;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("deprecation")
@Override
public Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
Principal authenticatedUser = null;
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart",
gssName, realm.getClass().getName()));
}
if (!(realm instanceof org.apache.catalina.GSSRealm)) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail",
gssName, realm.getClass().getName()));
}
continue;
}
authenticatedUser = ((org.apache.catalina.GSSRealm) realm).authenticate(gssName, gssCredential);
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail",
gssName, realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
gssName, realm.getClass().getName()));
}
break;
}
}
return authenticatedUser;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasRole(Wrapper wrapper, Principal principal, String role) {
for (Realm realm : realms) {
if (realm.hasRole(wrapper, principal, role)) {
return true;
}
}
return false;
}
@Override
@Deprecated
protected String getName() {
return name;
}
@Override
protected String getPassword(String username) {
// This method should never be called
// Stack trace will show where this was called from
UnsupportedOperationException uoe =
new UnsupportedOperationException(
sm.getString("combinedRealm.getPassword"));
log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe);
throw uoe;
}
@Override
protected Principal getPrincipal(String username) {
// This method should never be called
// Stack trace will show where this was called from
UnsupportedOperationException uoe =
new UnsupportedOperationException(
sm.getString("combinedRealm.getPrincipal"));
log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe);
throw uoe;
}
@Override
public boolean isAvailable() {
for (Realm realm : realms) {
if (!realm.isAvailable()) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.catalina.realm;
/**
* Manifest constants for this Java package.
*
* @author Craig R. McClanahan
*
* @deprecated Unused. Will be removed in Tomcat 9
*/
@Deprecated
public final class Constants {
public static final String Package = "org.apache.catalina.realm";
// Authentication methods for login configuration
public static final String FORM_METHOD = "FORM";
// Form based authentication constants
public static final String FORM_ACTION = "/j_security_check";
// User data constraints for transport guarantee
public static final String NONE_TRANSPORT = "NONE";
public static final String INTEGRAL_TRANSPORT = "INTEGRAL";
public static final String CONFIDENTIAL_TRANSPORT = "CONFIDENTIAL";
}

View File

@@ -0,0 +1,587 @@
/*
* 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.catalina.realm;
import java.security.Principal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.naming.Context;
import javax.sql.DataSource;
import org.apache.catalina.LifecycleException;
import org.apache.naming.ContextBindings;
/**
*
* Implementation of <b>Realm</b> that works with any JDBC JNDI DataSource.
* See the JDBCRealm.howto for more details on how to set up the database and
* for configuration options.
*
* @author Glenn L. Nielsen
* @author Craig R. McClanahan
* @author Carson McDonald
* @author Ignacio Ortega
*/
public class DataSourceRealm extends RealmBase {
// ----------------------------------------------------- Instance Variables
/**
* The generated string for the roles PreparedStatement
*/
private String preparedRoles = null;
/**
* The generated string for the credentials PreparedStatement
*/
private String preparedCredentials = null;
/**
* The name of the JNDI JDBC DataSource
*/
protected String dataSourceName = null;
/**
* Context local datasource.
*/
protected boolean localDataSource = false;
/**
* Descriptive information about this Realm implementation.
* @deprecated This will be removed in Tomcat 9 onwards.
*/
@Deprecated
protected static final String name = "DataSourceRealm";
/**
* The column in the user role table that names a role
*/
protected String roleNameCol = null;
/**
* The column in the user table that holds the user's credentials
*/
protected String userCredCol = null;
/**
* The column in the user table that holds the user's name
*/
protected String userNameCol = null;
/**
* The table that holds the relation between user's and roles
*/
protected String userRoleTable = null;
/**
* The table that holds user data.
*/
protected String userTable = null;
/**
* Last connection attempt.
*/
private volatile boolean connectionSuccess = true;
// ------------------------------------------------------------- Properties
/**
* @return the name of the JNDI JDBC DataSource.
*/
public String getDataSourceName() {
return dataSourceName;
}
/**
* Set the name of the JNDI JDBC DataSource.
*
* @param dataSourceName the name of the JNDI JDBC DataSource
*/
public void setDataSourceName( String dataSourceName) {
this.dataSourceName = dataSourceName;
}
/**
* @return if the datasource will be looked up in the webapp JNDI Context.
*/
public boolean getLocalDataSource() {
return localDataSource;
}
/**
* Set to true to cause the datasource to be looked up in the webapp JNDI
* Context.
*
* @param localDataSource the new flag value
*/
public void setLocalDataSource(boolean localDataSource) {
this.localDataSource = localDataSource;
}
/**
* @return the column in the user role table that names a role.
*/
public String getRoleNameCol() {
return roleNameCol;
}
/**
* Set the column in the user role table that names a role.
*
* @param roleNameCol The column name
*/
public void setRoleNameCol( String roleNameCol ) {
this.roleNameCol = roleNameCol;
}
/**
* @return the column in the user table that holds the user's credentials.
*/
public String getUserCredCol() {
return userCredCol;
}
/**
* Set the column in the user table that holds the user's credentials.
*
* @param userCredCol The column name
*/
public void setUserCredCol( String userCredCol ) {
this.userCredCol = userCredCol;
}
/**
* @return the column in the user table that holds the user's name.
*/
public String getUserNameCol() {
return userNameCol;
}
/**
* Set the column in the user table that holds the user's name.
*
* @param userNameCol The column name
*/
public void setUserNameCol( String userNameCol ) {
this.userNameCol = userNameCol;
}
/**
* @return the table that holds the relation between user's and roles.
*/
public String getUserRoleTable() {
return userRoleTable;
}
/**
* Set the table that holds the relation between user's and roles.
*
* @param userRoleTable The table name
*/
public void setUserRoleTable( String userRoleTable ) {
this.userRoleTable = userRoleTable;
}
/**
* @return the table that holds user data..
*/
public String getUserTable() {
return userTable;
}
/**
* Set the table that holds user data.
*
* @param userTable The table name
*/
public void setUserTable( String userTable ) {
this.userTable = userTable;
}
// --------------------------------------------------------- Public Methods
/**
* Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*
* If there are any errors with the JDBC connection, executing
* the query or anything we return null (don't authenticate). This
* event is also logged, and the connection will be closed so that
* a subsequent request will automatically re-open it.
*
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in
* authenticating this username
* @return the associated principal, or <code>null</code> if there is none.
*/
@Override
public Principal authenticate(String username, String credentials) {
// No user or no credentials
// Can't possibly authenticate, don't bother the database then
if (username == null || credentials == null) {
return null;
}
Connection dbConnection = null;
// Ensure that we have an open database connection
dbConnection = open();
if (dbConnection == null) {
// If the db connection open fails, return "not authenticated"
return null;
}
try
{
// Acquire a Principal object for this user
return authenticate(dbConnection, username, credentials);
}
finally
{
close(dbConnection);
}
}
@Override
public boolean isAvailable() {
return connectionSuccess;
}
// -------------------------------------------------------- Package Methods
// ------------------------------------------------------ Protected Methods
/**
* Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*
* @param dbConnection The database connection to be used
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in
* authenticating this username
* @return the associated principal, or <code>null</code> if there is none.
*/
protected Principal authenticate(Connection dbConnection,
String username,
String credentials) {
// No user or no credentials
// Can't possibly authenticate, don't bother the database then
if (username == null || credentials == null) {
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure",
username));
return null;
}
// Look up the user's credentials
String dbCredentials = getPassword(dbConnection, username);
if(dbCredentials == null) {
// User was not found in the database.
// Waste a bit of time as not to reveal that the user does not exist.
getCredentialHandler().mutate(credentials);
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure",
username));
return null;
}
// Validate the user's credentials
boolean validated = getCredentialHandler().matches(credentials, dbCredentials);
if (validated) {
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("dataSourceRealm.authenticateSuccess",
username));
} else {
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure",
username));
return null;
}
ArrayList<String> list = getRoles(dbConnection, username);
// Create and return a suitable Principal for this user
return new GenericPrincipal(username, credentials, list);
}
/**
* Close the specified database connection.
*
* @param dbConnection The connection to be closed
*/
protected void close(Connection dbConnection) {
// Do nothing if the database connection is already closed
if (dbConnection == null)
return;
// Commit if not auto committed
try {
if (!dbConnection.getAutoCommit()) {
dbConnection.commit();
}
} catch (SQLException e) {
containerLog.error("Exception committing connection before closing:", e);
}
// Close this database connection, and log any errors
try {
dbConnection.close();
} catch (SQLException e) {
containerLog.error(sm.getString("dataSourceRealm.close"), e); // Just log it here
}
}
/**
* Open the specified database connection.
*
* @return Connection to the database
*/
protected Connection open() {
try {
Context context = null;
if (localDataSource) {
context = ContextBindings.getClassLoader();
context = (Context) context.lookup("comp/env");
} else {
context = getServer().getGlobalNamingContext();
}
DataSource dataSource = (DataSource)context.lookup(dataSourceName);
Connection connection = dataSource.getConnection();
connectionSuccess = true;
return connection;
} catch (Exception e) {
connectionSuccess = false;
// Log the problem for posterity
containerLog.error(sm.getString("dataSourceRealm.exception"), e);
}
return null;
}
@Override
@Deprecated
protected String getName() {
return name;
}
/**
* @return the password associated with the given principal's user name.
*/
@Override
protected String getPassword(String username) {
Connection dbConnection = null;
// Ensure that we have an open database connection
dbConnection = open();
if (dbConnection == null) {
return null;
}
try {
return getPassword(dbConnection, username);
} finally {
close(dbConnection);
}
}
/**
* Return the password associated with the given principal's user name.
*
* @param dbConnection The database connection to be used
* @param username Username for which password should be retrieved
*
* @return the password for the specified user
*/
protected String getPassword(Connection dbConnection, String username) {
String dbCredentials = null;
try (PreparedStatement stmt = dbConnection.prepareStatement(preparedCredentials)) {
stmt.setString(1, username);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
dbCredentials = rs.getString(1);
}
return (dbCredentials != null) ? dbCredentials.trim() : null;
}
} catch (SQLException e) {
containerLog.error(sm.getString("dataSourceRealm.getPassword.exception", username), e);
}
return null;
}
/**
* Return the Principal associated with the given user name.
* @param username the user name
* @return the principal object
*/
@Override
protected Principal getPrincipal(String username) {
Connection dbConnection = open();
if (dbConnection == null) {
return new GenericPrincipal(username, null, null);
}
try {
return new GenericPrincipal(username,
getPassword(dbConnection, username),
getRoles(dbConnection, username));
} finally {
close(dbConnection);
}
}
/**
* Return the roles associated with the given user name.
* @param username User name for which roles should be retrieved
* @return an array list of the role names
*/
protected ArrayList<String> getRoles(String username) {
Connection dbConnection = null;
// Ensure that we have an open database connection
dbConnection = open();
if (dbConnection == null) {
return null;
}
try {
return getRoles(dbConnection, username);
} finally {
close(dbConnection);
}
}
/**
* Return the roles associated with the given user name.
*
* @param dbConnection The database connection to be used
* @param username User name for which roles should be retrieved
*
* @return an array list of the role names
*/
protected ArrayList<String> getRoles(Connection dbConnection, String username) {
if (allRolesMode != AllRolesMode.STRICT_MODE && !isRoleStoreDefined()) {
// Using an authentication only configuration and no role store has
// been defined so don't spend cycles looking
return null;
}
ArrayList<String> list = null;
try (PreparedStatement stmt = dbConnection.prepareStatement(preparedRoles)) {
stmt.setString(1, username);
try (ResultSet rs = stmt.executeQuery()) {
list = new ArrayList<>();
while (rs.next()) {
String role = rs.getString(1);
if (role != null) {
list.add(role.trim());
}
}
return list;
}
} catch(SQLException e) {
containerLog.error(sm.getString("dataSourceRealm.getRoles.exception", username), e);
}
return null;
}
private boolean isRoleStoreDefined() {
return userRoleTable != null || roleNameCol != null;
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
// Create the roles PreparedStatement string
StringBuilder temp = new StringBuilder("SELECT ");
temp.append(roleNameCol);
temp.append(" FROM ");
temp.append(userRoleTable);
temp.append(" WHERE ");
temp.append(userNameCol);
temp.append(" = ?");
preparedRoles = temp.toString();
// Create the credentials PreparedStatement string
temp = new StringBuilder("SELECT ");
temp.append(userCredCol);
temp.append(" FROM ");
temp.append(userTable);
temp.append(" WHERE ");
temp.append(userNameCol);
temp.append(" = ?");
preparedCredentials = temp.toString();
super.startInternal();
}
}

View File

@@ -0,0 +1,295 @@
/*
* 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.catalina.realm;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import org.apache.catalina.CredentialHandler;
import org.apache.juli.logging.Log;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.res.StringManager;
/**
* Base implementation for the Tomcat provided {@link CredentialHandler}s.
*/
public abstract class DigestCredentialHandlerBase implements CredentialHandler {
protected static final StringManager sm =
StringManager.getManager(DigestCredentialHandlerBase.class);
public static final int DEFAULT_SALT_LENGTH = 32;
private int iterations = getDefaultIterations();
private int saltLength = getDefaultSaltLength();
private final Object randomLock = new Object();
private volatile Random random = null;
private boolean logInvalidStoredCredentials = false;
/**
* @return the number of iterations of the associated algorithm that will be
* used when creating a new stored credential for a given input credential.
*/
public int getIterations() {
return iterations;
}
/**
* Set the number of iterations of the associated algorithm that will be
* used when creating a new stored credential for a given input credential.
* @param iterations the iterations count
*/
public void setIterations(int iterations) {
this.iterations = iterations;
}
/**
* @return the salt length that will be used when creating a new stored
* credential for a given input credential.
*/
public int getSaltLength() {
return saltLength;
}
/**
* Set the salt length that will be used when creating a new stored
* credential for a given input credential.
* @param saltLength the salt length
*/
public void setSaltLength(int saltLength) {
this.saltLength = saltLength;
}
/**
* When checking input credentials against stored credentials will a warning
* message be logged if invalid stored credentials are discovered?
* @return <code>true</code> if logging will occur
*/
public boolean getLogInvalidStoredCredentials() {
return logInvalidStoredCredentials;
}
/**
* Set whether a warning message will be logged if invalid stored
* credentials are discovered while checking input credentials against
* stored credentials?
* @param logInvalidStoredCredentials <code>true</code> to log, the
* default value is <code>false</code>
*/
public void setLogInvalidStoredCredentials(boolean logInvalidStoredCredentials) {
this.logInvalidStoredCredentials = logInvalidStoredCredentials;
}
@Override
public String mutate(String userCredential) {
byte[] salt = null;
int iterations = getIterations();
int saltLength = getSaltLength();
if (saltLength == 0) {
salt = new byte[0];
} else if (saltLength > 0) {
// Double checked locking. OK since random is volatile.
if (random == null) {
synchronized (randomLock) {
if (random == null) {
random = new SecureRandom();
}
}
}
salt = new byte[saltLength];
// Concurrent use of this random is unlikely to be a performance
// issue as it is only used during stored password generation.
random.nextBytes(salt);
}
String serverCredential = mutate(userCredential, salt, iterations);
// Failed to generate server credential from user credential. Points to
// a configuration issue. The root cause should have been logged in the
// mutate() method.
if (serverCredential == null) {
return null;
}
if (saltLength == 0 && iterations == 1) {
// Output the simple/old format for backwards compatibility
return serverCredential;
} else {
StringBuilder result =
new StringBuilder((saltLength << 1) + 10 + serverCredential.length() + 2);
result.append(HexUtils.toHexString(salt));
result.append('$');
result.append(iterations);
result.append('$');
result.append(serverCredential);
return result.toString();
}
}
/**
* Checks whether the provided credential matches the stored credential when
* the stored credential is in the form salt$iteration-count$credential
*
* @param inputCredentials The input credential
* @param storedCredentials The stored credential
*
* @return <code>true</code> if they match, otherwise <code>false</code>
*/
protected boolean matchesSaltIterationsEncoded(String inputCredentials,
String storedCredentials) {
if (storedCredentials == null) {
// Stored credentials are invalid
// This may be expected if nested credential handlers are being used
logInvalidStoredCredentials(null);
return false;
}
int sep1 = storedCredentials.indexOf('$');
int sep2 = storedCredentials.indexOf('$', sep1 + 1);
if (sep1 < 0 || sep2 < 0) {
// Stored credentials are invalid
// This may be expected if nested credential handlers are being used
logInvalidStoredCredentials(storedCredentials);
return false;
}
String hexSalt = storedCredentials.substring(0, sep1);
int iterations = Integer.parseInt(storedCredentials.substring(sep1 + 1, sep2));
String storedHexEncoded = storedCredentials.substring(sep2 + 1);
byte[] salt;
try {
salt = HexUtils.fromHexString(hexSalt);
} catch (IllegalArgumentException iae) {
logInvalidStoredCredentials(storedCredentials);
return false;
}
String inputHexEncoded = mutate(inputCredentials, salt, iterations,
HexUtils.fromHexString(storedHexEncoded).length * Byte.SIZE);
if (inputHexEncoded == null) {
// Failed to mutate user credentials. Automatic fail.
// Root cause should be logged by mutate()
return false;
}
return storedHexEncoded.equalsIgnoreCase(inputHexEncoded);
}
private void logInvalidStoredCredentials(String storedCredentials) {
if (logInvalidStoredCredentials) {
// Logging credentials could be a security concern but they are
// invalid and that is probably a bigger problem
getLog().warn(sm.getString("credentialHandler.invalidStoredCredential",
storedCredentials));
}
}
/**
* @return the default salt length used by the {@link CredentialHandler}.
*/
protected int getDefaultSaltLength() {
return DEFAULT_SALT_LENGTH;
}
/**
* Generates the equivalent stored credentials for the given input
* credentials, salt and iterations. If the algorithm requires a key length,
* the default will be used.
*
* @param inputCredentials User provided credentials
* @param salt Salt, if any
* @param iterations Number of iterations of the algorithm associated
* with this CredentialHandler applied to the
* inputCredentials to generate the equivalent
* stored credentials
*
* @return The equivalent stored credentials for the given input
* credentials or <code>null</code> if the generation fails
*/
protected abstract String mutate(String inputCredentials, byte[] salt, int iterations);
/**
* Generates the equivalent stored credentials for the given input
* credentials, salt, iterations and key length. The default implementation
* calls ignores the key length and calls
* {@link #mutate(String, byte[], int)}. Sub-classes that use the key length
* should override this method.
*
* @param inputCredentials User provided credentials
* @param salt Salt, if any
* @param iterations Number of iterations of the algorithm associated
* with this CredentialHandler applied to the
* inputCredentials to generate the equivalent
* stored credentials
* @param keyLength Length of the produced digest in bits for
* implementations where it's applicable
*
* @return The equivalent stored credentials for the given input
* credentials or <code>null</code> if the generation fails
*/
protected String mutate(String inputCredentials, byte[] salt, int iterations, int keyLength) {
return mutate(inputCredentials, salt, iterations);
}
/**
* Set the algorithm used to convert input credentials to stored
* credentials.
* @param algorithm the algorithm
* @throws NoSuchAlgorithmException if the specified algorithm
* is not supported
*/
public abstract void setAlgorithm(String algorithm) throws NoSuchAlgorithmException;
/**
* @return the algorithm used to convert input credentials to stored
* credentials.
*/
public abstract String getAlgorithm();
/**
* @return the default number of iterations used by the
* {@link CredentialHandler}.
*/
protected abstract int getDefaultIterations();
/**
* @return the logger for the CredentialHandler instance.
*/
protected abstract Log getLog();
}

View File

@@ -0,0 +1,281 @@
/*
* 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.catalina.realm;
import java.io.Serializable;
import java.security.Principal;
import java.util.Arrays;
import java.util.List;
import javax.security.auth.login.LoginContext;
import org.apache.catalina.TomcatPrincipal;
import org.ietf.jgss.GSSCredential;
/**
* Generic implementation of <strong>java.security.Principal</strong> that
* is available for use by <code>Realm</code> implementations.
*
* @author Craig R. McClanahan
*/
public class GenericPrincipal implements TomcatPrincipal, Serializable {
private static final long serialVersionUID = 1L;
// ----------------------------------------------------------- Constructors
/**
* Construct a new Principal, associated with the specified Realm, for the
* specified username and password, with the specified role names
* (as Strings).
*
* @param name The username of the user represented by this Principal
* @param password Credentials used to authenticate this user
* @param roles List of roles (must be Strings) possessed by this user
*/
public GenericPrincipal(String name, String password, List<String> roles) {
this(name, password, roles, null);
}
/**
* Construct a new Principal, associated with the specified Realm, for the
* specified username and password, with the specified role names
* (as Strings).
*
* @param name The username of the user represented by this Principal
* @param password Credentials used to authenticate this user
* @param roles List of roles (must be Strings) possessed by this user
* @param userPrincipal - the principal to be returned from the request
* getUserPrincipal call if not null; if null, this will be returned
*/
public GenericPrincipal(String name, String password, List<String> roles,
Principal userPrincipal) {
this(name, password, roles, userPrincipal, null);
}
/**
* Construct a new Principal, associated with the specified Realm, for the
* specified username and password, with the specified role names
* (as Strings).
*
* @param name The username of the user represented by this Principal
* @param password Credentials used to authenticate this user
* @param roles List of roles (must be Strings) possessed by this user
* @param userPrincipal - the principal to be returned from the request
* getUserPrincipal call if not null; if null, this will be returned
* @param loginContext - If provided, this will be used to log out the user
* at the appropriate time
*/
public GenericPrincipal(String name, String password, List<String> roles,
Principal userPrincipal, LoginContext loginContext) {
this(name, password, roles, userPrincipal, loginContext, null);
}
/**
* Construct a new Principal, associated with the specified Realm, for the
* specified username and password, with the specified role names
* (as Strings).
*
* @param name The username of the user represented by this Principal
* @param password Credentials used to authenticate this user
* @param roles List of roles (must be Strings) possessed by this user
* @param userPrincipal - the principal to be returned from the request
* getUserPrincipal call if not null; if null, this will be returned
* @param loginContext - If provided, this will be used to log out the user
* at the appropriate time
* @param gssCredential - If provided, the user's delegated credentials
*/
public GenericPrincipal(String name, String password, List<String> roles,
Principal userPrincipal, LoginContext loginContext,
GSSCredential gssCredential) {
super();
this.name = name;
this.password = password;
this.userPrincipal = userPrincipal;
if (roles == null) {
this.roles = new String[0];
} else {
this.roles = roles.toArray(new String[roles.size()]);
if (this.roles.length > 1) {
Arrays.sort(this.roles);
}
}
this.loginContext = loginContext;
this.gssCredential = gssCredential;
}
// -------------------------------------------------------------- Properties
/**
* The username of the user represented by this Principal.
*/
protected final String name;
@Override
public String getName() {
return this.name;
}
/**
* The authentication credentials for the user represented by
* this Principal.
*/
protected final String password;
public String getPassword() {
return this.password;
}
/**
* The set of roles associated with this user.
*/
protected final String roles[];
public String[] getRoles() {
return this.roles;
}
/**
* The authenticated Principal to be exposed to applications.
*/
protected final Principal userPrincipal;
@Override
public Principal getUserPrincipal() {
if (userPrincipal != null) {
return userPrincipal;
} else {
return this;
}
}
/**
* The JAAS LoginContext, if any, used to authenticate this Principal.
* Kept so we can call logout().
*/
protected final transient LoginContext loginContext;
/**
* The user's delegated credentials.
*/
protected transient GSSCredential gssCredential = null;
@Override
public GSSCredential getGssCredential() {
return this.gssCredential;
}
protected void setGssCredential(GSSCredential gssCredential) {
this.gssCredential = gssCredential;
}
// ---------------------------------------------------------- Public Methods
/**
* Does the user represented by this Principal possess the specified role?
*
* @param role Role to be tested
*
* @return <code>true</code> if this Principal has been assigned the given
* role, otherwise <code>false</code>
*/
public boolean hasRole(String role) {
if ("*".equals(role)) { // Special 2.4 role meaning everyone
return true;
}
if (role == null) {
return false;
}
return Arrays.binarySearch(roles, role) >= 0;
}
/**
* Return a String representation of this object, which exposes only
* information that should be public.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("GenericPrincipal[");
sb.append(this.name);
sb.append("(");
for (int i = 0; i < roles.length; i++ ) {
sb.append( roles[i]).append(",");
}
sb.append(")]");
return sb.toString();
}
/**
* Calls logout, if necessary, on any associated JAASLoginContext. May in
* the future be extended to cover other logout requirements.
*
* @throws Exception If something goes wrong with the logout. Uses Exception
* to allow for future expansion of this method to cover
* other logout mechanisms that might throw a different
* exception to LoginContext
*/
@Override
public void logout() throws Exception {
if (loginContext != null) {
loginContext.logout();
}
if (gssCredential != null) {
gssCredential.dispose();
}
}
// ----------------------------------------------------------- Serialization
private Object writeReplace() {
return new SerializablePrincipal(name, password, roles, userPrincipal);
}
private static class SerializablePrincipal implements Serializable {
private static final long serialVersionUID = 1L;
private final String name;
private final String password;
private final String[] roles;
private final Principal principal;
public SerializablePrincipal(String name, String password, String[] roles,
Principal principal) {
this.name = name;
this.password = password;
this.roles = roles;
if (principal instanceof Serializable) {
this.principal = principal;
} else {
this.principal = null;
}
}
private Object readResolve() {
return new GenericPrincipal(name, password, Arrays.asList(roles), principal);
}
}
}

View File

@@ -0,0 +1,227 @@
/*
* 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.catalina.realm;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.tomcat.util.res.StringManager;
/**
* <p>Implementation of the JAAS <code>CallbackHandler</code> interface,
* used to negotiate delivery of the username and credentials that were
* specified to our constructor. No interaction with the user is required
* (or possible).</p>
*
* <p>This <code>CallbackHandler</code> will pre-digest the supplied
* password, if required by the <code>&lt;Realm&gt;</code> element in
* <code>server.xml</code>.</p>
* <p>At present, <code>JAASCallbackHandler</code> knows how to handle callbacks of
* type <code>javax.security.auth.callback.NameCallback</code> and
* <code>javax.security.auth.callback.PasswordCallback</code>.</p>
*
* @author Craig R. McClanahan
* @author Andrew R. Jaquith
*/
public class JAASCallbackHandler implements CallbackHandler {
// ------------------------------------------------------------ Constructor
/**
* Construct a callback handler configured with the specified values.
* Note that if the <code>JAASRealm</code> instance specifies digested passwords,
* the <code>password</code> parameter will be pre-digested here.
*
* @param realm Our associated JAASRealm instance
* @param username Username to be authenticated with
* @param password Password to be authenticated with
*/
public JAASCallbackHandler(JAASRealm realm, String username,
String password) {
this(realm, username, password, null, null, null, null, null, null,
null);
}
/**
* Construct a callback handler for DIGEST authentication.
*
* @param realm Our associated JAASRealm instance
* @param username Username to be authenticated with
* @param password Password to be authenticated with
* @param nonce Server generated nonce
* @param nc Nonce count
* @param cnonce Client generated nonce
* @param qop Quality of protection applied to the message
* @param realmName Realm name
* @param md5a2 Second MD5 digest used to calculate the digest
* MD5(Method + ":" + uri)
* @param authMethod The authentication method in use
*/
public JAASCallbackHandler(JAASRealm realm, String username,
String password, String nonce, String nc,
String cnonce, String qop, String realmName,
String md5a2, String authMethod) {
this.realm = realm;
this.username = username;
if (realm.hasMessageDigest()) {
this.password = realm.getCredentialHandler().mutate(password);
}
else {
this.password = password;
}
this.nonce = nonce;
this.nc = nc;
this.cnonce = cnonce;
this.qop = qop;
this.realmName = realmName;
this.md5a2 = md5a2;
this.authMethod = authMethod;
}
// ----------------------------------------------------- Instance Variables
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(JAASCallbackHandler.class);
/**
* The password to be authenticated with.
*/
protected final String password;
/**
* The associated <code>JAASRealm</code> instance.
*/
protected final JAASRealm realm;
/**
* The username to be authenticated with.
*/
protected final String username;
/**
* Server generated nonce.
*/
protected final String nonce;
/**
* Nonce count.
*/
protected final String nc;
/**
* Client generated nonce.
*/
protected final String cnonce;
/**
* Quality of protection applied to the message.
*/
protected final String qop;
/**
* Realm name.
*/
protected final String realmName;
/**
* Second MD5 digest.
*/
protected final String md5a2;
/**
* The authentication method to be used. If null, assume BASIC/FORM.
*/
protected final String authMethod;
// --------------------------------------------------------- Public Methods
/**
* Retrieve the information requested in the provided <code>Callbacks</code>.
* This implementation only recognizes {@link NameCallback},
* {@link PasswordCallback} and {@link TextInputCallback}.
* {@link TextInputCallback} is used to pass the various additional
* parameters required for DIGEST authentication.
*
* @param callbacks The set of <code>Callback</code>s to be processed
*
* @exception IOException if an input/output error occurs
* @exception UnsupportedCallbackException if the login method requests
* an unsupported callback type
*/
@Override
public void handle(Callback callbacks[])
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
if (realm.getContainer().getLogger().isTraceEnabled())
realm.getContainer().getLogger().trace(sm.getString("jaasCallback.username", username));
((NameCallback) callbacks[i]).setName(username);
} else if (callbacks[i] instanceof PasswordCallback) {
final char[] passwordcontents;
if (password != null) {
passwordcontents = password.toCharArray();
} else {
passwordcontents = new char[0];
}
((PasswordCallback) callbacks[i]).setPassword
(passwordcontents);
} else if (callbacks[i] instanceof TextInputCallback) {
TextInputCallback cb = ((TextInputCallback) callbacks[i]);
if (cb.getPrompt().equals("nonce")) {
cb.setText(nonce);
} else if (cb.getPrompt().equals("nc")) {
cb.setText(nc);
} else if (cb.getPrompt().equals("cnonce")) {
cb.setText(cnonce);
} else if (cb.getPrompt().equals("qop")) {
cb.setText(qop);
} else if (cb.getPrompt().equals("realmName")) {
cb.setText(realmName);
} else if (cb.getPrompt().equals("md5a2")) {
cb.setText(md5a2);
} else if (cb.getPrompt().equals("authMethod")) {
cb.setText(authMethod);
} else if (cb.getPrompt().equals("catalinaBase")) {
cb.setText(realm.getContainer().getCatalinaBase().getAbsolutePath());
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
}

View File

@@ -0,0 +1,432 @@
/*
* 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.catalina.realm;
import java.io.File;
import java.io.IOException;
import java.security.Principal;
import java.util.Map;
import java.util.Map.Entry;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.CredentialHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.digester.Digester;
/**
* <p>Implementation of the JAAS <strong>LoginModule</strong> interface,
* primarily for use in testing <code>JAASRealm</code>. It utilizes an
* XML-format data file of username/password/role information identical to
* that supported by <code>org.apache.catalina.realm.MemoryRealm</code>.</p>
*
* <p>This class recognizes the following string-valued options, which are
* specified in the configuration file and passed to {@link
* #initialize(Subject, CallbackHandler, Map, Map)} in the <code>options</code>
* argument:</p>
* <ul>
* <li><strong>pathname</strong> - Relative (to the pathname specified by the
* "catalina.base" system property) or absolute pathname to the
* XML file containing our user information, in the format supported by
* {@link MemoryRealm}. The default value matches the MemoryRealm
* default.</li>
* <li><strong>credentialHandlerClassName</strong> - The fully qualified class
* name of the CredentialHandler to use. If not specified, {@link
* MessageDigestCredentialHandler} will be used.</li>
* <li>Any additional options will be used to identify and call setters on the
* {@link CredentialHandler}. For example, <code>algorithm=SHA256</code>
* would result in a call to {@link
* MessageDigestCredentialHandler#setAlgorithm(String)} with a parameter of
* <code>"SHA256"</code></li>
* </ul>
*
* <p><strong>IMPLEMENTATION NOTE</strong> - This class implements
* <code>Realm</code> only to satisfy the calling requirements of the
* <code>GenericPrincipal</code> constructor. It does not actually perform
* the functionality required of a <code>Realm</code> implementation.</p>
*
* @author Craig R. McClanahan
*/
public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
// We need to extend MemoryRealm to avoid class cast
private static final Log log = LogFactory.getLog(JAASMemoryLoginModule.class);
// ----------------------------------------------------- Instance Variables
/**
* The callback handler responsible for answering our requests.
*/
protected CallbackHandler callbackHandler = null;
/**
* Has our own <code>commit()</code> returned successfully?
*/
protected boolean committed = false;
/**
* The configuration information for this <code>LoginModule</code>.
*/
protected Map<String,?> options = null;
/**
* The absolute or relative pathname to the XML configuration file.
*/
protected String pathname = "conf/tomcat-users.xml";
/**
* The <code>Principal</code> identified by our validation, or
* <code>null</code> if validation failed.
*/
protected Principal principal = null;
/**
* The state information that is shared with other configured
* <code>LoginModule</code> instances.
*/
protected Map<String,?> sharedState = null;
/**
* The subject for which we are performing authentication.
*/
protected Subject subject = null;
// --------------------------------------------------------- Public Methods
public JAASMemoryLoginModule() {
if (log.isDebugEnabled()) {
log.debug("MEMORY LOGIN MODULE");
}
}
/**
* Phase 2 of authenticating a <code>Subject</code> when Phase 1
* fails. This method is called if the <code>LoginContext</code>
* failed somewhere in the overall authentication chain.
*
* @return <code>true</code> if this method succeeded, or
* <code>false</code> if this <code>LoginModule</code> should be
* ignored
*
* @exception LoginException if the abort fails
*/
@Override
public boolean abort() throws LoginException {
// If our authentication was not successful, just return false
if (principal == null) {
return false;
}
// Clean up if overall authentication failed
if (committed) {
logout();
} else {
committed = false;
principal = null;
}
if (log.isDebugEnabled()) {
log.debug("Abort");
}
return true;
}
/**
* Phase 2 of authenticating a <code>Subject</code> when Phase 1
* was successful. This method is called if the <code>LoginContext</code>
* succeeded in the overall authentication chain.
*
* @return <code>true</code> if the authentication succeeded, or
* <code>false</code> if this <code>LoginModule</code> should be
* ignored
*
* @exception LoginException if the commit fails
*/
@Override
public boolean commit() throws LoginException {
if (log.isDebugEnabled()) {
log.debug("commit " + principal);
}
// If authentication was not successful, just return false
if (principal == null) {
return false;
}
// Add our Principal to the Subject if needed
if (!subject.getPrincipals().contains(principal)) {
subject.getPrincipals().add(principal);
// Add the roles as additional subjects as per the contract with the
// JAASRealm
if (principal instanceof GenericPrincipal) {
String roles[] = ((GenericPrincipal) principal).getRoles();
for (int i = 0; i < roles.length; i++) {
subject.getPrincipals().add(new GenericPrincipal(roles[i], null, null));
}
}
}
committed = true;
return true;
}
/**
* Initialize this <code>LoginModule</code> with the specified
* configuration information.
*
* @param subject The <code>Subject</code> to be authenticated
* @param callbackHandler A <code>CallbackHandler</code> for communicating
* with the end user as necessary
* @param sharedState State information shared with other
* <code>LoginModule</code> instances
* @param options Configuration information for this specific
* <code>LoginModule</code> instance
*/
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String,?> sharedState, Map<String,?> options) {
if (log.isDebugEnabled()) {
log.debug("Init");
}
// Save configuration values
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
// Perform instance-specific initialization
Object option = options.get("pathname");
if (option instanceof String) {
this.pathname = (String) option;
}
CredentialHandler credentialHandler = null;
option = options.get("credentialHandlerClassName");
if (option instanceof String) {
try {
Class<?> clazz = Class.forName((String) option);
credentialHandler = (CredentialHandler) clazz.getConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new IllegalArgumentException(e);
}
}
if (credentialHandler == null) {
credentialHandler = new MessageDigestCredentialHandler();
}
for (Entry<String,?> entry : options.entrySet()) {
if ("pathname".equals(entry.getKey())) {
continue;
}
if ("credentialHandlerClassName".equals(entry.getKey())) {
continue;
}
// Skip any non-String values since any value we are interested in
// will be a String.
if (entry.getValue() instanceof String) {
IntrospectionUtils.setProperty(credentialHandler, entry.getKey(),
(String) entry.getValue());
}
}
setCredentialHandler(credentialHandler);
// Load our defined Principals
load();
}
/**
* Phase 1 of authenticating a <code>Subject</code>.
*
* @return <code>true</code> if the authentication succeeded, or
* <code>false</code> if this <code>LoginModule</code> should be
* ignored
*
* @exception LoginException if the authentication fails
*/
@Override
public boolean login() throws LoginException {
// Set up our CallbackHandler requests
if (callbackHandler == null)
throw new LoginException("No CallbackHandler specified");
Callback callbacks[] = new Callback[9];
callbacks[0] = new NameCallback("Username: ");
callbacks[1] = new PasswordCallback("Password: ", false);
callbacks[2] = new TextInputCallback("nonce");
callbacks[3] = new TextInputCallback("nc");
callbacks[4] = new TextInputCallback("cnonce");
callbacks[5] = new TextInputCallback("qop");
callbacks[6] = new TextInputCallback("realmName");
callbacks[7] = new TextInputCallback("md5a2");
callbacks[8] = new TextInputCallback("authMethod");
// Interact with the user to retrieve the username and password
String username = null;
String password = null;
String nonce = null;
String nc = null;
String cnonce = null;
String qop = null;
String realmName = null;
String md5a2 = null;
String authMethod = null;
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password =
new String(((PasswordCallback) callbacks[1]).getPassword());
nonce = ((TextInputCallback) callbacks[2]).getText();
nc = ((TextInputCallback) callbacks[3]).getText();
cnonce = ((TextInputCallback) callbacks[4]).getText();
qop = ((TextInputCallback) callbacks[5]).getText();
realmName = ((TextInputCallback) callbacks[6]).getText();
md5a2 = ((TextInputCallback) callbacks[7]).getText();
authMethod = ((TextInputCallback) callbacks[8]).getText();
} catch (IOException | UnsupportedCallbackException e) {
throw new LoginException(e.toString());
}
// Validate the username and password we have received
if (authMethod == null) {
// BASIC or FORM
principal = super.authenticate(username, password);
} else if (authMethod.equals(HttpServletRequest.DIGEST_AUTH)) {
principal = super.authenticate(username, password, nonce, nc,
cnonce, qop, realmName, md5a2);
} else if (authMethod.equals(HttpServletRequest.CLIENT_CERT_AUTH)) {
principal = super.getPrincipal(username);
} else {
throw new LoginException("Unknown authentication method");
}
if (log.isDebugEnabled()) {
log.debug("login " + username + " " + principal);
}
// Report results based on success or failure
if (principal != null) {
return true;
} else {
throw new FailedLoginException("Username or password is incorrect");
}
}
/**
* Log out this user.
*
* @return <code>true</code> in all cases because the
* <code>LoginModule</code> should not be ignored
*
* @exception LoginException if logging out failed
*/
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(principal);
committed = false;
principal = null;
return true;
}
// ---------------------------------------------------------- Realm Methods
// ------------------------------------------------------ Protected Methods
/**
* Load the contents of our configuration file.
*/
protected void load() {
// Validate the existence of our configuration file
File file = new File(pathname);
if (!file.isAbsolute()) {
String catalinaBase = getCatalinaBase();
if (catalinaBase == null) {
log.warn("Unable to determine Catalina base to load file " + pathname);
return;
} else {
file = new File(catalinaBase, pathname);
}
}
if (!file.canRead()) {
log.warn("Cannot load configuration file " + file.getAbsolutePath());
return;
}
// Load the contents of our configuration file
Digester digester = new Digester();
digester.setValidating(false);
digester.addRuleSet(new MemoryRuleSet());
try {
digester.push(this);
digester.parse(file);
} catch (Exception e) {
log.warn("Error processing configuration file " + file.getAbsolutePath(), e);
return;
} finally {
digester.reset();
}
}
private String getCatalinaBase() {
// Have to get this via a callback as that is the only link we have back
// to the defining Realm. Can't use the system property as that may not
// be set/correct in an embedded scenario
if (callbackHandler == null) {
return null;
}
Callback callbacks[] = new Callback[1];
callbacks[0] = new TextInputCallback("catalinaBase");
String result = null;
try {
callbackHandler.handle(callbacks);
result = ((TextInputCallback) callbacks[0]).getText();
} catch (IOException | UnsupportedCallbackException e) {
return null;
}
return result;
}
}

View File

@@ -0,0 +1,697 @@
/*
* 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.catalina.realm;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.CredentialExpiredException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
/**
* <p>Implementation of <b>Realm</b> that authenticates users via the <em>Java
* Authentication and Authorization Service</em> (JAAS). JAAS support requires
* either JDK 1.4 (which includes it as part of the standard platform) or
* JDK 1.3 (with the plug-in <code>jaas.jar</code> file).</p>
*
* <p>The value configured for the <code>appName</code> property is passed to
* the <code>javax.security.auth.login.LoginContext</code> constructor, to
* specify the <em>application name</em> used to select the set of relevant
* <code>LoginModules</code> required.</p>
*
* <p>The JAAS Specification describes the result of a successful login as a
* <code>javax.security.auth.Subject</code> instance, which can contain zero
* or more <code>java.security.Principal</code> objects in the return value
* of the <code>Subject.getPrincipals()</code> method. However, it provides
* no guidance on how to distinguish Principals that describe the individual
* user (and are thus appropriate to return as the value of
* request.getUserPrincipal() in a web application) from the Principal(s)
* that describe the authorized roles for this user. To maintain as much
* independence as possible from the underlying <code>LoginMethod</code>
* implementation executed by JAAS, the following policy is implemented by
* this Realm:</p>
* <ul>
* <li>The JAAS <code>LoginModule</code> is assumed to return a
* <code>Subject</code> with at least one <code>Principal</code> instance
* representing the user himself or herself, and zero or more separate
* <code>Principals</code> representing the security roles authorized
* for this user.</li>
* <li>On the <code>Principal</code> representing the user, the Principal
* name is an appropriate value to return via the Servlet API method
* <code>HttpServletRequest.getRemoteUser()</code>.</li>
* <li>On the <code>Principals</code> representing the security roles, the
* name is the name of the authorized security role.</li>
* <li>This Realm will be configured with two lists of fully qualified Java
* class names of classes that implement
* <code>java.security.Principal</code> - one that identifies class(es)
* representing a user, and one that identifies class(es) representing
* a security role.</li>
* <li>As this Realm iterates over the <code>Principals</code> returned by
* <code>Subject.getPrincipals()</code>, it will identify the first
* <code>Principal</code> that matches the "user classes" list as the
* <code>Principal</code> for this user.</li>
* <li>As this Realm iterates over the <code>Principals</code> returned by
* <code>Subject.getPrincipals()</code>, it will accumulate the set of
* all <code>Principals</code> matching the "role classes" list as
* identifying the security roles for this user.</li>
* <li>It is a configuration error for the JAAS login method to return a
* validated <code>Subject</code> without a <code>Principal</code> that
* matches the "user classes" list.</li>
* <li>By default, the enclosing Container's name serves as the
* application name used to obtain the JAAS LoginContext ("Catalina" in
* a default installation). Tomcat must be able to find an application
* with this name in the JAAS configuration file. Here is a hypothetical
* JAAS configuration file entry for a database-oriented login module that uses
* a Tomcat-managed JNDI database resource:
* <blockquote><pre>Catalina {
org.foobar.auth.DatabaseLoginModule REQUIRED
JNDI_RESOURCE=jdbc/AuthDB
USER_TABLE=users
USER_ID_COLUMN=id
USER_NAME_COLUMN=name
USER_CREDENTIAL_COLUMN=password
ROLE_TABLE=roles
ROLE_NAME_COLUMN=name
PRINCIPAL_FACTORY=org.foobar.auth.impl.SimplePrincipalFactory;
};</pre></blockquote></li>
* <li>To set the JAAS configuration file
* location, set the <code>CATALINA_OPTS</code> environment variable
* similar to the following:
<blockquote><code>CATALINA_OPTS="-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config"</code></blockquote>
* </li>
* <li>As part of the login process, JAASRealm registers its own <code>CallbackHandler</code>,
* called (unsurprisingly) <code>JAASCallbackHandler</code>. This handler supplies the
* HTTP requests's username and credentials to the user-supplied <code>LoginModule</code></li>
* <li>As with other <code>Realm</code> implementations, digested passwords are supported if
* the <code>&lt;Realm&gt;</code> element in <code>server.xml</code> contains a
* <code>digest</code> attribute; <code>JAASCallbackHandler</code> will digest the password
* prior to passing it back to the <code>LoginModule</code></li>
* </ul>
*
* @author Craig R. McClanahan
* @author Yoav Shapira
*/
public class JAASRealm extends RealmBase {
private static final Log log = LogFactory.getLog(JAASRealm.class);
// ----------------------------------------------------- Instance Variables
/**
* The application name passed to the JAAS <code>LoginContext</code>,
* which uses it to select the set of relevant <code>LoginModule</code>s.
*/
protected String appName = null;
/**
* Descriptive information about this <code>Realm</code> implementation.
* @deprecated This will be removed in Tomcat 9 onwards.
*/
@Deprecated
protected static final String name = "JAASRealm";
/**
* The list of role class names, split out for easy processing.
*/
protected final List<String> roleClasses = new ArrayList<>();
/**
* The set of user class names, split out for easy processing.
*/
protected final List<String> userClasses = new ArrayList<>();
/**
* Whether to use context ClassLoader or default ClassLoader.
* True means use context ClassLoader, and True is the default
* value.
*/
protected boolean useContextClassLoader = true;
/**
* Path to find a JAAS configuration file, if not set global JVM JAAS
* configuration will be used.
*/
protected String configFile;
protected volatile Configuration jaasConfiguration;
protected volatile boolean jaasConfigurationLoaded = false;
/**
* Keeps track if JAAS invocation of login modules was successful or not. By
* default it is true unless we detect JAAS login module can't perform the
* login. This will be used for realm's {@link #isAvailable()} status so
* that {@link LockOutRealm} will not lock the user out if JAAS login
* modules are unavailable to perform the actual login.
*/
private volatile boolean invocationSuccess = true;
// ------------------------------------------------------------- Properties
/**
* @return the path of the JAAS configuration file.
*/
public String getConfigFile() {
return configFile;
}
/**
* Set the JAAS configuration file.
* @param configFile The JAAS configuration file
*/
public void setConfigFile(String configFile) {
this.configFile = configFile;
}
/**
* Set the JAAS <code>LoginContext</code> app name.
* @param name The application name that will be used to retrieve
* the set of relevant <code>LoginModule</code>s
*/
public void setAppName(String name) {
appName = name;
}
/**
* @return the application name.
*/
public String getAppName() {
return appName;
}
/**
* Sets whether to use the context or default ClassLoader.
* True means use context ClassLoader.
*
* @param useContext True means use context ClassLoader
*/
public void setUseContextClassLoader(boolean useContext) {
useContextClassLoader = useContext;
}
/**
* Returns whether to use the context or default ClassLoader.
* True means to use the context ClassLoader.
*
* @return The value of useContextClassLoader
*/
public boolean isUseContextClassLoader() {
return useContextClassLoader;
}
@Override
public void setContainer(Container container) {
super.setContainer(container);
if (appName == null) {
appName = makeLegalForJAAS(container.getName());
log.info("Set JAAS app name " + appName);
}
}
/**
* Comma-delimited list of <code>java.security.Principal</code> classes
* that represent security roles.
*/
protected String roleClassNames = null;
public String getRoleClassNames() {
return this.roleClassNames;
}
/**
* Sets the list of comma-delimited classes that represent roles. The
* classes in the list must implement <code>java.security.Principal</code>.
* The supplied list of classes will be parsed when {@link #start()} is
* called.
* @param roleClassNames The class names list
*/
public void setRoleClassNames(String roleClassNames) {
this.roleClassNames = roleClassNames;
}
/**
* Parses a comma-delimited list of class names, and store the class names
* in the provided List. Each class must implement
* <code>java.security.Principal</code>.
*
* @param classNamesString a comma-delimited list of fully qualified class names.
* @param classNamesList the list in which the class names will be stored.
* The list is cleared before being populated.
*/
protected void parseClassNames(String classNamesString, List<String> classNamesList) {
classNamesList.clear();
if (classNamesString == null) return;
ClassLoader loader = this.getClass().getClassLoader();
if (isUseContextClassLoader())
loader = Thread.currentThread().getContextClassLoader();
String[] classNames = classNamesString.split("[ ]*,[ ]*");
for (int i=0; i<classNames.length; i++) {
if (classNames[i].length()==0) continue;
try {
Class<?> principalClass = Class.forName(classNames[i], false,
loader);
if (Principal.class.isAssignableFrom(principalClass)) {
classNamesList.add(classNames[i]);
} else {
log.error("Class "+classNames[i]+" is not implementing "+
"java.security.Principal! Class not added.");
}
} catch (ClassNotFoundException e) {
log.error("Class "+classNames[i]+" not found! Class not added.");
}
}
}
/**
* Comma-delimited list of <code>java.security.Principal</code> classes
* that represent individual users.
*/
protected String userClassNames = null;
public String getUserClassNames() {
return this.userClassNames;
}
/**
* Sets the list of comma-delimited classes that represent individual
* users. The classes in the list must implement
* <code>java.security.Principal</code>. The supplied list of classes will
* be parsed when {@link #start()} is called.
* @param userClassNames The class names list
*/
public void setUserClassNames(String userClassNames) {
this.userClassNames = userClassNames;
}
// --------------------------------------------------------- Public Methods
/**
* Return the <code>Principal</code> associated with the specified username
* and credentials, if there is one; otherwise return <code>null</code>.
*
* @param username Username of the <code>Principal</code> to look up
* @param credentials Password or other credentials to use in
* authenticating this username
* @return the associated principal, or <code>null</code> if there is none.
*/
@Override
public Principal authenticate(String username, String credentials) {
return authenticate(username,
new JAASCallbackHandler(this, username, credentials));
}
/**
* Return the <code>Principal</code> associated with the specified username
* and digest, if there is one; otherwise return <code>null</code>.
*
* @param username Username of the <code>Principal</code> to look up
* @param clientDigest Digest to use in authenticating this username
* @param nonce Server generated nonce
* @param nc Nonce count
* @param cnonce Client generated nonce
* @param qop Quality of protection applied to the message
* @param realmName Realm name
* @param md5a2 Second MD5 digest used to calculate the digest
* MD5(Method + ":" + uri)
* @return the associated principal, or <code>null</code> if there is none.
*/
@Override
public Principal authenticate(String username, String clientDigest,
String nonce, String nc, String cnonce, String qop,
String realmName, String md5a2) {
return authenticate(username,
new JAASCallbackHandler(this, username, clientDigest, nonce,
nc, cnonce, qop, realmName, md5a2,
HttpServletRequest.DIGEST_AUTH));
}
// -------------------------------------------------------- Package Methods
// ------------------------------------------------------ Protected Methods
/**
* Perform the actual JAAS authentication.
* @param username The user name
* @param callbackHandler The callback handler
* @return the associated principal, or <code>null</code> if there is none.
*/
protected Principal authenticate(String username,
CallbackHandler callbackHandler) {
// Establish a LoginContext to use for authentication
try {
LoginContext loginContext = null;
if( appName==null ) appName="Tomcat";
if( log.isDebugEnabled())
log.debug(sm.getString("jaasRealm.beginLogin", username, appName));
// What if the LoginModule is in the container class loader ?
ClassLoader ocl = null;
if (!isUseContextClassLoader()) {
ocl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
}
try {
Configuration config = getConfig();
loginContext = new LoginContext(
appName, null, callbackHandler, config);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
log.error(sm.getString("jaasRealm.unexpectedError"), e);
// There is configuration issue with JAAS so mark the realm as
// unavailable
invocationSuccess = false;
return null;
} finally {
if(!isUseContextClassLoader()) {
Thread.currentThread().setContextClassLoader(ocl);
}
}
if( log.isDebugEnabled())
log.debug("Login context created " + username);
// Negotiate a login via this LoginContext
Subject subject = null;
try {
loginContext.login();
subject = loginContext.getSubject();
// We were able to perform login successfully so mark JAAS realm as
// available as it could have been set to false in prior attempts.
// Change invocationSuccess variable only when we know the outcome
// of the JAAS operation to keep variable consistent.
invocationSuccess = true;
if (subject == null) {
if( log.isDebugEnabled())
log.debug(sm.getString("jaasRealm.failedLogin", username));
return (null);
}
} catch (AccountExpiredException e) {
if (log.isDebugEnabled())
log.debug(sm.getString("jaasRealm.accountExpired", username));
// JAAS checked LoginExceptions are successful authentication
// invocations so mark JAAS realm as available
invocationSuccess = true;
return null;
} catch (CredentialExpiredException e) {
if (log.isDebugEnabled())
log.debug(sm.getString("jaasRealm.credentialExpired", username));
// JAAS checked LoginExceptions are successful authentication
// invocations so mark JAAS realm as available
invocationSuccess = true;
return null;
} catch (FailedLoginException e) {
if (log.isDebugEnabled())
log.debug(sm.getString("jaasRealm.failedLogin", username));
// JAAS checked LoginExceptions are successful authentication
// invocations so mark JAAS realm as available
invocationSuccess = true;
return null;
} catch (LoginException e) {
log.warn(sm.getString("jaasRealm.loginException", username), e);
// JAAS checked LoginExceptions are successful authentication
// invocations so mark JAAS realm as available
invocationSuccess = true;
return null;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
log.error(sm.getString("jaasRealm.unexpectedError"), e);
// JAAS throws exception different than LoginException so mark the
// realm as unavailable
invocationSuccess = false;
return null;
}
if( log.isDebugEnabled())
log.debug(sm.getString("jaasRealm.loginContextCreated", username));
// Return the appropriate Principal for this authenticated Subject
Principal principal = createPrincipal(username, subject, loginContext);
if (principal == null) {
log.debug(sm.getString("jaasRealm.authenticateFailure", username));
return null;
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("jaasRealm.authenticateSuccess", username));
}
return principal;
} catch( Throwable t) {
log.error( "error ", t);
//JAAS throws exception different than LoginException so mark the realm as unavailable
invocationSuccess = false;
return null;
}
}
@Override
@Deprecated
protected String getName() {
return name;
}
/**
* @return the password associated with the given principal's user name. This
* always returns null as the JAASRealm has no way of obtaining this
* information.
*/
@Override
protected String getPassword(String username) {
return null;
}
/**
* @return the <code>Principal</code> associated with the given user name.
*/
@Override
protected Principal getPrincipal(String username) {
return authenticate(username,
new JAASCallbackHandler(this, username, null, null, null, null,
null, null, null, HttpServletRequest.CLIENT_CERT_AUTH));
}
/**
* Identify and return a <code>java.security.Principal</code> instance
* representing the authenticated user for the specified <code>Subject</code>.
* The Principal is constructed by scanning the list of Principals returned
* by the JAASLoginModule. The first <code>Principal</code> object that matches
* one of the class names supplied as a "user class" is the user Principal.
* This object is returned to the caller.
* Any remaining principal objects returned by the LoginModules are mapped to
* roles, but only if their respective classes match one of the "role class" classes.
* If a user Principal cannot be constructed, return <code>null</code>.
* @param username The associated user name
* @param subject The <code>Subject</code> representing the logged-in user
* @param loginContext Associated with the Principal so
* {@link LoginContext#logout()} can be called later
* @return the principal object
*/
protected Principal createPrincipal(String username, Subject subject,
LoginContext loginContext) {
// Prepare to scan the Principals for this Subject
List<String> roles = new ArrayList<>();
Principal userPrincipal = null;
// Scan the Principals for this Subject
for (Principal principal : subject.getPrincipals()) {
String principalClass = principal.getClass().getName();
if( log.isDebugEnabled() ) {
log.debug(sm.getString("jaasRealm.checkPrincipal", principal, principalClass));
}
if (userPrincipal == null && userClasses.contains(principalClass)) {
userPrincipal = principal;
if( log.isDebugEnabled() ) {
log.debug(sm.getString("jaasRealm.userPrincipalSuccess", principal.getName()));
}
}
if (roleClasses.contains(principalClass)) {
roles.add(principal.getName());
if( log.isDebugEnabled() ) {
log.debug(sm.getString("jaasRealm.rolePrincipalAdd", principal.getName()));
}
}
}
// Print failure message if needed
if (userPrincipal == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("jaasRealm.userPrincipalFailure"));
log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
}
return null;
} else {
if (roles.size() == 0) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
}
}
}
// Return the resulting Principal for our authenticated user
return new GenericPrincipal(username, null, roles, userPrincipal,
loginContext);
}
/**
* Ensure the given name is legal for JAAS configuration.
* Added for Bugzilla 30869, made protected for easy customization
* in case my implementation is insufficient, which I think is
* very likely.
*
* @param src The name to validate
* @return A string that's a valid JAAS realm name
*/
protected String makeLegalForJAAS(final String src) {
String result = src;
// Default name is "other" per JAAS spec
if(result == null) {
result = "other";
}
// Strip leading slash if present, as Sun JAAS impl
// barfs on it (see Bugzilla 30869 bug report).
if(result.startsWith("/")) {
result = result.substring(1);
}
return result;
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
// These need to be called after loading configuration, in case
// useContextClassLoader appears after them in xml config
parseClassNames(userClassNames, userClasses);
parseClassNames(roleClassNames, roleClasses);
super.startInternal();
}
/**
* Load custom JAAS Configuration.
* @return the loaded configuration
*/
protected Configuration getConfig() {
// Local copy to avoid possible NPE due to concurrent change
String configFile = this.configFile;
try {
if (jaasConfigurationLoaded) {
return jaasConfiguration;
}
synchronized (this) {
if (configFile == null) {
jaasConfigurationLoaded = true;
return null;
}
URL resource = Thread.currentThread().getContextClassLoader().getResource(configFile);
URI uri = resource.toURI();
@SuppressWarnings("unchecked")
Class<Configuration> sunConfigFile = (Class<Configuration>)
Class.forName("com.sun.security.auth.login.ConfigFile");
Constructor<Configuration> constructor =
sunConfigFile.getConstructor(URI.class);
Configuration config = constructor.newInstance(uri);
this.jaasConfiguration = config;
this.jaasConfigurationLoaded = true;
return this.jaasConfiguration;
}
} catch (URISyntaxException ex) {
throw new RuntimeException(ex);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
} catch (SecurityException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
@Override
public boolean isAvailable() {
return invocationSuccess;
}
}

View File

@@ -0,0 +1,761 @@
/*
* 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.catalina.realm;
import java.security.Principal;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import org.apache.catalina.LifecycleException;
import org.apache.tomcat.util.ExceptionUtils;
/**
*
* Implementation of <b>Realm</b> that works with any JDBC supported database.
* See the JDBCRealm.howto for more details on how to set up the database and
* for configuration options.
*
* <p>For a <b>Realm</b> implementation that supports connection pooling and
* doesn't require synchronisation of <code>authenticate()</code>,
* <code>getPassword()</code>, <code>roles()</code> and
* <code>getPrincipal()</code> or the ugly connection logic use the
* <code>DataSourceRealm</code>.</p>
*
* @author Craig R. McClanahan
* @author Carson McDonald
* @author Ignacio Ortega
*/
public class JDBCRealm
extends RealmBase {
// ----------------------------------------------------- Instance Variables
/**
* The connection username to use when trying to connect to the database.
*/
protected String connectionName = null;
/**
* The connection URL to use when trying to connect to the database.
*/
protected String connectionPassword = null;
/**
* The connection URL to use when trying to connect to the database.
*/
protected String connectionURL = null;
/**
* The connection to the database.
*/
protected Connection dbConnection = null;
/**
* Instance of the JDBC Driver class we use as a connection factory.
*/
protected Driver driver = null;
/**
* The JDBC driver to use.
*/
protected String driverName = null;
/**
* Descriptive information about this Realm implementation.
* @deprecated This will be removed in Tomcat 9 onwards.
*/
@Deprecated
protected static final String name = "JDBCRealm";
/**
* The PreparedStatement to use for authenticating users.
*/
protected PreparedStatement preparedCredentials = null;
/**
* The PreparedStatement to use for identifying the roles for
* a specified user.
*/
protected PreparedStatement preparedRoles = null;
/**
* The column in the user role table that names a role
*/
protected String roleNameCol = null;
/**
* The column in the user table that holds the user's credentials
*/
protected String userCredCol = null;
/**
* The column in the user table that holds the user's name
*/
protected String userNameCol = null;
/**
* The table that holds the relation between user's and roles
*/
protected String userRoleTable = null;
/**
* The table that holds user data.
*/
protected String userTable = null;
// ------------------------------------------------------------- Properties
/**
* @return the username to use to connect to the database.
*/
public String getConnectionName() {
return connectionName;
}
/**
* Set the username to use to connect to the database.
*
* @param connectionName Username
*/
public void setConnectionName(String connectionName) {
this.connectionName = connectionName;
}
/**
* @return the password to use to connect to the database.
*/
public String getConnectionPassword() {
return connectionPassword;
}
/**
* Set the password to use to connect to the database.
*
* @param connectionPassword User password
*/
public void setConnectionPassword(String connectionPassword) {
this.connectionPassword = connectionPassword;
}
/**
* @return the URL to use to connect to the database.
*/
public String getConnectionURL() {
return connectionURL;
}
/**
* Set the URL to use to connect to the database.
*
* @param connectionURL The new connection URL
*/
public void setConnectionURL( String connectionURL ) {
this.connectionURL = connectionURL;
}
/**
* @return the JDBC driver that will be used.
*/
public String getDriverName() {
return driverName;
}
/**
* Set the JDBC driver that will be used.
*
* @param driverName The driver name
*/
public void setDriverName( String driverName ) {
this.driverName = driverName;
}
/**
* @return the column in the user role table that names a role.
*/
public String getRoleNameCol() {
return roleNameCol;
}
/**
* Set the column in the user role table that names a role.
*
* @param roleNameCol The column name
*/
public void setRoleNameCol( String roleNameCol ) {
this.roleNameCol = roleNameCol;
}
/**
* @return the column in the user table that holds the user's credentials.
*/
public String getUserCredCol() {
return userCredCol;
}
/**
* Set the column in the user table that holds the user's credentials.
*
* @param userCredCol The column name
*/
public void setUserCredCol( String userCredCol ) {
this.userCredCol = userCredCol;
}
/**
* @return the column in the user table that holds the user's name.
*/
public String getUserNameCol() {
return userNameCol;
}
/**
* Set the column in the user table that holds the user's name.
*
* @param userNameCol The column name
*/
public void setUserNameCol( String userNameCol ) {
this.userNameCol = userNameCol;
}
/**
* @return the table that holds the relation between user's and roles.
*/
public String getUserRoleTable() {
return userRoleTable;
}
/**
* Set the table that holds the relation between user's and roles.
*
* @param userRoleTable The table name
*/
public void setUserRoleTable( String userRoleTable ) {
this.userRoleTable = userRoleTable;
}
/**
* @return the table that holds user data..
*/
public String getUserTable() {
return userTable;
}
/**
* Set the table that holds user data.
*
* @param userTable The table name
*/
public void setUserTable( String userTable ) {
this.userTable = userTable;
}
// --------------------------------------------------------- Public Methods
/**
* Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*
* If there are any errors with the JDBC connection, executing
* the query or anything we return null (don't authenticate). This
* event is also logged, and the connection will be closed so that
* a subsequent request will automatically re-open it.
*
*
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in
* authenticating this username
* @return the associated principal, or <code>null</code> if there is none.
*/
@Override
public synchronized Principal authenticate(String username, String credentials) {
// Number of tries is the number of attempts to connect to the database
// during this login attempt (if we need to open the database)
// This needs rewritten with better pooling support, the existing code
// needs signature changes since the Prepared statements needs cached
// with the connections.
// The code below will try twice if there is an SQLException so the
// connection may try to be opened again. On normal conditions (including
// invalid login - the above is only used once.
int numberOfTries = 2;
while (numberOfTries>0) {
try {
// Ensure that we have an open database connection
open();
// Acquire a Principal object for this user
Principal principal = authenticate(dbConnection,
username, credentials);
// Return the Principal (if any)
return principal;
} catch (SQLException e) {
// Log the problem for posterity
containerLog.error(sm.getString("jdbcRealm.exception"), e);
// Close the connection so that it gets reopened next time
if (dbConnection != null)
close(dbConnection);
}
numberOfTries--;
}
// Worst case scenario
return null;
}
// -------------------------------------------------------- Package Methods
// ------------------------------------------------------ Protected Methods
/**
* Attempt to authenticate the user with the provided credentials.
*
* @param dbConnection The database connection to be used
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in authenticating
* this username
*
* @return Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*/
public synchronized Principal authenticate(Connection dbConnection,
String username,
String credentials) {
// No user or no credentials
// Can't possibly authenticate, don't bother the database then
if (username == null || credentials == null) {
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("jdbcRealm.authenticateFailure",
username));
return null;
}
// Look up the user's credentials
String dbCredentials = getPassword(username);
if (dbCredentials == null) {
// User was not found in the database.
// Waste a bit of time as not to reveal that the user does not exist.
getCredentialHandler().mutate(credentials);
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("jdbcRealm.authenticateFailure",
username));
return null;
}
// Validate the user's credentials
boolean validated = getCredentialHandler().matches(credentials, dbCredentials);
if (validated) {
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("jdbcRealm.authenticateSuccess",
username));
} else {
if (containerLog.isTraceEnabled())
containerLog.trace(sm.getString("jdbcRealm.authenticateFailure",
username));
return null;
}
ArrayList<String> roles = getRoles(username);
// Create and return a suitable Principal for this user
return new GenericPrincipal(username, credentials, roles);
}
@Override
public boolean isAvailable() {
return (dbConnection != null);
}
/**
* Close the specified database connection.
*
* @param dbConnection The connection to be closed
*/
protected void close(Connection dbConnection) {
// Do nothing if the database connection is already closed
if (dbConnection == null)
return;
// Close our prepared statements (if any)
try {
preparedCredentials.close();
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
}
this.preparedCredentials = null;
try {
preparedRoles.close();
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
}
this.preparedRoles = null;
// Close this database connection, and log any errors
try {
dbConnection.close();
} catch (SQLException e) {
containerLog.warn(sm.getString("jdbcRealm.close"), e); // Just log it here
} finally {
this.dbConnection = null;
}
}
/**
* Return a PreparedStatement configured to perform the SELECT required
* to retrieve user credentials for the specified username.
*
* @param dbConnection The database connection to be used
* @param username Username for which credentials should be retrieved
* @return the prepared statement
* @exception SQLException if a database error occurs
*/
protected PreparedStatement credentials(Connection dbConnection,
String username)
throws SQLException {
if (preparedCredentials == null) {
StringBuilder sb = new StringBuilder("SELECT ");
sb.append(userCredCol);
sb.append(" FROM ");
sb.append(userTable);
sb.append(" WHERE ");
sb.append(userNameCol);
sb.append(" = ?");
if(containerLog.isDebugEnabled()) {
containerLog.debug("credentials query: " + sb.toString());
}
preparedCredentials =
dbConnection.prepareStatement(sb.toString());
}
if (username == null) {
preparedCredentials.setNull(1,java.sql.Types.VARCHAR);
} else {
preparedCredentials.setString(1, username);
}
return preparedCredentials;
}
@Override
@Deprecated
protected String getName() {
return name;
}
/**
* Get the password for the specified user.
* @param username The user name
* @return the password associated with the given principal's user name.
*/
@Override
protected synchronized String getPassword(String username) {
// Look up the user's credentials
String dbCredentials = null;
// Number of tries is the number of attempts to connect to the database
// during this login attempt (if we need to open the database)
// This needs rewritten with better pooling support, the existing code
// needs signature changes since the Prepared statements needs cached
// with the connections.
// The code below will try twice if there is an SQLException so the
// connection may try to be opened again. On normal conditions (including
// invalid login - the above is only used once.
int numberOfTries = 2;
while (numberOfTries > 0) {
try {
// Ensure that we have an open database connection
open();
PreparedStatement stmt = credentials(dbConnection, username);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
dbCredentials = rs.getString(1);
}
dbConnection.commit();
if (dbCredentials != null) {
dbCredentials = dbCredentials.trim();
}
return dbCredentials;
}
} catch (SQLException e) {
// Log the problem for posterity
containerLog.error(sm.getString("jdbcRealm.exception"), e);
}
// Close the connection so that it gets reopened next time
if (dbConnection != null) {
close(dbConnection);
}
numberOfTries--;
}
return null;
}
/**
* Get the principal associated with the specified user.
* @param username The user name
* @return the Principal associated with the given user name.
*/
@Override
protected synchronized Principal getPrincipal(String username) {
return new GenericPrincipal(username,
getPassword(username),
getRoles(username));
}
/**
* Return the roles associated with the given user name.
* @param username The user name
* @return an array list of the role names
*/
protected ArrayList<String> getRoles(String username) {
if (allRolesMode != AllRolesMode.STRICT_MODE && !isRoleStoreDefined()) {
// Using an authentication only configuration and no role store has
// been defined so don't spend cycles looking
return null;
}
// Number of tries is the number of attempts to connect to the database
// during this login attempt (if we need to open the database)
// This needs rewritten wuth better pooling support, the existing code
// needs signature changes since the Prepared statements needs cached
// with the connections.
// The code below will try twice if there is an SQLException so the
// connection may try to be opened again. On normal conditions (including
// invalid login - the above is only used once.
int numberOfTries = 2;
while (numberOfTries>0) {
try {
// Ensure that we have an open database connection
open();
PreparedStatement stmt = roles(dbConnection, username);
try (ResultSet rs = stmt.executeQuery()) {
// Accumulate the user's roles
ArrayList<String> roleList = new ArrayList<>();
while (rs.next()) {
String role = rs.getString(1);
if (null!=role) {
roleList.add(role.trim());
}
}
return roleList;
} finally {
dbConnection.commit();
}
} catch (SQLException e) {
// Log the problem for posterity
containerLog.error(sm.getString("jdbcRealm.exception"), e);
// Close the connection so that it gets reopened next time
if (dbConnection != null)
close(dbConnection);
}
numberOfTries--;
}
return null;
}
/**
* Open (if necessary) and return a database connection for use by
* this Realm.
* @return the opened connection
* @exception SQLException if a database error occurs
*/
protected Connection open() throws SQLException {
// Do nothing if there is a database connection already open
if (dbConnection != null)
return dbConnection;
// Instantiate our database driver if necessary
if (driver == null) {
try {
Class<?> clazz = Class.forName(driverName);
driver = (Driver) clazz.getConstructor().newInstance();
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new SQLException(e.getMessage(), e);
}
}
// Open a new connection
Properties props = new Properties();
if (connectionName != null)
props.put("user", connectionName);
if (connectionPassword != null)
props.put("password", connectionPassword);
dbConnection = driver.connect(connectionURL, props);
if (dbConnection == null) {
throw new SQLException(sm.getString(
"jdbcRealm.open.invalidurl",driverName, connectionURL));
}
dbConnection.setAutoCommit(false);
return dbConnection;
}
/**
* Return a PreparedStatement configured to perform the SELECT required
* to retrieve user roles for the specified username.
*
* @param dbConnection The database connection to be used
* @param username Username for which roles should be retrieved
* @return the prepared statement
* @exception SQLException if a database error occurs
*/
protected synchronized PreparedStatement roles(Connection dbConnection,
String username)
throws SQLException {
if (preparedRoles == null) {
StringBuilder sb = new StringBuilder("SELECT ");
sb.append(roleNameCol);
sb.append(" FROM ");
sb.append(userRoleTable);
sb.append(" WHERE ");
sb.append(userNameCol);
sb.append(" = ?");
preparedRoles =
dbConnection.prepareStatement(sb.toString());
}
preparedRoles.setString(1, username);
return preparedRoles;
}
private boolean isRoleStoreDefined() {
return userRoleTable != null || roleNameCol != null;
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
// Validate that we can open our connection - but let tomcat
// startup in case the database is temporarily unavailable
try {
open();
} catch (SQLException e) {
containerLog.error(sm.getString("jdbcRealm.open"), e);
}
super.startInternal();
}
/**
* Gracefully terminate the active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
@Override
protected void stopInternal() throws LifecycleException {
super.stopInternal();
// Close any open DB connection
close(this.dbConnection);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
# 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.
combinedRealm.addRealm=Add [{0}] realm, making a total of [{1}] realms
combinedRealm.authFail=Failed to authenticate user [{0}] with realm [{1}]
combinedRealm.authStart=Attempting to authenticate user [{0}] with realm [{1}]
combinedRealm.authSuccess=Authenticated user [{0}] with realm [{1}]
combinedRealm.getPassword=The getPassword() method should never be called
combinedRealm.getPrincipal=The getPrincipal() method should never be called
combinedRealm.realmStartFail=Failed to start [{0}] realm
combinedRealm.unexpectedMethod=An unexpected call was made to a method on the combined realm
credentialHandler.invalidStoredCredential=The invalid stored credential string [{0}] was provided by the Realm to match with the user provided credentials
dataSourceRealm.authenticateFailure=Username [{0}] NOT successfully authenticated
dataSourceRealm.authenticateSuccess=Username [{0}] successfully authenticated
dataSourceRealm.close=Exception closing database connection
dataSourceRealm.exception=Exception performing authentication
dataSourceRealm.getPassword.exception=Exception retrieving password for [{0}]
dataSourceRealm.getRoles.exception=Exception retrieving roles for [{0}]
jaasCallback.username=Returned username [{0}]
jaasRealm.accountExpired=Username [{0}] NOT authenticated due to expired account
jaasRealm.authenticateFailure=Username [{0}] NOT successfully authenticated
jaasRealm.authenticateSuccess=Username [{0}] successfully authenticated as Principal [{1}] -- Subject was created too
jaasRealm.beginLogin=JAASRealm login requested for username [{0}] using LoginContext for application [{1}]
jaasRealm.checkPrincipal=Checking Principal [{0}] [{1}]
jaasRealm.credentialExpired=Username [{0}] NOT authenticated due to expired credential
jaasRealm.failedLogin=Username [{0}] NOT authenticated due to failed login
jaasRealm.loginContextCreated=JAAS LoginContext created for username [{0}]
jaasRealm.loginException=Login exception authenticating username [{0}]
jaasRealm.rolePrincipalAdd=Adding role Principal [{0}] to this user Principal''s roles
jaasRealm.rolePrincipalFailure=No valid role Principals found.
jaasRealm.unexpectedError=Unexpected error
jaasRealm.userPrincipalFailure=No valid user Principal found
jaasRealm.userPrincipalSuccess=Principal [{0}] is a valid user class. We will use this as the user Principal.
jdbcRealm.authenticateFailure=Username [{0}] NOT successfully authenticated
jdbcRealm.authenticateSuccess=Username [{0}] successfully authenticated
jdbcRealm.close=Exception closing database connection
jdbcRealm.exception=Exception performing authentication
jdbcRealm.open=Exception opening database connection
jdbcRealm.open.invalidurl=Driver [{0}] does not support the url [{1}]
jndiRealm.authenticateFailure=Username [{0}] NOT successfully authenticated
jndiRealm.authenticateSuccess=Username [{0}] successfully authenticated
jndiRealm.cipherSuites=Enable [{0}] as cipher suites for tls connection.
jndiRealm.close=Exception closing directory server connection
jndiRealm.emptyCipherSuites=Empty String for cipher suites given. Using default cipher suites.
jndiRealm.exception=Exception performing authentication
jndiRealm.exception.retry=Exception performing authentication. Retrying...
jndiRealm.invalidHostnameVerifier=[{0}] not a valid class name for a HostnameVerifier
jndiRealm.invalidSslProtocol=Given protocol [{0}] is invalid. It has to be one of [{1}]
jndiRealm.invalidSslSocketFactory=[{0}] not a valid class name for an SSLSocketFactory
jndiRealm.negotiatedTls=Negotiated tls connection using protocol [{0}]
jndiRealm.open=Exception opening directory server connection
jndiRealm.tlsClose=Exception closing tls response
lockOutRealm.authLockedUser=An attempt was made to authenticate the locked user [{0}]
lockOutRealm.removeWarning=User [{0}] was removed from the failed users cache after [{1}] seconds to keep the cache size within the limit set
mdCredentialHandler.unknownEncoding=The encoding [{0}] is not supported so the current setting of [{1}] will still be used
memoryRealm.authenticateFailure=Username [{0}] NOT successfully authenticated
memoryRealm.authenticateSuccess=Username [{0}] successfully authenticated
memoryRealm.loadExist=Memory database file [{0}] cannot be read
memoryRealm.loadPath=Loading users from memory database file [{0}]
memoryRealm.readXml=Exception while reading memory database file
memoryRealm.xmlFeatureEncoding=Exception configuring digester to permit java encoding names in XML files. Only IANA encoding names will be supported.
pbeCredentialHandler.invalidKeySpec=Unable to generate a password based key
realmBase.algorithm=Invalid message digest algorithm [{0}] specified
realmBase.authenticateFailure=Username [{0}] NOT successfully authenticated
realmBase.authenticateSuccess=Username [{0}] successfully authenticated
realmBase.cannotGetRoles=Cannot get roles from principal [{0}]
realmBase.createUsernameRetriever.ClassCastException=Class [{0}] is not an X509UsernameRetriever.
realmBase.createUsernameRetriever.newInstance=Cannot create object of type [{0}].
realmBase.credentialNotDelegated=Credential for user [{0}] has not been delegated though storing was requested
realmBase.delegatedCredentialFail=Unable to obtain delegated credential for user [{0}]
realmBase.digest=Error digesting user credentials
realmBase.forbidden=Access to the requested resource has been denied
realmBase.gotX509Username=Got user name from X509 certificate: [{0}]
realmBase.gssContextNotEstablished=Authenticator implementation error: the passed security context is not fully established
realmBase.gssNameFail=Failed to extract name from established GSSContext
realmBase.hasRoleFailure=Username [{0}] does NOT have role [{1}]
realmBase.hasRoleSuccess=Username [{0}] has role [{1}]
userDatabaseRealm.lookup=Exception looking up UserDatabase under key [{0}]
userDatabaseRealm.noDatabase=No UserDatabase component found under key [{0}]

View File

@@ -0,0 +1,51 @@
# 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.
combinedRealm.authFail=Kann Benutzer [{0}] mit Realm [{1}] nicht authentisieren
combinedRealm.authSuccess=Authentifizierter Benutzer [{0}] mit Realm [{1}]
dataSourceRealm.authenticateFailure=Benutzername [{0}] konnte NICHT authentifiziert werden
dataSourceRealm.authenticateSuccess=Benutzername [{0}] konnte erfolgreich authentifiziert werden
dataSourceRealm.getPassword.exception=Ausnahme beim Holen des Passwortes für [{0}]
dataSourceRealm.getRoles.exception=Ausnahme beim Holen der Rollen für [{0}]
jaasRealm.accountExpired=Benutzername [{0}] konnte auf Grund eines abgelaufenen Kontos NICHT authentifiziert werden
jaasRealm.authenticateFailure=Benutzername [{0}] konnte NICHT authentifiziert werden
jaasRealm.credentialExpired=Benutzername [{0}] konnte auf Grund abgelaufener Zugangsdaten NICHT authentifiziert werden
jaasRealm.failedLogin=Benutzername [{0}] konnte auf Grund einer fehlerhaften Anmeldung NICHT authentifiziert werden
jaasRealm.loginContextCreated=JAAS LoginContext für Benutzername [{0}] erzeugt
jdbcRealm.authenticateFailure=Benutzername [{0}] konnte NICHT authentifiziert werden
jdbcRealm.authenticateSuccess=Benutzername [{0}] konnte erfolgreich authentifiziert werden
jndiRealm.authenticateFailure=Benutzername [{0}] konnte NICHT authentifiziert werden
jndiRealm.authenticateSuccess=Benutzername [{0}] konnte erfolgreich authentifiziert werden
lockOutRealm.authLockedUser=Es wurde versucht den gesperrten Benutzer [{0}] zu authentifizieren
memoryRealm.authenticateFailure=Benutzername [{0}] konnte NICHT authentifiziert werden
memoryRealm.authenticateSuccess=Benutzername [{0}] konnte erfolgreich authentifiziert werden.\n
memoryRealm.loadExist=Datei [{0}] für Memory Database kann nicht gelesen werden
realmBase.authenticateFailure=Benutzername [{0}] konnte NICHT authentifiziert werden
realmBase.authenticateSuccess=Benutzername [{0}] konnte erfolgreich authentifiziert werden
realmBase.createUsernameRetriever.ClassCastException=Klasse [{0}] ist keine X509UsernameRetriever Klasse.
realmBase.digest=Fehler beim Anwenden des Digest auf die Benutzer-Credentials
realmBase.forbidden=Zugriff auf die gewünschte Resource wurde verweigert.
realmBase.gotX509Username=Benutzername aus dem X.509 Zertifikate extrahiert: [{0}]
realmBase.hasRoleFailure=Benutzername [{0}] hat NICHT die Rolle [{1}]
realmBase.hasRoleSuccess=Benutzername [{0}] hat die Rolle [{1}]
userDatabaseRealm.noDatabase=Keine UserDatabase Komponente unter dem Schlüssel [{0}] gefunden

View File

@@ -0,0 +1,84 @@
# 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.
combinedRealm.addRealm=Añadir reino [{0}], totalizando [{1}] reinos
combinedRealm.authFail=No pude autenticar al usuario [{0}] con el reino [{1}]
combinedRealm.authStart=Intentando autentica al usuario [{0}] con el reino [{1}]
combinedRealm.authSuccess=Usuario autenticado [{0}] con reino [{1}]
combinedRealm.getPassword=No se debería de llamar nunca al método getPassword()
combinedRealm.getPrincipal=No se debería de llamar nunca al método getPrincipal()
combinedRealm.realmStartFail=No pude arrancar reino [{0}]
combinedRealm.unexpectedMethod=Una llamada inesperada se realizó a un método del reino combinado
dataSourceRealm.authenticateFailure=Nombre de usuario [{0}] NO autenticado con éxito
dataSourceRealm.authenticateSuccess=Nombre de usuario [{0}] autenticado con éxito
dataSourceRealm.close=Excepción cerrando conexión a base de datos
dataSourceRealm.exception=Excepción realizando autenticación
dataSourceRealm.getPassword.exception=Excepción recuperando contraseña para [{0}]
dataSourceRealm.getRoles.exception=Excepción recuperando papeles para [{0}]
jaasCallback.username=Devuelto nombre de usuario [{0}]
jaasRealm.accountExpired=El usuario [{0}] NO ha sido autentificado porque ha expirado su cuenta
jaasRealm.authenticateFailure=Nombre de usuario [{0}] NO autenticado con éxito
jaasRealm.authenticateSuccess=Nombre de usuario [{0}] autenticado con éxito como Principal [{1}] -- También se ha creado el Asunto
jaasRealm.beginLogin=JAASRealm login requerido para nombre de usuario [{0}] usando LoginContext para aplicación [{1}]
jaasRealm.checkPrincipal=Revisando Principal [{0}] [{1}]
jaasRealm.credentialExpired=El usuario [{0}] NO ha sido autentificado porque ha expirado su credencial
jaasRealm.failedLogin=El usuario [{0}] NO ha sido autentificado porque ha fallado el login
jaasRealm.loginContextCreated=JAAS LoginContext creado para nombre de usuario [{0}]
jaasRealm.loginException=Excepción de Login autenticando nombre de usuario [{0}]
jaasRealm.rolePrincipalAdd=Añadiend papel Principal [{0}] a los papeles de este usuario Principal
jaasRealm.rolePrincipalFailure=No se han hallado papeles de Principal válidos.
jaasRealm.unexpectedError=Error inesperado
jaasRealm.userPrincipalFailure=No se ha hallado usuario Principal
jaasRealm.userPrincipalSuccess=El Principal [{0}] es una clase válida de usuario. La vamos a usar como usuario Principal.
jdbcRealm.authenticateFailure=El usuario [{0}] NO ha sido autentificado correctamente
jdbcRealm.authenticateSuccess=El usuario [{0}] ha sido autentificado correctamente
jdbcRealm.close=Excepción al cerrar la conexión a la base de datos
jdbcRealm.exception=Excepción al realizar la autentificación
jdbcRealm.open=Excepción abriendo la conexión a la base de datos
jdbcRealm.open.invalidurl=El conductor [{0}] no soporta la url [{1}]
jndiRealm.authenticateFailure=Autentificación fallida para el usuario [{0}]
jndiRealm.authenticateSuccess=Autentificación correcta para el usuario [{0}]
jndiRealm.close=Excepción al cerrar la conexión al servidor de directorio
jndiRealm.exception=Excepción al realizar la autentificación
jndiRealm.open=Excepción al abrir la conexión al servidor de directorio
lockOutRealm.authLockedUser=Se ha intentado autenticar al usuario bloqueado [{0}]
lockOutRealm.removeWarning=Se ha quitado al usuario [{0}] de la caché de usuarios fallidos tras [{1}] secgundos para mantener la medida de caché dentro de los límites
memoryRealm.authenticateFailure=Autentificación fallida para el usuario [{0}]
memoryRealm.authenticateSuccess=Autentificación correcta para el usuario [{0}]
memoryRealm.loadExist=El fichero de usuarios [{0}] no ha podido ser leído
memoryRealm.loadPath=Cargando los usuarios desde el fichero [{0}]
memoryRealm.readXml=Excepción mientras se leía el fichero de usuarios
memoryRealm.xmlFeatureEncoding=Excepción al configurar resumidor para permitir nombres con codificación java en ficheros XML. Sóllo se soportan nombres con codificación IANA.
realmBase.algorithm=El algoritmo resumen (digest) [{0}] es inválido
realmBase.authenticateFailure=Nombre de usuario [{0}] NO autenticado con éxito
realmBase.authenticateSuccess=Nombre de usuario [{0}] autenticado con éxito
realmBase.createUsernameRetriever.ClassCastException=La clase [{0}] no es una X509UsernameRetriever.
realmBase.delegatedCredentialFail=No pude obtener credenciales de delegado para el usuario [{0}]
realmBase.digest=Error procesando las credenciales del usuario
realmBase.forbidden=El acceso al recurso pedido ha sido denegado
realmBase.gssNameFail=No pude extraer el nombre desde el GSSContext establecido
realmBase.hasRoleFailure=El usuario [{0}] NO desempeña el papel de [{1}]
realmBase.hasRoleSuccess=El usuario [{0}] desempeña el papel de [{1}]
userDatabaseRealm.lookup=Excepción buscando en Base de datos de Usuario mediante la clave [{0}]
userDatabaseRealm.noDatabase=No se ha hallado componente de Base de datos de Usuario mediante la clave [{0}]

View File

@@ -0,0 +1,103 @@
# 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.
combinedRealm.addRealm=Ajout du royaume [{0}], [{1}] royaumes au total
combinedRealm.authFail=Echec d''authentification de l''utilisateur [{0}] avec le domaine [{1}]
combinedRealm.authStart=Tentative d''authentifier l''utilisateur [{0}] avec le royaume [{1}]
combinedRealm.authSuccess=Authentifié l''utilisateur [{0}] avec le domaine [{1}]
combinedRealm.getPassword=La méthode getPassword() ne doit jamais être appelée
combinedRealm.getPrincipal=La méthode getPrincipal() ne devrait jamais être appelée
combinedRealm.realmStartFail=Impossible de démarrer le royaume [{0}]
combinedRealm.unexpectedMethod=Un appel de méthode inattendu à été effectué sur le royaumes combiné
credentialHandler.invalidStoredCredential=Le royaume a fourni des identifiants [{0}] invalides à comparer avec ceux fournis par le client
dataSourceRealm.authenticateFailure=Le nom d''utilisateur [{0}] n''a pas été authentifié
dataSourceRealm.authenticateSuccess=Le nom d''utilisateur [{0}] a été authentifié avec succès
dataSourceRealm.close=Exception lors de la fermeture de la connection vers la base de données
dataSourceRealm.exception=Exception lors de l'anthentification
dataSourceRealm.getPassword.exception=Exception lors de la récupération du mot de passe pour [{0}]
dataSourceRealm.getRoles.exception=Exception lors de la récupération des rôles de [{0}]
jaasCallback.username=Nom d''utilisateur renvoyé [{0}]
jaasRealm.accountExpired=le nom d''utilisateur [{0}] N''A PAS été authentifié car le compte a expiré
jaasRealm.authenticateFailure=Le nom d''utilisateur [{0}] n''a pas été authentifié avec succès
jaasRealm.authenticateSuccess=le nom d''utilisateur [{0}] a été authentifié avec succès
jaasRealm.beginLogin=La connection avec le JAASRealm a demandé le nom d''utilisateur [{0}] en utilisant le LoginContext de l''application [{1}]
jaasRealm.checkPrincipal=Vérification du principal [{0}] [{1}]
jaasRealm.credentialExpired=le nom d''utilisateur [{0}] N''A PAS été authentifié car son crédit a expiré (expired credential)
jaasRealm.failedLogin=le nom d''utilisateur [{0}] N''A PAS été authentifié car son contrôle d''accès (login) a échoué
jaasRealm.loginContextCreated=Le LoginContext JAAS a été crée pour le nom d''utilisateur [{0}]
jaasRealm.loginException=Exception lors de l''authentification par login du nom d''utilisateur [{0}]
jaasRealm.rolePrincipalAdd=Ajout du rôle Pincipal [{0}] aux rôles du principal de l''utilisateur
jaasRealm.rolePrincipalFailure=Aucun principal avec un rôle valide trouvé
jaasRealm.unexpectedError=Erreur inattendue
jaasRealm.userPrincipalFailure=Aucun principal valide trouvé
jaasRealm.userPrincipalSuccess=Le principal [{0}] est une classe utilisateur valide, elle sera utilisée comme principal de l''utilisateur
jdbcRealm.authenticateFailure=le nom d''utilisateur [{0}] N''A PAS été authentifié
jdbcRealm.authenticateSuccess=le nom d''utilisateur [{0}] a été authentifié avec succès
jdbcRealm.close=Exception lors de la fermeture de la connexion à la base de données
jdbcRealm.exception=Exception pendant le traitement de l'authentification
jdbcRealm.open=Exception lors de l'ouverture de la base de données
jdbcRealm.open.invalidurl=Le pilote [{0}] ne supporte pas l''URL [{1}]
jndiRealm.authenticateFailure=Le nom d''utilisateur [{0}] N''A PAS été authentifié
jndiRealm.authenticateSuccess=Le nom d''utilisateur [{0}] a été authentifié avec succès
jndiRealm.cipherSuites=La suite de chiffres [{0}] a été activée pour la connection TLS
jndiRealm.close=Exception lors de la fermeture de la connexion au serveur d'accès (directory server)
jndiRealm.emptyCipherSuites=Une chaîne vide est donnée comme suite de chiffres, la suite de chiffres par défaut sera utilisée
jndiRealm.exception=Exception pendant le traitement de l'authentification
jndiRealm.exception.retry=Erreur pendant l'authentification, nouvel essai
jndiRealm.invalidHostnameVerifier=[{0}] n''est pas un nom de classe valide pour un HostnameVerifier
jndiRealm.invalidSslProtocol=Le protocole fourni [{0}] est invalide, il doit être parmi [{1}]
jndiRealm.invalidSslSocketFactory=[{0}] n''est pas un nom de classe valide pour une SSLSocketFactory
jndiRealm.negotiatedTls=La connection TLS a été négociée en utilisant le protocole [{0}]
jndiRealm.open=Exception lors de l'ouverture de la connexion au serveur d'accès (directory server)
jndiRealm.tlsClose=Exception en fermant la réponse TLS
lockOutRealm.authLockedUser=Une tentative d''authentification a été effectuée pour l''utilisateur verrouillé ("locked user") [{0}]
lockOutRealm.removeWarning=L''utilisateur [{0}] a été enlevé du cache des utilisateurs en échec après [{1}] secondes pour garder la taille du cache dans les limites définies
mdCredentialHandler.unknownEncoding=L''encodage [{0}] n''est pas supporté donc la configuration actuelle [{1}] va continuer à être utilisée
memoryRealm.authenticateFailure=le nom d''utilisateur [{0}] N''A PAS été authentifié
memoryRealm.authenticateSuccess=le nom d''utilisateur [{0}] a été authentifié avec succès
memoryRealm.loadExist=Le fichier base de données mémoire (memory database) [{0}] ne peut être lu
memoryRealm.loadPath=Chargement des utilisateurs depuis le fichier base de données mémoire (memory database) [{0}]
memoryRealm.readXml=Exception lors de la lecture du fichier base de données mémoire (memory database)
memoryRealm.xmlFeatureEncoding=Exception lors de la configuration du Digester pour permettre des noms d'encodage Java dans les fichiers XML, seuls le noms IANA seront supportés
pbeCredentialHandler.invalidKeySpec=Impossible de générer une clé basée sur le mot de passe
realmBase.algorithm=L''algorithme de hachage de message [{0}] indiqué est invalide
realmBase.authenticateFailure=Le nom d''utilisateur [{0}] N''A PAS été authentifié
realmBase.authenticateSuccess=Le nom d''utilisateur [{0}] a été authentifié avec succès
realmBase.cannotGetRoles=Impossible d''obtenir les rôles du principal [{0}]
realmBase.createUsernameRetriever.ClassCastException=La classe [{0}] n''est pas un X509UsernameRetriever.
realmBase.createUsernameRetriever.newInstance=Impossible de créer un objet du type [{0}]
realmBase.credentialNotDelegated=Les identifiants de l'utilisateur [{0}} n'ont pas été délégués alors que leur stockage a été requis
realmBase.delegatedCredentialFail=Impossible d''obtenir les identifiants délégués pour l''utilisateur [{0}]
realmBase.digest=Erreur lors du hachage de l''identifiant utilisateur
realmBase.forbidden=L'accès à la ressource demandée a été interdit
realmBase.gotX509Username=Obtenu le nom d''utilisateur dans le certificat X509: [{0}]
realmBase.gssContextNotEstablished=Erreur d'implémentation de l'authenticateur: le contexte de sécurité passé n'est pas complètement établi
realmBase.gssNameFail=Impossible d'extraire le nom du GSSContext qui a été établi
realmBase.hasRoleFailure=Le nom d''utilisateur [{0}] N''A PAS de rôle [{1}]
realmBase.hasRoleSuccess=Le nom d''utilisateur [{0}] a pour rôle [{1}]
userDatabaseRealm.lookup=Exception lors de la recherche dans la base de données utilisateurs avec la clé [{0}]
userDatabaseRealm.noDatabase=Aucun composant base de données utilisateurs trouvé pour la clé [{0}]

View File

@@ -0,0 +1,102 @@
# 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.
combinedRealm.addRealm=Realm[{0}]を追加し、合計[{1}]Realmを作成します。
combinedRealm.authFail=レルム [{1}] でユーザー [{0}] の認証に失敗しました。
combinedRealm.authStart=ユーザー [{0}] をRealm [{1}] で認証します。
combinedRealm.authSuccess=レルム [{1}] でユーザー [{0}] を認証しました。
combinedRealm.getPassword=getPassword() メソッドを呼び出してはいけません。
combinedRealm.getPrincipal=getPrincipal()メソッドは決して呼び出されるべきではありません。
combinedRealm.realmStartFail=Realm[{0}]の開始に失敗しました。
combinedRealm.unexpectedMethod=Combined Realmについて予期せぬメソッド呼び出しが発生しました。
credentialHandler.invalidStoredCredential=ユーザ提供の資格情報とマッチするためにRealm によって提供された無効な格納された資格情報文字列[[{0}]
dataSourceRealm.authenticateFailure=ユーザ名 [{0}] は認証に失敗しました
dataSourceRealm.authenticateSuccess=ユーザ名 [{0}] は認証に成功しました
dataSourceRealm.close=データベース接続をクローズ中の例外です
dataSourceRealm.exception=認証を実行中の例外です
dataSourceRealm.getPassword.exception=[{0}] のパスワードを取得中に異常が発生しました。
dataSourceRealm.getRoles.exception=[{0}] のロールを取得する途中で例外が発生しました。
jaasCallback.username=返されたユーザー名[{0}]
jaasRealm.accountExpired=ユーザ名 [{0}] はアカウントの期限が切れているために認証されません
jaasRealm.authenticateFailure=ユーザー名[{0}]は正常に認証されませんでした
jaasRealm.authenticateSuccess=ユーザ名 [{0}] は認証に成功しました
jaasRealm.beginLogin=アプリケーション[{1}]のLoginContextを使用して、ユーザー名[{0}]に対するJAASRealmログインが要求されました。
jaasRealm.checkPrincipal=プリンシパル[{0}] [{1}]をチェックします。
jaasRealm.credentialExpired=ユーザ名 [{0}] は証明書の期限が切れたために認証されません
jaasRealm.failedLogin=ユーザ名 [{0}] はログインに失敗したために認証されませんでした
jaasRealm.loginContextCreated=ユーザー名[{0}]用に作成されたJAAS LoginContext
jaasRealm.loginException=ユーザ名 [{0}] の認証中にログイン例外が発生しました
jaasRealm.rolePrincipalAdd=このユーザープリンシパルロールにロールプリンシパル[{0}]を追加
jaasRealm.rolePrincipalFailure=有効な役割はありません。
jaasRealm.unexpectedError=予測しないエラーです
jaasRealm.userPrincipalFailure=有効なユーザープリンシパルが見つかりません。
jaasRealm.userPrincipalSuccess=プリンシパル[{0}]は有効なユーザークラスです。 これをユーザープリンシパルとして使用します。
jdbcRealm.authenticateFailure=ユーザ名 [{0}] は認証に失敗しました
jdbcRealm.authenticateSuccess=ユーザ名 [{0}] は認証に成功しました
jdbcRealm.close=データベース接続クローズ中の例外です
jdbcRealm.exception=認証実行中の例外です
jdbcRealm.open=データベース接続オープン中に例外が発生しました
jdbcRealm.open.invalidurl=ドライバー [{0}] は url [{1}] に対応していません。
jndiRealm.authenticateFailure=ユーザ名 [{0}] は認証に失敗しました
jndiRealm.authenticateSuccess=ユーザ名 [{0}] は認証に成功しました
jndiRealm.cipherSuites=TLS 接続で暗号スイート [{0}] を有効化しました。
jndiRealm.close=ディレクトリサーバ接続クローズ中の例外です
jndiRealm.emptyCipherSuites=指定された暗号スイートの空の文字列。 デフォルトの暗号スイートを使用します。
jndiRealm.exception=認証実行中の例外です
jndiRealm.exception.retry=認証中に例外が発生しました。再試行します。
jndiRealm.invalidHostnameVerifier=[{0}]はHostnameVerifierの有効なクラス名ではありません。
jndiRealm.invalidSslProtocol=指定されたプロトコル[{0}]は無効です。 [{1}]のいずれかでなければなりません。
jndiRealm.invalidSslSocketFactory=[{0}]はSSLSocketFactoryの有効なクラス名ではありません
jndiRealm.negotiatedTls=プロトコル[{0}]を使用して交渉されたTLS接続
jndiRealm.open=ディレクトリサーバ接続オープン中の例外です
jndiRealm.tlsClose=TLSレスポンスを閉じる際の例外
lockOutRealm.authLockedUser=ロックされたユーザー [{0}] の認証が試行されました。
lockOutRealm.removeWarning=キャッシュサイズが制限内に収まるようにするため、[{1}]秒後にユーザー[{0}]が失敗したユーザーキャッシュから削除されました。
mdCredentialHandler.unknownEncoding=エンコーディング [{0}] には未対応のため現在の設定 [{1}] を使用します。
memoryRealm.authenticateFailure=ユーザ名 [{0}] は認証に失敗しました
memoryRealm.authenticateSuccess=ユーザ名 [{0}] は認証に成功しました
memoryRealm.loadExist=メモリデータベースファイル [{0}] を読むことができません
memoryRealm.loadPath=メモリデータベースファイル [{0}] からユーザをロードします
memoryRealm.readXml=メモリデータベースファイルを読み込み中の例外です
memoryRealm.xmlFeatureEncoding=XMLファイルのJavaエンコーディング名を許可するためにdigesterを設定する例外。 IANAのエンコーディング名のみがサポートされます。
pbeCredentialHandler.invalidKeySpec=パスワードベースの鍵を生成できません。
realmBase.algorithm=無効なメッセージダイジェストアルゴリズム [{0}] が指定されています
realmBase.authenticateFailure=ユーザ名 [{0}] は認証に失敗しました
realmBase.authenticateSuccess=ユーザ名 [{0}] は認証に成功しました
realmBase.cannotGetRoles=プリンシパル[{0}]からロールを取得できません
realmBase.createUsernameRetriever.ClassCastException=クラス[{0}]はX509UsernameRetrieverではありません。
realmBase.createUsernameRetriever.newInstance=タイプ[{0}]のオブジェクトを作成できません。
realmBase.delegatedCredentialFail=ユーザー[{0}]の委任された資格情報を取得できません
realmBase.digest=ユーザの証明書のダイジェストエラー
realmBase.forbidden=要求されたリソースへのアクセスが拒否されました
realmBase.gotX509Username=X509証明書のユーザー名を取得しました[{0}]
realmBase.gssContextNotEstablished=Authenticator 実装エラー:渡されたセキュリティコンテキストが完全に確立されていません。
realmBase.gssNameFail=確立されたGSSContextから名前を抽出できませんでした。
realmBase.hasRoleFailure=ユーザ名 [{0}] はロール [{1}] を持っていません
realmBase.hasRoleSuccess=ユーザ名 [{0}] はロール [{1}] を持っています
userDatabaseRealm.lookup=キー [{0}] でユーザデータベースを検索中の例外です
userDatabaseRealm.noDatabase=キー [{0}] でユーザデータベースコンポーネントが見つかりません

View File

@@ -0,0 +1,103 @@
# 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.
combinedRealm.addRealm=Realm [{0}]을(를) 추가하여, 전체 Realm 개수는 [{1}]이(가) 됩니다.
combinedRealm.authFail=사용자 [{0}]을(를) realm [{1}]을(를) 사용하여 인증하지 못했습니다.
combinedRealm.authStart=사용자 [{0}]을(를), realm [{1}]을(를) 사용하여 인증 시도 중
combinedRealm.authSuccess=사용자 [{0}]을(를) realm [{1}]을(를) 사용하여 인증했습니다.
combinedRealm.getPassword=getPassword() 메소드는 절대 호출되서는 안됩니다.
combinedRealm.getPrincipal=getPrincipal() 메소드는 절대로 호출되서는 안됩니다.
combinedRealm.realmStartFail=[{0}] realm을 시작하지 못했습니다.
combinedRealm.unexpectedMethod=CombinedRealm의 메소드에 예기치 않은 호출이었습니다.
credentialHandler.invalidStoredCredential=사용자가 제공한 credentials과 부합하는지 검사하기 위하여, 유효하지 않은 저장된 credentials 문자열 [{0}]이(가), Realm에 의해 제공되었습니다.
dataSourceRealm.authenticateFailure=사용자명 [{0}]은(는) 성공적으로 인증되지 않았습니다.
dataSourceRealm.authenticateSuccess=사용자명 [{0}]이(가) 성공적으로 인증되었습니다.
dataSourceRealm.close=데이터베이스 연결을 닫는 중 예외 발생
dataSourceRealm.exception=인증 처리 수행 중 예외 발생
dataSourceRealm.getPassword.exception=[{0}]을(를) 위한 비밀번호를 조회하던 중 예외 발생
dataSourceRealm.getRoles.exception=사용자 [{0}]을(를) 위한 역할들을 조회하는 중 예외 발생
jaasCallback.username=반환된 사용자명 [{0}]
jaasRealm.accountExpired=사용자명 [{0}]은(는) 만료된 계정이라서 인증되지 않았습니다.
jaasRealm.authenticateFailure=사용자명 [{0}]은(는) 성공적으로 인증되지 못했습니다.
jaasRealm.authenticateSuccess=사용자명 [{0}]이(가) 성공적으로 Principal [{1}](으)로서 인증되었습니다 -- Subject 또한 생성되었습니다.
jaasRealm.beginLogin=애플리케이션 [{1}]을(를) 위한 LoginContext를 사용하여, 사용자명 [{0}]을(를) 위한 JAASRealm 로그인이 요청되었습니다.
jaasRealm.checkPrincipal=Principal [{0}] [{1}]을(를) 점검합니다.
jaasRealm.credentialExpired=만료된 credentials로 인하여, 사용자명 [{0}]이(가) 인증되지 않았습니다.
jaasRealm.failedLogin=사용자명 [{0}]은(는) 로그인 실패로 인하여 인증되지 않았습니다.
jaasRealm.loginContextCreated=사용자명 [{0}]을(를) 위해 생성된 JAAS LoginContext
jaasRealm.loginException=사용자 이름 [{0}]을(를) 인증하는 중 로그인 예외 발생
jaasRealm.rolePrincipalAdd=이 사용자 Principal의 역할들에, 역할 Principal [{0}]을(를) 추가합니다.
jaasRealm.rolePrincipalFailure=유효한 역할 Principal들을 찾을 수 없습니다.
jaasRealm.unexpectedError=예기치 않은 오류
jaasRealm.userPrincipalFailure=유효한 사용자 Principal을 찾을 수 없습니다.
jaasRealm.userPrincipalSuccess=Principal [{0}]은(는) 유효한 사용자 클래스입니다. 이를 사용자 Principal로 사용하겠습니다.
jdbcRealm.authenticateFailure=사용자명 [{0}]이(가) 성공적으로 인증되지 못했습니다.
jdbcRealm.authenticateSuccess=사용자명 [{0}]이(가) 성공적으로 인증되었습니다.
jdbcRealm.close=데이터베이스 연결을 닫는 중 예외 발생
jdbcRealm.exception=인증 처리 수행 중 예외 발생
jdbcRealm.open=데이터베이스 연결을 여는 중 예외 발생
jdbcRealm.open.invalidurl=드라이버 [{0}]은(는) URL [{1}]을(를) 지원하지 않습니다.
jndiRealm.authenticateFailure=사용자명 [{0}]이(가) 성공적으로 인증되지 못했습니다.
jndiRealm.authenticateSuccess=사용자명 [{0}]이(가) 성공적으로 인증되었습니다.
jndiRealm.cipherSuites=이 tls 연결을 위한 cipher suite들로서, [{0}]을(를) 사용 가능하게 합니다.
jndiRealm.close=디렉토리 서버 연결을 닫는 중 예외 발생
jndiRealm.emptyCipherSuites=주어진 cipher suite들에 빈 문자열이 설정되었습니다. 기본 cipher suite들을 사용합니다.
jndiRealm.exception=인증 처리 수행 중 예외 발생
jndiRealm.exception.retry=인증 처리 수행 중 예외 발생. 재시도합니다...
jndiRealm.invalidHostnameVerifier=[{0}]은(는), HostnameVerifier를 위한 클래스 이름으로서, 유효하지 않습니다.
jndiRealm.invalidSslProtocol=주어진 프로토콜 [{0}]은(는) 유효하지 않습니다. 반드시 [{1}] 중의 하나여야 합니다.
jndiRealm.invalidSslSocketFactory=[{0}]은(는) SSLSocketFactory 객체를 위해 유효한 클래스 이름이 아닙니다.
jndiRealm.negotiatedTls=프로토콜 [{0}]을(를) 사용하여 TLS 연결을 negotiate 했습니다.
jndiRealm.open=디렉토리 서버 연결을 여는 중 예외 발생
jndiRealm.tlsClose=tls 응답을 닫는 중 예외 발생
lockOutRealm.authLockedUser=잠금 상태인 사용자 [{0}]을(를) 인증하려는 시도가 이루어졌습니다.
lockOutRealm.removeWarning=캐시 크기를 한계값 내에서 유지하기 위하여, 사용자 [{0}]을(를), [{1}]초 후에 실패 사용자 캐시로부터 제거했습니다.
mdCredentialHandler.unknownEncoding=인코딩 [{0}]이(가) 지원되지 않아서, 현 설정 [{1}]이(가) 계속 사용될 것입니다.
memoryRealm.authenticateFailure=사용자명 [{0}]이(가) 성공적으로 인증되지 못했습니다.
memoryRealm.authenticateSuccess=사용자명 [{0}]이(가) 성공적으로 인증되었습니다.
memoryRealm.loadExist=메모리 데이터베이스 파일 [{0}]을(를) 읽을 수 없습니다.
memoryRealm.loadPath=메모리 데이터베이스 파일 [{0}](으)로부터 사용자들을 로드합니다.
memoryRealm.readXml=메모리 데이터베이스 파일을 읽는 중 예외 발생
memoryRealm.xmlFeatureEncoding=XML 파일들에서 자바 인코딩 이름들을 허용하기 위해 digester를 설정하는 중 예외 발생. 오직 IANA 인코딩 이름들만 지원될 것입니다.
pbeCredentialHandler.invalidKeySpec=비밀번호 기반의 키를 생성할 수 없습니다.
realmBase.algorithm=유효하지 않은 메시지 Digest 알고리즘 [{0}]이(가) 지정되었습니다.
realmBase.authenticateFailure=사용자명 [{0}]이(가) 성공적으로 인증되지 못했습니다.
realmBase.authenticateSuccess=사용자명 [{0}]이(가) 성공적으로 인증되었습니다.
realmBase.cannotGetRoles=Principal [{0}](으)로부터 역할들을 얻을 수 없습니다.
realmBase.createUsernameRetriever.ClassCastException=클래스 [{0}]이(가) X509UsernameRetriever 타입이 아닙니다.
realmBase.createUsernameRetriever.newInstance=타입이 [{0}]인 객체를 생성할 수 없습니다.
realmBase.credentialNotDelegated=인증서 저장 옵션이 요청되었지만, 사용자 [{0}]을(를) 위한 인증서 대리 처리가 사용 가능하지 않습니다.
realmBase.delegatedCredentialFail=사용자 [{0}]을(를) 위한 대리 인증서를 얻을 수 없습니다.
realmBase.digest=사용자의 credentials를 digest하는 중 오류 발생
realmBase.forbidden=요청된 리소스에 대한 접근이 거부되었습니다.
realmBase.gotX509Username=X509 인증서로부터 사용자 이름을 구했습니다: [{0}]
realmBase.gssContextNotEstablished=Authenticator 구현 오류: 전달된 보안 컨텍스트가 완전히 확립되지 않았습니다.
realmBase.gssNameFail=확립된 GSSContext로부터, 이름을 추출하지 못했습니다.
realmBase.hasRoleFailure=사용자명 [{0}]은(는) 역할 [{1}]을(를) 가지고 있지 않습니다.
realmBase.hasRoleSuccess=사용자명 [{0}]이(가) 역할 [{1}]을(를) 가지고 있습니다.
userDatabaseRealm.lookup=키 [{0}]을(를) 사용하여 사용자 데이터베이스를 찾는 중 예외 발생
userDatabaseRealm.noDatabase=키 [{0}]을(를) 사용하여 UserDatabase 구성요소를 찾을 수 없습니다.

View File

@@ -0,0 +1,18 @@
# 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.
lockOutRealm.authLockedUser=Заблокированный пользователь [{0}] совершил попытку авторизоваться
realmBase.forbidden=Доступ к запрашиваемому ресурсу был заблокирован

View File

@@ -0,0 +1,56 @@
# 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.
combinedRealm.authFail=无法使用域[{1}]对用户[{0}]进行身份验证
combinedRealm.authSuccess=认证用户[{0}],权限[{1}]
dataSourceRealm.getPassword.exception=获取用户名[{0}]对应的密码失败。
dataSourceRealm.getRoles.exception=:)检索角色[{0}]异常
jaasCallback.username=返回用户名 [{0}]
jaasRealm.authenticateFailure=用户 [{0}] 认证失败
jaasRealm.authenticateSuccess=用户名 [{0}] 已被成功认证为身份 [{1}] -- 主体也已创建
jaasRealm.failedLogin=由于登录失败,用户名 [{0}] 无法授权
jaasRealm.loginContextCreated=为用户名创建的JAAS 登陆上下文[{0}]
jaasRealm.loginException=登录异常,认证用户名 [{0}]
jdbcRealm.authenticateFailure=用户名称[{0}]未校验成功
jdbcRealm.open=打开数据库连接时发生异常
jndiRealm.authenticateFailure=用户名[{0}]没有成功认证
jndiRealm.authenticateSuccess=用户名[{0}]成功认证
jndiRealm.cipherSuites=启用 [{0}] 作为 TLS 连接的加密套件。
jndiRealm.exception=执行认证异常
jndiRealm.negotiatedTls=使用协议[{0}]协商的TLS连接
jndiRealm.open=打开目录服务器链接异常
lockOutRealm.authLockedUser=尝试对锁定的用户[{0}]进行身份验证
memoryRealm.loadExist=内存数据库文件[{0}]无法读取
memoryRealm.loadPath=从内存数据库文件 [{0}] 加载用户
memoryRealm.readXml=读取内存数据库文件时出现异常
memoryRealm.xmlFeatureEncoding=配置Digester以允许XML文件中的java编码名称的异常。只支持IANA编码名称。
pbeCredentialHandler.invalidKeySpec=无法生成基于密码的密钥
realmBase.authenticateFailure=用户名 [{0}] 认证失败
realmBase.createUsernameRetriever.ClassCastException=类[{0}] 不是一个X509UsernameRetriever.
realmBase.digest=对用户凭证摘要发生错误
realmBase.forbidden=已拒绝访问所请求的资源
realmBase.hasRoleFailure=用户[{0}]没有角色[{1}]
realmBase.hasRoleSuccess=用户名[{0}] 有角色[{1}]
userDatabaseRealm.noDatabase=未找到key[{0}]对应的UserDatabase组件。

View File

@@ -0,0 +1,428 @@
/*
* 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.catalina.realm;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.catalina.LifecycleException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
/**
* This class extends the CombinedRealm (hence it can wrap other Realms) to
* provide a user lock out mechanism if there are too many failed
* authentication attempts in a given period of time. To ensure correct
* operation, there is a reasonable degree of synchronisation in this Realm.
* This Realm does not require modification to the underlying Realms or the
* associated user storage mechanisms. It achieves this by recording all failed
* logins, including those for users that do not exist. To prevent a DOS by
* deliberating making requests with invalid users (and hence causing this cache
* to grow) the size of the list of users that have failed authentication is
* limited.
*/
public class LockOutRealm extends CombinedRealm {
private static final Log log = LogFactory.getLog(LockOutRealm.class);
/**
* Descriptive information about this Realm implementation.
*/
protected static final String name = "LockOutRealm";
/**
* The number of times in a row a user has to fail authentication to be
* locked out. Defaults to 5.
*/
protected int failureCount = 5;
/**
* The time (in seconds) a user is locked out for after too many
* authentication failures. Defaults to 300 (5 minutes).
*/
protected int lockOutTime = 300;
/**
* Number of users that have failed authentication to keep in cache. Over
* time the cache will grow to this size and may not shrink. Defaults to
* 1000.
*/
protected int cacheSize = 1000;
/**
* If a failed user is removed from the cache because the cache is too big
* before it has been in the cache for at least this period of time (in
* seconds) a warning message will be logged. Defaults to 3600 (1 hour).
*/
protected int cacheRemovalWarningTime = 3600;
/**
* Users whose last authentication attempt failed. Entries will be ordered
* in access order from least recent to most recent.
*/
protected Map<String,LockRecord> failedUsers = null;
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Configure the list of failed users to delete the oldest entry once it
// exceeds the specified size
failedUsers = new LinkedHashMap<String, LockRecord>(cacheSize, 0.75f,
true) {
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(
Map.Entry<String, LockRecord> eldest) {
if (size() > cacheSize) {
// Check to see if this element has been removed too quickly
long timeInCache = (System.currentTimeMillis() -
eldest.getValue().getLastFailureTime())/1000;
if (timeInCache < cacheRemovalWarningTime) {
log.warn(sm.getString("lockOutRealm.removeWarning",
eldest.getKey(), Long.valueOf(timeInCache)));
}
return true;
}
return false;
}
};
super.startInternal();
}
/**
* Return the Principal associated with the specified username, which
* matches the digest calculated using the given parameters using the
* method described in RFC 2069; otherwise return <code>null</code>.
*
* @param username Username of the Principal to look up
* @param clientDigest Digest which has been submitted by the client
* @param nonce Unique (or supposedly unique) token which has been used
* for this request
* @param realmName Realm name
* @param md5a2 Second MD5 digest used to calculate the digest :
* MD5(Method + ":" + uri)
*/
@Override
public Principal authenticate(String username, String clientDigest,
String nonce, String nc, String cnonce, String qop,
String realmName, String md5a2) {
Principal authenticatedUser = super.authenticate(username, clientDigest, nonce, nc, cnonce,
qop, realmName, md5a2);
return filterLockedAccounts(username, authenticatedUser);
}
/**
* Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in
* authenticating this username
*/
@Override
public Principal authenticate(String username, String credentials) {
Principal authenticatedUser = super.authenticate(username, credentials);
return filterLockedAccounts(username, authenticatedUser);
}
/**
* Return the Principal associated with the specified chain of X509
* client certificates. If there is none, return <code>null</code>.
*
* @param certs Array of client certificates, with the first one in
* the array being the certificate of the client itself.
*/
@Override
public Principal authenticate(X509Certificate[] certs) {
String username = null;
if (certs != null && certs.length >0) {
username = certs[0].getSubjectDN().getName();
}
Principal authenticatedUser = super.authenticate(certs);
return filterLockedAccounts(username, authenticatedUser);
}
/**
* {@inheritDoc}
*/
@Override
public Principal authenticate(GSSContext gssContext, boolean storeCreds) {
if (gssContext.isEstablished()) {
String username = null;
GSSName name = null;
try {
name = gssContext.getSrcName();
} catch (GSSException e) {
log.warn(sm.getString("realmBase.gssNameFail"), e);
return null;
}
username = name.toString();
Principal authenticatedUser = super.authenticate(gssContext, storeCreds);
return filterLockedAccounts(username, authenticatedUser);
}
// Fail in all other cases
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
String username = gssName.toString();
Principal authenticatedUser = super.authenticate(gssName, gssCredential);
return filterLockedAccounts(username, authenticatedUser);
}
/*
* Filters authenticated principals to ensure that <code>null</code> is
* returned for any user that is currently locked out.
*/
private Principal filterLockedAccounts(String username, Principal authenticatedUser) {
// Register all failed authentications
if (authenticatedUser == null && isAvailable()) {
registerAuthFailure(username);
}
if (isLocked(username)) {
// If the user is currently locked, authentication will always fail
log.warn(sm.getString("lockOutRealm.authLockedUser", username));
return null;
}
if (authenticatedUser != null) {
registerAuthSuccess(username);
}
return authenticatedUser;
}
/**
* Unlock the specified username. This will remove all records of
* authentication failures for this user.
*
* @param username The user to unlock
*/
public void unlock(String username) {
// Auth success clears the lock record so...
registerAuthSuccess(username);
}
/*
* Checks to see if the current user is locked. If this is associated with
* a login attempt, then the last access time will be recorded and any
* attempt to authenticated a locked user will log a warning.
*/
public boolean isLocked(String username) {
LockRecord lockRecord = null;
synchronized (this) {
lockRecord = failedUsers.get(username);
}
// No lock record means user can't be locked
if (lockRecord == null) {
return false;
}
// Check to see if user is locked
if (lockRecord.getFailures() >= failureCount &&
(System.currentTimeMillis() -
lockRecord.getLastFailureTime())/1000 < lockOutTime) {
return true;
}
// User has not, yet, exceeded lock thresholds
return false;
}
/*
* After successful authentication, any record of previous authentication
* failure is removed.
*/
private synchronized void registerAuthSuccess(String username) {
// Successful authentication means removal from the list of failed users
failedUsers.remove(username);
}
/*
* After a failed authentication, add the record of the failed
* authentication.
*/
private void registerAuthFailure(String username) {
LockRecord lockRecord = null;
synchronized (this) {
if (!failedUsers.containsKey(username)) {
lockRecord = new LockRecord();
failedUsers.put(username, lockRecord);
} else {
lockRecord = failedUsers.get(username);
if (lockRecord.getFailures() >= failureCount &&
((System.currentTimeMillis() -
lockRecord.getLastFailureTime())/1000)
> lockOutTime) {
// User was previously locked out but lockout has now
// expired so reset failure count
lockRecord.setFailures(0);
}
}
}
lockRecord.registerFailure();
}
/**
* Get the number of failed authentication attempts required to lock the
* user account.
* @return the failureCount
*/
public int getFailureCount() {
return failureCount;
}
/**
* Set the number of failed authentication attempts required to lock the
* user account.
* @param failureCount the failureCount to set
*/
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
/**
* Get the period for which an account will be locked.
* @return the lockOutTime
*/
public int getLockOutTime() {
return lockOutTime;
}
@Override
protected String getName() {
return name;
}
/**
* Set the period for which an account will be locked.
* @param lockOutTime the lockOutTime to set
*/
public void setLockOutTime(int lockOutTime) {
this.lockOutTime = lockOutTime;
}
/**
* Get the maximum number of users for which authentication failure will be
* kept in the cache.
* @return the cacheSize
*/
public int getCacheSize() {
return cacheSize;
}
/**
* Set the maximum number of users for which authentication failure will be
* kept in the cache.
* @param cacheSize the cacheSize to set
*/
public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
}
/**
* Get the minimum period a failed authentication must remain in the cache
* to avoid generating a warning if it is removed from the cache to make
* space for a new entry.
* @return the cacheRemovalWarningTime
*/
public int getCacheRemovalWarningTime() {
return cacheRemovalWarningTime;
}
/**
* Set the minimum period a failed authentication must remain in the cache
* to avoid generating a warning if it is removed from the cache to make
* space for a new entry.
* @param cacheRemovalWarningTime the cacheRemovalWarningTime to set
*/
public void setCacheRemovalWarningTime(int cacheRemovalWarningTime) {
this.cacheRemovalWarningTime = cacheRemovalWarningTime;
}
protected static class LockRecord {
private final AtomicInteger failures = new AtomicInteger(0);
private long lastFailureTime = 0;
public int getFailures() {
return failures.get();
}
public void setFailures(int theFailures) {
failures.set(theFailures);
}
public long getLastFailureTime() {
return lastFailureTime;
}
public void registerFailure() {
failures.incrementAndGet();
lastFailureTime = System.currentTimeMillis();
}
}
}

View File

@@ -0,0 +1,279 @@
/*
* 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.catalina.realm;
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.catalina.LifecycleException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.file.ConfigFileLoader;
/**
* Simple implementation of <b>Realm</b> that reads an XML file to configure
* the valid users, passwords, and roles. The file format (and default file
* location) are identical to those currently supported by Tomcat 3.X.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that the in-memory
* collection representing our defined users (and their roles) is initialized
* at application startup and never modified again. Therefore, no thread
* synchronization is performed around accesses to the principals collection.
*
* @author Craig R. McClanahan
*/
public class MemoryRealm extends RealmBase {
private static final Log log = LogFactory.getLog(MemoryRealm.class);
// ----------------------------------------------------- Instance Variables
/**
* The Digester we will use to process in-memory database files.
*/
private static Digester digester = null;
/**
* Descriptive information about this Realm implementation.
* @deprecated This will be removed in Tomcat 9 onwards.
*/
@Deprecated
protected static final String name = "MemoryRealm";
/**
* The pathname (absolute or relative to Catalina's current working
* directory) of the XML file containing our database information.
*/
private String pathname = "conf/tomcat-users.xml";
/**
* The set of valid Principals for this Realm, keyed by user name.
*/
private final Map<String,GenericPrincipal> principals = new HashMap<>();
// ------------------------------------------------------------- Properties
/**
* @return the pathname of our XML file containing user definitions.
*/
public String getPathname() {
return pathname;
}
/**
* Set the pathname of our XML file containing user definitions. If a
* relative pathname is specified, it is resolved against "catalina.base".
*
* @param pathname The new pathname
*/
public void setPathname(String pathname) {
this.pathname = pathname;
}
// --------------------------------------------------------- Public Methods
/**
* Return the Principal associated with the specified username and
* credentials, if there is one; otherwise return <code>null</code>.
*
* @param username Username of the Principal to look up
* @param credentials Password or other credentials to use in
* authenticating this username
* @return the associated principal, or <code>null</code> if there is none.
*/
@Override
public Principal authenticate(String username, String credentials) {
// No user or no credentials
// Can't possibly authenticate, don't bother the database then
if (username == null || credentials == null) {
if (log.isDebugEnabled())
log.debug(sm.getString("memoryRealm.authenticateFailure", username));
return null;
}
GenericPrincipal principal = principals.get(username);
if(principal == null || principal.getPassword() == null) {
// User was not found in the database or the password was null
// Waste a bit of time as not to reveal that the user does not exist.
getCredentialHandler().mutate(credentials);
if (log.isDebugEnabled())
log.debug(sm.getString("memoryRealm.authenticateFailure", username));
return null;
}
boolean validated = getCredentialHandler().matches(credentials, principal.getPassword());
if (validated) {
if (log.isDebugEnabled())
log.debug(sm.getString("memoryRealm.authenticateSuccess", username));
return principal;
} else {
if (log.isDebugEnabled())
log.debug(sm.getString("memoryRealm.authenticateFailure", username));
return null;
}
}
// -------------------------------------------------------- Package Methods
/**
* Add a new user to the in-memory database.
*
* @param username User's username
* @param password User's password (clear text)
* @param roles Comma-delimited set of roles associated with this user
*/
void addUser(String username, String password, String roles) {
// Accumulate the list of roles for this user
ArrayList<String> list = new ArrayList<>();
roles += ",";
while (true) {
int comma = roles.indexOf(',');
if (comma < 0)
break;
String role = roles.substring(0, comma).trim();
list.add(role);
roles = roles.substring(comma + 1);
}
// Construct and cache the Principal for this user
GenericPrincipal principal =
new GenericPrincipal(username, password, list);
principals.put(username, principal);
}
// ------------------------------------------------------ Protected Methods
/**
* @return a configured <code>Digester</code> to use for processing
* the XML input file, creating a new one if necessary.
*/
protected synchronized Digester getDigester() {
if (digester == null) {
digester = new Digester();
digester.setValidating(false);
try {
digester.setFeature(
"http://apache.org/xml/features/allow-java-encodings",
true);
} catch (Exception e) {
log.warn(sm.getString("memoryRealm.xmlFeatureEncoding"), e);
}
digester.addRuleSet(new MemoryRuleSet());
}
return digester;
}
@Override
@Deprecated
protected String getName() {
return name;
}
/**
* @return the password associated with the given principal's user name.
*/
@Override
protected String getPassword(String username) {
GenericPrincipal principal = principals.get(username);
if (principal != null) {
return principal.getPassword();
} else {
return null;
}
}
/**
* @return the Principal associated with the given user name.
*/
@Override
protected Principal getPrincipal(String username) {
return principals.get(username);
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
String pathName = getPathname();
try (InputStream is = ConfigFileLoader.getInputStream(pathName)) {
// Load the contents of the database file
if (log.isDebugEnabled()) {
log.debug(sm.getString("memoryRealm.loadPath", pathName));
}
Digester digester = getDigester();
try {
synchronized (digester) {
digester.push(this);
digester.parse(is);
}
} catch (Exception e) {
throw new LifecycleException(sm.getString("memoryRealm.readXml"), e);
} finally {
digester.reset();
}
} catch (IOException ioe) {
throw new LifecycleException(sm.getString("memoryRealm.loadExist", pathName), ioe);
}
super.startInternal();
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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.catalina.realm;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.Rule;
import org.apache.tomcat.util.digester.RuleSetBase;
import org.xml.sax.Attributes;
/**
* <p><strong>RuleSet</strong> for recognizing the users defined in the
* XML file processed by <code>MemoryRealm</code>.</p>
*
* @author Craig R. McClanahan
*/
@SuppressWarnings("deprecation")
public class MemoryRuleSet extends RuleSetBase {
// ----------------------------------------------------- Instance Variables
/**
* The matching pattern prefix to use for recognizing our elements.
*/
protected final String prefix;
// ------------------------------------------------------------ Constructor
/**
* Construct an instance of this <code>RuleSet</code> with the default
* matching pattern prefix.
*/
public MemoryRuleSet() {
this("tomcat-users/");
}
/**
* Construct an instance of this <code>RuleSet</code> with the specified
* matching pattern prefix.
*
* @param prefix Prefix for matching pattern rules (including the
* trailing slash character)
*/
public MemoryRuleSet(String prefix) {
super();
this.prefix = prefix;
}
// --------------------------------------------------------- Public Methods
/**
* <p>Add the set of Rule instances defined in this RuleSet to the
* specified <code>Digester</code> instance, associating them with
* our namespace URI (if any). This method should only be called
* by a Digester instance.</p>
*
* @param digester Digester instance to which the new Rule instances
* should be added.
*/
@Override
public void addRuleInstances(Digester digester) {
digester.addRule(prefix + "user", new MemoryUserRule());
}
}
/**
* Private class used when parsing the XML database file.
*/
final class MemoryUserRule extends Rule {
/**
* Construct a new instance of this <code>Rule</code>.
*/
public MemoryUserRule() {
// No initialisation required
}
/**
* Process a <code>&lt;user&gt;</code> element from the XML database file.
*
* @param attributes The attribute list for this element
*/
@Override
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
String username = attributes.getValue("username");
if (username == null) {
username = attributes.getValue("name");
}
String password = attributes.getValue("password");
String roles = attributes.getValue("roles");
MemoryRealm realm =
(MemoryRealm) digester.peek(digester.getCount() - 1);
realm.addUser(username, password, roles);
}
}

View File

@@ -0,0 +1,190 @@
/*
* 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.catalina.realm;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.security.ConcurrentMessageDigest;
/**
* This credential handler supports the following forms of stored passwords:
* <ul>
* <li><b>encodedCredential</b> - a hex encoded digest of the password digested
* using the configured digest</li>
* <li><b>{MD5}encodedCredential</b> - a Base64 encoded MD5 digest of the
* password</li>
* <li><b>{SHA}encodedCredential</b> - a Base64 encoded SHA1 digest of the
* password</li>
* <li><b>{SSHA}encodedCredential</b> - 20 character salt followed by the salted
* SHA1 digest Base64 encoded</li>
* <li><b>salt$iterationCount$encodedCredential</b> - a hex encoded salt,
* iteration code and a hex encoded credential, each separated by $</li>
* </ul>
*
* <p>
* If the stored password form does not include an iteration count then an
* iteration count of 1 is used.
* <p>
* If the stored password form does not include salt then no salt is used.
*/
public class MessageDigestCredentialHandler extends DigestCredentialHandlerBase {
private static final Log log = LogFactory.getLog(MessageDigestCredentialHandler.class);
public static final int DEFAULT_ITERATIONS = 1;
private Charset encoding = StandardCharsets.UTF_8;
private String algorithm = null;
public String getEncoding() {
return encoding.name();
}
public void setEncoding(String encodingName) {
if (encodingName == null) {
encoding = StandardCharsets.UTF_8;
} else {
try {
this.encoding = B2CConverter.getCharset(encodingName);
} catch (UnsupportedEncodingException e) {
log.warn(sm.getString("mdCredentialHandler.unknownEncoding",
encodingName, encoding.name()));
}
}
}
@Override
public String getAlgorithm() {
return algorithm;
}
@Override
public void setAlgorithm(String algorithm) throws NoSuchAlgorithmException {
ConcurrentMessageDigest.init(algorithm);
this.algorithm = algorithm;
}
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
if (inputCredentials == null || storedCredentials == null) {
return false;
}
if (getAlgorithm() == null) {
// No digests, compare directly
return storedCredentials.equals(inputCredentials);
} else {
// Some directories and databases prefix the password with the hash
// type. The string is in a format compatible with Base64.encode not
// the normal hex encoding of the digest
if (storedCredentials.startsWith("{MD5}") ||
storedCredentials.startsWith("{SHA}")) {
// Server is storing digested passwords with a prefix indicating
// the digest type
String serverDigest = storedCredentials.substring(5);
String userDigest = Base64.encodeBase64String(ConcurrentMessageDigest.digest(
getAlgorithm(), inputCredentials.getBytes(StandardCharsets.ISO_8859_1)));
return userDigest.equals(serverDigest);
} else if (storedCredentials.startsWith("{SSHA}")) {
// Server is storing digested passwords with a prefix indicating
// the digest type and the salt used when creating that digest
String serverDigestPlusSalt = storedCredentials.substring(6);
// Need to convert the salt to bytes to apply it to the user's
// digested password.
byte[] serverDigestPlusSaltBytes =
Base64.decodeBase64(serverDigestPlusSalt);
final int saltPos = 20;
byte[] serverDigestBytes = new byte[saltPos];
System.arraycopy(serverDigestPlusSaltBytes, 0,
serverDigestBytes, 0, saltPos);
final int saltLength = serverDigestPlusSaltBytes.length - saltPos;
byte[] serverSaltBytes = new byte[saltLength];
System.arraycopy(serverDigestPlusSaltBytes, saltPos,
serverSaltBytes, 0, saltLength);
// Generate the digested form of the user provided password
// using the salt
byte[] userDigestBytes = ConcurrentMessageDigest.digest(getAlgorithm(),
inputCredentials.getBytes(StandardCharsets.ISO_8859_1),
serverSaltBytes);
return Arrays.equals(userDigestBytes, serverDigestBytes);
} else if (storedCredentials.indexOf('$') > -1) {
return matchesSaltIterationsEncoded(inputCredentials, storedCredentials);
} else {
// Hex hashes should be compared case-insensitively
String userDigest = mutate(inputCredentials, null, 1);
if (userDigest == null) {
// Failed to mutate user credentials. Automatic fail.
// Root cause should be logged by mutate()
return false;
}
return storedCredentials.equalsIgnoreCase(userDigest);
}
}
}
@Override
protected String mutate(String inputCredentials, byte[] salt, int iterations) {
if (algorithm == null) {
return inputCredentials;
} else {
byte[] userDigest;
if (salt == null) {
userDigest = ConcurrentMessageDigest.digest(algorithm, iterations,
inputCredentials.getBytes(encoding));
} else {
userDigest = ConcurrentMessageDigest.digest(algorithm, iterations,
salt, inputCredentials.getBytes(encoding));
}
return HexUtils.toHexString(userDigest);
}
}
@Override
protected int getDefaultIterations() {
return DEFAULT_ITERATIONS;
}
@Override
protected Log getLog() {
return log;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.catalina.realm;
import java.util.ArrayList;
import java.util.List;
import org.apache.catalina.CredentialHandler;
public class NestedCredentialHandler implements CredentialHandler {
private final List<CredentialHandler> credentialHandlers = new ArrayList<>();
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
for (CredentialHandler handler : credentialHandlers) {
if (handler.matches(inputCredentials, storedCredentials)) {
return true;
}
}
return false;
}
/**
* The input credentials will be passed to the first nested
* {@link CredentialHandler}. If no nested {@link CredentialHandler} are
* configured then <code>null</code> will be returned.
*
* {@inheritDoc}
*/
@Override
public String mutate(String inputCredentials) {
if (credentialHandlers.isEmpty()) {
return null;
}
return credentialHandlers.get(0).mutate(inputCredentials);
}
public void addCredentialHandler(CredentialHandler handler) {
credentialHandlers.add(handler);
}
public CredentialHandler[] getCredentialHandlers() {
return credentialHandlers.toArray(new CredentialHandler[0]);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.catalina.realm;
import java.security.Principal;
/**
* Minimal Realm implementation that always returns null when an attempt is made
* to validate a user name and password. It is intended to be used as a default
* Realm implementation when no other Realm is specified.
*/
public class NullRealm extends RealmBase {
private static final String NAME = "NullRealm";
@Override
@Deprecated
protected String getName() {
return NAME;
}
@Override
protected String getPassword(String username) {
// Always return null
return null;
}
@Override
protected Principal getPrincipal(String username) {
// Always return null
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
/*
* 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.catalina.realm;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.HexUtils;
public class SecretKeyCredentialHandler extends DigestCredentialHandlerBase {
private static final Log log = LogFactory.getLog(SecretKeyCredentialHandler.class);
public static final String DEFAULT_ALGORITHM = "PBKDF2WithHmacSHA1";
public static final int DEFAULT_KEY_LENGTH = 160;
public static final int DEFAULT_ITERATIONS = 20000;
private SecretKeyFactory secretKeyFactory;
private int keyLength = DEFAULT_KEY_LENGTH;
public SecretKeyCredentialHandler() throws NoSuchAlgorithmException {
setAlgorithm(DEFAULT_ALGORITHM);
}
@Override
public String getAlgorithm() {
return secretKeyFactory.getAlgorithm();
}
@Override
public void setAlgorithm(String algorithm) throws NoSuchAlgorithmException {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm);
this.secretKeyFactory = secretKeyFactory;
}
public int getKeyLength() {
return keyLength;
}
public void setKeyLength(int keyLength) {
this.keyLength = keyLength;
}
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
return matchesSaltIterationsEncoded(inputCredentials, storedCredentials);
}
@Override
protected String mutate(String inputCredentials, byte[] salt, int iterations) {
return mutate(inputCredentials, salt, iterations, getKeyLength());
}
@Override
protected String mutate(String inputCredentials, byte[] salt, int iterations, int keyLength) {
try {
KeySpec spec = new PBEKeySpec(inputCredentials.toCharArray(), salt, iterations, keyLength);
return HexUtils.toHexString(secretKeyFactory.generateSecret(spec).getEncoded());
} catch (InvalidKeySpecException | IllegalArgumentException e) {
log.warn(sm.getString("pbeCredentialHandler.invalidKeySpec"), e);
return null;
}
}
@Override
protected int getDefaultIterations() {
return DEFAULT_ITERATIONS;
}
@Override
protected Log getLog() {
return log;
}
}

View File

@@ -0,0 +1,257 @@
/*
* 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.catalina.realm;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.naming.Context;
import org.apache.catalina.Group;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Role;
import org.apache.catalina.User;
import org.apache.catalina.UserDatabase;
import org.apache.catalina.Wrapper;
import org.apache.catalina.users.MemoryUserDatabase;
import org.apache.tomcat.util.ExceptionUtils;
/**
* Implementation of {@link org.apache.catalina.Realm} that is based on an
* implementation of {@link UserDatabase} made available through the global JNDI
* resources configured for this instance of Catalina. Set the
* <code>resourceName</code> parameter to the global JNDI resources name for the
* configured instance of <code>UserDatabase</code> that we should consult.
*
* @author Craig R. McClanahan
* @since 4.1
*/
public class UserDatabaseRealm extends RealmBase {
// ----------------------------------------------------- Instance Variables
/**
* The <code>UserDatabase</code> we will use to authenticate users and
* identify associated roles.
*/
protected UserDatabase database = null;
/**
* Descriptive information about this Realm implementation.
* @deprecated This will be removed in Tomcat 9 onwards.
*/
@Deprecated
protected static final String name = "UserDatabaseRealm";
/**
* The global JNDI name of the <code>UserDatabase</code> resource we will be
* utilizing.
*/
protected String resourceName = "UserDatabase";
// ------------------------------------------------------------- Properties
/**
* @return the global JNDI name of the <code>UserDatabase</code> resource we
* will be using.
*/
public String getResourceName() {
return resourceName;
}
/**
* Set the global JNDI name of the <code>UserDatabase</code> resource we
* will be using.
*
* @param resourceName The new global JNDI name
*/
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
// --------------------------------------------------------- Public Methods
/**
* Return <code>true</code> if the specified Principal has the specified
* security role, within the context of this Realm; otherwise return
* <code>false</code>. This implementation returns <code>true</code> if the
* <code>User</code> has the role, or if any <code>Group</code> that the
* <code>User</code> is a member of has the role.
*
* @param principal Principal for whom the role is to be checked
* @param role Security role to be checked
*/
@Override
public boolean hasRole(Wrapper wrapper, Principal principal, String role) {
// Check for a role alias defined in a <security-role-ref> element
if (wrapper != null) {
String realRole = wrapper.findSecurityReference(role);
if (realRole != null)
role = realRole;
}
if (principal instanceof GenericPrincipal) {
GenericPrincipal gp = (GenericPrincipal) principal;
if (gp.getUserPrincipal() instanceof User) {
principal = gp.getUserPrincipal();
}
}
if (!(principal instanceof User)) {
// Play nice with SSO and mixed Realms
// No need to pass the wrapper here because role mapping has been
// performed already a few lines above
return super.hasRole(null, principal, role);
}
if ("*".equals(role)) {
return true;
} else if (role == null) {
return false;
}
User user = (User) principal;
Role dbrole = database.findRole(role);
if (dbrole == null) {
return false;
}
if (user.isInRole(dbrole)) {
return true;
}
Iterator<Group> groups = user.getGroups();
while (groups.hasNext()) {
Group group = groups.next();
if (group.isInRole(dbrole)) {
return true;
}
}
return false;
}
// ------------------------------------------------------ Protected Methods
@Override
@Deprecated
protected String getName() {
return name;
}
@Override
public void backgroundProcess() {
if (database instanceof MemoryUserDatabase) {
((MemoryUserDatabase) database).backgroundProcess();
}
}
/**
* Return the password associated with the given principal's user name.
*/
@Override
protected String getPassword(String username) {
User user = database.findUser(username);
if (user == null) {
return null;
}
return user.getPassword();
}
/**
* Return the Principal associated with the given user name.
*/
@Override
protected Principal getPrincipal(String username) {
User user = database.findUser(username);
if (user == null) {
return null;
}
List<String> roles = new ArrayList<>();
Iterator<Role> uroles = user.getRoles();
while (uroles.hasNext()) {
Role role = uroles.next();
roles.add(role.getName());
}
Iterator<Group> groups = user.getGroups();
while (groups.hasNext()) {
Group group = groups.next();
uroles = group.getRoles();
while (uroles.hasNext()) {
Role role = uroles.next();
roles.add(role.getName());
}
}
return new GenericPrincipal(username, user.getPassword(), roles, user);
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Prepare for the beginning of active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
try {
Context context = getServer().getGlobalNamingContext();
database = (UserDatabase) context.lookup(resourceName);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), e);
database = null;
}
if (database == null) {
throw new LifecycleException(
sm.getString("userDatabaseRealm.noDatabase", resourceName));
}
super.startInternal();
}
/**
* Gracefully terminate the active use of the public methods of this
* component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
@Override
protected void stopInternal() throws LifecycleException {
// Perform normal superclass finalization
super.stopInternal();
// Release reference to our user database
database = null;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.catalina.realm;
import java.security.cert.X509Certificate;
/**
* An X509UsernameRetriever that returns a certificate's entire
* SubjectDN as the username.
*/
public class X509SubjectDnRetriever implements X509UsernameRetriever {
@Override
public String getUsername(X509Certificate clientCert) {
return clientCert.getSubjectDN().getName();
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.catalina.realm;
import java.security.cert.X509Certificate;
/**
* Provides an interface for retrieving a user name from an X509Certificate.
*/
public interface X509UsernameRetriever {
/**
* Gets a user name from an X509Certificate.
*
* @param cert The certificate containing the user name.
* @return An appropriate user name obtained from one or more fields
* in the certificate.
*/
public String getUsername(X509Certificate cert);
}

View File

@@ -0,0 +1,547 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<mbeans-descriptors>
<mbean name="DataSourceRealm"
className="org.apache.catalina.mbeans.ClassNameMBean"
description="Implementation of Realm that works with any JNDI configured DataSource"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.DataSourceRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="dataSourceName"
description="The JNDI named JDBC DataSource for your database"
type="java.lang.String"/>
<attribute name="localDataSource"
description="Configures if the DataSource is local to the webapp"
type="boolean"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="roleNameCol"
description="The column in the user role table that names a role"
type="java.lang.String"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="userCredCol"
description="The column in the user table that holds the user's credentials"
type="java.lang.String"/>
<attribute name="userNameCol"
description="The column in the user table that holds the user's username"
type="java.lang.String"/>
<attribute name="userRoleTable"
description="The table that holds the relation between user's and roles"
type="java.lang.String"/>
<attribute name="userTable"
description="The table that holds user data"
type="java.lang.String"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="JAASRealm"
description="Implementation of Realm that authenticates users via the Java Authentication and Authorization Service (JAAS)"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.JAASRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="appName"
description="The application name passed to the JAAS LoginContext, which uses it to select the set of relevant LoginModules"
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="roleClassNames"
description="Comma-delimited list of javax.security.Principal classes that represent security roles"
type="java.lang.String"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="userClassNames"
description="Comma-delimited list of javax.security.Principal classes that represent individual users"
type="java.lang.String"/>
<attribute name="useContextClassLoader"
description="Sets whether to use the context or default ClassLoader."
is="true"
type="boolean"/>
<attribute name="validate"
description="Should we validate client certificate chains when they are presented?"
type="boolean"/>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="JDBCRealm"
description="Implementation of Realm that works with any JDBC supported database"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.JDBCRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="connectionName"
description="The connection username to use when trying to connect to the database"
type="java.lang.String"/>
<attribute name="connectionPassword"
description="The connection password to use when trying to connect to the database"
type="java.lang.String"/>
<attribute name="connectionURL"
description="The connection URL to use when trying to connect to the database"
type="java.lang.String"/>
<attribute name="driverName"
description="The JDBC driver to use"
type="java.lang.String"/>
<attribute name="roleNameCol"
description="The column in the user role table that names a role"
type="java.lang.String"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="userCredCol"
description="The column in the user table that holds the user's credentials"
type="java.lang.String"/>
<attribute name="userNameCol"
description="The column in the user table that holds the user's username"
type="java.lang.String"/>
<attribute name="userRoleTable"
description="The table that holds the relation between user's and roles"
type="java.lang.String"/>
<attribute name="userTable"
description="The table that holds user data"
type="java.lang.String"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="JNDIRealm"
description="Implementation of Realm that works with a directory server accessed via the Java Naming and Directory Interface (JNDI) APIs"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.JNDIRealm">
<attribute name="adCompat"
description=" The current settings for handling PartialResultExceptions"
type="boolean"/>
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="alternateURL"
description="The Alternate URL"
type="java.lang.String"/>
<attribute name="authentication"
description="The type of authentication to use"
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="commonRole"
description="The common role"
type="java.lang.String"/>
<attribute name="connectionName"
description="The connection username for the server we will contact"
type="java.lang.String"/>
<attribute name="connectionPassword"
description="The connection password for the server we will contact"
type="java.lang.String"/>
<attribute name="connectionTimeout"
description="The connection timeout"
type="java.lang.String"/>
<attribute name="connectionURL"
description="The connection URL for the server we will contact"
type="java.lang.String"/>
<attribute name="contextFactory"
description="The JNDI context factory for this Realm"
type="java.lang.String"/>
<attribute name="protocol"
description="The protocol to be used"
type="java.lang.String"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="referrals"
description="The current setting for handling JNDI referrals."
type="java.lang.String"/>
<attribute name="roleBase"
description="The base element for role searches"
type="java.lang.String"/>
<attribute name="roleName"
description="The name of the attribute containing roles held elsewhere"
type="java.lang.String"/>
<attribute name="roleNested"
description="The 'The nested group search flag' flag"
type="boolean"/>
<attribute name="roleSearch"
description="The message format used to select roles for a user"
type="java.lang.String"/>
<attribute name="roleSearchAsUser"
description="Should the search for user roles be performed as the authenticating user?"
is="true"
type="boolean"/>
<attribute name="roleSubtree"
description="Should we search the entire subtree for matching memberships?"
type="boolean"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="userBase"
description="The base element for user searches"
type="java.lang.String"/>
<attribute name="userPassword"
description="The attribute name used to retrieve the user password"
type="java.lang.String"/>
<attribute name="userPattern"
description="The message format used to select a user"
type="java.lang.String"/>
<attribute name="userRoleName"
description="The name of the attribute in the user's entry containing roles for that user"
type="java.lang.String"/>
<attribute name="userSearch"
description="The message format used to search for a user"
type="java.lang.String"/>
<attribute name="userSearchAsUser"
description="Should the search for the user's DN be performed as the authenticating user?"
is="true"
type="boolean"/>
<attribute name="userSubtree"
description="Should we search the entire subtree for matching users?"
type="boolean"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="MemoryRealm"
description="Simple implementation of Realm that reads an XML file to configure the valid users, passwords, and roles"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.MemoryRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="pathname"
description="The pathname of the XML file containing our database information"
type="java.lang.String"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="UserDatabaseRealm"
description="Realm connected to a UserDatabase as a global JNDI resource"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.UserDatabaseRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="resourceName"
description="The global JNDI name of the UserDatabase resource to use"
type="java.lang.String"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="CombinedRealm"
description="Realm implementation that can be used to chain multiple realms"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.CombinedRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="realms"
description="The set of realms that the combined realm is wrapping"
type="[Ljavax.management.ObjectName;"
writeable="false"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="addRealm"
description="Add a new Realm to the set of Realms wrapped by this realm"
impact="ACTION"
returnType="void">
<parameter name="theRealm"
description="New Realm to add"
type="org.apache.catalina.Realm"/>
</operation>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
<mbean name="LockOutRealm"
description="Realm implementation that can be used to wrap existing realms to provide a user lock-out capability"
domain="Catalina"
group="Realm"
type="org.apache.catalina.realm.LockOutRealm">
<attribute name="allRolesMode"
description="The all roles mode."
type="java.lang.String"/>
<attribute name="cacheSize"
description="Number of users that have failed authentication to keep in cache. Over time the cache will grow to this size and may not shrink. Defaults to 1000."
type="int" />
<attribute name="cacheRemovalWarningTime"
description="If a failed user is removed from the cache because the cache is too big before it has been in the cache for at least this period of time (in seconds) a warning message will be logged. Defaults to 3600 (1 hour)."
type="int" />
<attribute name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute name="failureCount"
description="The number of times in a row a user has to fail authentication to be locked out. Defaults to 5."
type="int" />
<attribute name="lockOutTime"
description="The time (in seconds) a user is locked out for after too many authentication failures. Defaults to 300 (5 minutes)."
type="int" />
<attribute name="realms"
description="The set of realms that the lockout realm is wrapping"
type="[Ljavax.management.ObjectName;"
writeable="false"/>
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
<attribute name="validate"
description="The 'validate certificate chains' flag."
type="boolean"/>
<operation name="addRealm"
description="Add a new Realm to the set of Realms wrapped by this realm"
impact="ACTION"
returnType="void">
<parameter name="theRealm"
description="New Realm to add"
type="org.apache.catalina.Realm"/>
</operation>
<operation name="isLocked"
description="Determine if the specified user is locked"
impact="ACTION"
returnType="boolean">
<parameter name="username"
description="User to test for being locked"
type="java.lang.String"/>
</operation>
<operation name="unlock"
description="Unlock the specified user"
impact="ACTION"
returnType="void">
<parameter name="username"
description="User to unlock"
type="java.lang.String"/>
</operation>
<operation name="start" description="Start" impact="ACTION" returnType="void" />
<operation name="stop" description="Stop" impact="ACTION" returnType="void" />
<operation name="init" description="Init" impact="ACTION" returnType="void" />
<operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
</mbean>
</mbeans-descriptors>

View File

@@ -0,0 +1,30 @@
<!--
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.
-->
<body>
<p>This package contains <code>Realm</code> implementations for the
various supported realm technologies for authenticating users and
identifying their associated roles. The <code>Realm</code> that is
associated with a web application's <code>Context</code> (or a hierarchically
superior Container) is used to resolve authentication and role presence
questions when a web application uses container managed security as described
in the Servlet API Specification.</p>
<p>The implementations share a common base class that supports basic
functionality for all of the standard <code>Realm</code> implementations.</p>
</body>