You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2017/01/27 11:22:57 UTC

[14/19] cxf-fediz git commit: FEDIZ-155 - Move .java components out of idp webapp and into a separate JAR

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
new file mode 100644
index 0000000..7b8c3eb
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
@@ -0,0 +1,415 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.protocols;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.zip.DataFormatException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.UriBuilder;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.apache.cxf.common.util.Base64Exception;
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.fediz.core.util.CertsUtils;
+import org.apache.cxf.fediz.core.util.DOMUtils;
+import org.apache.cxf.fediz.service.idp.IdpConstants;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
+import org.apache.cxf.fediz.service.idp.util.WebUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
+import org.apache.cxf.rs.security.saml.sso.AuthnRequestBuilder;
+import org.apache.cxf.rs.security.saml.sso.DefaultAuthnRequestBuilder;
+import org.apache.cxf.rs.security.saml.sso.EHCacheTokenReplayCache;
+import org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator;
+import org.apache.cxf.rs.security.saml.sso.SAMLSSOResponseValidator;
+import org.apache.cxf.rs.security.saml.sso.SSOConstants;
+import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
+import org.apache.cxf.rs.security.saml.sso.TokenReplayCache;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.ws.security.tokenstore.SecurityToken;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.util.DOM2Writer;
+import org.apache.xml.security.stax.impl.util.IDGenerator;
+import org.apache.xml.security.utils.Base64;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.webflow.execution.RequestContext;
+
+@Component
+public class TrustedIdpSAMLProtocolHandler extends AbstractTrustedIdpProtocolHandler {
+    /**
+     * Whether to sign the request or not. The default is "true".
+     */
+    public static final String SIGN_REQUEST = "sign.request";
+    
+    /**
+     * Whether to require a KeyInfo or not when processing a (signed) Response. The default is "true".
+     */
+    public static final String REQUIRE_KEYINFO = "require.keyinfo";
+    
+    /**
+     * Whether the assertions contained in the Response must be signed or not (if the response itself
+     * is not signed). The default is "true".
+     */
+    public static final String REQUIRE_SIGNED_ASSERTIONS = "require.signed.assertions";
+    
+    /**
+     * Whether we have to "know" the issuer of the SAML Response or not. The default is "true".
+     */
+    public static final String REQUIRE_KNOWN_ISSUER = "require.known.issuer";
+    
+    /**
+     * Whether we BASE-64 decode the response or not. The default is "true".
+     */
+    public static final String SUPPORT_BASE64_ENCODING = "support.base64.encoding";
+    
+    /**
+     * Whether we support Deflate encoding or not. The default is "false".
+     */
+    public static final String SUPPORT_DEFLATE_ENCODING = "support.deflate.encoding";
+
+    public static final String PROTOCOL = "urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser";
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrustedIdpSAMLProtocolHandler.class);
+    private static final String SAML_SSO_REQUEST_ID = "saml-sso-request-id";
+
+    private AuthnRequestBuilder authnRequestBuilder = new DefaultAuthnRequestBuilder();
+    private TokenReplayCache<String> replayCache;
+
+    static {
+        OpenSAMLUtil.initSamlEngine();
+    }
+
+    @Override
+    public String getProtocol() {
+        return PROTOCOL;
+    }
+
+    @Override
+    public URL mapSignInRequest(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+
+        try {
+            Document doc = DOMUtils.createDocument();
+            doc.appendChild(doc.createElement("root"));
+            // Create the AuthnRequest
+            AuthnRequest authnRequest = 
+                authnRequestBuilder.createAuthnRequest(
+                    null, idp.getRealm(), idp.getIdpUrl().toString()
+                );
+            
+            boolean signRequest = isBooleanPropertyConfigured(trustedIdp, SIGN_REQUEST, true);
+            if (signRequest) {
+                authnRequest.setDestination(trustedIdp.getUrl());
+            }
+            Element authnRequestElement = OpenSAMLUtil.toDom(authnRequest, doc);
+            String authnRequestEncoded = encodeAuthnRequest(authnRequestElement);
+
+            String urlEncodedRequest = URLEncoder.encode(authnRequestEncoded, "UTF-8");
+
+            UriBuilder ub = UriBuilder.fromUri(trustedIdp.getUrl());
+
+            ub.queryParam(SSOConstants.SAML_REQUEST, urlEncodedRequest);
+            
+            String wctx = context.getFlowScope().getString(IdpConstants.TRUSTED_IDP_CONTEXT);
+            ub.queryParam(SSOConstants.RELAY_STATE, wctx);
+            if (signRequest) {
+                signRequest(urlEncodedRequest, wctx, idp, ub);
+            }
+            
+            // Store the Request ID
+            String authnRequestId = authnRequest.getID();
+            WebUtils.putAttributeInExternalContext(context, SAML_SSO_REQUEST_ID, authnRequestId);
+
+            HttpServletResponse response = WebUtils.getHttpServletResponse(context);
+            response.addHeader("Cache-Control", "no-cache, no-store");
+            response.addHeader("Pragma", "no-cache");
+
+            return ub.build().toURL();
+        } catch (MalformedURLException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        } catch (UnsupportedEncodingException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        } catch (Exception ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        }
+    }
+
+    @Override
+    public SecurityToken mapSignInResponse(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+
+        try {
+            String encodedSAMLResponse = (String) WebUtils.getAttributeFromFlowScope(context, 
+                                                                                     SSOConstants.SAML_RESPONSE);
+            
+            // Read the response + convert to an OpenSAML Response Object
+            org.opensaml.saml.saml2.core.Response samlResponse = 
+                readSAMLResponse(encodedSAMLResponse, trustedIdp);
+            
+            Crypto crypto = CertsUtils.getCryptoFromCertificate(trustedIdp.getCertificate());
+            validateSamlResponseProtocol(samlResponse, crypto, trustedIdp);
+            // Validate the Response
+            SSOValidatorResponse validatorResponse = 
+                validateSamlSSOResponse(samlResponse, idp, trustedIdp, context);
+
+            // Create new Security token with new id. 
+            // Parameters for freshness computation are copied from original IDP_TOKEN
+            String id = IDGenerator.generateID("_");
+            SecurityToken idpToken = 
+                new SecurityToken(id, validatorResponse.getCreated(), validatorResponse.getSessionNotOnOrAfter());
+
+            idpToken.setToken(validatorResponse.getAssertionElement());
+            String whr = (String) WebUtils.getAttributeFromFlowScope(context, IdpConstants.HOME_REALM);
+            LOG.info("[IDP_TOKEN={}] created from [RP_TOKEN={}] issued by home realm [{}]",
+                     id, validatorResponse.getResponseId(), whr);
+            LOG.debug("Created date={}", validatorResponse.getCreated());
+            LOG.debug("Expired date={}", validatorResponse.getSessionNotOnOrAfter());
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Validated: "
+                    + System.getProperty("line.separator") + validatorResponse.getAssertion());
+            }
+            return idpToken;
+        } catch (BadRequestException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            LOG.warn("Unexpected exception occured", ex);
+            throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage());
+        }
+    }
+    
+    private String encodeAuthnRequest(Element authnRequest) throws IOException {
+        String requestMessage = DOM2Writer.nodeToString(authnRequest);
+        
+        if (LOG.isDebugEnabled()) {
+            LOG.debug(requestMessage);
+        }
+
+        DeflateEncoderDecoder encoder = new DeflateEncoderDecoder();
+        byte[] deflatedBytes = encoder.deflateToken(requestMessage.getBytes("UTF-8"));
+
+        return Base64Utility.encode(deflatedBytes);
+    }
+    
+    /**
+     * Sign a request according to the redirect binding spec for Web SSO
+     */
+    private void signRequest(
+        String authnRequest,
+        String relayState,
+        Idp config,
+        UriBuilder ub
+    ) throws Exception {
+        Crypto crypto = CertsUtils.getCryptoFromCertificate(config.getCertificate());
+        if (crypto == null) {
+            LOG.error("No crypto instance of properties file configured for signature");
+            throw new IllegalStateException("Invalid IdP configuration");
+        }
+        
+        String alias = crypto.getDefaultX509Identifier();
+        X509Certificate cert = CertsUtils.getX509CertificateFromCrypto(crypto, alias);
+        if (cert == null) {
+            LOG.error("No cert was found to sign the request using alias: " + alias);
+            throw new IllegalStateException("Invalid IdP configuration");
+        }
+
+        String sigAlgo = SSOConstants.RSA_SHA1;
+        String pubKeyAlgo = cert.getPublicKey().getAlgorithm();
+        String jceSigAlgo = "SHA1withRSA";
+        LOG.debug("automatic sig algo detection: " + pubKeyAlgo);
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = SSOConstants.DSA_SHA1;
+            jceSigAlgo = "SHA1withDSA";
+        }
+        LOG.debug("Using Signature algorithm " + sigAlgo);
+        
+        ub.queryParam(SSOConstants.SIG_ALG, URLEncoder.encode(sigAlgo, "UTF-8"));
+        
+        // Get the password
+        String password = config.getCertificatePassword();
+        
+        // Get the private key
+        PrivateKey privateKey = crypto.getPrivateKey(alias, password);
+        
+        // Sign the request
+        Signature signature = Signature.getInstance(jceSigAlgo);
+        signature.initSign(privateKey);
+       
+        String requestToSign = 
+            SSOConstants.SAML_REQUEST + "=" + authnRequest + "&"
+            + SSOConstants.RELAY_STATE + "=" + relayState + "&"
+            + SSOConstants.SIG_ALG + "=" + URLEncoder.encode(sigAlgo, "UTF-8");
+
+        signature.update(requestToSign.getBytes("UTF-8"));
+        byte[] signBytes = signature.sign();
+        
+        String encodedSignature = Base64.encode(signBytes);
+        
+        ub.queryParam(SSOConstants.SIGNATURE, URLEncoder.encode(encodedSignature, "UTF-8"));
+    }
+
+    private org.opensaml.saml.saml2.core.Response readSAMLResponse(String samlResponse, TrustedIdp trustedIdp) {
+        if (StringUtils.isEmpty(samlResponse)) {
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+
+        String samlResponseDecoded = samlResponse;
+        
+        InputStream tokenStream = null;
+        if (isBooleanPropertyConfigured(trustedIdp, SUPPORT_BASE64_ENCODING, true)) {
+            try {
+                byte[] deflatedToken = Base64Utility.decode(samlResponseDecoded);
+                tokenStream = isBooleanPropertyConfigured(trustedIdp, SUPPORT_DEFLATE_ENCODING, false)
+                    ? new DeflateEncoderDecoder().inflateToken(deflatedToken)
+                    : new ByteArrayInputStream(deflatedToken); 
+            } catch (Base64Exception ex) {
+                throw ExceptionUtils.toBadRequestException(ex, null);
+            } catch (DataFormatException ex) {
+                throw ExceptionUtils.toBadRequestException(ex, null);
+            }
+        } else {
+            try {
+                tokenStream = new ByteArrayInputStream(samlResponseDecoded.getBytes("UTF-8"));
+            } catch (UnsupportedEncodingException ex) {
+                throw ExceptionUtils.toBadRequestException(ex, null);
+            }
+        }
+
+        Document responseDoc = null;
+        try {
+            responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8"));
+        } catch (Exception ex) {
+            throw new WebApplicationException(400);
+        }
+        
+        LOG.debug("Received response: " + DOM2Writer.nodeToString(responseDoc.getDocumentElement()));
+        
+        XMLObject responseObject = null;
+        try {
+            responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
+        } catch (WSSecurityException ex) {
+            throw ExceptionUtils.toBadRequestException(ex, null);
+        }
+        if (!(responseObject instanceof org.opensaml.saml.saml2.core.Response)) {
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+        return (org.opensaml.saml.saml2.core.Response)responseObject;
+
+    }
+    
+    /**
+     * Validate the received SAML Response as per the protocol
+     */
+    private void validateSamlResponseProtocol(
+        org.opensaml.saml.saml2.core.Response samlResponse, Crypto crypto, TrustedIdp trustedIdp
+    ) {
+        try {
+            SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator();
+            protocolValidator.setKeyInfoMustBeAvailable(
+                isBooleanPropertyConfigured(trustedIdp, REQUIRE_KEYINFO, true));
+            protocolValidator.validateSamlResponse(samlResponse, crypto, null);
+        } catch (WSSecurityException ex) {
+            LOG.debug(ex.getMessage(), ex);
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+    }
+    
+    /**
+     * Validate the received SAML Response as per the Web SSO profile
+     */
+    private SSOValidatorResponse validateSamlSSOResponse(
+        org.opensaml.saml.saml2.core.Response samlResponse,
+        Idp idp, 
+        TrustedIdp trustedIdp,
+        RequestContext requestContext
+    ) {
+        try {
+            SAMLSSOResponseValidator ssoResponseValidator = new SAMLSSOResponseValidator();
+            ssoResponseValidator.setAssertionConsumerURL(idp.getIdpUrl().toString());
+
+            HttpServletRequest servletRequest = WebUtils.getHttpServletRequest(requestContext);
+            ssoResponseValidator.setClientAddress(servletRequest.getRemoteAddr());
+
+            String issuer = trustedIdp.getIssuer();
+            if (issuer == null || issuer.isEmpty()) {
+                LOG.debug("Issuer name is not defined in trusted 3rd party configuration. "
+                    + "Using URL instead for issuer validation");
+                issuer = trustedIdp.getUrl();
+            }
+            LOG.debug("Using {} for issuer validation", issuer);
+            ssoResponseValidator.setIssuerIDP(issuer);
+            
+            // Get the stored request ID
+            String requestId = 
+                (String)WebUtils.getAttributeFromExternalContext(requestContext, SAML_SSO_REQUEST_ID);
+            ssoResponseValidator.setRequestId(requestId);
+            ssoResponseValidator.setSpIdentifier(idp.getRealm());
+            ssoResponseValidator.setEnforceAssertionsSigned(
+                isBooleanPropertyConfigured(trustedIdp, REQUIRE_SIGNED_ASSERTIONS, true));
+            ssoResponseValidator.setEnforceKnownIssuer(
+                isBooleanPropertyConfigured(trustedIdp, REQUIRE_KNOWN_ISSUER, true));
+            
+            HttpServletRequest httpServletRequest = WebUtils.getHttpServletRequest(requestContext);
+            boolean post = "POST".equals(httpServletRequest.getMethod());
+            if (post) {
+                ssoResponseValidator.setReplayCache(getReplayCache());
+            }
+
+            return ssoResponseValidator.validateSamlResponse(samlResponse, post);
+        } catch (WSSecurityException ex) {
+            LOG.debug(ex.getMessage(), ex);
+            throw ExceptionUtils.toBadRequestException(ex, null);
+        }
+    }
+    
+    public void setReplayCache(TokenReplayCache<String> replayCache) {
+        this.replayCache = replayCache;
+    }
+    
+    public TokenReplayCache<String> getReplayCache() {
+        if (replayCache == null) {
+            replayCache = new EHCacheTokenReplayCache();
+        }
+        return replayCache;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpWSFedProtocolHandler.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpWSFedProtocolHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpWSFedProtocolHandler.java
new file mode 100644
index 0000000..ea8feb4
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpWSFedProtocolHandler.java
@@ -0,0 +1,231 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.protocols;
+
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+
+import org.w3c.dom.Element;
+import org.apache.cxf.fediz.core.FederationConstants;
+import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.config.TrustManager;
+import org.apache.cxf.fediz.core.config.jaxb.AudienceUris;
+import org.apache.cxf.fediz.core.config.jaxb.CertificateStores;
+import org.apache.cxf.fediz.core.config.jaxb.ContextConfig;
+import org.apache.cxf.fediz.core.config.jaxb.FederationProtocolType;
+import org.apache.cxf.fediz.core.config.jaxb.KeyStoreType;
+import org.apache.cxf.fediz.core.config.jaxb.TrustManagersType;
+import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuerType;
+import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuers;
+import org.apache.cxf.fediz.core.config.jaxb.ValidationType;
+import org.apache.cxf.fediz.core.exception.ProcessingException;
+import org.apache.cxf.fediz.core.processor.FederationProcessorImpl;
+import org.apache.cxf.fediz.core.processor.FedizProcessor;
+import org.apache.cxf.fediz.core.processor.FedizRequest;
+import org.apache.cxf.fediz.core.processor.FedizResponse;
+import org.apache.cxf.fediz.core.util.CertsUtils;
+import org.apache.cxf.fediz.service.idp.IdpConstants;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
+import org.apache.cxf.fediz.service.idp.util.WebUtils;
+import org.apache.cxf.ws.security.tokenstore.SecurityToken;
+import org.apache.wss4j.common.crypto.CertificateStore;
+import org.apache.xml.security.stax.impl.util.IDGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.webflow.execution.RequestContext;
+
+@Component
+public class TrustedIdpWSFedProtocolHandler extends AbstractTrustedIdpProtocolHandler {
+    
+    /**
+     * Whether to add the home realm parameter to the URL for redirection or not. The default is "true".
+     */
+    public static final String HOME_REALM_PROPAGATION = "home.realm.propagation";
+    
+    public static final String PROTOCOL = "http://docs.oasis-open.org/wsfed/federation/200706";
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrustedIdpWSFedProtocolHandler.class);
+
+    @Override
+    public String getProtocol() {
+        return PROTOCOL;
+    }
+    
+    @Override
+    public URL mapSignInRequest(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+        
+        try {
+            StringBuilder sb = new StringBuilder();
+            sb.append(trustedIdp.getUrl());
+            sb.append("?").append(FederationConstants.PARAM_ACTION).append('=');
+            sb.append(FederationConstants.ACTION_SIGNIN);
+            sb.append("&").append(FederationConstants.PARAM_TREALM).append('=');
+            sb.append(URLEncoder.encode(idp.getRealm(), "UTF-8"));
+            sb.append("&").append(FederationConstants.PARAM_REPLY).append('=');
+            sb.append(URLEncoder.encode(idp.getIdpUrl().toString(), "UTF-8"));
+            
+            if (isBooleanPropertyConfigured(trustedIdp, HOME_REALM_PROPAGATION, true)) {
+                sb.append("&").append(FederationConstants.PARAM_HOME_REALM).append('=');
+                sb.append(trustedIdp.getRealm());
+            }
+            
+            String wfresh = context.getFlowScope().getString(FederationConstants.PARAM_FRESHNESS);
+            if (wfresh != null) {
+                sb.append("&").append(FederationConstants.PARAM_FRESHNESS).append('=');
+                sb.append(URLEncoder.encode(wfresh, "UTF-8"));
+            }
+            String wctx = context.getFlowScope().getString(IdpConstants.TRUSTED_IDP_CONTEXT);
+            sb.append("&").append(FederationConstants.PARAM_CONTEXT).append('=');
+            sb.append(wctx);
+        
+            return new URL(sb.toString());
+        } catch (MalformedURLException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        } catch (UnsupportedEncodingException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        }
+    }
+    
+    @Override
+    public SecurityToken mapSignInResponse(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+
+        try {
+            String whr = (String) WebUtils.getAttributeFromFlowScope(context, IdpConstants.HOME_REALM);
+    
+            if (whr == null) {
+                LOG.warn("Home realm is null");
+                throw new IllegalStateException("Home realm is null");
+            }
+    
+            String wresult = (String) WebUtils.getAttributeFromFlowScope(context,
+                                                                         FederationConstants.PARAM_RESULT);
+    
+            if (wresult == null) {
+                LOG.warn("Parameter wresult not found");
+                throw new IllegalStateException("No security token issued");
+            }
+    
+            FedizContext fedContext = getFedizContext(idp, trustedIdp);
+    
+            FedizRequest wfReq = new FedizRequest();
+            wfReq.setAction(FederationConstants.ACTION_SIGNIN);
+            wfReq.setResponseToken(wresult);
+    
+            FedizProcessor wfProc = new FederationProcessorImpl();
+            FedizResponse wfResp = wfProc.processRequest(wfReq, fedContext);
+    
+            fedContext.close();
+    
+            Element e = wfResp.getToken();
+    
+            // Create new Security token with new id. 
+            // Parameters for freshness computation are copied from original IDP_TOKEN
+            String id = IDGenerator.generateID("_");
+            SecurityToken idpToken = new SecurityToken(id,
+                                                       wfResp.getTokenCreated(), wfResp.getTokenExpires());
+    
+            idpToken.setToken(e);
+            LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]",
+                     id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer());
+            LOG.debug("Created date={}", wfResp.getTokenCreated());
+            LOG.debug("Expired date={}", wfResp.getTokenExpires());
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Validated 'wresult' : "
+                    + System.getProperty("line.separator") + wresult);
+            }
+            return idpToken;
+        } catch (IllegalStateException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            LOG.warn("Unexpected exception occured", ex);
+            throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage());
+        }
+    }
+    
+    
+    private FedizContext getFedizContext(Idp idpConfig,
+            TrustedIdp trustedIdpConfig) throws ProcessingException {
+
+        ContextConfig config = new ContextConfig();
+
+        config.setName("whatever");
+
+        // Configure certificate store
+        String certificate = trustedIdpConfig.getCertificate();
+        boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
+        if (isCertificateLocation) {
+            CertificateStores certStores = new CertificateStores();
+            TrustManagersType tm0 = new TrustManagersType();
+            KeyStoreType ks0 = new KeyStoreType();
+            ks0.setType("PEM");
+            // ks0.setType("JKS");
+            // ks0.setPassword("changeit");
+            ks0.setFile(trustedIdpConfig.getCertificate());
+            tm0.setKeyStore(ks0);
+            certStores.getTrustManager().add(tm0);
+            config.setCertificateStores(certStores);
+        }
+        
+        // Configure trusted IDP
+        TrustedIssuers trustedIssuers = new TrustedIssuers();
+        TrustedIssuerType ti0 = new TrustedIssuerType();
+        ti0.setCertificateValidation(ValidationType.PEER_TRUST);
+        ti0.setName(trustedIdpConfig.getName());
+        // ti0.setSubject(".*CN=www.sts.com.*");
+        trustedIssuers.getIssuer().add(ti0);
+        config.setTrustedIssuers(trustedIssuers);
+
+        FederationProtocolType protocol = new FederationProtocolType();
+        config.setProtocol(protocol);
+
+        AudienceUris audienceUris = new AudienceUris();
+        audienceUris.getAudienceItem().add(idpConfig.getRealm());
+        config.setAudienceUris(audienceUris);
+
+        FedizContext fedContext = new FedizContext(config);
+        if (!isCertificateLocation) {
+            CertificateStore cs = null;
+            
+            X509Certificate cert;
+            try {
+                cert = CertsUtils.parseX509Certificate(trustedIdpConfig.getCertificate());
+            } catch (Exception ex) {
+                LOG.error("Failed to parse trusted certificate", ex);
+                throw new ProcessingException("Failed to parse trusted certificate");
+            }
+            cs = new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0]));
+            
+            TrustManager tm = new TrustManager(cs);
+            fedContext.getCertificateStores().add(tm);
+        }
+        
+        fedContext.init();
+        return fedContext;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationService.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationService.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationService.java
new file mode 100644
index 0000000..2034dca
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationService.java
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Application;
+import org.apache.cxf.fediz.service.idp.domain.RequestClaim;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+
+@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Path("applications")
+public interface ApplicationService {
+
+    @GET
+    @PreAuthorize("hasRole('APPLICATION_LIST')")
+    Applications getApplications(@QueryParam("start") int start,
+                                 @QueryParam("size") @DefaultValue("2") int size,
+                                 @QueryParam("expand") @DefaultValue("all")  List<String> expand,
+                                 @Context UriInfo uriInfo);
+
+    @GET
+    @Path("{realm}")
+    @PreAuthorize("hasRole('APPLICATION_LIST')")
+    Application getApplication(@PathParam("realm") String realm,
+                               @QueryParam("expand") @DefaultValue("all")  List<String> expand);
+
+    @POST
+    @PreAuthorize("hasRole('APPLICATION_CREATE')")
+    Response addApplication(@Context UriInfo ui, Application service);
+    
+    @PUT
+    @Path("{realm}")
+    @PreAuthorize("hasRole('APPLICATION_UPDATE')")
+    Response updateApplication(@Context UriInfo ui, @PathParam("realm") String realm, Application application);
+    
+    @DELETE
+    @Path("{realm}")
+    @PreAuthorize("hasRole('APPLICATION_DELETE')")
+    Response deleteApplication(@PathParam("realm") String realm);
+    
+    @POST
+    @Path("{realm}/claims")
+    @PreAuthorize("hasRole('APPLICATION_UPDATE')")
+    Response addClaimToApplication(@Context UriInfo ui, @PathParam("realm") String realm, RequestClaim claim);
+    
+    @DELETE
+    @Path("{realm}/claims/{claimType}")
+    @PreAuthorize("hasRole('APPLICATION_UPDATE')")
+    Response removeClaimFromApplication(@Context UriInfo ui, @PathParam("realm") String realm,
+                                        @PathParam("claimType") String claimType);
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationServiceImpl.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationServiceImpl.java
new file mode 100644
index 0000000..1b2f6ff
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ApplicationServiceImpl.java
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Application;
+import org.apache.cxf.fediz.service.idp.domain.Claim;
+import org.apache.cxf.fediz.service.idp.domain.RequestClaim;
+import org.apache.cxf.fediz.service.idp.service.ApplicationDAO;
+import org.apache.cxf.fediz.service.idp.service.ClaimDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ApplicationServiceImpl implements ApplicationService {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(ApplicationServiceImpl.class);
+
+    @Autowired
+    private ApplicationDAO applicationDAO;
+    
+    @Autowired
+    private ClaimDAO claimDAO;
+           
+    @Override
+    public Applications getApplications(int start, int size, List<String> expand, UriInfo uriInfo) {
+        List<Application> applications = applicationDAO.getApplications(start, size, expand);
+        
+        for (Application a : applications) {
+            URI self = uriInfo.getAbsolutePathBuilder().path(a.getRealm()).build();
+            a.setHref(self);
+        }
+        
+        Applications list = new Applications();
+        list.setApplications(applications);
+        return list;
+    }
+    
+    @Override
+    public Application getApplication(String realm, List<String> expand) {
+        Application application = applicationDAO.getApplication(realm, expand);
+        if (application == null) {
+            throw new NotFoundException();
+        } else {
+            return application;
+        }
+    }
+    
+    @Override
+    public Response addApplication(UriInfo ui, Application application) {
+        LOG.info("add Service config");
+        if (application.getRequestedClaims() != null && application.getRequestedClaims().size() > 0) {
+            LOG.warn("Application resource contains sub resource 'claims'");
+            throw new WebApplicationException(Status.BAD_REQUEST);
+        }
+        Application createdApplication = applicationDAO.addApplication(application);
+        
+        UriBuilder uriBuilder = UriBuilder.fromUri(ui.getRequestUri());
+        uriBuilder.path("{index}");
+        URI location = uriBuilder.build(createdApplication.getRealm());
+        return Response.created(location).entity(application).build();
+    }
+    
+    @Override
+    public Response updateApplication(UriInfo ui, String realm, Application application) {
+        if (!realm.equals(application.getRealm().toString())) {
+            throw new BadRequestException();
+        }
+        if (application.getRequestedClaims() != null && application.getRequestedClaims().size() > 0) {
+            LOG.warn("Application resource contains sub resource 'claims'");
+            throw new WebApplicationException(Status.BAD_REQUEST);
+        }
+        applicationDAO.updateApplication(realm, application);
+        
+        return Response.noContent().build();
+    }
+ 
+    @Override
+    public Response deleteApplication(String realm) {
+        applicationDAO.deleteApplication(realm);
+        
+        return Response.noContent().build();
+    }
+    
+    @Override
+    public Response addClaimToApplication(UriInfo ui, String realm, RequestClaim claim) {
+        Application application = applicationDAO.getApplication(realm, null);
+        if (application.getRequestedClaims().contains(claim)) {
+            LOG.warn("Claim '" + claim.getClaimType() + "' already added");
+            //[TODO] Status.CONFLICT correct if the relation to with Claim already exists
+            throw new WebApplicationException(Status.CONFLICT);
+        }
+        Claim foundClaim = claimDAO.getClaim(claim.getClaimType().toString());
+        RequestClaim rc = new RequestClaim(foundClaim);
+        application.getRequestedClaims().add(rc);
+        applicationDAO.addClaimToApplication(application, claim);
+        
+        return Response.noContent().build();
+    }
+    
+    @Override
+    public Response removeClaimFromApplication(UriInfo ui, String realm,  String claimType) {
+        Application application = applicationDAO.getApplication(realm, null);
+        
+        RequestClaim foundItem = null; 
+        for (RequestClaim item : application.getRequestedClaims()) {
+            if (item.getClaimType().toString().equals(claimType)) {
+                foundItem = item;
+                break;
+            }
+        }
+        if (foundItem == null) {
+            LOG.warn("Claim '" + claimType + "' not found");
+            throw new WebApplicationException(Status.NOT_FOUND);
+        }
+        application.getRequestedClaims().remove(foundItem);
+        applicationDAO.removeClaimFromApplication(application, foundItem);
+        
+        return Response.noContent().build();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Applications.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Applications.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Applications.java
new file mode 100644
index 0000000..5773a07
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Applications.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.Collection;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.cxf.fediz.service.idp.domain.Application;
+
+@XmlRootElement(name = "applications", namespace = "http://org.apache.cxf.fediz/")
+public class Applications {
+
+    private Collection<Application> applications;
+
+    public Applications() {
+    }
+
+    public Applications(Collection<Application> applications) {
+        this.applications = applications;
+    }
+
+    @XmlElementRef
+    public Collection<Application> getApplications() {
+        return applications;
+    }
+
+    public void setApplications(Collection<Application> applications) {
+        this.applications = applications;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimService.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimService.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimService.java
new file mode 100644
index 0000000..47dac60
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimService.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Claim;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+
+@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Path("claims")
+public interface ClaimService {
+
+    @GET
+    @PreAuthorize("hasRole('CLAIM_LIST')")
+    Response getClaims(@QueryParam("start") int start,
+                       @QueryParam("size") @DefaultValue("2") int size,
+                       @Context UriInfo uriInfo);
+    
+    @GET
+    @Path("{claimType}")
+    @PreAuthorize("hasRole('CLAIM_READ')")
+    Claim getClaim(@PathParam("claimType") String claimType);
+
+    @POST
+    @PreAuthorize("hasRole('CLAIM_CREATE')")
+    Response addClaim(@Context UriInfo ui, Claim claim);
+    
+    @PUT
+    @Path("{claimType}")
+    @PreAuthorize("hasRole('CLAIM_UPDATE')")
+    Response updateClaim(@Context UriInfo ui, @PathParam("claimType") String claimType, Claim claim);
+    
+    @DELETE
+    @Path("{claimType}")
+    @PreAuthorize("hasRole('CLAIM_DELETE')")
+    Response deleteClaim(@PathParam("claimType") String claimType);
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimServiceImpl.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimServiceImpl.java
new file mode 100644
index 0000000..141bfab
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/ClaimServiceImpl.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Claim;
+import org.apache.cxf.fediz.service.idp.service.ClaimDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ClaimServiceImpl implements ClaimService {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(ClaimServiceImpl.class);
+
+    @Autowired
+    private ClaimDAO claimDAO;
+
+    @Override
+    public Response getClaims(int start, int size, UriInfo uriInfo) {
+        List<Claim> claims = claimDAO.getClaims(start, size);
+        
+        for (Claim c : claims) {
+            URI self = uriInfo.getAbsolutePathBuilder().path(c.getClaimType().toString()).build();
+            c.setHref(self);
+        }
+        
+        Claims list = new Claims();
+        list.setClaims(claims);
+        
+        
+        //return Response.ok(list).type(MediaType.APPLICATION_JSON_TYPE).build();
+        return Response.ok(list).build();
+    }
+    
+    @Override
+    public Response addClaim(UriInfo ui, Claim claim) {
+        LOG.info("add Claim config");
+        
+        Claim createdClaim = claimDAO.addClaim(claim);
+        
+        UriBuilder uriBuilder = UriBuilder.fromUri(ui.getRequestUri());
+        uriBuilder.path("{index}");
+        URI location = uriBuilder.build(createdClaim.getClaimType().toString());
+        return Response.created(location).entity(claim).build();
+    }
+    
+    @Override
+    public Claim getClaim(String claimType) {
+        Claim claim = claimDAO.getClaim(claimType);
+        if (claim == null) {
+            throw new NotFoundException();
+        } else {
+            return claim;
+        }
+    }
+
+    @Override
+    public Response updateClaim(UriInfo ui, String claimType, Claim claim) {
+        if (!claimType.equals(claim.getClaimType().toString())) {
+            throw new BadRequestException();
+        }
+        claimDAO.updateClaim(claimType, claim);
+        
+        return Response.noContent().build();
+    }
+
+    @Override
+    public Response deleteClaim(String claimType) {
+        claimDAO.deleteClaim(claimType);
+        
+        return Response.noContent().build();
+    }
+           
+    
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Claims.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Claims.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Claims.java
new file mode 100644
index 0000000..891effd
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Claims.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.Collection;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.cxf.fediz.service.idp.domain.Claim;
+
+@XmlRootElement(name = "claims", namespace = "http://org.apache.cxf.fediz/")
+public class Claims {
+
+    private Collection<Claim> claims;
+
+    public Claims() {
+    }
+
+    public Claims(Collection<Claim> claims) {
+        this.claims = claims;
+    }
+
+    @XmlElementRef
+    public Collection<Claim> getClaims() {
+        return claims;
+    }
+
+    public void setClaims(Collection<Claim> claims) {
+        this.claims = claims;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementService.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementService.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementService.java
new file mode 100644
index 0000000..4bc392c
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementService.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Entitlement;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+
+@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Path("entitlements")
+public interface EntitlementService {
+
+    @GET
+    @PreAuthorize("hasRole('ENTITLEMENT_LIST')")
+    Entitlements getEntitlements(@QueryParam("start") int start,
+                                 @QueryParam("size") @DefaultValue("5") int size,
+                                 @Context UriInfo uriInfo);
+
+    @GET
+    @Path("{name}")
+    @PreAuthorize("hasRole('ENTITLEMENT_READ')")
+    Entitlement getEntitlement(@PathParam("name") String name);
+
+    @POST
+    @PreAuthorize("hasRole('ENTITLEMENT_CREATE')")
+    Response addEntitlement(@Context UriInfo ui, Entitlement entitlement);
+    
+    @PUT
+    @Path("{name}")
+    @PreAuthorize("hasRole('ENTITLEMENT_UPDATE')")
+    Response updateEntitlement(@Context UriInfo ui, @PathParam("name") String name, Entitlement entitlement);
+    
+    @DELETE
+    @Path("{name}")
+    @PreAuthorize("hasRole('ENTITLEMENT_DELETE')")
+    Response deleteEntitlement(@PathParam("name") String name);
+    
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementServiceImpl.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementServiceImpl.java
new file mode 100644
index 0000000..9c89c04
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/EntitlementServiceImpl.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Entitlement;
+import org.apache.cxf.fediz.service.idp.service.EntitlementDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EntitlementServiceImpl implements EntitlementService {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(EntitlementServiceImpl.class);
+
+    @Autowired
+    private EntitlementDAO entitlementDAO;
+
+    @Override
+    public Entitlements getEntitlements(int start, int size, UriInfo uriInfo) {
+        List<Entitlement> entitlements = entitlementDAO.getEntitlements(start, size);
+        
+        Entitlements list = new Entitlements();
+        list.setEntitlements(entitlements);
+        
+        return list;
+    }
+    
+    @Override
+    public Response addEntitlement(UriInfo ui, Entitlement entitlement) {
+        Entitlement createdEntitlement = entitlementDAO.addEntitlement(entitlement);
+        
+        UriBuilder uriBuilder = UriBuilder.fromUri(ui.getRequestUri());
+        uriBuilder.path("{index}");
+        URI location = uriBuilder.build(createdEntitlement.getName());
+        
+        LOG.debug("Entitlement '" + createdEntitlement.getName() + "' added");
+        return Response.created(location).entity(entitlement).build();
+    }
+    
+    @Override
+    public Entitlement getEntitlement(String name) {
+        Entitlement entitlement = entitlementDAO.getEntitlement(name);
+        if (entitlement == null) {
+            throw new NotFoundException();
+        } else {
+            return entitlement;
+        }
+    }
+
+    @Override
+    public Response updateEntitlement(UriInfo ui, String name, Entitlement entitlement) {
+        if (!name.equals(entitlement.getName())) {
+            throw new BadRequestException();
+        }
+        entitlementDAO.updateEntitlement(name, entitlement);
+        
+        LOG.debug("Entitlement '" + entitlement.getName() + "' updated");
+        return Response.noContent().build();
+    }
+
+    @Override
+    public Response deleteEntitlement(String name) {
+        entitlementDAO.deleteEntitlement(name);
+        
+        LOG.debug("Entitlement '" + name + "' deleted");
+        return Response.noContent().build();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Entitlements.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Entitlements.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Entitlements.java
new file mode 100644
index 0000000..8f2e91a
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Entitlements.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.Collection;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.cxf.fediz.service.idp.domain.Entitlement;
+
+@XmlRootElement(name = "entitlements", namespace = "http://org.apache.cxf.fediz/")
+public class Entitlements {
+
+    private Collection<Entitlement> entitlements;
+
+    public Entitlements() {
+    }
+
+    public Entitlements(Collection<Entitlement> entitlements) {
+        this.entitlements = entitlements;
+    }
+
+    @XmlElementRef
+    public Collection<Entitlement> getEntitlements() {
+        return entitlements;
+    }
+
+    public void setEntitlements(Collection<Entitlement> entitlements) {
+        this.entitlements = entitlements;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpService.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpService.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpService.java
new file mode 100644
index 0000000..b4692e8
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpService.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Application;
+import org.apache.cxf.fediz.service.idp.domain.Claim;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+@Path("idps")
+public interface IdpService {
+
+    @GET
+    @PreAuthorize("hasRole('IDP_LIST')")
+    Idps getIdps(@QueryParam("start") int start,
+                 @QueryParam("size") @DefaultValue("2") int size,
+                 @QueryParam("expand") @DefaultValue("all")  List<String> expand,
+                 @Context UriInfo uriInfo);
+
+    @GET
+    @Path("{realm}")
+    @PreAuthorize("hasRole('IDP_READ')")
+    Idp getIdp(@PathParam("realm") String realm,
+               @QueryParam("expand") @DefaultValue("all")  List<String> expand);
+
+    @POST
+    @PreAuthorize("hasRole('IDP_CREATE')")
+    Response addIdp(@Context UriInfo ui, Idp idp);
+    
+    @PUT
+    @Path("{realm}")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response updateIdp(@Context UriInfo ui, @PathParam("realm") String realm, Idp idp);
+    
+    @DELETE
+    @Path("{realm}")
+    @PreAuthorize("hasRole('IDP_DELETE')")
+    Response deleteIdp(@PathParam("realm") String realm);
+    
+    @POST
+    @Path("{realm}/applications")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response addApplicationToIdp(@Context UriInfo ui, @PathParam("realm") String realm,
+                                 Application application);
+    
+    @DELETE
+    @Path("{realm}/applications/{realmApplication}")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response removeApplicationFromIdp(@Context UriInfo ui, @PathParam("realm") String realm,
+                                      @PathParam("realmApplication") String applicationRealm);
+    
+    @POST
+    @Path("{realm}/trusted-idps")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response addTrustedIdpToIdp(@Context UriInfo ui, @PathParam("realm") String realm,
+                                TrustedIdp trustedIdp);
+    
+    @DELETE
+    @Path("{realm}/trusted-idps/{realmTrustedIdp}")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response removeTrustedIdpFromIdp(@Context UriInfo ui, @PathParam("realm") String realm,
+                                     @PathParam("realmTrustedIdp") String trustedIdpRealm);
+    
+    @POST
+    @Path("{realm}/claims")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response addClaimToIdp(@Context UriInfo ui, @PathParam("realm") String realm,
+                           Claim claim);
+    
+    @DELETE
+    @Path("{realm}/claims/{claimType}")
+    @PreAuthorize("hasRole('IDP_UPDATE')")
+    Response removeClaimFromIdp(@Context UriInfo ui, @PathParam("realm") String realm,
+                                @PathParam("claimType") String claimType);    
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpServiceImpl.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpServiceImpl.java
new file mode 100644
index 0000000..d4b5c40
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/IdpServiceImpl.java
@@ -0,0 +1,240 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.fediz.service.idp.domain.Application;
+import org.apache.cxf.fediz.service.idp.domain.Claim;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
+import org.apache.cxf.fediz.service.idp.service.ApplicationDAO;
+import org.apache.cxf.fediz.service.idp.service.ClaimDAO;
+import org.apache.cxf.fediz.service.idp.service.IdpDAO;
+import org.apache.cxf.fediz.service.idp.service.TrustedIdpDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class IdpServiceImpl implements IdpService {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(IdpServiceImpl.class);
+
+    @Autowired
+    private IdpDAO idpDAO;
+    
+    @Autowired
+    private ApplicationDAO applicationDAO;
+    
+    @Autowired
+    private TrustedIdpDAO trustedIdpDAO;
+    
+    @Autowired
+    private ClaimDAO claimDAO;
+           
+    @Override
+    public Idps getIdps(int start, int size, List<String> expand, UriInfo uriInfo) {
+        List<Idp> idps = idpDAO.getIdps(start, size, expand);
+        
+        Idps list = new Idps();
+        list.setIdps(idps);
+        return list;
+    }
+    
+    @Override
+    public Idp getIdp(String realm, List<String> expand) {
+        Idp idp = idpDAO.getIdp(realm, expand);
+        if (idp == null) {
+            LOG.warn("IdP not found for realm {}", realm);
+            throw new NotFoundException();
+        } else {
+            return idp;
+        }
+    }
+    
+    @Override
+    public Response addIdp(UriInfo ui, Idp idp) {
+        LOG.info("add IDP config");
+        if (idp.getApplications() != null && idp.getApplications().size() > 0) {
+            LOG.warn("IDP resource contains sub resource 'applications'");
+            throw new WebApplicationException(Status.BAD_REQUEST);
+        }
+        if (idp.getTrustedIdps() != null && idp.getTrustedIdps().size() > 0) {
+            LOG.warn("IDP resource contains sub resource 'trusted-idps'");
+            throw new WebApplicationException(Status.BAD_REQUEST);
+        }
+        Idp createdIdp = idpDAO.addIdp(idp);
+        
+        UriBuilder uriBuilder = UriBuilder.fromUri(ui.getRequestUri());
+        uriBuilder.path("{index}");
+        URI location = uriBuilder.build(createdIdp.getRealm());
+        return Response.created(location).entity(idp).build();
+    }
+    
+    @Override
+    public Response updateIdp(UriInfo ui, String realm, Idp idp) {
+        if (!realm.equals(idp.getRealm().toString())) {
+            throw new BadRequestException();
+        }
+        if (idp.getApplications() != null && idp.getApplications().size() > 0) {
+            LOG.warn("IDP resource contains sub resource 'applications'");
+            throw new WebApplicationException(Status.BAD_REQUEST);
+        }
+        if (idp.getTrustedIdps() != null && idp.getTrustedIdps().size() > 0) {
+            LOG.warn("IDP resource contains sub resource 'trusted-idps'");
+            throw new WebApplicationException(Status.BAD_REQUEST);
+        }
+        idpDAO.updateIdp(realm, idp);
+        
+        return Response.noContent().build();
+    }
+
+    @Override
+    public Response deleteIdp(String realm) {
+        idpDAO.deleteIdp(realm);
+        
+        return Response.noContent().build();
+    }
+
+    @Override
+    public Response addApplicationToIdp(UriInfo ui, String realm, Application application) {
+        Idp idp = idpDAO.getIdp(realm, Arrays.asList("all"));
+        for (Application idpApplication : idp.getApplications()) {
+            if (idpApplication.getRealm() != null && idpApplication.getRealm().equals(application.getRealm())) {
+                LOG.warn("Application '" + application.getRealm() + "' already added");
+                throw new WebApplicationException(Status.CONFLICT);
+            }
+        }
+        Application application2 = applicationDAO.getApplication(application.getRealm(), null);
+        idpDAO.addApplicationToIdp(idp, application2);
+        
+        return Response.noContent().build();
+    }
+    
+    @Override
+    public Response removeApplicationFromIdp(UriInfo ui, String realm,  String applicationRealm) {
+        Idp idp = idpDAO.getIdp(realm, Arrays.asList("all"));
+        
+        Application foundItem = null; 
+        for (Application item : idp.getApplications()) {
+            if (item.getRealm().equals(applicationRealm)) {
+                foundItem = item;
+                break;
+            }
+        }
+        if (foundItem == null) {
+            LOG.warn("Application '" + applicationRealm + "' not found");
+            throw new WebApplicationException(Status.NOT_FOUND);
+        }
+        idpDAO.removeApplicationFromIdp(idp, foundItem);
+        
+        return Response.noContent().build();
+    }
+    
+    
+    
+    
+    @Override
+    public Response addTrustedIdpToIdp(UriInfo ui, String realm, TrustedIdp trustedIdp) {
+        Idp idp = idpDAO.getIdp(realm, Arrays.asList("all"));
+        for (TrustedIdp idpTrustedIdp : idp.getTrustedIdps()) {
+            if (idpTrustedIdp.getRealm() != null && idpTrustedIdp.getRealm().equals(trustedIdp.getRealm())) {
+                LOG.warn("Trusted IDP '" + trustedIdp.getRealm() + "' already added");
+                throw new WebApplicationException(Status.CONFLICT);
+            }
+        }
+        TrustedIdp trustedIpd2 = trustedIdpDAO.getTrustedIDP(trustedIdp.getRealm());
+        
+        idpDAO.addTrustedIdpToIdp(idp, trustedIpd2);
+        
+        return Response.noContent().build();
+    }
+    
+    @Override
+    public Response removeTrustedIdpFromIdp(UriInfo ui, String realm, String trustedIdpRealm) {
+        Idp idp = idpDAO.getIdp(realm, Arrays.asList("all"));
+        
+        TrustedIdp foundItem = null; 
+        for (TrustedIdp item : idp.getTrustedIdps()) {
+            if (item.getRealm().equals(trustedIdpRealm)) {
+                foundItem = item;
+                break;
+            }
+        }
+        if (foundItem == null) {
+            LOG.warn("Trusted IDP '" + trustedIdpRealm + "' not found");
+            throw new WebApplicationException(Status.NOT_FOUND);
+        }
+        idpDAO.removeTrustedIdpFromIdp(idp, foundItem);
+        
+        return Response.noContent().build();
+    }   
+    
+    @Override
+    public Response addClaimToIdp(UriInfo ui, String realm, Claim claim) {
+        Idp idp = idpDAO.getIdp(realm, Arrays.asList("all"));
+        for (Claim idpClaim : idp.getClaimTypesOffered()) {
+            if (idpClaim.getClaimType() != null 
+                && idpClaim.getClaimType().toString().equals(claim.getClaimType().toString())) {
+                LOG.warn("Claim '" + claim.getClaimType() + "' already added");
+                throw new WebApplicationException(Status.CONFLICT);
+            }
+        }
+        Claim claim2 = claimDAO.getClaim(claim.getClaimType().toString());
+        idpDAO.addClaimToIdp(idp, claim2);
+        
+        return Response.noContent().build();
+    }
+    
+    @Override
+    public Response removeClaimFromIdp(UriInfo ui, String realm, String claimType) {
+        Idp idp = idpDAO.getIdp(realm, Arrays.asList("all"));
+        
+        Claim foundItem = null; 
+        for (Claim item : idp.getClaimTypesOffered()) {
+            if (item.getClaimType().toString().equals(claimType)) {
+                foundItem = item;
+                break;
+            }
+        }
+        if (foundItem == null) {
+            LOG.warn("Claim '" + claimType + "' not found");
+            throw new WebApplicationException(Status.NOT_FOUND);
+        }
+        idpDAO.removeClaimFromIdp(idp, foundItem);
+                
+        return Response.noContent().build();
+    }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Idps.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Idps.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Idps.java
new file mode 100644
index 0000000..08d7f50
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/Idps.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.Collection;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+
+@XmlRootElement(name = "idps", namespace = "http://org.apache.cxf.fediz/")
+public class Idps {
+
+    private Collection<Idp> idps;
+
+    public Idps() {
+    }
+
+    public Idps(Collection<Idp> idps) {
+        this.idps = idps;
+    }
+
+    @XmlElementRef
+    public Collection<Idp> getIdps() {
+        return idps;
+    }
+
+    public void setIdps(Collection<Idp> idps) {
+        this.idps = idps;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/QueryResourceInfoComparator.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/QueryResourceInfoComparator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/QueryResourceInfoComparator.java
new file mode 100644
index 0000000..1e87bfc
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/QueryResourceInfoComparator.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.fediz.service.idp.rest;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.jaxrs.ext.ResourceComparator;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfoComparator;
+import org.apache.cxf.jaxrs.model.Parameter;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+
+public class QueryResourceInfoComparator extends OperationResourceInfoComparator implements ResourceComparator {
+
+    public QueryResourceInfoComparator() {
+        super(null, null);
+    }
+
+    @Override
+    public int compare(final ClassResourceInfo cri1, final ClassResourceInfo cri2, final Message message) {
+        // Leave Class selection to CXF
+        return 0;
+    }
+
+    @Override
+    public int compare(final OperationResourceInfo oper1, final OperationResourceInfo oper2, final Message message) {
+        // Check if CXF can make a decision
+        int cxfResult = super.compare(oper1, oper2);
+        if (cxfResult != 0) {
+            return cxfResult;
+        }
+
+        int op1Counter = getMatchingRate(oper1, message);
+        int op2Counter = getMatchingRate(oper2, message);
+
+        return op1Counter == op2Counter
+                ? 0
+                : op1Counter < op2Counter
+                ? 1
+                : -1;
+    }
+
+    /**
+     * This method calculates a number indicating a good or bad match between values provided within the request and
+     * expected method parameters. A higher number means a better match.
+     *
+     * @param operation The operation to be rated, based on contained parameterInfo values.
+     * @param message A message containing query and header values from user request
+     * @return A positive or negative number, indicating a good match between query and method
+     */
+    protected int getMatchingRate(final OperationResourceInfo operation, final Message message) {
+        List<Parameter> params = operation.getParameters();
+        if (params == null || params.isEmpty()) {
+            return 0;
+        }
+
+        // Get Request QueryParams
+        String query = (String) message.get(Message.QUERY_STRING);
+        String path = (String) message.get(Message.REQUEST_URI);
+        Map<String, List<String>> qParams = JAXRSUtils.getStructuredParams(query, "&", true, false);
+        Map<String, List<String>> mParams = JAXRSUtils.getMatrixParams(path, true);
+        // Get Request Headers
+        Map<?, ?> qHeader = (java.util.Map<?, ?>) message.get(Message.PROTOCOL_HEADERS);
+
+        int rate = 0;
+        for (Parameter p : params) {
+            switch (p.getType()) {
+            case QUERY:
+                if (qParams.containsKey(p.getName())) {
+                    rate += 2;
+                } else if (p.getDefaultValue() == null) {
+                    rate -= 1;
+                }
+                break;
+            case MATRIX:
+                if (mParams.containsKey(p.getName())) {
+                    rate += 2;
+                } else if (p.getDefaultValue() == null) {
+                    rate -= 1;
+                }
+                break;
+            case HEADER:
+                if (qHeader.containsKey(p.getName())) {
+                    rate += 2;
+                } else if (p.getDefaultValue() == null) {
+                    rate -= 1;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        return rate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bf309400/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/RestServiceExceptionMapper.java
----------------------------------------------------------------------
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/RestServiceExceptionMapper.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/RestServiceExceptionMapper.java
new file mode 100644
index 0000000..c7a1e1e
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/rest/RestServiceExceptionMapper.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.fediz.service.idp.rest;
+
+import javax.validation.ConstraintViolationException;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.dao.DataRetrievalFailureException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.security.access.AccessDeniedException;
+
+@Provider
+public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
+
+    public static final String APPLICATION_ERROR_CODE = "X-Application-Error-Code";
+    
+    public static final String APPLICATION_ERROR_INFO = "X-Application-Error-Info";
+    
+    private static final String BASIC_REALM_UNAUTHORIZED = "Basic realm=\"Apache Fediz authentication\"";
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestServiceExceptionMapper.class);
+
+    @Override
+    public Response toResponse(final Exception ex) {
+        LOG.warn("Exception occured processing REST request: " + ex.getMessage(), ex);
+
+        if (ex instanceof AccessDeniedException) {
+            return Response.status(Response.Status.UNAUTHORIZED).
+                    header(HttpHeaders.WWW_AUTHENTICATE, BASIC_REALM_UNAUTHORIZED).
+                    build();
+        }
+        if (ex instanceof ConstraintViolationException) {
+            ConstraintViolationException cve = (ConstraintViolationException)ex;
+            LOG.debug("{}\n{}", ex.getMessage(), cve.getConstraintViolations().toString());
+            return buildResponse(Response.Status.BAD_REQUEST, ex);
+        }
+        if (ex instanceof DataIntegrityViolationException) {
+            return buildResponse(Response.Status.CONFLICT, ex);
+        }
+        
+        if (ex instanceof EmptyResultDataAccessException) {
+            return buildResponse(Response.Status.NOT_FOUND, ex);
+        }
+        
+        if (ex instanceof DataRetrievalFailureException) {
+            return buildResponse(Response.Status.NOT_FOUND, ex);
+        }
+
+        // Rest is interpreted as InternalServerError
+        return buildResponse(Response.Status.INTERNAL_SERVER_ERROR, ex);
+    }
+
+    Response buildResponse(final Status status, final Exception ex) {
+        ResponseBuilder responseBuilder = Response.status(status);
+        return responseBuilder.header(APPLICATION_ERROR_CODE, ex.getClass().getName())
+                              .header(APPLICATION_ERROR_INFO, ex.getMessage())
+                              .status(status).build();
+    }
+
+}