Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
- Added create, retrieve, and update endpoints
TODO: version mismatch should return 412, delete, and retrieve all (update security)
  • Loading branch information
jlo authored and cf-identity committed Jan 21, 2016
1 parent ec2bd14 commit fa19840
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 175 deletions.
Expand Up @@ -6,6 +6,7 @@
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
Expand All @@ -25,40 +26,51 @@
@Controller @Controller
public class ClientMetadataAdminEndpoints { public class ClientMetadataAdminEndpoints {


private ClientMetaDetailsProvisioning clientMetaDetailsProvisioning; private ClientMetadataProvisioning clientMetadataProvisioning;
private ClientDetailsService clients; private ClientDetailsService clients;


@RequestMapping(value = "/oauth/clients/{client}/meta", method = RequestMethod.POST) @RequestMapping(value = "/oauth/clients/{client}/meta", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
public ClientMetaDetails createClientUIDetails(@RequestBody ClientMetaDetails clientMetaDetails, public ClientMetadata createClientMetadata(@RequestBody ClientMetadata clientMetadata,
@PathVariable("client") String clientId) @PathVariable("client") String clientId)
throws ClientNotFoundException { throws ClientNotFoundException {

try { try {
clients.loadClientByClientId(clientId); clients.loadClientByClientId(clientId);
} catch (NoSuchClientException nsce) { } catch (NoSuchClientException nsce) {
throw new ClientNotFoundException(clientId); throw new ClientNotFoundException(clientId );
} }


clientMetaDetails.setClientId(clientId); clientMetadata.setClientId(clientId);
return clientMetaDetailsProvisioning.create(clientMetaDetails); return clientMetadataProvisioning.create(clientMetadata);
} }


// GET // GET
@RequestMapping(value = "/oauth/clients/{client}/meta", method = RequestMethod.GET) @RequestMapping(value = "/oauth/clients/{client}/meta", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
public ClientMetaDetails retrieveClientUIDetails(@PathVariable("client") String clientId) { public ClientMetadata retrieveClientMetadata(@PathVariable("client") String clientId) {
return null; return clientMetadataProvisioning.retrieve(clientId);
} }


// PUT (Update) // PUT (Update)
@RequestMapping(value = "/oauth/clients/{client}/meta", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public ClientMetadata updateClientMetadata(@RequestBody ClientMetadata clientMetadata,
@RequestHeader(value = "If-Match", required = false) Integer etag,
@PathVariable("client") String clientId) {
if (etag == null) {
throw new ClientMetadataException("Missing If-Match header", HttpStatus.PRECONDITION_FAILED);
}

clientMetadata.setVersion(etag);
return clientMetadataProvisioning.update(clientId, clientMetadata);
}


// DELETE // DELETE


// GET (retrieveAll) // GET (retrieveAll)


public void setClientMetaDetailsProvisioning(ClientMetaDetailsProvisioning clientMetaDetailsProvisioning) { public void setClientMetadataProvisioning(ClientMetadataProvisioning clientMetadataProvisioning) {
this.clientMetaDetailsProvisioning = clientMetaDetailsProvisioning; this.clientMetadataProvisioning = clientMetadataProvisioning;
} }


public void setClients(ClientDetailsService clients) { public void setClients(ClientDetailsService clients) {
Expand Down
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/ *******************************************************************************/
package org.cloudfoundry.identity.uaa.client; package org.cloudfoundry.identity.uaa.client;


import org.cloudfoundry.identity.uaa.error.UaaException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;


import java.util.Map; import java.util.Map;
Expand All @@ -20,7 +21,7 @@
* @author Luke Taylor * @author Luke Taylor
* @author Dave Syer * @author Dave Syer
*/ */
public class ClientMetadataException extends RuntimeException { public class ClientMetadataException extends UaaException {


private final HttpStatus status; private final HttpStatus status;
protected Map<String, Object> extraInfo; protected Map<String, Object> extraInfo;
Expand Down
@@ -1,6 +1,5 @@
package org.cloudfoundry.identity.uaa.client; package org.cloudfoundry.identity.uaa.client;


import org.cloudfoundry.identity.uaa.error.UaaException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;


Expand All @@ -16,21 +15,11 @@
* subcomponents is subject to the terms and conditions of the * subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file. * subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/ *******************************************************************************/
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Client not found") @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Client metadata not found")
public class ClientMetadataNotFoundException extends UaaException { public class ClientMetadataNotFoundException extends ClientMetadataException {


// private String clientId; public ClientMetadataNotFoundException(String clientId) {

super("No existing metadata found for client " + clientId, HttpStatus.NOT_FOUND);
// public ClientNotFoundException(String clientId) {
// this.clientId = clientId;
// }

public ClientMetadataNotFoundException(String msg) {
super("client_not_found", msg, HttpStatus.NOT_FOUND.value());
} }



// public String getClientId() {
// return this.clientId;
// }
} }
Expand Up @@ -14,5 +14,5 @@
* subcomponents is subject to the terms and conditions of the * subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file. * subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/ *******************************************************************************/
public interface ClientMetadataProvisioning extends ResourceManager<ClientMetaDetails> { public interface ClientMetadataProvisioning extends ResourceManager<ClientMetadata> {
} }
@@ -1,5 +1,9 @@
package org.cloudfoundry.identity.uaa.client; package org.cloudfoundry.identity.uaa.client;


import org.cloudfoundry.identity.uaa.error.UaaException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/******************************************************************************* /*******************************************************************************
* Cloud Foundry * Cloud Foundry
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved. * Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved.
Expand All @@ -12,5 +16,13 @@
* subcomponents is subject to the terms and conditions of the * subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file. * subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/ *******************************************************************************/
public class ClientNotFoundException { @ResponseStatus(value = HttpStatus.CONFLICT, reason = "Client not found.c Unable to create metadata resource")
public class ClientNotFoundException extends UaaException {

public ClientNotFoundException(String clientId) {
super("client_not_found",
"Client" + clientId + "not found. Unable to create metadata resource",
HttpStatus.CONFLICT.value());
}

} }
Expand Up @@ -5,6 +5,7 @@
import org.cloudfoundry.identity.uaa.provider.IdpAlreadyExistsException; import org.cloudfoundry.identity.uaa.provider.IdpAlreadyExistsException;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
Expand Down Expand Up @@ -38,15 +39,15 @@ public class JdbcClientMetadataProvisioning implements ClientMetadataProvisionin


private static final Log logger = LogFactory.getLog(JdbcClientMetadataProvisioning.class); private static final Log logger = LogFactory.getLog(JdbcClientMetadataProvisioning.class);


private static final String CLIENT_UI_DETAILS_FIELDS = "id, client_id, identity_zone_id, show_on_home_page, app_launch_url, app_icon, version"; private static final String CLIENT_METADATA_FIELDS = "id, client_id, identity_zone_id, show_on_home_page, app_launch_url, app_icon, version";
private static final String CLIENT_UI_DETAILS_QUERY = "select " + CLIENT_UI_DETAILS_FIELDS + " from oauth_client_ui_details where client_id=? and identity_zone_id=?"; private static final String CLIENT_METADATA_QUERY = "select " + CLIENT_METADATA_FIELDS + " from oauth_client_metadata where client_id=? and identity_zone_id=?";
private static final String CLIENT_UI_DETAILS_CREATE = "insert into oauth_client_ui_details(" + CLIENT_UI_DETAILS_FIELDS + ") values (?,?,?,?,?,?,?)"; private static final String CLIENT_METADATA_CREATE = "insert into oauth_client_metadata(" + CLIENT_METADATA_FIELDS + ") values (?,?,?,?,?,?,?)";
private static final String CLIENT_UI_DETAILS_UPDATE_FIELDS = "show_on_home_page, app_launch_url, app_icon, version"; private static final String CLIENT_METADATA_UPDATE_FIELDS = "show_on_home_page, app_launch_url, app_icon, version";
private static final String CLIENT_UI_DETAILS_UPDATE = "update oauth_client_ui_details set " + CLIENT_UI_DETAILS_UPDATE_FIELDS.replace(",", "=?,") + "=?" + " where client_id=? and identity_zone_id=? and version=?"; private static final String CLIENT_METADATA_UPDATE = "update oauth_client_metadata set " + CLIENT_METADATA_UPDATE_FIELDS.replace(",", "=?,") + "=?" + " where client_id=? and identity_zone_id=? and version=?";
private static final String CLIENT_UI_DETAILS_DELETE_QUERY = "delete from oauth_client_ui_details where client_id=? and identity_zone_id=?"; private static final String CLIENT_METADATA_DELETE_QUERY = "delete from oauth_client_metadata where client_id=? and identity_zone_id=?";


private JdbcTemplate template; private JdbcTemplate template;
private final RowMapper<ClientMetaDetails> mapper = new ClientUIDetailsRowMapper(); private final RowMapper<ClientMetadata> mapper = new ClientMetadataRowMapper();


JdbcClientMetadataProvisioning(JdbcTemplate template) { JdbcClientMetadataProvisioning(JdbcTemplate template) {
Assert.notNull(template); Assert.notNull(template);
Expand All @@ -58,23 +59,27 @@ public void setTemplate(JdbcTemplate template) {
} }


@Override @Override
public List<ClientMetaDetails> retrieveAll() { public List<ClientMetadata> retrieveAll() {
logger.debug("Retrieving UI details for all client"); logger.debug("Retrieving UI details for all client");
return template.query(CLIENT_UI_DETAILS_QUERY, mapper, IdentityZoneHolder.get().getId()); return template.query(CLIENT_METADATA_QUERY, mapper, IdentityZoneHolder.get().getId());
} }


@Override @Override
public ClientMetaDetails retrieve(String clientId) { public ClientMetadata retrieve(String clientId) {
logger.debug("Retrieving UI details for client: " + clientId); logger.debug("Retrieving UI details for client: " + clientId);
return template.queryForObject(CLIENT_UI_DETAILS_QUERY, mapper, clientId, IdentityZoneHolder.get().getId()); try {
return template.queryForObject(CLIENT_METADATA_QUERY, mapper, clientId, IdentityZoneHolder.get().getId());
} catch (EmptyResultDataAccessException erdae) {
throw new ClientMetadataNotFoundException("No existing metadata found for client " + clientId);
}
} }


@Override @Override
public ClientMetaDetails create(ClientMetaDetails resource) { public ClientMetadata create(ClientMetadata resource) {
logger.debug("Creating new UI details for client: " + resource.getClientId()); logger.debug("Creating new UI details for client: " + resource.getClientId());
final String id = UUID.randomUUID().toString(); final String id = UUID.randomUUID().toString();
try { try {
template.update(CLIENT_UI_DETAILS_CREATE, new PreparedStatementSetter() { template.update(CLIENT_METADATA_CREATE, new PreparedStatementSetter() {
@Override @Override
public void setValues(PreparedStatement ps) throws SQLException { public void setValues(PreparedStatement ps) throws SQLException {
int pos = 1; int pos = 1;
Expand All @@ -91,7 +96,6 @@ public void setValues(PreparedStatement ps) throws SQLException {
} else { } else {
ps.setBinaryStream(pos++, new ByteArrayInputStream(new byte[] {}), (int) 0); ps.setBinaryStream(pos++, new ByteArrayInputStream(new byte[] {}), (int) 0);
} }
// pos++;
ps.setInt(pos++, 1); ps.setInt(pos++, 1);
} }
}); });
Expand All @@ -102,9 +106,9 @@ public void setValues(PreparedStatement ps) throws SQLException {
} }


@Override @Override
public ClientMetaDetails update(String clientId, ClientMetaDetails resource) { public ClientMetadata update(String clientId, ClientMetadata resource) {
logger.debug("Updating UI details for client: " + clientId); logger.debug("Updating metadata for client: " + clientId);
int updated = template.update(CLIENT_UI_DETAILS_UPDATE, new PreparedStatementSetter() { int updated = template.update(CLIENT_METADATA_UPDATE, new PreparedStatementSetter() {
@Override @Override
public void setValues(PreparedStatement ps) throws SQLException { public void setValues(PreparedStatement ps) throws SQLException {
int pos = 1; int pos = 1;
Expand All @@ -114,73 +118,74 @@ public void setValues(PreparedStatement ps) throws SQLException {
String appIcon = resource.getAppIcon(); String appIcon = resource.getAppIcon();
if (appIcon != null) { if (appIcon != null) {
byte[] decodedAppIcon = Base64.decode(appIcon.getBytes()); byte[] decodedAppIcon = Base64.decode(appIcon.getBytes());
ps.setBinaryStream(pos, new ByteArrayInputStream(decodedAppIcon), (int) decodedAppIcon.length); ps.setBinaryStream(pos++, new ByteArrayInputStream(decodedAppIcon), (int) decodedAppIcon.length);
} else {
ps.setBinaryStream(pos++, new ByteArrayInputStream(new byte[] {}), (int) 0);
} }
pos++;
ps.setInt(pos++, resource.getVersion() + 1); ps.setInt(pos++, resource.getVersion() + 1);
ps.setString(pos++, clientId); ps.setString(pos++, clientId);
ps.setString(pos++, IdentityZoneHolder.get().getId()); ps.setString(pos++, IdentityZoneHolder.get().getId());
ps.setInt(pos++, resource.getVersion()); ps.setInt(pos++, resource.getVersion());
} }
}); });


ClientMetaDetails resultingClientMetaDetails = retrieve(clientId); ClientMetadata resultingClientMetadata = retrieve(clientId);


if (updated == 0) { if (updated == 0) {
throw new OptimisticLockingFailureException(String.format( throw new OptimisticLockingFailureException(String.format(
"Attempt to update the UI details of client (%s) failed with incorrect version: expected=%d but found=%d", "Attempt to update the UI details of client (%s) failed with incorrect version: expected=%d but found=%d",
clientId, clientId,
resultingClientMetaDetails.getVersion(), resultingClientMetadata.getVersion(),
resource.getVersion())); resource.getVersion()));
} else if (updated > 1) { } else if (updated > 1) {
throw new IncorrectResultSizeDataAccessException(1); throw new IncorrectResultSizeDataAccessException(1);
} }


return resultingClientMetaDetails; return resultingClientMetadata;
} }


@Override @Override
public ClientMetaDetails delete(String clientId, int version) { public ClientMetadata delete(String clientId, int version) {
logger.debug("Deleting UI details for client: " + clientId); logger.debug("Deleting UI details for client: " + clientId);
ClientMetaDetails clientMetaDetails = retrieve(clientId); ClientMetadata clientMetadata = retrieve(clientId);
int updated; int updated;


if (version < 0) { if (version < 0) {
updated = template.update(CLIENT_UI_DETAILS_DELETE_QUERY, clientId, IdentityZoneHolder.get().getId()); updated = template.update(CLIENT_METADATA_DELETE_QUERY, clientId, IdentityZoneHolder.get().getId());
} else { } else {
updated = template.update(CLIENT_UI_DETAILS_DELETE_QUERY + " and version=?", clientId, IdentityZoneHolder.get().getId(), version); updated = template.update(CLIENT_METADATA_DELETE_QUERY + " and version=?", clientId, IdentityZoneHolder.get().getId(), version);
} }


if (updated == 0) { if (updated == 0) {
throw new OptimisticLockingFailureException(String.format( throw new OptimisticLockingFailureException(String.format(
"Attempt to delete the UI details of client (%s) failed with incorrect version: expected=%d but found=%d", "Attempt to delete the UI details of client (%s) failed with incorrect version: expected=%d but found=%d",
clientId, clientId,
clientMetaDetails.getVersion(), clientMetadata.getVersion(),
version)); version));
} }


return clientMetaDetails; return clientMetadata;
} }




private class ClientUIDetailsRowMapper implements RowMapper<ClientMetaDetails> { private class ClientMetadataRowMapper implements RowMapper<ClientMetadata> {


@Override @Override
public ClientMetaDetails mapRow(ResultSet rs, int rowNum) throws SQLException { public ClientMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
ClientMetaDetails clientMetaDetails = new ClientMetaDetails(); ClientMetadata clientMetadata = new ClientMetadata();
int pos = 1; int pos = 1;
pos++; // id pos++; // id
clientMetaDetails.setClientId(rs.getString(pos++)); clientMetadata.setClientId(rs.getString(pos++));
clientMetaDetails.setIdentityZoneId(rs.getString(pos++)); clientMetadata.setIdentityZoneId(rs.getString(pos++));
clientMetaDetails.setShowOnHomePage(rs.getBoolean(pos++)); clientMetadata.setShowOnHomePage(rs.getBoolean(pos++));
try { try {
clientMetaDetails.setAppLaunchUrl(new URL(rs.getString(pos++))); clientMetadata.setAppLaunchUrl(new URL(rs.getString(pos++)));
} catch (MalformedURLException mue) { } catch (MalformedURLException mue) {
// it is safe to ignore this as client_meta_details rows are always created from a ClientMetaDetails instance whose launch url property is strongly typed to URL // it is safe to ignore this as client_metadata rows are always created from a ClientMetadata instance whose launch url property is strongly typed to URL
} }
clientMetaDetails.setAppIcon(new String(Base64.encode(rs.getBytes(pos++)))); clientMetadata.setAppIcon(new String(Base64.encode(rs.getBytes(pos++))));
clientMetaDetails.setVersion(rs.getInt(pos++)); clientMetadata.setVersion(rs.getInt(pos++));
return clientMetaDetails; return clientMetadata;
} }
} }
} }
Expand Up @@ -14,7 +14,6 @@


import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.authentication.event.UnverifiedUserAuthenticationEvent;
import org.cloudfoundry.identity.uaa.authentication.manager.AuthEvent; import org.cloudfoundry.identity.uaa.authentication.manager.AuthEvent;
import org.cloudfoundry.identity.uaa.authentication.manager.ExternalGroupAuthorizationEvent; import org.cloudfoundry.identity.uaa.authentication.manager.ExternalGroupAuthorizationEvent;
import org.cloudfoundry.identity.uaa.authentication.manager.InvitedUserAuthenticatedEvent; import org.cloudfoundry.identity.uaa.authentication.manager.InvitedUserAuthenticatedEvent;
Expand Down

0 comments on commit fa19840

Please sign in to comment.