/******************************************************************************* * Copyright (c) 2012 IBM Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Sam Padgett - initial API and implementation * Michael Fiedler - updates to use file persistence *******************************************************************************/ package org.eclipse.lyo.server.oauth.consumerstore; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.sql.SQLException; import org.apache.log4j.Logger; import org.apache.commons.codec.binary.Base64; import org.eclipse.lyo.server.oauth.core.consumer.AbstractConsumerStore; import org.eclipse.lyo.server.oauth.core.consumer.ConsumerStoreException; import org.eclipse.lyo.server.oauth.core.consumer.LyoOAuthConsumer; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.ResIterator; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.shared.JenaException; import com.hp.hpl.jena.shared.PropertyNotFoundException; import com.hp.hpl.jena.util.FileManager; import com.hp.hpl.jena.util.FileUtils; import com.hp.hpl.jena.vocabulary.RDF; /** * A simple RDF consumer store backed by an XML file on the filesystem. * * NOTE: The shared consumer secret is stored as Base64 and is only obfuscated, not encrypted. * * @author Samuel Padgett <spadgett@us.ibm.com> */ public class FileSystemConsumerStore extends AbstractConsumerStore { protected final static String LYO_OAUTH_NAMESPACE = "http://eclipse.org/lyo/server/oauth#"; protected final static String CONSUMER_RESOURCE = LYO_OAUTH_NAMESPACE + "Consumer"; protected final static String CALLBACK_URL = LYO_OAUTH_NAMESPACE + "callback"; protected final static String CONSUMER_NAME = LYO_OAUTH_NAMESPACE + "consumerName"; protected final static String CONSUMER_KEY = LYO_OAUTH_NAMESPACE + "consumerKey"; protected final static String CONSUMER_SECRET = LYO_OAUTH_NAMESPACE + "consumerSecret"; protected final static String PROVISIONAL = LYO_OAUTH_NAMESPACE + "provisional"; protected final static String TRUSTED = LYO_OAUTH_NAMESPACE + "trusted"; private Logger logger = Logger.getLogger(FileSystemConsumerStore.class); private Model model; private String oauthStore; public FileSystemConsumerStore(String oauthStoreRoot) throws SQLException, ConsumerStoreException, ClassNotFoundException { this.oauthStore = oauthStoreRoot; createModel(); loadConsumers(); } public FileSystemConsumerStore(Model model, String oauthStoreRoot) throws ConsumerStoreException { this.oauthStore = oauthStoreRoot; this.model = model; loadConsumers(); } protected void createModel() { try { model = FileManager.get().loadModel(this.oauthStore); } catch (Exception e) { model = ModelFactory.createDefaultModel(); } } protected void writeModel() throws FileNotFoundException { Model writeModel = ModelFactory.createDefaultModel(); writeModel.add(this.model); OutputStream os = new FileOutputStream(oauthStore); writeModel.write(os, FileUtils.langXMLAbbrev); } protected synchronized void loadConsumers() throws ConsumerStoreException { ResIterator i = model.listResourcesWithProperty(RDF.type, model.createResource(CONSUMER_RESOURCE)); while (i.hasNext()) { Resource consumerResource = i.next(); try { add(fromResource(consumerResource)); } catch (PropertyNotFoundException e) { // The resource is missing some properties. // Not good, but other consumer resources might // be OK, so continue. (Log the error, though.) logger.error( "Could not load consumer " + consumerResource.getProperty(model .createProperty(CONSUMER_NAME)) + " (" + consumerResource.getProperty(model .createProperty(CONSUMER_KEY)) + ")", e); } catch (UnsupportedEncodingException ue) { throw new ConsumerStoreException(ue); } catch (JenaException e) { // Some other runtime exception occurred. throw new ConsumerStoreException(e); } } } @Override public synchronized LyoOAuthConsumer addConsumer(final LyoOAuthConsumer consumer) throws ConsumerStoreException { if (model == null) { throw new ConsumerStoreException("Consumer store not initialized."); } try { removeProperties(consumer); toResource(consumer); LyoOAuthConsumer retConsumer = add(consumer); writeModel(); return retConsumer; } catch (UnsupportedEncodingException ue) { throw new ConsumerStoreException(ue); } catch (FileNotFoundException fe) { throw new ConsumerStoreException(fe); } } @Override public synchronized LyoOAuthConsumer removeConsumer(final String consumerKey) throws ConsumerStoreException { if (model == null) { throw new ConsumerStoreException("Consumer store not initialized."); } try { removeProperties(consumerKey); LyoOAuthConsumer retConsumer = remove(consumerKey); writeModel(); return retConsumer; } catch (FileNotFoundException fe) { throw new ConsumerStoreException(fe); } } @Override public LyoOAuthConsumer updateConsumer(LyoOAuthConsumer consumer) throws ConsumerStoreException { // addConsumer() also works for update. return addConsumer(consumer); } @Override public void closeConsumerStore() { try { writeModel(); } catch (Exception e) { logger.error("Error finalizing model to disk"); } this.model.close(); } /** * Removes any properties previously associated with the consumer. * * @param consumerKey * the consumer key */ protected void removeProperties(String consumerKey) { ResIterator i = model.listResourcesWithProperty( model.createProperty(CONSUMER_KEY), model.createLiteral(consumerKey)); while (i.hasNext()) { i.next().removeProperties(); } } /** * Removes any properties previously associated with the consumer. * * @param consumer the consumer */ protected void removeProperties(LyoOAuthConsumer consumer) { removeProperties(consumer.consumerKey); } protected Resource toResource(LyoOAuthConsumer consumer) throws UnsupportedEncodingException { Resource resource = model.createResource(); resource.addProperty(RDF.type, model.createResource(CONSUMER_RESOURCE)); resource.addProperty(model.createProperty(CONSUMER_NAME), consumer.getName()); resource.addProperty(model.createProperty(CONSUMER_KEY), consumer.consumerKey); String encodedSecret = new String(Base64.encodeBase64(consumer.consumerSecret.getBytes("UTF8")),"UTF8"); resource.addProperty(model.createProperty(CONSUMER_SECRET), encodedSecret); resource.addProperty(model.createProperty(PROVISIONAL), (consumer.isProvisional()) ? "true" : "false"); resource.addProperty(model.createProperty(TRUSTED), (consumer.isTrusted()) ? "true" : "false"); return resource; } protected LyoOAuthConsumer fromResource(Resource resource) throws UnsupportedEncodingException { String key = resource.getRequiredProperty( model.createProperty(CONSUMER_KEY)).getString(); String encodedSecret = resource.getRequiredProperty( model.createProperty(CONSUMER_SECRET)).getString(); String secret = new String(Base64.decodeBase64(encodedSecret.getBytes("UTF8")),"UTF8"); LyoOAuthConsumer consumer = new LyoOAuthConsumer(key, secret); consumer.setName(resource.getRequiredProperty( model.createProperty(CONSUMER_NAME)).getString()); String provisional = resource.getProperty( model.createProperty(PROVISIONAL)).getString(); consumer.setProvisional("true".equals(provisional)); String trusted = resource.getProperty(model.createProperty(TRUSTED)) .getString(); consumer.setTrusted("true".equals(trusted)); return consumer; } }