summaryrefslogtreecommitdiffstats
path: root/base/common/src/com/netscape/cmscore/realm/PKIRealm.java
diff options
context:
space:
mode:
authorEndi Sukma Dewata <edewata@redhat.com>2012-07-30 19:50:23 -0500
committerEndi Sukma Dewata <edewata@redhat.com>2012-07-31 23:16:34 -0500
commit7d4a40bdd6bf6ef37705be7131fdc179bb5c1e7d (patch)
treebe962766b1f7afa710650322a436251d2ead963d /base/common/src/com/netscape/cmscore/realm/PKIRealm.java
parent0d2ce4c6a9a4c05a0098b13cf6743cfe7f2fc6e5 (diff)
downloadpki-7d4a40bdd6bf6ef37705be7131fdc179bb5c1e7d.tar.gz
pki-7d4a40bdd6bf6ef37705be7131fdc179bb5c1e7d.tar.xz
pki-7d4a40bdd6bf6ef37705be7131fdc179bb5c1e7d.zip
Refactored PKI JNDI realm.
The PKI JNDI realm has been modified to utilize the authentication and authorization subsystems in PKI engine directly. It's no longer necessary to define the LDAP connection settings in Tomcat's configuration files. Ticket #126
Diffstat (limited to 'base/common/src/com/netscape/cmscore/realm/PKIRealm.java')
-rw-r--r--base/common/src/com/netscape/cmscore/realm/PKIRealm.java376
1 files changed, 376 insertions, 0 deletions
diff --git a/base/common/src/com/netscape/cmscore/realm/PKIRealm.java b/base/common/src/com/netscape/cmscore/realm/PKIRealm.java
new file mode 100644
index 000000000..53b31131c
--- /dev/null
+++ b/base/common/src/com/netscape/cmscore/realm/PKIRealm.java
@@ -0,0 +1,376 @@
+package com.netscape.cmscore.realm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletResponse;
+
+import netscape.security.x509.X509CertImpl;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.realm.RealmBase;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthManager;
+import com.netscape.certsrv.authentication.IAuthSubsystem;
+import com.netscape.certsrv.authentication.IAuthToken;
+import com.netscape.certsrv.authorization.AuthzToken;
+import com.netscape.certsrv.authorization.IAuthzSubsystem;
+import com.netscape.certsrv.usrgrp.EUsrGrpException;
+import com.netscape.certsrv.usrgrp.IGroup;
+import com.netscape.certsrv.usrgrp.IUGSubsystem;
+import com.netscape.certsrv.usrgrp.IUser;
+import com.netscape.cms.servlet.common.AuthCredentials;
+import com.netscape.cmscore.authentication.CertUserDBAuthentication;
+import com.netscape.cmscore.authentication.PasswdUserDBAuthentication;
+
+/**
+ * PKI Realm
+ *
+ * This realm provides an authentication service against PKI user database.
+ * The realm also provides an authorization service that validates request
+ * URL's against the access control list defined in the internal database.
+ */
+
+public class PKIRealm extends RealmBase {
+
+ public final static String PROP_AUTH_FILE_PATH = "/WEB-INF/auth.properties";
+ public final static int EXPRESSION_SIZE = 2;
+
+ ThreadLocal<IAuthToken> authToken = new ThreadLocal<IAuthToken>();
+ Properties authzProperties;
+
+ public PKIRealm() {
+ logDebug("Creating PKI realm");
+ }
+
+ @Override
+ protected void initInternal() throws LifecycleException {
+ logDebug("Initializing PKI realm");
+ super.initInternal();
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ logDebug("Starting PKI realm");
+ super.startInternal();
+ }
+
+ @Override
+ protected String getName() {
+ return "PKIRealm";
+ }
+
+ @Override
+ public Principal authenticate(String username, String password) {
+ logDebug("Authenticating username "+username+" with password.");
+
+ try {
+ IAuthSubsystem authSub = (IAuthSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTH);
+ IAuthManager authMgr = authSub.getAuthManager(IAuthSubsystem.PASSWDUSERDB_AUTHMGR_ID);
+
+ AuthCredentials creds = new AuthCredentials();
+ creds.set(PasswdUserDBAuthentication.CRED_UID, username);
+ creds.set(PasswdUserDBAuthentication.CRED_PWD, password);
+
+ IAuthToken token = authMgr.authenticate(creds); // throws exception if authentication fails
+ authToken.set(token);
+
+ return getPrincipal(username);
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @Override
+ public Principal authenticate(final X509Certificate certs[]) {
+ logDebug("Authenticating certificate chain:");
+
+ try {
+ X509CertImpl certImpls[] = new X509CertImpl[certs.length];
+ for (int i=0; i<certs.length; i++) {
+ X509Certificate cert = certs[i];
+ logDebug(" "+cert.getSubjectDN());
+
+ // Convert sun.security.x509.X509CertImpl to netscape.security.x509.X509CertImpl
+ certImpls[i] = new X509CertImpl(cert.getEncoded());
+ }
+
+ IAuthSubsystem authSub = (IAuthSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTH);
+ IAuthManager authMgr = authSub.getAuthManager(IAuthSubsystem.CERTUSERDB_AUTHMGR_ID);
+
+ AuthCredentials creds = new AuthCredentials();
+ creds.set(CertUserDBAuthentication.CRED_CERT, certImpls);
+
+ IAuthToken token = authMgr.authenticate(creds); // throws exception if authentication fails
+ authToken.set(token);
+
+ String username = token.getInString(CertUserDBAuthentication.TOKEN_USERID);
+ logDebug("User ID: "+username);
+
+ return getPrincipal(username);
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Principal getPrincipal(String username) {
+ try {
+ IUser user = getUser(username);
+ return getPrincipal(user);
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ protected Principal getPrincipal(IUser user) throws EUsrGrpException {
+ List<String> roles = getRoles(user);
+ return new GenericPrincipal(user.getUserID(), null, roles);
+ }
+
+ protected IUser getUser(String username) throws EUsrGrpException {
+ IUGSubsystem ugSub = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
+ IUser user = ugSub.getUser(username);
+ logDebug("User DN: "+user.getUserDN());
+ return user;
+ }
+
+ protected List<String> getRoles(IUser user) throws EUsrGrpException {
+
+ List<String> roles = new ArrayList<String>();
+
+ IUGSubsystem ugSub = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
+ Enumeration<IGroup> groups = ugSub.findGroupsByUser(user.getUserDN());
+
+ logDebug("Roles:");
+ while (groups.hasMoreElements()) {
+ IGroup group = groups.nextElement();
+
+ String name = group.getName();
+ logDebug(" "+name);
+ roles.add(name);
+ }
+
+ return roles;
+ }
+
+ @Override
+ protected String getPassword(String username) {
+ return null;
+ }
+
+ /**
+ * Perform access control based on the specified authorization constraint.
+ * Return <code>true</code> if this constraint is satisfied and processing
+ * should continue, or <code>false</code> otherwise.
+ * override to check for custom PKI ACL's authz permissions.
+ *
+ * @param request Request we are processing
+ * @param response Response we are creating
+ * @param constraints Security constraint we are enforcing
+ * @param context The Context to which client of this class is attached.
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ @Override
+ public boolean hasResourcePermission(Request request,
+ Response response,
+ SecurityConstraint[] constraints,
+ Context context)
+ throws IOException {
+
+ String requestURI = request.getDecodedRequestURI();
+ logDebug("Checking permission: "+requestURI);
+
+ boolean allowed = super.hasResourcePermission(request, response, constraints, context);
+ logDebug("Resource permission: "+allowed);
+
+ if (allowed) {
+ allowed = checkACL(request, response, constraints, context);
+ logDebug("ACL permission: "+allowed);
+ }
+
+ if (!allowed) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("realmBase.forbidden"));
+ }
+
+ return allowed;
+ }
+
+ public boolean checkACL(Request request,
+ Response response,
+ SecurityConstraint[] constraints,
+ Context context) {
+
+ try {
+ loadAuthzProperties(context);
+ if (!hasAuthzProperties()) return false;
+
+ String requestURI = request.getDecodedRequestURI();
+ String match = getACLEntry(requestURI);
+ if (match == null) return false;
+
+ logDebug("ACL: "+match);
+ String[] authzParams = match.split("\\,");
+
+ String resource = null;
+ String operation = null;
+
+ if (authzParams.length >= EXPRESSION_SIZE) {
+ resource = authzParams[0];
+ operation = authzParams[1];
+
+ if (resource != null) {
+ resource = resource.trim();
+ }
+
+ if (operation != null) {
+ operation = operation.trim();
+ }
+ }
+
+ IAuthzSubsystem mAuthz = (IAuthzSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTHZ);
+ IAuthToken token = authToken.get();
+
+ logDebug("Auth token:");
+ Enumeration<String> names = token.getElements();
+ while (names.hasMoreElements()) {
+ String name = names.nextElement();
+ Object value = token.get(name);
+ logDebug(" " + name +": " + value);
+ }
+
+ logDebug("Resource: " + resource);
+ logDebug("Operation: " + operation);
+
+ AuthzToken authzToken = mAuthz.authorize("DirAclAuthz", token, resource, operation);
+ if (authzToken != null) return true;
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ // Search for the proper auth.properties entry corresponding
+ // to a particular incoming URL
+ // TODO: In the admin interface, often the operation is sent
+ // as one of the parameters to the message.
+ // There may be a way to extract this information at this level.
+ // The parameter name to scan for could be configured with the Realm.
+
+ public String getACLEntry(String requestURI) {
+
+ if (!hasAuthzProperties()) {
+ return null;
+ }
+
+ logDebug("Checking path: "+requestURI);
+ String aclEntryData = authzProperties.getProperty(requestURI);
+
+ if (aclEntryData != null) {
+ logDebug("Found exact match: "+aclEntryData);
+ return aclEntryData;
+ }
+
+ // Check for a partial match such as
+ // ex: /kra/pki/keyrequest/2
+ // TODO: Check into more sophisticated
+ // methods of doing this mapping.
+ // Perhaps Rest gives us this more
+ // sophisticated mapping ability.
+
+ Properties props = authzProperties;
+ Enumeration<?> e = props.propertyNames();
+
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ if (requestURI.startsWith(key)) {
+ aclEntryData = props.getProperty(key);
+ logDebug("Found partial match ["+key+"]: "+aclEntryData);
+ break;
+ }
+ }
+
+ if (aclEntryData == null) {
+ logDebug("No match found");
+ }
+
+ return aclEntryData;
+
+ }
+
+ // Check to see if we have read in the auth properties file
+ public boolean hasAuthzProperties() {
+
+ if (authzProperties != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Load the custom mapping file auth.properties, which maps urls to acl resourceID and operation value
+ // example entry: /kra/pki/config/cert/transport = certServer.kra.pki.config.cert.transport,read
+ // TODO: Look into a more sophisticated method than this simple properties file if appropriate.
+ public synchronized void loadAuthzProperties(Context context) throws IOException {
+
+ if (authzProperties == null && context != null) {
+
+ InputStream inputStream = context.getServletContext().getResourceAsStream(PROP_AUTH_FILE_PATH);
+
+ if (inputStream == null) {
+ logDebug("Resource "+PROP_AUTH_FILE_PATH+" not found.");
+ throw new IOException("Resource "+PROP_AUTH_FILE_PATH+" not found.");
+ }
+
+ try {
+ logDebug("Loading authorization properties");
+
+ Properties properties = new Properties();
+ properties.load(inputStream);
+
+ authzProperties = properties;
+
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /*
+ * TODO: Figure out how to do real logging
+ */
+ public void logErr(String msg) {
+ System.err.println(msg);
+ }
+
+ public void logDebug(String msg) {
+ System.out.println("PKIRealm: "+msg);
+ }
+}