001/*
002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.platform.oauth.tokens;
021
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.UUID;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.DocumentModelList;
033import org.nuxeo.ecm.directory.DirectoryException;
034import org.nuxeo.ecm.directory.Session;
035import org.nuxeo.ecm.directory.api.DirectoryService;
036import org.nuxeo.runtime.api.Framework;
037import org.nuxeo.runtime.model.DefaultComponent;
038
039/**
040 * Service implementation for {@link OAuthTokenStore}.
041 * <p>
042 * This service is responsible for managing storage of the {@link OAuthToken}. A simple SQL Directory is used for ACCESS
043 * Token whereas a simple in memory storage is used for REQUEST Tokens.
044 *
045 * @author tiry
046 */
047public class OAuthTokenStoreImpl extends DefaultComponent implements OAuthTokenStore {
048
049    protected static final Log log = LogFactory.getLog(OAuthTokenStoreImpl.class);
050
051    public static final String DIRECTORY_NAME = "oauthTokens";
052
053    protected Map<String, OAuthToken> requestTokenStore = new HashMap<String, OAuthToken>();
054
055    @Override
056    public OAuthToken addVerifierToRequestToken(String token, Long duration) {
057        NuxeoOAuthToken rToken = (NuxeoOAuthToken) getRequestToken(token);
058        if (rToken != null) {
059            rToken.verifier = "NX-VERIF-" + UUID.randomUUID().toString();
060            rToken.durationInMinutes = duration;
061        }
062        return rToken;
063    }
064
065    @Override
066    public OAuthToken createAccessTokenFromRequestToken(OAuthToken requestToken) {
067        NuxeoOAuthToken aToken = new NuxeoOAuthToken((NuxeoOAuthToken) requestToken);
068        String token = "NX-AT-" + UUID.randomUUID().toString();
069        aToken.token = token;
070        aToken.tokenSecret = "NX-ATS-" + UUID.randomUUID().toString();
071        aToken.type = OAuthToken.Type.ACCESS;
072
073        try {
074            aToken = storeAccessTokenAsDirectoryEntry(aToken);
075            removeRequestToken(requestToken.getToken());
076            return aToken;
077        } catch (DirectoryException e) {
078            log.error("Error during directory persistence", e);
079            return null;
080        }
081    }
082
083    @Override
084    public NuxeoOAuthToken getClientAccessToken(String appId, String owner) {
085        DirectoryService ds = Framework.getService(DirectoryService.class);
086        try (Session session = ds.open(DIRECTORY_NAME)) {
087            Map<String, Serializable> filter = new HashMap<String, Serializable>();
088            filter.put("appId", appId);
089            filter.put("clientId", owner);
090            filter.put("clientToken", 1);
091            DocumentModelList entries = session.query(filter);
092            if (entries.size() == 0) {
093                return null;
094            }
095            if (entries.size() > 1) {
096                log.error("Found several tokens");
097            }
098            return getTokenFromDirectoryEntry(entries.get(0));
099        }
100    }
101
102    @Override
103    public void removeClientAccessToken(String appId, String owner) {
104        DirectoryService ds = Framework.getService(DirectoryService.class);
105        try (Session session = ds.open(DIRECTORY_NAME)) {
106            Map<String, Serializable> filter = new HashMap<String, Serializable>();
107            filter.put("appId", appId);
108            filter.put("clientId", owner);
109            filter.put("clientToken", 1);
110            DocumentModelList entries = session.query(filter);
111            if (entries.size() == 0) {
112                return;
113            }
114            if (entries.size() > 1) {
115                log.error("Found several tokens");
116            }
117            session.deleteEntry(entries.get(0));
118        }
119    }
120
121    @Override
122    public void storeClientAccessToken(String consumerKey, String callBack, String token, String tokenSecret,
123            String appId, String owner) {
124        NuxeoOAuthToken aToken = new NuxeoOAuthToken(consumerKey, callBack);
125        aToken.token = token;
126        aToken.tokenSecret = tokenSecret;
127        if (appId != null) {
128            aToken.appId = appId;
129        }
130
131        aToken.clientToken = true;
132        aToken.clientId = owner;
133        try {
134            aToken = storeAccessTokenAsDirectoryEntry(aToken);
135        } catch (DirectoryException e) {
136            log.error("Error during directory persistence", e);
137        }
138    }
139
140    protected NuxeoOAuthToken getTokenFromDirectory(String token) {
141        DirectoryService ds = Framework.getService(DirectoryService.class);
142        try (Session session = ds.open(DIRECTORY_NAME)) {
143            DocumentModel entry = session.getEntry(token);
144            if (entry == null) {
145                return null;
146            }
147            return getTokenFromDirectoryEntry(entry);
148        }
149    }
150
151    protected NuxeoOAuthToken getTokenFromDirectoryEntry(DocumentModel entry) {
152        return new NuxeoOAuthToken(entry);
153    }
154
155    protected NuxeoOAuthToken storeAccessTokenAsDirectoryEntry(NuxeoOAuthToken aToken) {
156        DirectoryService ds = Framework.getService(DirectoryService.class);
157        try (Session session = ds.open(DIRECTORY_NAME)) {
158            DocumentModel entry = session.getEntry(aToken.getToken());
159            if (entry == null) {
160                Map<String, Object> init = new HashMap<String, Object>();
161                init.put("token", aToken.getToken());
162                entry = session.createEntry(init);
163            }
164
165            aToken.updateEntry(entry);
166            session.updateEntry(entry);
167
168            return getTokenFromDirectoryEntry(session.getEntry(aToken.getToken()));
169        }
170    }
171
172    @Override
173    public OAuthToken createRequestToken(String consumerKey, String callBack) {
174
175        NuxeoOAuthToken rToken = new NuxeoOAuthToken(consumerKey, callBack);
176        String token = "NX-RT-" + consumerKey + "-" + UUID.randomUUID().toString();
177        rToken.token = token;
178        rToken.tokenSecret = "NX-RTS-" + consumerKey + UUID.randomUUID().toString();
179        rToken.type = OAuthToken.Type.REQUEST;
180        requestTokenStore.put(token, rToken);
181
182        return rToken;
183    }
184
185    @Override
186    public OAuthToken getAccessToken(String token) {
187
188        try {
189            return getTokenFromDirectory(token);
190        } catch (DirectoryException e) {
191            log.error("Error while accessing Token SQL storage", e);
192            return null;
193        }
194    }
195
196    @Override
197    public OAuthToken getRequestToken(String token) {
198        return requestTokenStore.get(token);
199    }
200
201    @Override
202    public List<OAuthToken> listAccessTokenForConsumer(String consumerKey) {
203        List<OAuthToken> result = new ArrayList<OAuthToken>();
204
205        DirectoryService ds = Framework.getService(DirectoryService.class);
206        try (Session session = ds.open(DIRECTORY_NAME)) {
207            Map<String, Serializable> filter = new HashMap<String, Serializable>();
208            filter.put("consumerKey", consumerKey);
209            filter.put("clientToken", 0);
210            DocumentModelList entries = session.query(filter);
211            for (DocumentModel entry : entries) {
212                result.add(new NuxeoOAuthToken(entry));
213            }
214        } catch (DirectoryException e) {
215            log.error("Error during token listing", e);
216        }
217        return result;
218    }
219
220    @Override
221    public List<OAuthToken> listAccessTokenForUser(String login) {
222        List<OAuthToken> result = new ArrayList<OAuthToken>();
223        DirectoryService ds = Framework.getService(DirectoryService.class);
224        try (Session session = ds.open(DIRECTORY_NAME)) {
225            Map<String, Serializable> filter = new HashMap<>();
226            filter.put("nuxeoLogin", login);
227            filter.put("clientToken", 0);
228            DocumentModelList entries = session.query(filter);
229            for (DocumentModel entry : entries) {
230                result.add(new NuxeoOAuthToken(entry));
231            }
232        } catch (DirectoryException e) {
233            log.error("Error during token listing", e);
234        }
235        return result;
236    }
237
238    @Override
239    public void removeAccessToken(String token) {
240        DirectoryService ds = Framework.getService(DirectoryService.class);
241        try (Session session = ds.open(DIRECTORY_NAME)) {
242            session.deleteEntry(token);
243        }
244    }
245
246    @Override
247    public void removeRequestToken(String token) {
248        requestTokenStore.remove(token);
249    }
250
251}