added extension mechanism for saving client information in between runs
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;
|
||||
|
||||
|
@ -53,7 +55,10 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
|
|||
private static Logger logger = LoggerFactory.getLogger(DynamicServerConfigurationService.class);
|
||||
|
||||
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,22 +122,41 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
|
|||
public RegisteredClient load(ServerConfiguration serverConfig) throws Exception {
|
||||
RestTemplate restTemplate = new RestTemplate(httpFactory);
|
||||
|
||||
// dynamically register this client
|
||||
JsonObject jsonRequest = ClientDetailsEntityJsonProcessor.serialize(template);
|
||||
|
||||
RegisteredClient knownClient = registeredClientService.getByIssuer(serverConfig.getIssuer());
|
||||
if (knownClient == null) {
|
||||
|
||||
// dynamically register this client
|
||||
JsonObject jsonRequest = ClientDetailsEntityJsonProcessor.serialize(template);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<String>(jsonRequest.toString(), headers);
|
||||
|
||||
String registered = restTemplate.postForObject(serverConfig.getRegistrationEndpointUri(), entity, String.class);
|
||||
// TODO: handle HTTP errors
|
||||
|
||||
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));
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<String>(jsonRequest.toString(), headers);
|
||||
|
||||
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);
|
||||
|
||||
return client;
|
||||
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…
Reference in New Issue