added privacy-preserving client logo cache
parent
43509b7dfb
commit
d1033b693f
|
@ -0,0 +1,67 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2015 The MITRE Corporation
|
||||
* and the MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.model;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class CachedImage {
|
||||
|
||||
private byte[] data;
|
||||
private String contentType;
|
||||
private long length;
|
||||
|
||||
/**
|
||||
* @return the data
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* @param data the data to set
|
||||
*/
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
/**
|
||||
* @return the contentType
|
||||
*/
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
/**
|
||||
* @param contentType the contentType to set
|
||||
*/
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
/**
|
||||
* @return the length
|
||||
*/
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
/**
|
||||
* @param length the length to set
|
||||
*/
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2015 The MITRE Corporation
|
||||
* and the MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.service;
|
||||
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.openid.connect.model.CachedImage;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface ClientLogoLoadingService {
|
||||
|
||||
/**
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
public CachedImage getLogo(ClientDetailsEntity client);
|
||||
|
||||
}
|
|
@ -84,7 +84,7 @@
|
|||
<c:if test="${ not empty client.logoUri }">
|
||||
<ul class="thumbnails">
|
||||
<li class="span5">
|
||||
<a class="thumbnail" data-toggle="modal" data-target="#logoModal"><img src="${ fn:escapeXml(client.logoUri) }" /></a>
|
||||
<a class="thumbnail" data-toggle="modal" data-target="#logoModal"><img src="api/clients/${ client.id }/logo" /></a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Modal -->
|
||||
|
@ -103,7 +103,7 @@
|
|||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<img src="${ fn:escapeXml(client.logoUri) }" />
|
||||
<img src="api/clients/${ client.id }/logo" />
|
||||
<c:if test="${ not empty client.clientUri }">
|
||||
<a href="<c:out value="${ client.clientUri }" />"><c:out value="${ client.clientUri }" /></a>
|
||||
</c:if>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
<div class="media">
|
||||
<% if (client.logoUri) { %>
|
||||
<span class="pull-left"><img class="media-object client-logo" src="<%- client.logoUri %>"></span>
|
||||
<span class="pull-left"><img class="media-object client-logo" src="api/clients/<%- client.id ->/logo"></span>
|
||||
<% } %>
|
||||
|
||||
<div class="media-body">
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2015 The MITRE Corporation
|
||||
* and the MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.openid.connect.model.CachedImage;
|
||||
import org.mitre.openid.connect.service.ClientLogoLoadingService;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
@Service("inMemoryClientLogoLoadingService")
|
||||
public class InMemoryClientLogoLoadingService implements ClientLogoLoadingService {
|
||||
|
||||
private LoadingCache<ClientDetailsEntity, CachedImage> cache;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public InMemoryClientLogoLoadingService() {
|
||||
|
||||
cache = CacheBuilder.newBuilder()
|
||||
.maximumSize(100)
|
||||
.expireAfterAccess(14, TimeUnit.DAYS)
|
||||
.build(new ClientLogoFetcher());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.service.ClientLogoLoadingService#getLogo(org.mitre.oauth2.model.ClientDetailsEntity)
|
||||
*/
|
||||
@Override
|
||||
public CachedImage getLogo(ClientDetailsEntity client) {
|
||||
try {
|
||||
if (client != null && !Strings.isNullOrEmpty(client.getLogoUri())) {
|
||||
return cache.get(client);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (UncheckedExecutionException | ExecutionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class ClientLogoFetcher extends CacheLoader<ClientDetailsEntity, CachedImage> {
|
||||
private HttpClient httpClient = HttpClientBuilder.create().useSystemProperties().build();
|
||||
private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.google.common.cache.CacheLoader#load(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public CachedImage load(ClientDetailsEntity key) throws Exception {
|
||||
try {
|
||||
HttpResponse response = httpClient.execute(new HttpGet(key.getLogoUri()));
|
||||
|
||||
HttpEntity entity = response.getEntity();
|
||||
|
||||
CachedImage image = new CachedImage();
|
||||
|
||||
image.setContentType(entity.getContentType().getValue());
|
||||
image.setLength(entity.getContentLength());
|
||||
image.setData(IOUtils.toByteArray(entity.getContent()));
|
||||
|
||||
return image;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Unable to load client image.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -24,6 +24,8 @@ import org.mitre.oauth2.model.ClientDetailsEntity;
|
|||
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
|
||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||
import org.mitre.oauth2.web.AuthenticationUtilities;
|
||||
import org.mitre.openid.connect.model.CachedImage;
|
||||
import org.mitre.openid.connect.service.ClientLogoLoadingService;
|
||||
import org.mitre.openid.connect.view.ClientEntityViewForAdmins;
|
||||
import org.mitre.openid.connect.view.ClientEntityViewForUsers;
|
||||
import org.mitre.openid.connect.view.HttpCodeView;
|
||||
|
@ -32,8 +34,10 @@ import org.mitre.openid.connect.view.JsonErrorView;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
@ -74,6 +78,9 @@ public class ClientAPI {
|
|||
@Autowired
|
||||
private ClientDetailsEntityService clientService;
|
||||
|
||||
@Autowired
|
||||
private ClientLogoLoadingService clientLogoLoadingService;
|
||||
|
||||
private JsonParser parser = new JsonParser();
|
||||
|
||||
private Gson gson = new GsonBuilder()
|
||||
|
@ -393,5 +400,30 @@ public class ClientAPI {
|
|||
return ClientEntityViewForUsers.VIEWNAME;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logo image for a client
|
||||
* @param id
|
||||
*/
|
||||
@RequestMapping(value = "/{id}/logo", method=RequestMethod.GET, produces = { MediaType.IMAGE_GIF_VALUE, MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE })
|
||||
public ResponseEntity<byte[]> getClientLogo(@PathVariable("id") Long id, Model model) {
|
||||
|
||||
ClientDetailsEntity client = clientService.getClientById(id);
|
||||
|
||||
if (client == null) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
} else if (Strings.isNullOrEmpty(client.getLogoUri())) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
} else {
|
||||
// get the image from cache
|
||||
CachedImage image = clientLogoLoadingService.getLogo(client);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.parseMediaType(image.getContentType()));
|
||||
headers.setContentLength(image.getLength());
|
||||
|
||||
return new ResponseEntity<>(image.getData(), headers, HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue