|
Glassfish example source code file (RealmAdapter.java)
The Glassfish RealmAdapter.java source code/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.web.security;
import com.sun.enterprise.config.serverbeans.SecurityService;
import com.sun.enterprise.security.auth.digest.impl.HttpAlgorithmParameterImpl;
import com.sun.enterprise.security.web.integration.WebSecurityManager;
import com.sun.enterprise.security.web.integration.WebSecurityManagerFactory;
import com.sun.enterprise.security.web.integration.WebPrincipal;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.security.auth.Subject;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sun.security.x509.X500Name;
import org.apache.catalina.Authenticator;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.HttpRequest;
import org.apache.catalina.HttpResponse;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Realm;
import org.apache.catalina.authenticator.AuthenticatorBase;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.deploy.SecurityConstraint;
import org.apache.catalina.realm.Constants;
import org.apache.catalina.realm.RealmBase;
import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.internal.api.ServerContext;
//import com.sun.enterprise.Switch;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.RunAsIdentityDescriptor;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.deployment.WebComponentDescriptor;
//import com.sun.enterprise.deployment.interfaces.SecurityRoleMapper;
import com.sun.enterprise.deployment.web.LoginConfiguration;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.SecurityUtil;
import com.sun.enterprise.security.WebSecurityDeployerProbeProvider;
import com.sun.enterprise.security.auth.login.LoginContextDriver;
import com.sun.enterprise.security.auth.realm.certificate.CertificateRealm;
import com.sun.enterprise.security.integration.RealmInitializer;
import com.sun.logging.LogDomains;
import com.sun.enterprise.security.jmac.config.HttpServletConstants;
import com.sun.enterprise.security.jmac.config.HttpServletHelper;
/*V3:Comment
import com.sun.enterprise.webservice.monitoring.WebServiceEngineImpl;
import com.sun.enterprise.webservice.monitoring.AuthenticationListener;
*/
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.annotations.Service;
import com.sun.enterprise.security.auth.digest.api.DigestAlgorithmParameter;
import com.sun.enterprise.security.auth.login.DigestCredentials;
import com.sun.enterprise.security.auth.digest.api.Key;
import com.sun.enterprise.security.auth.digest.api.DigestParameterGenerator;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.component.Habitat;
import static com.sun.enterprise.security.auth.digest.api.Constants.A1;
import com.sun.enterprise.security.authorize.PolicyContextHandlerImpl;
import java.net.URLEncoder;
import javax.security.jacc.PolicyContext;
import org.jvnet.hk2.component.PerLookup;
import org.jvnet.hk2.component.PostConstruct;
/**
* This is the realm adapter used to authenticate users and authorize
* access to web resources. The authenticate method is called by Tomcat
* to authenticate users. The hasRole method is called by Tomcat during
* the authorization process.
* @author Harpreet Singh
* @author JeanFrancois Arcand
*/
@Service
@Scoped(PerLookup.class)
public class RealmAdapter extends RealmBase implements RealmInitializer, PostConstruct {
//private static final String UNCONSTRAINED = "unconstrained";
private static final Logger _logger = LogDomains.getLogger(RealmAdapter.class, LogDomains.WEB_LOGGER);
private static final ResourceBundle rb = _logger.getResourceBundle();
public static final String SECURITY_CONTEXT = "SecurityContext";
public static final String BASIC = "BASIC";
public static final String FORM = "FORM";
private static final String SERVER_AUTH_CONTEXT = "__javax.security.auth.message.ServerAuthContext";
private static final String MESSAGE_INFO = "__javax.security.auth.message.MessageInfo";
private static final WebSecurityDeployerProbeProvider websecurityProbeProvider = new WebSecurityDeployerProbeProvider();
// name of system property that can be used to define
// corresponding default provider for system apps.
private static final String SYSTEM_HTTPSERVLET_SECURITY_PROVIDER =
"system_httpservlet_security_provider";
//private String realm = "default";
//private SecurityRoleMapper mapper = null;
private WebBundleDescriptor webDesc = null;
// BEGIN IASRI 4747594
private HashMap<String,String> runAsPrincipals = null;
// END IASRI 4747594
// required for realm-per-app login
private String _realmName = null;
/**
* Descriptive information about this Realm implementation.
*/
protected static final String name = "J2EE-RI-RealmAdapter";
/**
* The context Id value needed by the jacc architecture.
*/
private String CONTEXT_ID = null;
private Container virtualServer;
/**
* A <code>WebSecurityManager object associated with a CONTEXT_ID
*/
protected volatile WebSecurityManager webSecurityManager = null;
/**
* The factory used for creating <code>WebSecurityManager object.
*/
protected WebSecurityManagerFactory webSecurityManagerFactory = null;
protected boolean isCurrentURIincluded = false;
//private ArrayList roles = null;
/* the following fields are used to implement a bypass of
* FBL related targets
*/
protected final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private boolean contextEvaluated = false;
private String loginPage = null;
private String errorPage = null;
private final static SecurityConstraint[] emptyConstraints =
new SecurityConstraint[]{};
/**
* the default provider id for system apps if one has been established.
* the default provider for system apps is established by defining
* a system property.
*/
private static String defaultSystemProviderID =
getDefaultSystemProviderID();
//private String appID;
private String moduleID;
private boolean isSystemApp;
//private String jmacProviderRegisID = null;
private HttpServletHelper helper = null;
//PERF Fix.
//there maybe a race condition but since its a boolean it does not matter.
//as all threads would evaluate the same result.
private Boolean secExtEnabled = null;
@Inject
private ServerContext serverContext;
@Inject
private Habitat habitat;
@Inject
private SecurityService secService;
public RealmAdapter() {
//used during Injection in WebContainer (glue code)
}
/**
* Create for WS Ejb endpoint authentication.
* Roles related data is not available here.
*/
public RealmAdapter(String realmName, String moduleID) {
_realmName = realmName;
this.moduleID = moduleID;
}
/**
* Create the realm adapter. Extracts the role to user/group mapping
* from the runtime deployment descriptor.
* @param the web bundle deployment descriptor.
* @param isSystemApp if the app is a system app.
public RealmAdapter(WebBundleDescriptor descriptor, boolean isSystemApp) {
this(descriptor, isSystemApp, null);
}*/
/**
* Create the realm adapter. Extracts the role to user/group mapping
* from the runtime deployment descriptor.
* @param the web bundle deployment descriptor.
* @param isSystemApp if the app is a system app.
* @param realmName The realm name to use if the app does not specify its
* own
public RealmAdapter(WebBundleDescriptor descriptor,
boolean isSystemApp,
String realmName) {
this.isSystemApp = isSystemApp;
webDesc = descriptor;
Application app = descriptor.getApplication();
mapper = app.getRoleMapper();
LoginConfiguration loginConfig = descriptor.getLoginConfiguration();
_realmName = app.getRealm();
if (_realmName == null && loginConfig != null) {
_realmName = loginConfig.getRealmName();
}
if (realmName != null && (_realmName == null || _realmName.equals(""))) {
_realmName = realmName;
}
// BEGIN IASRI 4747594
CONTEXT_ID = WebSecurityManager.getContextID(descriptor);
runAsPrincipals = new HashMap();
Iterator bundle = webDesc.getWebComponentDescriptors().iterator();
while (bundle.hasNext()) {
WebComponentDescriptor wcd = (WebComponentDescriptor) bundle.next();
RunAsIdentityDescriptor runAsDescriptor = wcd.getRunAsIdentity();
if (runAsDescriptor != null) {
String principal = runAsDescriptor.getPrincipal();
String servlet = wcd.getCanonicalName();
if (principal == null || servlet == null) {
_logger.warning("web.realmadapter.norunas");
} else {
runAsPrincipals.put(servlet, principal);
_logger.fine("Servlet " + servlet +
" will run-as: " + principal);
}
}
}
// END IASRI 4747594
this.appID = app.getRegistrationName();
// helper are set until setVirtualServer is invoked
} */
public void destroy() {
super.destroy();
if (helper != null) {
helper.disable();
}
}
/**
* Sets the virtual server on which the web module (with which this
* RealmAdapter is associated with) has been deployed.
*
* @param container The virtual server
*/
public void setVirtualServer(Object container) {
this.virtualServer = (Container)container;
//this was causing classloading failure.
//TODO:reexamine after TP2
//this.helper = getConfigHelper();
}
public WebBundleDescriptor getWebDescriptor() {
return webDesc;
}
// utility method to get web security anager.
// will log warning if the manager is not found in the factory, and
// logNull is true.
public WebSecurityManager getWebSecurityManager(boolean logNull) {
if (webSecurityManager == null) {
synchronized (this) {
webSecurityManager = webSecurityManagerFactory.getManager(CONTEXT_ID,null, false);
}
if (webSecurityManager == null && logNull) {
_logger.log(Level.WARNING, "realmAdapter.noWebSecMgr",
CONTEXT_ID);
}
}
return webSecurityManager;
}
/**
* Check if the given principal has the provided role. Returns
* true if the principal has the specified role, false otherwise.
* @return true if the principal has the specified role.
* @param request Request we are processing
* @param response Response we are creating
* @param the principal
* @param the role
*/
//START OF SJSAS 6232464
//public boolean hasRole(Principal principal, String role) {
public boolean hasRole(HttpRequest request,
HttpResponse response,
Principal principal,
String role) {
WebSecurityManager secMgr = getWebSecurityManager(true);
if (secMgr == null) {
return false;
}
//add HttpResponse and HttpResponse to the parameters, and remove
//instance variable currentRequest from this class. References to
//this.currentRequest are also removed from other methods.
//String servletName = getResourceName( currentRequest.getRequestURI(),
// currentRequest.getContextPath());
String servletName = getCanonicalName(request);
// END S1AS8PE 4966609
boolean isGranted = secMgr.hasRoleRefPermission(servletName, role, principal);
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("Checking if servlet " + servletName + " with principal " + principal + " has role " + role + " isGranted: " + isGranted);
}
return isGranted;
}
public boolean hasRole(String servletName, Principal principal, String role) {
WebSecurityManager secMgr = getWebSecurityManager(true);
if (secMgr == null) {
return false;
}
return secMgr.hasRoleRefPermission(servletName, role, principal);
}
public void logout() {
setSecurityContext(null);
resetPolicyContext();
}
public Principal authenticate(HttpServletRequest hreq) {
try {
DigestParameterGenerator generator = DigestParameterGenerator.getInstance(DigestParameterGenerator.HTTP_DIGEST);
DigestAlgorithmParameter[] params = generator.generateParameters(new HttpAlgorithmParameterImpl(hreq));
Key key = null;
for(int i=0;i<params.length;i++){
DigestAlgorithmParameter dap = params[i];
if(A1.equals(dap.getName()) && (dap instanceof Key)){
key = (Key)dap;
break;
}
}
DigestCredentials creds = new DigestCredentials(_realmName,key.getUsername(), params);
LoginContextDriver.login(creds);
SecurityContext secCtx = SecurityContext.getCurrent();
return new WebPrincipal(creds.getUserName(),(char[])null, secCtx);
} catch (Exception le) {
if (_logger.isLoggable(Level.WARNING)) {
_logger.log(Level.WARNING,"web.login.failed", le.toString());
}
}
return null;
}
/**
* Authenticates and sets the SecurityContext in the TLS.
* @return the authenticated principal.
* @param the user name.
* @param the password.
*/
public Principal authenticate(String username, char[] password) {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("Tomcat callback for authenticate user/password");
_logger.fine("usename = " + username);
}
if (authenticate(username, password, null)) {
SecurityContext secCtx = SecurityContext.getCurrent();
assert (secCtx != null); // or auth should've failed
return new WebPrincipal(username, password, secCtx);
} else {
return null;
}
}
public Principal authenticate(X509Certificate certs[]) {
if (authenticate(null, null, certs)) {
SecurityContext secCtx = SecurityContext.getCurrent();
assert (secCtx != null); // or auth should've failed
return new WebPrincipal(certs, secCtx);
} else {
return null;
}
}
/* IASRI 4688449
This method was only used by J2EEInstanceListener to set the security
context prior to invocations by re-authenticating a previously set
WebPrincipal. This is now cached so no need.
*/
public boolean authenticate(WebPrincipal prin) {
if (prin.isUsingCertificate()) {
return authenticate(null, null, prin.getCertificates());
} else {
return authenticate(prin.getName(), prin.getPassword(), null);
}
}
/**
* Authenticates and sets the SecurityContext in the TLS.
* @return true if authentication succeeded, false otherwise.
* @param the username.
* @param the authentication method.
* @param the authentication data.
*/
protected boolean authenticate(String username, char[] password,
X509Certificate[] certs) {
String realm_name = null;
boolean success = false;
try {
if (certs != null) {
Subject subject = new Subject();
X509Certificate certificate = certs[0];
X500Name x500Name = (X500Name) certificate.getSubjectDN();
subject.getPublicCredentials().add(x500Name);
// Put the certificate chain as an List in the subject, to be accessed by user's LoginModule.
final List<X509Certificate> certificateCred = Arrays.asList(certs);
subject.getPublicCredentials().add(certificateCred);
LoginContextDriver.doX500Login(subject, moduleID);
realm_name = CertificateRealm.AUTH_TYPE;
} else {
realm_name = _realmName;
LoginContextDriver.login(username, password, realm_name);
}
success = true;
} catch (Exception le) {
success = false;
if (_logger.isLoggable(Level.WARNING)) {
_logger.log(Level.WARNING,"web.login.failed", le.toString());
_logger.log(Level.WARNING,"Exception", le);
}
}
if (success) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "Web login succeeded for: " + username);
}
}
return success;
}
// BEGIN IASRI 4747594
/**
* Set the run-as principal into the SecurityContext when needed.
*
* <P>This method will attempt to obtain the name of the servlet from
* the ComponentInvocation. Note that there may not be one since this
* gets called also during internal processing (not clear..) not just
* part of servlet requests. However, if it is not a servlet request
* there is no need (or possibility) to have a run-as setting so no
* further action is taken.
*
* <P>If the servlet name is present the runAsPrincipals cache is
* checked to find the run-as principal to use (if any). If one is set,
* the SecurityContext is switched to this principal.
*
* @param inv The invocation object to process.
*
*/
public void preSetRunAsIdentity(ComponentInvocation inv) {
//Optimization to avoid the expensivce call to getServletName
//for cases with no run-as descriptors
if(runAsPrincipals != null && runAsPrincipals.isEmpty()) {
return;
}
String servletName = this.getServletName(inv);
if (servletName == null) {
return;
}
String runAs = runAsPrincipals.get(servletName);
if (runAs != null) {
// The existing SecurityContext is saved - however, this seems
// meaningless - see bug 4757733. For now, keep it unchanged
// in case there are some dependencies elsewhere in RI.
SecurityContext old = getSecurityContext();
inv.setOldSecurityContext(old);
// Set the run-as principal into SecurityContext
loginForRunAs(runAs);
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("run-as principal for " + servletName +
" set to: " + runAs);
}
}
}
/**
* Obtain servlet name from invocation.
*
* <P>In order to obtain the servlet name the following must be true:
* The ComponentInvocation contains a 'class' of type HttpServlet, which
* contains a valid ServletConfig object. This method returns the
* value returned by getServletName() on the ServletConfig. If the above
* is not met, null is returned.
*
* @param inv The invocation object to process.
* @return Servlet name or null.
*
*/
private String getServletName(ComponentInvocation inv) {
Object invInstance = inv.getInstance();
if (invInstance instanceof HttpServlet) {
HttpServlet thisServlet = (HttpServlet) invInstance;
ServletConfig svc = thisServlet.getServletConfig();
if (svc != null) {
return thisServlet.getServletName();
}
}
return null;
}
/**
* Attempts to restore old SecurityContext (but fails).
*
* <P>In theory this method seems to attempt to check if a run-as
* principal was set by preSetRunAsIdentity() (based on the indirect
* assumption that if the servlet in the given invocation has a run-as
* this must've been the case). If so, it retrieves the oldSecurityContext
* from the invocation object and set it in the SecurityContext.
*
* <P>The problem is that the invocation object is not the same object
* as was passed in to preSetRunAsIdentity() so it will never contain
* the right info - see bug 4757733.
*
* <P>In practice it means this method only ever sets the
* SecurityContext to null (if run-as matched) or does nothing. In
* particular note the implication that it <i>will be set to
* null after a run-as invocation completes. This behavior will be
* retained for the time being for consistency with RI. It must be fixed
* later.
*
* @param inv The invocation object to process.
*
*/
public void postSetRunAsIdentity(ComponentInvocation inv) {
//Optimization to avoid the expensivce call to getServletName
//for cases with no run-as descriptors
if(runAsPrincipals != null && runAsPrincipals.isEmpty()) {
return;
}
String servletName = this.getServletName(inv);
if (servletName == null) {
return;
}
String runAs = runAsPrincipals.get(servletName);
if (runAs != null) {
setSecurityContext((SecurityContext) inv.getOldSecurityContext()); // always null
}
}
// END IASRI 4747594
private void loginForRunAs(String principal) {
LoginContextDriver.loginPrincipal(principal, _realmName);
}
private SecurityContext getSecurityContext() {
return SecurityContext.getCurrent();
}
private void setSecurityContext(SecurityContext sc) {
SecurityContext.setCurrent(sc);
}
/**
* Used to detect when the principals in the subject correspond to the
* default or "ANONYMOUS" principal, and therefore a null principal
* should be set in the HttpServletRequest.
* @param principalSet
* @return true whe a null principal is to be set.
*/
private boolean principalSetContainsOnlyAnonymousPrincipal(Set<Principal> principalSet) {
boolean rvalue = false;
Principal defaultPrincipal = SecurityContext.getDefaultCallerPrincipal();
if (defaultPrincipal != null && principalSet != null) {
rvalue = principalSet.contains(defaultPrincipal);
}
if (rvalue) {
Iterator<Principal> it = principalSet.iterator();
while (it.hasNext()) {
if (!it.next().equals(defaultPrincipal)) {
return false;
}
}
}
return rvalue;
}
protected char[] getPassword(String username) {
throw new IllegalStateException("Should not reach here");
}
protected Principal getPrincipal(String username) {
throw new IllegalStateException("Should not reach here");
}
//START OF IASRI 4809144
/**
* This method is added to create a Principal based on the username only.
* Hercules stores the username as part of authentication failover and
* needs to create a Principal based on username only <sridhar.satuloori@sun.com>
* @param username
* @return Principal for the user username
* HERCULES:add
*/
public Principal createFailOveredPrincipal(String username) {
_logger.log(Level.FINEST, "IN createFailOveredPrincipal (" + username + ")");
//set the appropriate security context
loginForRunAs(username);
SecurityContext secCtx = SecurityContext.getCurrent();
_logger.log(Level.FINE, "Security context is " + secCtx);
assert (secCtx != null);
Principal principal = new WebPrincipal(username, (char[])null, secCtx);
_logger.log(Level.INFO, "Principal created for FailOvered user " + principal);
return principal;
}
//END OF IASRI 4809144
/**
* Perform access control based on the specified authorization constraint.
* Return <code>true if this constraint is satisfied and processing
* should continue, or <code>false otherwise.
*
* @param request Request we are processing
* @param response Response we are creating
* @param constraint Security constraint we are enforcing
* @param The Context to which client of this class is attached.
*
* @exception IOException if an input/output error occurs
*/
public boolean hasResourcePermission(HttpRequest request,
HttpResponse response,
SecurityConstraint[] constraints,
Context context)
throws IOException {
boolean isGranted = false;
try {
isGranted = invokeWebSecurityManager(
request, response, constraints);
} catch (IOException iex) {
throw iex;
} catch (Throwable ex) {
_logger.log(Level.SEVERE,"web_server.excep_authenticate_realmadapter", ex);
((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.setDetailMessage(rb.getString("realmBase.forbidden"));
return isGranted;
}
if (isGranted) {
return isGranted;
} else {
((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_FORBIDDEN);
response.setDetailMessage(rb.getString("realmBase.forbidden"));
// invoking secureResponse
invokePostAuthenticateDelegate(request, response, context);
return isGranted;
}
}
/**
* Invokes WebSecurityManager to perform access control check.
* Return <code>true if permission is granted, or
Other Glassfish examples (source code examples)Here is a short list of links related to this Glassfish RealmAdapter.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.