added extension mechanism for saving client information in between runs

pull/369/merge
Justin Richer 12 years ago
parent 70958376cb
commit 98d917f3b9

@ -0,0 +1,31 @@
/**
*
*/
package org.mitre.openid.connect.client.service;
import org.mitre.oauth2.model.RegisteredClient;
/**
* @author jricher
*
*/
public interface RegisteredClientService {
/**
* Get a remembered client (if one exists) to talk to the given issuer. This
* client likely doesn't have its full configuration information but contains
* the information needed to fetch it.
* @param issuer
* @return
*/
RegisteredClient getByIssuer(String issuer);
/**
* Save this client's information for talking to the given issuer. This will
* save only enough information to fetch the client's full configuration from
* the server.
* @param client
*/
void save(String issuer, RegisteredClient client);
}

@ -27,6 +27,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
import org.mitre.openid.connect.client.service.ClientConfigurationService;
import org.mitre.openid.connect.client.service.RegisteredClientService;
import org.mitre.openid.connect.config.ServerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,6 +35,7 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.web.client.RestTemplate;
@ -54,6 +56,9 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
private LoadingCache<ServerConfiguration, RegisteredClient> clients;
private RegisteredClientService registeredClientService = new InMemoryRegisteredClientService();
// TODO: make sure the template doesn't have "client_id", "client_secret", or "registration_access_token" set on it already
private RegisteredClient template;
public DynamicRegistrationClientConfigurationService() {
@ -84,6 +89,30 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
this.template = template;
}
/**
* @return the registeredClientService
*/
public RegisteredClientService getRegisteredClientService() {
return registeredClientService;
}
/**
* @param registeredClientService the registeredClientService to set
*/
public void setRegisteredClientService(RegisteredClientService registeredClientService) {
this.registeredClientService = registeredClientService;
}
/**
* Loader class that fetches the client information.
*
* If a client has been registered (ie, it's known to the RegisteredClientService), then this
* will fetch the client's configuration from the server.
*
* @author jricher
*
*/
public class DynamicClientRegistrationLoader extends CacheLoader<ServerConfiguration, RegisteredClient> {
private HttpClient httpClient = new DefaultHttpClient();
private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
@ -93,6 +122,10 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
public RegisteredClient load(ServerConfiguration serverConfig) throws Exception {
RestTemplate restTemplate = new RestTemplate(httpFactory);
RegisteredClient knownClient = registeredClientService.getByIssuer(serverConfig.getIssuer());
if (knownClient == null) {
// dynamically register this client
JsonObject jsonRequest = ClientDetailsEntityJsonProcessor.serialize(template);
@ -105,11 +138,26 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
String registered = restTemplate.postForObject(serverConfig.getRegistrationEndpointUri(), entity, String.class);
// TODO: handle HTTP errors
// TODO: save registration token and other important bits
RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
// save this client for later
registeredClientService.save(serverConfig.getIssuer(), client);
return client;
} else {
// load this client's information from the server
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, knownClient.getRegistrationAccessToken()));
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
String registered = restTemplate.getForObject(knownClient.getRegistrationClientUri(), String.class);
RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
return client;
}
}
}

@ -0,0 +1,36 @@
/**
*
*/
package org.mitre.openid.connect.client.service.impl;
import java.util.HashMap;
import java.util.Map;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.service.RegisteredClientService;
/**
* @author jricher
*
*/
public class InMemoryRegisteredClientService implements RegisteredClientService {
private Map<String, RegisteredClient> clients = new HashMap<String, RegisteredClient>();
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.RegisteredClientService#getByIssuer(java.lang.String)
*/
@Override
public RegisteredClient getByIssuer(String issuer) {
return clients.get(issuer);
}
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.RegisteredClientService#save(org.mitre.oauth2.model.RegisteredClient)
*/
@Override
public void save(String issuer, RegisteredClient client) {
clients.put(issuer, client);
}
}

@ -0,0 +1,142 @@
/**
*
*/
package org.mitre.openid.connect.client.service.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.service.RegisteredClientService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
/**
* @author jricher
*
*/
public class JsonFileRegisteredClientService implements RegisteredClientService {
private static Logger logger = LoggerFactory.getLogger(JsonFileRegisteredClientService.class);
private Gson gson = new GsonBuilder()
.registerTypeAdapter(RegisteredClient.class, new JsonSerializer<RegisteredClient>() {
@Override
public JsonElement serialize(RegisteredClient src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("token", src.getRegistrationAccessToken());
obj.addProperty("uri", src.getRegistrationClientUri());
if (src.getClientIdIssuedAt() != null) {
obj.addProperty("issued", src.getClientIdIssuedAt().getTime());
}
if (src.getClientSecretExpiresAt() != null) {
obj.addProperty("expires", src.getClientSecretExpiresAt().getTime());
}
return obj;
}
})
.registerTypeAdapter(RegisteredClient.class, new JsonDeserializer<RegisteredClient>() {
@Override
public RegisteredClient deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonObject()) {
JsonObject src = json.getAsJsonObject();
RegisteredClient rc = new RegisteredClient();
rc.setRegistrationAccessToken(src.get("token").getAsString());
rc.setRegistrationClientUri(src.get("uri").getAsString());
if (src.has("issued") && !src.get("issued").isJsonNull()) {
rc.setClientIdIssuedAt(new Date(src.get("issued").getAsLong()));
}
if (src.has("expires") && !src.get("expires").isJsonNull()) {
rc.setClientSecretExpiresAt(new Date(src.get("expires").getAsLong()));
}
return rc;
} else {
return null;
}
}
})
.create();
private File file;
private Map<String, RegisteredClient> clients = new HashMap<String, RegisteredClient>();
public JsonFileRegisteredClientService(String filename) {
this.file = new File(filename);
load();
}
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.RegisteredClientService#getByIssuer(java.lang.String)
*/
@Override
public RegisteredClient getByIssuer(String issuer) {
return clients.get(issuer);
}
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.RegisteredClientService#save(java.lang.String, org.mitre.oauth2.model.RegisteredClient)
*/
@Override
public void save(String issuer, RegisteredClient client) {
clients.put(issuer, client);
write();
}
/**
* Sync this file out to disk.
*/
private void write() {
try {
FileWriter out = new FileWriter(file);
gson.toJson(clients, new TypeToken<Map<String, RegisteredClient>>(){}.getType(), out);
out.close();
} catch (FileNotFoundException e) {
logger.error("Could not write to output file", e);
} catch (IOException e) {
logger.error("Could not write to output file", e);
}
}
/**
* Load the map in from disk.
*/
private void load() {
try {
FileReader in = new FileReader(file);
clients = gson.fromJson(in, new TypeToken<Map<String, RegisteredClient>>(){}.getType());
in.close();
} catch (FileNotFoundException e) {
logger.error("Could not read from input file", e);
} catch (IOException e) {
logger.error("Could not read from input file", e);
}
}
}
Loading…
Cancel
Save