Author: alessio.soldano(a)jboss.com
Date: 2013-05-20 08:00:01 -0400 (Mon, 20 May 2013)
New Revision: 17603
Modified:
stack/cxf/trunk/modules/server/src/main/java/org/jboss/wsf/stack/cxf/security/authentication/SubjectCreatingInterceptor.java
Log:
[JBWS-3541] Making SubjectCreatingInterceptor directly extend WSS4JInInterceptor and
adding filter on Group instances when looking for subject user principal
Modified:
stack/cxf/trunk/modules/server/src/main/java/org/jboss/wsf/stack/cxf/security/authentication/SubjectCreatingInterceptor.java
===================================================================
---
stack/cxf/trunk/modules/server/src/main/java/org/jboss/wsf/stack/cxf/security/authentication/SubjectCreatingInterceptor.java 2013-05-20
06:39:33 UTC (rev 17602)
+++
stack/cxf/trunk/modules/server/src/main/java/org/jboss/wsf/stack/cxf/security/authentication/SubjectCreatingInterceptor.java 2013-05-20
12:00:01 UTC (rev 17603)
@@ -1,6 +1,6 @@
/*
* JBoss, Home of Professional Open Source.
- * Copyright 2011, Red Hat Middleware LLC, and individual contributors
+ * Copyright 2013, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
@@ -21,14 +21,32 @@
*/
package org.jboss.wsf.stack.cxf.security.authentication;
+import java.security.Principal;
+import java.security.acl.Group;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
import javax.security.auth.Subject;
+import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.security.SecurityToken;
+import org.apache.cxf.common.security.UsernameToken;
import org.apache.cxf.interceptor.Fault;
-import org.apache.cxf.ws.security.wss4j.AbstractUsernameTokenAuthenticatingInterceptor;
+import org.apache.cxf.interceptor.security.DefaultSecurityContext;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.security.SecurityContext;
+import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JInInterceptor;
+import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
+import org.apache.ws.security.WSSecurityEngine;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.validate.UsernameTokenValidator;
+import org.apache.ws.security.validate.Validator;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jboss.wsf.spi.security.SecurityDomainContext;
import org.jboss.wsf.stack.cxf.security.nonce.NonceStore;
@@ -40,11 +58,15 @@
* @author alessio.soldano(a)jboss.com
*
*/
-public class SubjectCreatingInterceptor extends
AbstractUsernameTokenAuthenticatingInterceptor
+public class SubjectCreatingInterceptor extends WSS4JInInterceptor
{
+ private static final Logger LOG =
LogUtils.getL7dLogger(SubjectCreatingInterceptor.class);
+
private ThreadLocal<SecurityDomainContext> sdc = new
ThreadLocal<SecurityDomainContext>();
private SubjectCreator helper = new SubjectCreator();
+
+ private boolean supportDigestPasswords;
public SubjectCreatingInterceptor()
{
@@ -54,15 +76,38 @@
public SubjectCreatingInterceptor(Map<String, Object> properties)
{
super(properties);
+ getAfter().add(PolicyBasedWSS4JInInterceptor.class.getName());
}
+ public void setSupportDigestPasswords(boolean support)
+ {
+ supportDigestPasswords = support;
+ }
+
+ public boolean getSupportDigestPasswords()
+ {
+ return supportDigestPasswords;
+ }
+
@Override
public void handleMessage(SoapMessage msg) throws Fault {
Endpoint ep = msg.getExchange().get(Endpoint.class);
sdc.set(ep.getSecurityDomainContext());
try
{
- super.handleMessage(msg);
+ SecurityToken token = msg.get(SecurityToken.class);
+ SecurityContext context = msg.get(SecurityContext.class);
+ if (token == null || context == null || context.getUserPrincipal() == null) {
+ super.handleMessage(msg);
+ return;
+ }
+ UsernameToken ut = (UsernameToken)token;
+
+ Subject subject = createSubject(ut.getName(), ut.getPassword(), ut.isHashed(),
+ ut.getNonce(), ut.getCreatedTime());
+
+ SecurityContext sc = doCreateSecurityContext(context.getUserPrincipal(),
subject);
+ msg.put(SecurityContext.class, sc);
}
finally
{
@@ -72,8 +117,129 @@
}
}
}
+
+ @Override
+ protected SecurityContext createSecurityContext(final Principal p) {
+ Message msg = PhaseInterceptorChain.getCurrentMessage();
+ if (msg == null) {
+ throw new IllegalStateException("Current message is not
available");
+ }
+ return doCreateSecurityContext(p, msg.get(Subject.class));
+ }
+
+ /**
+ * Creates default SecurityContext which implements isUserInRole using the
+ * following approach : skip the first Subject principal, and then check optional
+ * Groups the principal is a member of. Subclasses can override this method and
implement
+ * a custom strategy instead
+ *
+ * @param p principal
+ * @param subject subject
+ * @return security context
+ */
+ protected SecurityContext doCreateSecurityContext(final Principal p, final Subject
subject) {
+ return new DefaultSecurityContext(p, subject);
+ }
+
+ protected void setSubject(String name, String password, boolean isDigest, String
nonce, String created)
+ throws WSSecurityException
+ {
+ Message msg = PhaseInterceptorChain.getCurrentMessage();
+ if (msg == null)
+ {
+ throw new IllegalStateException("Current message is not available");
+ }
+ Subject subject = null;
+ try
+ {
+ subject = createSubject(name, password, isDigest, nonce, created);
+ }
+ catch (Exception ex)
+ {
+ String errorMessage = "Failed Authentication : Subject has not been
created";
+ LOG.severe(errorMessage);
+ throw new WSSecurityException(errorMessage, ex);
+ }
+ if (subject == null || subject.getPrincipals().size() == 0 ||
!checkUserPrincipal(subject.getPrincipals(), name))
+ {
+ String errorMessage = "Failed Authentication : Invalid Subject";
+ LOG.severe(errorMessage);
+ throw new WSSecurityException(errorMessage);
+ }
+ msg.put(Subject.class, subject);
+ }
+
+ private boolean checkUserPrincipal(Set<Principal> principals, String name)
+ {
+ for (Principal p : principals) {
+ if (!(p instanceof Group)) {
+ return p.getName().equals(name);
+ }
+ }
+ return false;
+ }
- @Override
+ @Override
+ protected WSSecurityEngine getSecurityEngine(boolean utNoCallbacks) {
+ Map<QName, Object> profiles = new HashMap<QName, Object>(1);
+
+ Validator validator = new CustomValidator();
+ profiles.put(WSSecurityEngine.USERNAME_TOKEN, validator);
+ return createSecurityEngine(profiles);
+ }
+
+ protected class CustomValidator extends UsernameTokenValidator {
+
+ @Override
+ protected void verifyCustomPassword(
+ org.apache.ws.security.message.token.UsernameToken usernameToken,
+ RequestData data
+ ) throws WSSecurityException {
+ SubjectCreatingInterceptor.this.setSubject(
+ usernameToken.getName(), usernameToken.getPassword(), false, null, null
+ );
+ }
+
+ @Override
+ protected void verifyPlaintextPassword(
+ org.apache.ws.security.message.token.UsernameToken usernameToken,
+ RequestData data
+ ) throws WSSecurityException {
+ SubjectCreatingInterceptor.this.setSubject(
+ usernameToken.getName(), usernameToken.getPassword(), false, null, null
+ );
+ }
+
+ @Override
+ protected void verifyDigestPassword(
+ org.apache.ws.security.message.token.UsernameToken usernameToken,
+ RequestData data
+ ) throws WSSecurityException {
+ if (!supportDigestPasswords) {
+ throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
+ }
+ String user = usernameToken.getName();
+ String password = usernameToken.getPassword();
+ boolean isHashed = usernameToken.isHashed();
+ String nonce = usernameToken.getNonce();
+ String createdTime = usernameToken.getCreated();
+ SubjectCreatingInterceptor.this.setSubject(
+ user, password, isHashed, nonce, createdTime
+ );
+ }
+
+ @Override
+ protected void verifyUnknownPassword(
+ org.apache.ws.security.message.token.UsernameToken usernameToken,
+ RequestData data
+ ) throws WSSecurityException {
+ SubjectCreatingInterceptor.this.setSubject(
+ usernameToken.getName(), null, false, null, null
+ );
+ }
+
+ }
+
public Subject createSubject(String name, String password, boolean isDigest, String
nonce, String created)
{
return helper.createSubject(sdc.get(), name, password, isDigest, nonce, created);