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();
+ }
+
+}