/* * Copyright 2012 SURFnet bv, The Netherlands * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nl.surfnet.coin.janus; import nl.surfnet.coin.janus.domain.ARP; import nl.surfnet.coin.janus.domain.EntityMetadata; import nl.surfnet.coin.janus.domain.JanusEntity; import org.apache.commons.codec.binary.Hex; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Transformer; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; import org.springframework.util.Assert; import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.net.URI; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; /** * REST client implementation for Janus. */ public class JanusRestClient implements Janus { private static Logger LOG = LoggerFactory.getLogger(JanusRestClient.class); private RestTemplate restTemplate; private URI janusUri; private String user; private String secret; public JanusRestClient() { this.restTemplate = new RestTemplate(); restTemplate .setMessageConverters(Arrays.<HttpMessageConverter<?>> asList(new MappingJacksonHttpMessageConverter())); } /** * {@inheritDoc} */ @Override public EntityMetadata getMetadataByEntityId(String entityId) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put("entityid", entityId); final Collection metadataAsStrings = CollectionUtils.collect(Arrays.asList(Metadata.values()), new Transformer() { @Override public Object transform(Object input) { return ((Metadata) input).val(); } }); parameters.put("keys", StringUtils.join(metadataAsStrings, ',')); URI signedUri; try { signedUri = sign("getMetadata", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } @SuppressWarnings("unchecked") final Map<String, Object> restResponse = restTemplate.getForObject(signedUri, Map.class); Assert.notNull(restResponse, "Rest response from Janus should not be null"); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } final EntityMetadata entityMetadata = EntityMetadata.fromMetadataMap(restResponse); entityMetadata.setAppEntityId(entityId); return entityMetadata; } catch (IOException e) { LOG.error("While doing Janus-request", e); } return null; } @Override public List<String> getEntityIdsByMetaData(Metadata key, String value) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put("key", key.val()); parameters.put("value", value); URI signedUri; try { signedUri = sign("findIdentifiersByMetadata", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } @SuppressWarnings("unchecked") final List<String> restResponse = restTemplate.getForObject(signedUri, List.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } return restResponse; } catch (IOException e) { LOG.error("While doing Janus-request", e); } return null; } @Override public List<String> getAllowedSps(String idpentityid) { return getAllowedSps(idpentityid, null); } @Override public List<String> getAllowedSps(String idpentityid, String revision) { Assert.hasText(idpentityid, "idpentityid is a required parameter"); Map<String, String> parameters = new HashMap<String, String>(); parameters.put("idpentityid", idpentityid); if (!StringUtils.isBlank(revision)) { parameters.put("idprevision", revision); } URI signedUri; try { signedUri = sign("getAllowedSps", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } @SuppressWarnings("unchecked") final List<String> restResponse = restTemplate.getForObject(signedUri, List.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } return restResponse; } catch (IOException e) { LOG.error("While doing Janus-request", e); } return null; } @Override public List<EntityMetadata> getSpList() { Map<String, String> parameters = new HashMap<String, String>(); final Collection metadataAsStrings = CollectionUtils.collect(Arrays.asList(Metadata.values()), new Transformer() { @Override public Object transform(Object input) { return ((Metadata) input).val(); } }); parameters.put("keys", StringUtils.join(metadataAsStrings, ',')); URI signedUri; try { signedUri = sign("getSpList", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } @SuppressWarnings("unchecked") final Map<String, Map<String, Object>> restResponse = restTemplate.getForObject(signedUri, Map.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } List<EntityMetadata> entities = new ArrayList<EntityMetadata>(); for (Map.Entry<String, Map<String, Object>> entry : restResponse.entrySet()) { String entityId = entry.getKey(); final EntityMetadata e = EntityMetadata.fromMetadataMap(entry.getValue()); e.setAppEntityId(entityId); entities.add(e); } return entities; } catch (IOException e) { LOG.error("While doing Janus-request", e); } return null; } @Override public List<EntityMetadata> getIdpList() { Map<String, String> parameters = new HashMap<String, String>(); final Collection metadataAsStrings = CollectionUtils.collect(Arrays.asList(Metadata.values()), new Transformer() { @Override public Object transform(Object input) { return ((Metadata) input).val(); } }); parameters.put("keys", StringUtils.join(metadataAsStrings, ',')); URI signedUri; try { signedUri = sign("getIdpList", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } @SuppressWarnings("unchecked") final Map<String, Map<String, Object>> restResponse = restTemplate.getForObject(signedUri, Map.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } List<EntityMetadata> entities = new ArrayList<EntityMetadata>(); for (Map.Entry<String, Map<String, Object>> entry : restResponse.entrySet()) { String entityId = entry.getKey(); final EntityMetadata e = EntityMetadata.fromMetadataMap(entry.getValue()); e.setAppEntityId(entityId); entities.add(e); } return entities; } catch (IOException e) { LOG.error("While doing Janus-request", e); } return null; } @Override public ARP getArp(String entityId) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put("entityid", entityId); URI signedUri = null; try { signedUri = sign("arp", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } } catch (IOException e) { LOG.error("Could not do ARP request to Janus", e); } final Map restResponse = restTemplate.getForObject(signedUri, Map.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } return (restResponse == null) ? null : ARP.fromRestResponse(restResponse); } @Override public boolean isConnectionAllowed(String spEntityId, String idpEntityId) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put("spentityid", spEntityId); parameters.put("idpentityid", idpEntityId); URI signedUri = null; try { signedUri = sign("isConnectionAllowed", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } } catch (IOException e) { LOG.error("Could not do isConnectionAllowed request to Janus", e); } final List restResponse = restTemplate.getForObject(signedUri, List.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } return CollectionUtils.isEmpty(restResponse) ? false : (Boolean) restResponse.get(0); } @Override public JanusEntity getEntity(String entityId) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put("entityid", entityId); URI signedUri = null; try { signedUri = sign("getEntity", parameters); if (LOG.isTraceEnabled()) { LOG.trace("Signed Janus-request is: {}", signedUri); } } catch (IOException e) { LOG.error("Could not do getEntity request to Janus", e); } @SuppressWarnings("unchecked") final Map<String, Object> restResponse = restTemplate.getForObject(signedUri, Map.class); if (LOG.isTraceEnabled()) { LOG.trace("Janus-request returned: {}", restResponse.toString()); } return restResponse == null ? null : JanusEntity.fromJanusResponse(restResponse); } /** * Sign the given method call. * * @param method * the name of the method to call * @param parameters * additional parameters that need to be passed to Janus * @return URI with parameters janus_sig and janus_key * @throws IOException */ private URI sign(String method, Map<String, String> parameters) throws IOException { Map<String, String> keys = new TreeMap<String, String>(); keys.put("janus_key", user); keys.put("method", method); keys.putAll(parameters); keys.put("rest", "1"); keys.put("userid", user); Set<String> keySet = keys.keySet(); StringBuilder toSign = new StringBuilder(secret); for (String key : keySet) { toSign.append(key); toSign.append(keys.get(key)); } MessageDigest digest; try { digest = MessageDigest.getInstance("SHA-512"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Cannot use algorithm SHA-512", e); } digest.reset(); final String charsetName = "UTF-8"; byte[] input = digest.digest(toSign.toString().getBytes(charsetName)); char[] value = Hex.encodeHex(input); String janus_sig = new String(value); keys.put("janus_sig", janus_sig); StringBuilder url = new StringBuilder(); keySet = keys.keySet(); for (String key : keySet) { if (url.length() > 0) { url.append('&'); } url.append(key).append('=').append(URLEncoder.encode(keys.get(key), charsetName)); } String uri = url.toString(); return URI.create(janusUri + "?" + uri); } /** * @param restTemplate * the restTemplate to set */ public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } /** * @param janusUri * the janusUri to set */ public void setJanusUri(URI janusUri) { this.janusUri = janusUri; } /** * @param user * the user to set */ public void setUser(String user) { this.user = user; } /** * @param secret * the secret to set */ public void setSecret(String secret) { this.secret = secret; } }