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