Modified Attribute Query to use Attribute Authority interface.
authordfisher <dfisher@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 8 Mar 2007 00:17:48 +0000 (00:17 +0000)
committerdfisher <dfisher@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 8 Mar 2007 00:17:48 +0000 (00:17 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2158 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AbstractProfileHandler.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeAuthority.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQuery.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeAuthority.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeQuery.java

diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AbstractProfileHandler.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AbstractProfileHandler.java
new file mode 100644 (file)
index 0000000..231b6d7
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml1;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.log4j.Logger;
+import org.opensaml.Configuration;
+import org.opensaml.common.SAMLObject;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.common.binding.BindingException;
+import org.opensaml.common.binding.MessageDecoder;
+import org.opensaml.common.binding.MessageEncoder;
+import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
+import org.opensaml.xml.XMLObjectBuilderFactory;
+
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringEngine;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
+
+/**
+ * Common implementation details for profile handlers.
+ */
+public abstract class AbstractProfileHandler implements ProfileHandler {
+
+    /** SAML Version for this profile handler. */
+    public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_11;
+
+    /** Class logger. */
+    private static Logger log = Logger.getLogger(AbstractProfileHandler.class);
+    
+    /** For building XML. */
+    private XMLObjectBuilderFactory builderFactory;
+
+    /** For generating random ids. */
+    private SecureRandomIdentifierGenerator idGenerator;
+
+    /** For decoding requests. */
+    private MessageDecoder<ServletRequest> decoder;
+
+    /** For encoding responses. */
+    private MessageEncoder<ServletResponse> encoder;
+
+    /** Relying party configuration. */
+    private RelyingPartyConfiguration relyingPartyConfiguration;
+
+    /** For resolving attributes. */
+    private AttributeResolver resolver;
+
+    /** To determine releasable attributes. */
+    private FilteringEngine engine;
+
+    /**
+     * Default constructor.
+     */
+    public AbstractProfileHandler() {
+        builderFactory = Configuration.getBuilderFactory();
+        idGenerator = new SecureRandomIdentifierGenerator();
+    }
+
+    /**
+     * Returns the XML builder factory.
+     * 
+     * @return Returns the builderFactory.
+     */
+    public XMLObjectBuilderFactory getBuilderFactory() {
+        return builderFactory;
+    }
+
+    /**
+     * Returns the id generator.
+     * 
+     * @return Returns the idGenerator.
+     */
+    public SecureRandomIdentifierGenerator getIdGenerator() {
+        return idGenerator;
+    }
+
+    /**
+     * Sets the decoder.
+     * 
+     * @param d <code>MessageDecoder</code>
+     */
+    public void setDecoder(MessageDecoder<ServletRequest> d) {
+        decoder = d;
+    }
+
+    /**
+     * Returns the decoder.
+     * 
+     * @return <code>MessageDecoder</code>
+     */
+    public MessageDecoder<ServletRequest> getDecoder() {
+        return decoder;
+    }
+
+    /**
+     * Sets the encoder.
+     * 
+     * @param e <code>MessageEncoder</code>
+     */
+    public void setEncoder(MessageEncoder<ServletResponse> e) {
+        encoder = e;
+    }
+
+    /**
+     * Returns the encoder.
+     * 
+     * @return <code>MessageEncoder</code>
+     */
+    public MessageEncoder<ServletResponse> getEncoder() {
+        return encoder;
+    }
+
+    /**
+     * Sets the attribute resolver.
+     * 
+     * @param r <code>AttributeResolver</code>
+     */
+    public void setAttributeResolver(AttributeResolver r) {
+        resolver = r;
+    }
+
+    /**
+     * Returns the attribute resolver.
+     * 
+     * @return <code>AttributeResolver</code>
+     */
+    public AttributeResolver getAttributeResolver() {
+        return resolver;
+    }
+
+    /**
+     * Sets the filter engine.
+     * 
+     * @param e <code>FilterEngine</code>
+     */
+    public void setFilterEngine(FilteringEngine e) {
+        engine = e;
+    }
+
+    /**
+     * Returns the filter engine.
+     * 
+     * @return <code>FilterEngine</code>
+     */
+    public FilteringEngine getFilteringEngine() {
+        return engine;
+    }
+
+    /**
+     * Returns the relying party configuration.
+     * 
+     * @return Returns the relyingParty.
+     */
+    public RelyingPartyConfiguration getRelyingPartyConfiguration() {
+        return relyingPartyConfiguration;
+    }
+
+    /**
+     * Sets the relying party configuration.
+     * 
+     * @param c The relyingParty to set.
+     */
+    public void setRelyingPartyConfiguration(RelyingPartyConfiguration c) {
+        relyingPartyConfiguration = c;
+    }
+    
+    /**
+     * This decodes the attribute query message from the supplied request.
+     * 
+     * @param request <code>ServletRequest</code>
+     * @return <code>SAMLObject</code>
+     * @throws BindingException if the request cannot be decoded
+     */
+    protected SAMLObject decodeMessage(ServletRequest request) throws BindingException {
+        // call decode method on decoder
+        decoder.setRequest(request);
+        decoder.decode();
+        if (log.isDebugEnabled()) {
+            log.debug("decoded servlet request");
+        }
+
+        // get SAMLMessage from the decoder
+        final SAMLObject message = decoder.getSAMLMessage();
+        if (log.isDebugEnabled()) {
+            log.debug("retrieved attribute query message from decoder: " + message);
+        }
+
+        return message;
+    }
+    
+
+    /**
+     * This encodes the supplied response.
+     * 
+     * @param response <code>SAMLObject</code>
+     * @throws BindingException if the response cannot be encoded
+     */
+    protected void encodeResponse(SAMLObject response) throws BindingException {
+        encoder.setSAMLMessage(response);
+        encoder.encode();
+        if (log.isDebugEnabled()) {
+            log.debug("encoded saml1 response");
+        }
+    }    
+}
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeAuthority.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeAuthority.java
new file mode 100644 (file)
index 0000000..945a022
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml1;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletRequest;
+
+import org.apache.log4j.Logger;
+import org.opensaml.Configuration;
+import org.opensaml.common.SAMLObjectBuilder;
+import org.opensaml.saml1.core.AttributeDesignator;
+import org.opensaml.saml1.core.AttributeQuery;
+import org.opensaml.saml1.core.AttributeStatement;
+import org.opensaml.saml1.core.NameIdentifier;
+import org.opensaml.ws.security.SecurityPolicy;
+import org.opensaml.xml.XMLObjectBuilderFactory;
+
+import edu.internet2.middleware.shibboleth.common.attribute.Attribute;
+import edu.internet2.middleware.shibboleth.common.attribute.AttributeEncoder;
+import edu.internet2.middleware.shibboleth.common.attribute.SAML1AttributeAuthority;
+import edu.internet2.middleware.shibboleth.common.attribute.SAML1AttributeEncoder;
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.BasicFilterContext;
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringEngine;
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringException;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.ResolutionContext;
+import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
+
+/**
+ * SAML 1 Attribute Authority.
+ */
+public class AttributeAuthority implements SAML1AttributeAuthority {
+
+    /** Class logger. */
+    private static Logger log = Logger.getLogger(AttributeAuthority.class);
+
+    /** For building XML objects. */
+    private XMLObjectBuilderFactory builderFactory;
+
+    /** Attribute resolver. */
+    private AttributeResolver attributeResolver;
+
+    /** Security policy. */
+    private SecurityPolicy securityPolicy;
+
+    /** Relying party configuration. */
+    private RelyingPartyConfiguration relyingPartyConfiguration;
+
+    /** To determine releasable attributes. */
+    private FilteringEngine filteringEngine;
+
+    /** Servlet request containing the SAML message. */
+    private ServletRequest request;
+
+    /**
+     * Default constructor.
+     */
+    public AttributeAuthority() {
+        builderFactory = Configuration.getBuilderFactory();
+    }
+
+    /**
+     * Gets the attribute resolver.
+     * 
+     * @return Returns the attributeResolver.
+     */
+    public AttributeResolver getAttributeResolver() {
+        return attributeResolver;
+    }
+
+    /**
+     * Sets the attribute resolver.
+     * 
+     * @param ar The attributeResolver to set.
+     */
+    public void setAttributeResolver(AttributeResolver ar) {
+        this.attributeResolver = ar;
+    }
+
+    /**
+     * Gets the request.
+     * 
+     * @return Returns the request.
+     */
+    public ServletRequest getRequest() {
+        return request;
+    }
+
+    /**
+     * Sets the request.
+     * 
+     * @param r The request to set.
+     */
+    public void setRequest(ServletRequest r) {
+        this.request = r;
+    }
+
+    /**
+     * Gets the filtering engine.
+     * 
+     * @return Returns the filteringEngine.
+     */
+    public FilteringEngine getFilteringEngine() {
+        return filteringEngine;
+    }
+
+    /**
+     * Sets the filtering engine.
+     * 
+     * @param fe The filteringEngine to set.
+     */
+    public void setFilteringEngine(FilteringEngine fe) {
+        this.filteringEngine = fe;
+    }
+
+    /**
+     * Gets the relying party configuration.
+     * 
+     * @return Returns the relyingPartyConfiguration.
+     */
+    public RelyingPartyConfiguration getRelyingPartyConfiguration() {
+        return relyingPartyConfiguration;
+    }
+
+    /**
+     * Sets the relying party configuration.
+     * 
+     * @param rpc The relyingPartyConfiguration to set.
+     */
+    public void setRelyingPartyConfiguration(RelyingPartyConfiguration rpc) {
+        this.relyingPartyConfiguration = rpc;
+    }
+
+    /**
+     * Gets the security policy.
+     * 
+     * @return Returns the securityPolicy.
+     */
+    public SecurityPolicy getSecurityPolicy() {
+        return securityPolicy;
+    }
+
+    /**
+     * Sets the security policy.
+     * 
+     * @param sp The securityPolicy to set.
+     */
+    public void setSecurityPolicy(SecurityPolicy sp) {
+        this.securityPolicy = sp;
+    }
+
+    /** {@inheritDoc} */
+    public AttributeStatement performAttributeQuery(AttributeQuery query) throws AttributeResolutionException,
+            FilteringException {
+        // get attributes from the message
+        Set<String> releasedAttributes = getMessageAttributes(query.getAttributeDesignators());
+
+        // create resolution context from the resolver, using nameid element from the attribute query
+        ResolutionContext resolutionContext = attributeResolver.createResolutionContext(query.getSubject()
+                .getNameIdentifier().getNameIdentifier(), securityPolicy.getIssuer().toString(), request);
+        // get resolved attributes from the resolver
+        Map<String, Attribute> resolvedAttributes = getResolvedAttributes(resolutionContext, releasedAttributes);
+
+        // filter attributes
+        BasicFilterContext filterContext = new BasicFilterContext(query.getSubject().getNameIdentifier()
+                .getNameIdentifier(), relyingPartyConfiguration.getProviderID(), query.getSubject().getNameIdentifier()
+                .getNameIdentifier(), resolvedAttributes);
+        Set<Attribute> filteredAttributes = filteringEngine.filterAttributes(filterContext);
+
+        // encode attributes
+        List<org.opensaml.saml1.core.Attribute> encodedAttributes = encodeAttributes(filteredAttributes);
+
+        // return attribute statement
+        return buildAttributeStatement(encodedAttributes);
+    }
+
+    /** {@inheritDoc} */
+    public AttributeStatement performAttributeQuery(String entity, NameIdentifier subject) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public String getAttributeIDBySAMLAttribute(AttributeDesignator attribute) {
+        return attribute.getAttributeName();
+    }
+
+    /** {@inheritDoc} */
+    public AttributeDesignator getSAMLAttributeByAttributeID(String id) {
+        SAMLObjectBuilder<AttributeDesignator> attributeBuilder = (SAMLObjectBuilder<AttributeDesignator>) builderFactory
+                .getBuilder(AttributeDesignator.DEFAULT_ELEMENT_NAME);
+        AttributeDesignator attribute = attributeBuilder.buildObject();
+        attribute.setAttributeName(id);
+        return attribute;
+    }
+
+    /**
+     * This parses the attribute name from the supplied list of attribute designators.
+     * 
+     * @param messageAttributes <code>List</code>
+     * @return <code>Set</code>
+     */
+    private Set<String> getMessageAttributes(List<AttributeDesignator> messageAttributes) {
+        final Set<String> attributes = new HashSet<String>(messageAttributes.size());
+        for (AttributeDesignator a : messageAttributes) {
+            attributes.add(getAttributeIDBySAMLAttribute(a));
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("message contains the following attributes: " + attributes);
+        }
+        return attributes;
+    }
+
+    /**
+     * This resolves the supplied attribute names using the supplied resolution context.
+     * 
+     * @param context <code>ResolutionContext</code>
+     * @param releasedAttributes <code>Set</code>
+     * @return <code>Map</code> of attribute ID to attribute
+     * @throws AttributeResolutionException if an attribute cannot be resolved
+     */
+    private Map<String, Attribute> getResolvedAttributes(ResolutionContext context, Set<String> releasedAttributes)
+            throws AttributeResolutionException {
+        // call Attribute resolver
+        Set<Attribute> resolvedAttributes = getAttributeResolver().resolveAttributes(releasedAttributes, context);
+        if (log.isDebugEnabled()) {
+            log.debug("attribute resolver resolved the following attributes: " + resolvedAttributes);
+        }
+
+        Map<String, Attribute> attrs = new HashMap<String, Attribute>();
+        for (Attribute attr : resolvedAttributes) {
+            attrs.put(attr.getId(), attr);
+        }
+        return attrs;
+    }
+
+    /**
+     * This encodes the supplied list of attributes with that attribute's SAML1 encoder.
+     * 
+     * @param resolvedAttributes <code>Set</code>
+     * @return <code>List</code> of core attributes
+     */
+    private List<org.opensaml.saml1.core.Attribute> encodeAttributes(Set<Attribute> resolvedAttributes) {
+        // encode attributes
+        List<org.opensaml.saml1.core.Attribute> encodedAttributes = new ArrayList<org.opensaml.saml1.core.Attribute>();
+
+        for (Attribute attr : resolvedAttributes) {
+            AttributeEncoder<org.opensaml.saml1.core.Attribute> enc = attr
+                    .getEncoderByCategory(SAML1AttributeEncoder.CATEGORY);
+            encodedAttributes.add(enc.encode(attr));
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("attribute encoder encoded the following attributes: " + encodedAttributes);
+        }
+        return encodedAttributes;
+    }
+
+    /**
+     * This builds the attribute statement for this SAML request.
+     * 
+     * @param encodedAttributes <code>List</code> of attributes
+     * @return <code>AttributeStatement</code>
+     */
+    private AttributeStatement buildAttributeStatement(List<org.opensaml.saml1.core.Attribute> encodedAttributes) {
+        SAMLObjectBuilder<AttributeStatement> statementBuilder = (SAMLObjectBuilder<AttributeStatement>) builderFactory
+                .getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME);
+        AttributeStatement statement = statementBuilder.buildObject();
+        statement.getAttributes().addAll(encodedAttributes);
+        return statement;
+    }
+}
index 4f1d55c..469a8d9 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package edu.internet2.middleware.shibboleth.idp.profile.saml1;
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
-import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import org.apache.log4j.Logger;
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLObjectBuilder;
+import org.opensaml.common.binding.BindingException;
+import org.opensaml.saml1.core.Advice;
+import org.opensaml.saml1.core.Assertion;
+import org.opensaml.saml1.core.AttributeStatement;
+import org.opensaml.saml1.core.Conditions;
+import org.opensaml.saml1.core.Response;
+import org.opensaml.saml1.core.Status;
+import org.opensaml.saml1.core.StatusCode;
+import org.opensaml.saml1.core.Subject;
+import org.opensaml.saml1.core.SubjectStatement;
+import org.opensaml.xml.encryption.EncryptionException;
+
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringException;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
 
 /**
- * SAML 1 Attribute Query profile handler
+ * SAML 1 Attribute Query profile handler.
  */
-public class AttributeQuery implements ProfileHandler {
+public class AttributeQuery extends AbstractProfileHandler {
+
+    /** Class logger. */
+    private static Logger log = Logger.getLogger(AttributeQuery.class);
 
     /** {@inheritDoc} */
     public boolean processRequest(ServletRequest request, ServletResponse response) throws ServletException {
-        // TODO Auto-generated method stub
-        return false;
+        if (log.isDebugEnabled()) {
+            log.debug("begin processRequest");
+        }
+
+        // get message from the decoder
+        org.opensaml.saml1.core.AttributeQuery message = null;
+        try {
+            message = (org.opensaml.saml1.core.AttributeQuery) decodeMessage(request);
+        } catch (BindingException e) {
+            log.error("Error decoding attribute query message", e);
+            throw new ServletException("Error decoding attribute query message");
+        }
+
+        // get attribute statement from attribute authority
+        AttributeAuthority aa = new AttributeAuthority();
+        aa.setAttributeResolver(getAttributeResolver());
+        aa.setFilteringEngine(getFilteringEngine());
+        aa.setRelyingPartyConfiguration(getRelyingPartyConfiguration());
+        aa.setSecurityPolicy(getDecoder().getSecurityPolicy());
+        aa.setRequest(request);
+        AttributeStatement statement = null;
+        try {
+            statement = aa.performAttributeQuery(message);
+        } catch (AttributeResolutionException e) {
+            log.error("Error resolving attributes", e);
+            throw new ServletException("Error resolving attributes");
+        } catch (FilteringException e) {
+            log.error("Error filtering attributes", e);
+            throw new ServletException("Error filtering attributes");
+        }
+
+        // construct response
+        Response samlResponse = null;
+        try {
+            samlResponse = buildResponse(message, request.getRemoteHost(), new DateTime(), statement);
+        } catch (EncryptionException e) {
+            log.error("Error encrypting SAML response", e);
+            throw new ServletException("Error encrypting SAML response");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("built saml1 response: " + samlResponse);
+        }
+
+        // encode response
+        try {
+            encodeResponse(samlResponse);
+        } catch (BindingException e) {
+            log.error("Error encoding attribute query response", e);
+            throw new ServletException("Error encoding attribute query response");
+        }
+
+        return true;
+    }
+
+    /**
+     * This builds the response for this SAML request.
+     * 
+     * @param message <code>AttributeQuery</code>
+     * @param dest <code>String</code>
+     * @param issueInstant <code>DateTime</code>
+     * @param statement <code>AttributeStatement</code>
+     * @return <code>Response</code>
+     * @throws EncryptionException if an error occurs attempting to encrypt data
+     */
+    private Response buildResponse(org.opensaml.saml1.core.AttributeQuery message, String dest, DateTime issueInstant,
+            AttributeStatement statement) throws EncryptionException {
+        SAMLObjectBuilder<Response> responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(
+                Response.DEFAULT_ELEMENT_NAME);
+        /*
+         * required: samlp:Status, ID, Version, IssueInstant
+         */
+        Response response = responseBuilder.buildObject();
+        response.setVersion(SAML_VERSION);
+        response.setID(getIdGenerator().generateIdentifier());
+        response.setInResponseTo(getDecoder().getSecurityPolicy().getIssuer().toString());
+        response.setIssueInstant(issueInstant);
+        response.setRecipient(dest);
+
+        response.setStatus(buildStatus());
+        response.getAssertions().add(buildAssertion(message.getSubject(), issueInstant, statement));
+        return response;
+    }
+
+    /**
+     * This builds the status response for this SAML request.
+     * 
+     * @return <code>Status</code>
+     */
+    private Status buildStatus() {
+        // build status
+        SAMLObjectBuilder<Status> statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(
+                Status.DEFAULT_ELEMENT_NAME);
+        Status status = statusBuilder.buildObject();
+
+        // build status code
+        SAMLObjectBuilder<StatusCode> statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory()
+                .getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
+        StatusCode statusCode = statusCodeBuilder.buildObject();
+        statusCode.setValue("Success");
+        status.setStatusCode(statusCode);
+        return status;
+    }
+
+    /**
+     * This builds the assertion for this SAML request.
+     * 
+     * @param messageSubject <code>Subject</code>
+     * @param issueInstant <code>DateTime</code>
+     * @param statement <code>AttributeStatement</code> of attributes
+     * @return <code>Assertion</code>
+     * @throws EncryptionException if an error occurs attempting to encrypt data
+     */
+    private Assertion buildAssertion(Subject messageSubject, DateTime issueInstant, AttributeStatement statement)
+            throws EncryptionException {
+        // build assertion
+        SAMLObjectBuilder<Assertion> assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory().getBuilder(
+                Assertion.DEFAULT_ELEMENT_NAME);
+        /*
+         * required: saml:Issuer, ID, Version, IssueInstant
+         */
+        Assertion assertion = assertionBuilder.buildObject();
+        assertion.setID(getIdGenerator().generateIdentifier());
+        assertion.setIssueInstant(issueInstant);
+        assertion.setVersion(SAML_VERSION);
+        assertion.setIssuer(getRelyingPartyConfiguration().getProviderID());
+
+        // build subject
+        assertion.getSubjectStatements().add(buildSubjectStatement(messageSubject));
+        // build conditions
+        assertion.setConditions(buildConditions(issueInstant));
+        // build advice
+        assertion.setAdvice(buildAdvice());
+        // add attribute statement
+        assertion.getAttributeStatements().add(statement);
+        return assertion;
+    }
+
+    /**
+     * This builds the subject statement for this SAML request.
+     * 
+     * @param messageSubject <code>Subject</code>
+     * @return <code>SubjectStatement</code>
+     */
+    private SubjectStatement buildSubjectStatement(Subject messageSubject) {
+        // build subject
+        SAMLObjectBuilder<SubjectStatement> subjectStatementBuilder = (SAMLObjectBuilder<SubjectStatement>) getBuilderFactory()
+                .getBuilder(SubjectStatement.DEFAULT_ELEMENT_NAME);
+        SubjectStatement subjectStatement = subjectStatementBuilder.buildObject();
+        subjectStatement.setSubject(messageSubject);
+        return subjectStatement;
+    }
+
+    /**
+     * This builds the conditions for this SAML request.
+     * 
+     * @param issueInstant <code>DateTime</code>
+     * @return <code>Conditions</code>
+     */
+    private Conditions buildConditions(DateTime issueInstant) {
+        SAMLObjectBuilder<Conditions> conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory()
+                .getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
+        Conditions conditions = conditionsBuilder.buildObject();
+        conditions.setNotBefore(issueInstant);
+        // TODO conditions.setNotOnOrAfter();
+        // TODO add additional conditions : conditions.getConditions().add(Condition);
+        // TODO what about AudienceRestriction, OneTimeUse, ProxyRestriction?
+        return conditions;
+    }
+
+    /**
+     * This builds the advice for this SAML request.
+     * 
+     * @return <code>Advice</code>
+     */
+    private Advice buildAdvice() {
+        SAMLObjectBuilder<Advice> adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(
+                Advice.DEFAULT_ELEMENT_NAME);
+        Advice advice = adviceBuilder.buildObject();
+        // advice.getAssertionIDReferences().add();
+        // advice.getAssertionURIReferences().add();
+        // advice.getAssertions().add();
+        // advice.getEncryptedAssertions().add();
+        // advice.addNamespace(namespace);
+        return advice;
     }
 }
\ No newline at end of file
index a730aee..6240a53 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
 
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.log4j.Logger;
 import org.opensaml.Configuration;
+import org.opensaml.common.SAMLObject;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.common.binding.BindingException;
 import org.opensaml.common.binding.MessageDecoder;
 import org.opensaml.common.binding.MessageEncoder;
+import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
+import org.opensaml.saml2.encryption.Encrypter;
 import org.opensaml.saml2.metadata.provider.MetadataProvider;
 import org.opensaml.xml.XMLObjectBuilderFactory;
-import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
+
 import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringEngine;
 import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
 
 /**
  * Common implementation details for profile handlers.
  */
 public abstract class AbstractProfileHandler implements ProfileHandler {
 
-    /** For building XML objects. */
+    /** SAML Version for this profile handler. */
+    public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
+
+    /** Class logger. */
+    private static Logger log = Logger.getLogger(AbstractProfileHandler.class);
+
+    /** For building XML. */
     private XMLObjectBuilderFactory builderFactory;
-    
-    /** For generating secure random ids. */
-    private SecureRandomIdentifierGenerator idGenerator;        
+
+    /** For generating random ids. */
+    private SecureRandomIdentifierGenerator idGenerator;
 
     /** For decoding requests. */
-    private MessageDecoder decoder;
+    private MessageDecoder<ServletRequest> decoder;
 
     /** For encoding responses. */
-    private MessageEncoder encoder;
+    private MessageEncoder<ServletResponse> encoder;
+
+    /** Relying party configuration. */
+    private RelyingPartyConfiguration relyingPartyConfiguration;
 
     /** For resolving attributes. */
     private AttributeResolver resolver;
-    
+
     /** To determine releasable attributes. */
     private FilteringEngine engine;
-    
-    /** */
+
+    /** Attribute metadata provider. */
     private MetadataProvider provider;
-    
-    
+
+    /** For encrypting XML. */
+    private Encrypter encrypter;
+
     /**
      * Default constructor.
      */
     public AbstractProfileHandler() {
         builderFactory = Configuration.getBuilderFactory();
-        idGenerator = new SecureRandomIdentifierGenerator();        
-    }
-    
-    
-    /**
-     * Sets the builder factory.
-     * 
-     *  @param f <code>XMLObjectBuilderFactory</code>
-     */
-    public void setBuilderFactory(XMLObjectBuilderFactory f) {
-        builderFactory = f;    
+        idGenerator = new SecureRandomIdentifierGenerator();
     }
 
-    
     /**
-     * Returns the builder factory.
+     * Returns the XML builder factory.
      * 
-     * @return <code>XMLObjectBuilderFactory</code> 
+     * @return Returns the builderFactory.
      */
     public XMLObjectBuilderFactory getBuilderFactory() {
-        return builderFactory;    
-    }
-
-    
-    /**
-     * Sets the id generator.
-     * 
-     *  @param g <code>SecureRandomIdentifierGenerator</code>
-     */
-    public void setIdGenerator(SecureRandomIdentifierGenerator g) {
-        idGenerator = g;    
+        return builderFactory;
     }
 
-    
     /**
      * Returns the id generator.
      * 
-     * @return <code>SecureRandomIdentifierGenerator</code> 
+     * @return Returns the idGenerator.
      */
     public SecureRandomIdentifierGenerator getIdGenerator() {
-        return idGenerator;    
+        return idGenerator;
     }
 
-    
     /**
      * Sets the decoder.
      * 
-     *  @param d <code>MessageDecoder</code>
+     * @param d <code>MessageDecoder</code>
      */
-    public void setDecoder(MessageDecoder d) {
-        decoder = d;    
+    public void setDecoder(MessageDecoder<ServletRequest> d) {
+        decoder = d;
     }
 
-    
     /**
      * Returns the decoder.
      * 
-     * @return <code>MessageDecoder</code> 
+     * @return <code>MessageDecoder</code>
      */
-    public MessageDecoder getDecoder() {
-        return decoder;    
+    public MessageDecoder<ServletRequest> getDecoder() {
+        return decoder;
     }
 
-    
     /**
      * Sets the encoder.
      * 
-     *  @param e <code>MessageEncoder</code>
+     * @param e <code>MessageEncoder</code>
      */
-    public void setEncoder(MessageEncoder e) {
-        encoder = e;    
+    public void setEncoder(MessageEncoder<ServletResponse> e) {
+        encoder = e;
     }
-    
-    
+
     /**
      * Returns the encoder.
      * 
-     * @return <code>MessageEncoder</code> 
+     * @return <code>MessageEncoder</code>
      */
-    public MessageEncoder getEncoder() {
-        return encoder;    
+    public MessageEncoder<ServletResponse> getEncoder() {
+        return encoder;
     }
-    
 
     /**
      * Sets the attribute resolver.
      * 
-     *  @param r <code>AttributeResolver</code>
+     * @param r <code>AttributeResolver</code>
      */
     public void setAttributeResolver(AttributeResolver r) {
-        resolver = r;    
+        resolver = r;
     }
 
-
     /**
      * Returns the attribute resolver.
      * 
-     * @return <code>AttributeResolver</code> 
-     */    
+     * @return <code>AttributeResolver</code>
+     */
     public AttributeResolver getAttributeResolver() {
-        return resolver;    
+        return resolver;
     }
-    
 
     /**
      * Sets the filter engine.
      * 
-     *  @param e <code>FilterEngine</code>
+     * @param e <code>FilterEngine</code>
      */
     public void setFilterEngine(FilteringEngine e) {
-        engine = e;    
+        engine = e;
     }
-    
-    
+
     /**
      * Returns the filter engine.
      * 
-     * @return <code>FilterEngine</code> 
-     */    
-    public FilteringEngine getFilterEngine() {
-        return engine;    
+     * @return <code>FilterEngine</code>
+     */
+    public FilteringEngine getFilteringEngine() {
+        return engine;
     }
 
-    
     /**
      * Sets the metadata provider.
      * 
-     *  @param p <code>MetadataProvider</code>
+     * @param p <code>MetadataProvider</code>
      */
     public void setMetadataProvider(MetadataProvider p) {
-        provider = p;    
+        provider = p;
     }
 
-    
     /**
      * Returns the metadata provider.
      * 
-     * @return <code>MetadataProvider</code> 
-     */        
+     * @return <code>MetadataProvider</code>
+     */
     public MetadataProvider getMetadataProvider() {
-        return provider;    
+        return provider;
+    }
+
+    /**
+     * Returns the relying party configuration.
+     * 
+     * @return Returns the relyingParty.
+     */
+    public RelyingPartyConfiguration getRelyingPartyConfiguration() {
+        return relyingPartyConfiguration;
+    }
+
+    /**
+     * Sets the relying party configuration.
+     * 
+     * @param c The relyingParty to set.
+     */
+    public void setRelyingPartyConfiguration(RelyingPartyConfiguration c) {
+        relyingPartyConfiguration = c;
+    }
+
+    /**
+     * Returns the encrypter.
+     * 
+     * @return Returns the encrypter.
+     */
+    public Encrypter getEncrypter() {
+        return encrypter;
+    }
+
+    /**
+     * Sets the encrypter.
+     * 
+     * @param e The encrypter to set.
+     */
+    public void setEncrypter(Encrypter e) {
+        encrypter = e;
+    }
+
+    /**
+     * This decodes the attribute query message from the supplied request.
+     * 
+     * @param request <code>ServletRequest</code>
+     * @return <code>SAMLObject</code>
+     * @throws BindingException if the request cannot be decoded
+     */
+    protected SAMLObject decodeMessage(ServletRequest request) throws BindingException {
+        // call decode method on decoder
+        decoder.setRequest(request);
+        decoder.decode();
+        if (log.isDebugEnabled()) {
+            log.debug("decoded servlet request");
+        }
+
+        // get SAMLMessage from the decoder
+        final SAMLObject message = decoder.getSAMLMessage();
+        if (log.isDebugEnabled()) {
+            log.debug("retrieved attribute query message from decoder: " + message);
+        }
+
+        return message;
+    }
+
+    /**
+     * This encodes the supplied response.
+     * 
+     * @param response <code>SAMLObject</code>
+     * @throws BindingException if the response cannot be encoded
+     */
+    protected void encodeResponse(SAMLObject response) throws BindingException {
+        encoder.setSAMLMessage(response);
+        encoder.encode();
+        if (log.isDebugEnabled()) {
+            log.debug("encoded saml1 response");
+        }
     }
 }
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeAuthority.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeAuthority.java
new file mode 100644 (file)
index 0000000..050bd00
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml2;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletRequest;
+
+import org.apache.log4j.Logger;
+import org.opensaml.Configuration;
+import org.opensaml.common.SAMLObjectBuilder;
+import org.opensaml.saml2.core.AttributeQuery;
+import org.opensaml.saml2.core.AttributeStatement;
+import org.opensaml.saml2.core.NameID;
+import org.opensaml.ws.security.SecurityPolicy;
+import org.opensaml.xml.XMLObjectBuilderFactory;
+
+import edu.internet2.middleware.shibboleth.common.attribute.Attribute;
+import edu.internet2.middleware.shibboleth.common.attribute.AttributeEncoder;
+import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeAuthority;
+import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeEncoder;
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.BasicFilterContext;
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringEngine;
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringException;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.ResolutionContext;
+import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
+
+/**
+ * SAML 2.0 Attribute Authority.
+ */
+public class AttributeAuthority implements SAML2AttributeAuthority {
+
+    /** Class logger. */
+    private static Logger log = Logger.getLogger(AttributeAuthority.class);
+
+    /** For building XML objects. */
+    private XMLObjectBuilderFactory builderFactory;
+
+    /** Attribute resolver. */
+    private AttributeResolver attributeResolver;
+
+    /** Security policy. */
+    private SecurityPolicy securityPolicy;
+
+    /** Relying party configuration. */
+    private RelyingPartyConfiguration relyingPartyConfiguration;
+
+    /** To determine releasable attributes. */
+    private FilteringEngine filteringEngine;
+
+    /** Servlet request containing the SAML message. */
+    private ServletRequest request;
+
+    /**
+     * Default constructor.
+     */
+    public AttributeAuthority() {
+        builderFactory = Configuration.getBuilderFactory();
+    }
+
+    /**
+     * Gets the attribute resolver.
+     * 
+     * @return Returns the attributeResolver.
+     */
+    public AttributeResolver getAttributeResolver() {
+        return attributeResolver;
+    }
+
+    /**
+     * Sets the attribute resolver.
+     * 
+     * @param ar The attributeResolver to set.
+     */
+    public void setAttributeResolver(AttributeResolver ar) {
+        this.attributeResolver = ar;
+    }
+
+    /**
+     * Gets the request.
+     * 
+     * @return Returns the request.
+     */
+    public ServletRequest getRequest() {
+        return request;
+    }
+
+    /**
+     * Sets the request.
+     * 
+     * @param r The request to set.
+     */
+    public void setRequest(ServletRequest r) {
+        this.request = r;
+    }
+
+    /**
+     * Gets the filtering engine.
+     * 
+     * @return Returns the filteringEngine.
+     */
+    public FilteringEngine getFilteringEngine() {
+        return filteringEngine;
+    }
+
+    /**
+     * Sets the filtering engine.
+     * 
+     * @param fe The filteringEngine to set.
+     */
+    public void setFilteringEngine(FilteringEngine fe) {
+        this.filteringEngine = fe;
+    }
+
+    /**
+     * Gets the relying party configuration.
+     * 
+     * @return Returns the relyingPartyConfiguration.
+     */
+    public RelyingPartyConfiguration getRelyingPartyConfiguration() {
+        return relyingPartyConfiguration;
+    }
+
+    /**
+     * Sets the relying party configuration.
+     * 
+     * @param rpc The relyingPartyConfiguration to set.
+     */
+    public void setRelyingPartyConfiguration(RelyingPartyConfiguration rpc) {
+        this.relyingPartyConfiguration = rpc;
+    }
+
+    /**
+     * Gets the security policy.
+     * 
+     * @return Returns the securityPolicy.
+     */
+    public SecurityPolicy getSecurityPolicy() {
+        return securityPolicy;
+    }
+
+    /**
+     * Sets the security policy.
+     * 
+     * @param sp The securityPolicy to set.
+     */
+    public void setSecurityPolicy(SecurityPolicy sp) {
+        this.securityPolicy = sp;
+    }
+
+    /** {@inheritDoc} */
+    public AttributeStatement performAttributeQuery(AttributeQuery query) throws AttributeResolutionException,
+            FilteringException {
+        // get attributes from the message
+        Set<String> releasedAttributes = getMessageAttributes(query.getAttributes());
+
+        // create resolution context from the resolver, using nameid element from the attribute query
+        ResolutionContext resolutionContext = getAttributeResolver().createResolutionContext(
+                query.getSubject().getNameID(), securityPolicy.getIssuer().toString(), request);
+        // get resolved attributes from the resolver
+        Map<String, Attribute> resolvedAttributes = getResolvedAttributes(resolutionContext, releasedAttributes);
+
+        // filter attributes
+        BasicFilterContext filteringContext = new BasicFilterContext(query.getSubject().getNameID().toString(),
+                getRelyingPartyConfiguration().getProviderID(), query.getSubject().getNameID().toString(),
+                resolvedAttributes);
+        Set<Attribute> filteredAttributes = filteringEngine.filterAttributes(filteringContext);
+
+        // encode attributes
+        List<org.opensaml.saml2.core.Attribute> encodedAttributes = encodeAttributes(filteredAttributes);
+
+        // return attribute statement
+        return buildAttributeStatement(encodedAttributes);
+    }
+
+    /** {@inheritDoc} */
+    public AttributeStatement performAttributeQuery(String entity, NameID subject) throws AttributeResolutionException,
+            FilteringException {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public String getAttributeIDBySAMLAttribute(org.opensaml.saml2.core.Attribute attribute) {
+        return attribute.getName();
+    }
+
+    /** {@inheritDoc} */
+    public org.opensaml.saml2.core.Attribute getSAMLAttributeByAttributeID(String id) {
+        SAMLObjectBuilder<org.opensaml.saml2.core.Attribute> attributeBuilder = (SAMLObjectBuilder<org.opensaml.saml2.core.Attribute>) builderFactory
+                .getBuilder(org.opensaml.saml2.core.Attribute.DEFAULT_ELEMENT_NAME);
+        org.opensaml.saml2.core.Attribute attribute = attributeBuilder.buildObject();
+        attribute.setName(id);
+        return attribute;
+    }
+
+    /**
+     * This parses the attribute name from the supplied list of attributes.
+     * 
+     * @param messageAttributes <code>List</code>
+     * @return <code>Set</code>
+     */
+    private Set<String> getMessageAttributes(List<org.opensaml.saml2.core.Attribute> messageAttributes) {
+        final Set<String> attributes = new HashSet<String>(messageAttributes.size());
+        for (org.opensaml.saml2.core.Attribute a : messageAttributes) {
+            attributes.add(getAttributeIDBySAMLAttribute(a));
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("message contains the following attributes: " + attributes);
+        }
+        // TODO go to metadata for other attributes
+        return attributes;
+    }
+
+    /**
+     * This resolves the supplied attribute names using the supplied resolution context.
+     * 
+     * @param context <code>ResolutionContext</code>
+     * @param releasedAttributes <code>Set</code>
+     * @return <code>Map</code> of attribute ID to attribute
+     * @throws AttributeResolutionException if an attribute cannot be resolved
+     */
+    private Map<String, Attribute> getResolvedAttributes(ResolutionContext context, Set<String> releasedAttributes)
+            throws AttributeResolutionException {
+        // call Attribute resolver
+        Set<Attribute> resolvedAttributes = getAttributeResolver().resolveAttributes(releasedAttributes, context);
+        if (log.isDebugEnabled()) {
+            log.debug("attribute resolver resolved the following attributes: " + resolvedAttributes);
+        }
+
+        Map<String, Attribute> attrs = new HashMap<String, Attribute>();
+        for (Attribute attr : resolvedAttributes) {
+            attrs.put(attr.getId(), attr);
+        }
+        return attrs;
+    }
+
+    /**
+     * This encodes the supplied list of attributes with that attribute's SAML2 encoder.
+     * 
+     * @param resolvedAttributes <code>Set</code>
+     * @return <code>List</code> of core attributes
+     */
+    private List<org.opensaml.saml2.core.Attribute> encodeAttributes(Set<Attribute> resolvedAttributes) {
+        // encode attributes
+        List<org.opensaml.saml2.core.Attribute> encodedAttributes = new ArrayList<org.opensaml.saml2.core.Attribute>();
+
+        for (Attribute attr : resolvedAttributes) {
+            AttributeEncoder<org.opensaml.saml2.core.Attribute> enc = attr
+                    .getEncoderByCategory(SAML2AttributeEncoder.CATEGORY);
+            encodedAttributes.add(enc.encode(attr));
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("attribute encoder encoded the following attributes: " + encodedAttributes);
+        }
+        return encodedAttributes;
+    }
+
+    /**
+     * This builds the attribute statement for this SAML request.
+     * 
+     * @param encodedAttributes <code>List</code> of attributes
+     * @return <code>AttributeStatement</code>
+     */
+    private AttributeStatement buildAttributeStatement(List<org.opensaml.saml2.core.Attribute> encodedAttributes) {
+        SAMLObjectBuilder<AttributeStatement> statementBuilder = (SAMLObjectBuilder<AttributeStatement>) builderFactory
+                .getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME);
+        AttributeStatement statement = statementBuilder.buildObject();
+        statement.getAttributes().addAll(encodedAttributes);
+        return statement;
+    }
+}
index 212defb..3395303 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package edu.internet2.middleware.shibboleth.idp.profile.saml2;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashSet;
-import java.util.Set;
+package edu.internet2.middleware.shibboleth.idp.profile.saml2;
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
-import edu.internet2.middleware.shibboleth.common.attribute.Attribute;
-import edu.internet2.middleware.shibboleth.common.attribute.AttributeEncoder;
-import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeEncoder;
-import edu.internet2.middleware.shibboleth.common.attribute.resolver.ResolutionContext;
-import edu.internet2.middleware.shibboleth.idp.profile.AbstractProfileHandler;
-
+import org.apache.log4j.Logger;
 import org.joda.time.DateTime;
-
 import org.opensaml.common.SAMLObjectBuilder;
-import org.opensaml.common.SAMLVersion;
-
+import org.opensaml.common.binding.BindingException;
+import org.opensaml.saml2.core.Advice;
 import org.opensaml.saml2.core.Assertion;
 import org.opensaml.saml2.core.AttributeStatement;
 import org.opensaml.saml2.core.Conditions;
+import org.opensaml.saml2.core.Issuer;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Status;
 import org.opensaml.saml2.core.StatusCode;
 import org.opensaml.saml2.core.Subject;
+import org.opensaml.xml.encryption.EncryptionException;
+
+import edu.internet2.middleware.shibboleth.common.attribute.filtering.FilteringException;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
 
 /**
  * SAML 2.0 Attribute Query profile handler.
  */
 public class AttributeQuery extends AbstractProfileHandler {
 
-    /** SAML Version for this profile handler. */
-    public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
-    
-    
+    /** Class logger. */
+    private static Logger log = Logger.getLogger(AttributeQuery.class);
+
     /** {@inheritDoc} */
     public boolean processRequest(ServletRequest request, ServletResponse response) throws ServletException {
-        // call decode method on decoder
-        getDecoder().decode(request);
-        // get SAMLMessage from the decoder
-        final org.opensaml.saml2.core.AttributeQuery message =
-            (org.opensaml.saml2.core.AttributeQuery) getDecoder().getSAMLMessage();
-        
-        // intersect requested attributes with released attributes
-        final List<org.opensaml.saml2.core.Attribute> requestedAttributes = message.getAttributes();
-        final Set<String> releasedAttributes = new HashSet<String>(requestedAttributes.size());
-        for (org.opensaml.saml2.core.Attribute a: requestedAttributes) {
-            releasedAttributes.add(a.getName());            
+        if (log.isDebugEnabled()) {
+            log.debug("begin processRequest");
+        }
+
+        // get message from the decoder
+        org.opensaml.saml2.core.AttributeQuery message = null;
+        try {
+            message = (org.opensaml.saml2.core.AttributeQuery) decodeMessage(request);
+        } catch (BindingException e) {
+            log.error("Error decoding attribute query message", e);
+            throw new ServletException("Error decoding attribute query message");
         }
-        List<String> releaseableAttributes = getFilterEngine().getReleaseableAttributes(getDecoder().getIssuer());
-        releasedAttributes.retainAll(releaseableAttributes);
-        // TODO go to metadata for other attributes
-
-        // create resolution context from the resolver, using nameid element from the attribute query
-        ResolutionContext context = getAttributeResolver().createResolutionContext(
-                message.getSubject().getNameID(), getDecoder().getIssuer(), request);
-        // call Attribute resolver
-        Set<Attribute> resolvedAttributes = getAttributeResolver().resolveAttributes(releasedAttributes, context);
-        
+
+        // get attribute statement from attribute authority
+        AttributeAuthority aa = new AttributeAuthority();
+        aa.setAttributeResolver(getAttributeResolver());
+        aa.setFilteringEngine(getFilteringEngine());
+        aa.setRelyingPartyConfiguration(getRelyingPartyConfiguration());
+        aa.setSecurityPolicy(getDecoder().getSecurityPolicy());
+        aa.setRequest(request);
+        AttributeStatement statement = null;
+        try {
+            statement = aa.performAttributeQuery(message);
+        } catch (AttributeResolutionException e) {
+            log.error("Error resolving attributes", e);
+            throw new ServletException("Error resolving attributes");
+        } catch (FilteringException e) {
+            log.error("Error filtering attributes", e);
+            throw new ServletException("Error filtering attributes");
+        }
+
         // construct attribute response
-        List<org.opensaml.saml2.core.Attribute> encodedAttributes = new ArrayList<org.opensaml.saml2.core.Attribute>();
-        for (Attribute attr: resolvedAttributes) {
-            AttributeEncoder enc = attr.getEncoderByCategory(org.opensaml.saml2.core.Attribute.URI_REFERENCE);
-            encodedAttributes.add(enc.encode(attr));
+        Response samlResponse = null;
+        try {
+            samlResponse = buildResponse(message, request.getRemoteHost(), new DateTime(), statement);
+        } catch (EncryptionException e) {
+            log.error("Error encrypting SAML response", e);
+            throw new ServletException("Error encrypting SAML response");
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("built saml2 response: " + samlResponse);
+        }
+
+        try {
+            encodeResponse(samlResponse);
+        } catch (BindingException e) {
+            log.error("Error encoding attribute query response", e);
+            throw new ServletException("Error encoding attribute query response");
         }
 
-        Response samlResponse = buildResponse(getDecoder().getIssuer(), new DateTime(), encodedAttributes);
-        getEncoder().setSAMLMessage(response);
-        getEncoder().encode();
         return true;
     }
-    
-    
+
     /**
      * This builds the response for this SAML request.
      * 
-     *  @param issuer <code>String</code>
-     *  @param issueInstant <code>DateTime</code>
-     *  @param encodedAttributes <code>List</code> of attributes
-     *  @return <code>Response</code>
+     * @param message <code>AttributeQuery</code>
+     * @param dest <code>String</code>
+     * @param issueInstant <code>DateTime</code>
+     * @param statement <code>AttributeStatement</code> of attributes
+     * @return <code>Response</code>
+     * @throws EncryptionException if an error occurs attempting to encrypt data
      */
-    private Response buildResponse(
-            String issuer, DateTime issueInstant, List<org.opensaml.saml2.core.Attribute> encodedAttributes) {
+    public Response buildResponse(org.opensaml.saml2.core.AttributeQuery message, String dest, DateTime issueInstant,
+            AttributeStatement statement) throws EncryptionException {
         SAMLObjectBuilder<Response> responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(
-            Response.DEFAULT_ELEMENT_NAME);
+                Response.DEFAULT_ELEMENT_NAME);
+        /*
+         * required: samlp:Status, ID, Version, IssueInstant
+         */
         Response response = responseBuilder.buildObject();
         response.setVersion(SAML_VERSION);
         response.setID(getIdGenerator().generateIdentifier());
-        response.setInResponseTo(issuer);
+        response.setInResponseTo(getDecoder().getSecurityPolicy().getIssuer().toString());
         response.setIssueInstant(issueInstant);
+        response.setDestination(dest);
+
+        response.setIssuer(buildIssuer());
+        // TODO set consent
+        /*
+         * if (consent != null) { response.setConsent(consent); }
+         */
+        // TODO set extensions
+        /*
+         * if (extensions != null) { response.setExtensions(extensions); }
+         */
 
-        //response.setDestination(String);
-        //response.setConsent(String);
-        //response.setIssuer(Issuer);
-        //response.setExtensions(Extensions);
-        
         response.setStatus(buildStatus());
-        response.getAssertions().add(buildAssertion(issueInstant, encodedAttributes));        
+        if (getRelyingPartyConfiguration().encryptAssertion()) {
+            response.getEncryptedAssertions().add(
+                    getEncrypter().encrypt(buildAssertion(message.getSubject(), issueInstant, statement)));
+        } else {
+            response.getAssertions().add(buildAssertion(message.getSubject(), issueInstant, statement));
+        }
+        return response;
     }
-    
 
     /**
      * This builds the status response for this SAML request.
      * 
-     *  @return <code>Status</code>
+     * @return <code>Status</code>
      */
     private Status buildStatus() {
         // build status
@@ -130,55 +157,114 @@ public class AttributeQuery extends AbstractProfileHandler {
         Status status = statusBuilder.buildObject();
 
         // build status code
-        SAMLObjectBuilder statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(
-                StatusCode.DEFAULT_ELEMENT_NAME);
+        SAMLObjectBuilder<StatusCode> statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory()
+                .getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
         StatusCode statusCode = statusCodeBuilder.buildObject();
         statusCode.setValue(StatusCode.SUCCESS_URI);
         status.setStatusCode(statusCode);
         return status;
     }
-    
-    
+
     /**
      * This builds the assertion for this SAML request.
      * 
-     *  @param issueInstant <code>DateTime</code>
-     *  @param encodedAttributes <code>List</code> of attributes
-     *  @return <code>Assertion</code>
+     * @param messageSubject <code>Subject</code>
+     * @param issueInstant <code>DateTime</code>
+     * @param statement <code>AttributeStatement</code> of attributes
+     * @return <code>Assertion</code>
+     * @throws EncryptionException if an error occurs attempting to encrypt data
      */
-    private Assertion buildAssertion(DateTime issueInstant, List<org.opensaml.saml2.core.Attribute> encodedAttributes) {
+    private Assertion buildAssertion(Subject messageSubject, DateTime issueInstant, AttributeStatement statement)
+            throws EncryptionException {
         // build assertions
-        SAMLObjectBuilder assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory().getBuilder(
+        SAMLObjectBuilder<Assertion> assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory().getBuilder(
                 Assertion.DEFAULT_ELEMENT_NAME);
+        /*
+         * required: saml:Issuer, ID, Version, IssueInstant
+         */
         Assertion assertion = assertionBuilder.buildObject();
         assertion.setID(getIdGenerator().generateIdentifier());
         assertion.setIssueInstant(issueInstant);
         assertion.setVersion(SAML_VERSION);
-        // TODO assertion.setIssuer();
+        assertion.setIssuer(buildIssuer());
 
         // build subject
-        SAMLObjectBuilder subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(
+        assertion.setSubject(buildSubject(messageSubject));
+        // build conditions
+        assertion.setConditions(buildConditions(issueInstant));
+        // build advice
+        assertion.setAdvice(buildAdvice());
+        // add attribute statement
+        assertion.getAttributeStatements().add(statement);
+        return assertion;
+    }
+
+    /**
+     * This builds the issuer response for this SAML request.
+     * 
+     * @return <code>Issuer</code>
+     */
+    private Issuer buildIssuer() {
+        // build status
+        SAMLObjectBuilder<Issuer> issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(
+                Issuer.DEFAULT_ELEMENT_NAME);
+        Issuer issuer = issuerBuilder.buildObject();
+        issuer.setValue(getRelyingPartyConfiguration().getProviderID());
+        return issuer;
+    }
+
+    /**
+     * This builds the subject for this SAML request.
+     * 
+     * @param messageSubject <code>Subject</code>
+     * @return <code>Subject</code>
+     * @throws EncryptionException if encryption of the name id fails
+     */
+    private Subject buildSubject(Subject messageSubject) throws EncryptionException {
+        // build subject
+        SAMLObjectBuilder<Subject> subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(
                 Subject.DEFAULT_ELEMENT_NAME);
         Subject subject = subjectBuilder.buildObject();
-        // TOTO subject.setNameID(NameID); <- comes from request
-        assertion.setSubject(subject);
+        if (getRelyingPartyConfiguration().encryptNameID()) {
+            subject.setEncryptedID(getEncrypter().encrypt(messageSubject.getNameID()));
+        } else {
+            subject.setNameID(messageSubject.getNameID());
+            // TODO when is subject.setBaseID(newBaseID) called, if ever?
+        }
+        return subject;
+    }
 
-        // build conditions
-        SAMLObjectBuilder conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
-                Conditions.DEFAULT_ELEMENT_NAME);
+    /**
+     * This builds the conditions for this SAML request.
+     * 
+     * @param issueInstant <code>DateTime</code>
+     * @return <code>Conditions</code>
+     */
+    private Conditions buildConditions(DateTime issueInstant) {
+        SAMLObjectBuilder<Conditions> conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory()
+                .getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
         Conditions conditions = conditionsBuilder.buildObject();
         conditions.setNotBefore(issueInstant);
-        //TODO conditions.setNotOnOrAfter();
-        //TODO add additional conditions : conditions.getConditions().add(Condition); 
-        assertion.setConditions(conditions);
-
-        // build attribute statement
-        SAMLObjectBuilder statementBuilder = (SAMLObjectBuilder<AttributeStatement>) getBuilderFactory().getBuilder(
-                AttributeStatement.DEFAULT_ELEMENT_NAME);
-        AttributeStatement statement = statementBuilder.buildObject();
-        statement.getAttributes().addAll(encodedAttributes);
-        assertion.getAttributeStatements().add(statement);
-        
-        return assertion;
+        // TODO conditions.setNotOnOrAfter();
+        // TODO add additional conditions : conditions.getConditions().add(Condition);
+        // TODO what about AudienceRestriction, OneTimeUse, ProxyRestriction?
+        return conditions;
+    }
+
+    /**
+     * This builds the advice for this SAML request.
+     * 
+     * @return <code>Advice</code>
+     */
+    private Advice buildAdvice() {
+        SAMLObjectBuilder<Advice> adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(
+                Advice.DEFAULT_ELEMENT_NAME);
+        Advice advice = adviceBuilder.buildObject();
+        // advice.getAssertionIDReferences().add();
+        // advice.getAssertionURIReferences().add();
+        // advice.getAssertions().add();
+        // advice.getEncryptedAssertions().add();
+        // advice.addNamespace(namespace);
+        return advice;
     }
 }
\ No newline at end of file