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.Collections;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.UUID;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.DocumentModelList;
036import org.nuxeo.ecm.directory.DirectoryException;
037import org.nuxeo.ecm.directory.Session;
038import org.nuxeo.ecm.directory.api.DirectoryService;
039import org.nuxeo.runtime.api.Framework;
040import org.nuxeo.runtime.model.DefaultComponent;
041
042/**
043 * Service implementation for {@link OAuthTokenStore}.
044 * <p>
045 * This service is responsible for managing storage of the {@link OAuthToken}. A simple SQL Directory is used for ACCESS
046 * Token whereas a simple in memory storage is used for REQUEST Tokens.
047 *
048 * @author tiry
049 */
050public class OAuthTokenStoreImpl extends DefaultComponent implements OAuthTokenStore {
051
052    protected static final Log log = LogFactory.getLog(OAuthTokenStoreImpl.class);
053
054    public static final String DIRECTORY_NAME = "oauthTokens";
055
056    protected Map<String, OAuthToken> requestTokenStore = new HashMap<String, OAuthToken>();
057
058    @Override
059    public OAuthToken addVerifierToRequestToken(String token, Long duration) {
060        NuxeoOAuthToken rToken = (NuxeoOAuthToken) getRequestToken(token);
061        if (rToken != null) {
062            rToken.verifier = "NX-VERIF-" + UUID.randomUUID().toString();
063            rToken.durationInMinutes = duration;
064        }
065        return rToken;
066    }
067
068    @Override
069    public OAuthToken createAccessTokenFromRequestToken(OAuthToken requestToken) {
070        NuxeoOAuthToken aToken = new NuxeoOAuthToken((NuxeoOAuthToken) requestToken);
071        String token = "NX-AT-" + UUID.randomUUID().toString();
072        aToken.token = token;
073        aToken.tokenSecret = "NX-ATS-" + UUID.randomUUID().toString();
074        aToken.type = OAuthToken.Type.ACCESS;
075
076        try {
077            aToken = storeAccessTokenAsDirectoryEntry(aToken);
078            removeRequestToken(requestToken.getToken());
079            return aToken;
080        } catch (DirectoryException e) {
081            log.error("Error during directory persistence", e);
082            return null;
083        }
084    }
085
086    @Override
087    public NuxeoOAuthToken getClientAccessToken(String appId, String owner) {
088        DirectoryService ds = Framework.getService(DirectoryService.class);
089        try (Session session = ds.open(DIRECTORY_NAME)) {
090            Map<String, Serializable> filter = new HashMap<String, Serializable>();
091            filter.put("appId", appId);
092            filter.put("clientId", owner);
093            filter.put("clientToken", 1);
094            DocumentModelList entries = session.query(filter);
095            if (entries.size() == 0) {
096                return null;
097            }
098            if (entries.size() > 1) {
099                log.error("Found several tokens");
100            }
101            return getTokenFromDirectoryEntry(entries.get(0));
102        }
103    }
104
105    @Override
106    public void removeClientAccessToken(String appId, String owner) {
107        DirectoryService ds = Framework.getService(DirectoryService.class);
108        try (Session session = ds.open(DIRECTORY_NAME)) {
109            Map<String, Serializable> filter = new HashMap<String, Serializable>();
110            filter.put("appId", appId);
111            filter.put("clientId", owner);
112            filter.put("clientToken", 1);
113            DocumentModelList entries = session.query(filter);
114            if (entries.size() == 0) {
115                return;
116            }
117            if (entries.size() > 1) {
118                log.error("Found several tokens");
119            }
120            session.deleteEntry(entries.get(0));
121        }
122    }
123
124    @Override
125    public void storeClientAccessToken(String consumerKey, String callBack, String token, String tokenSecret,
126            String appId, String owner) {
127        NuxeoOAuthToken aToken = new NuxeoOAuthToken(consumerKey, callBack);
128        aToken.token = token;
129        aToken.tokenSecret = tokenSecret;
130        if (appId != null) {
131            aToken.appId = appId;
132        }
133
134        aToken.clientToken = true;
135        aToken.clientId = owner;
136        try {
137            aToken = storeAccessTokenAsDirectoryEntry(aToken);
138        } catch (DirectoryException e) {
139            log.error("Error during directory persistence", e);
140        }
141    }
142
143    protected NuxeoOAuthToken getTokenFromDirectory(String token) {
144        DirectoryService ds = Framework.getService(DirectoryService.class);
145        try (Session session = ds.open(DIRECTORY_NAME)) {
146            DocumentModel entry = session.getEntry(token);
147            if (entry == null) {
148                return null;
149            }
150            return getTokenFromDirectoryEntry(entry);
151        }
152    }
153
154    protected NuxeoOAuthToken getTokenFromDirectoryEntry(DocumentModel entry) {
155        return new NuxeoOAuthToken(entry);
156    }
157
158    protected NuxeoOAuthToken storeAccessTokenAsDirectoryEntry(NuxeoOAuthToken aToken) {
159        DirectoryService ds = Framework.getService(DirectoryService.class);
160        try (Session session = ds.open(DIRECTORY_NAME)) {
161            DocumentModel entry = session.getEntry(aToken.getToken());
162            if (entry == null) {
163                entry = session.createEntry(Collections.singletonMap("token", aToken.getToken()));
164            }
165
166            aToken.updateEntry(entry);
167            session.updateEntry(entry);
168
169            return getTokenFromDirectoryEntry(session.getEntry(aToken.getToken()));
170        }
171    }
172
173    @Override
174    public OAuthToken createRequestToken(String consumerKey, String callBack) {
175
176        NuxeoOAuthToken rToken = new NuxeoOAuthToken(consumerKey, callBack);
177        String token = "NX-RT-" + consumerKey + "-" + UUID.randomUUID().toString();
178        rToken.token = token;
179        rToken.tokenSecret = "NX-RTS-" + consumerKey + UUID.randomUUID().toString();
180        rToken.type = OAuthToken.Type.REQUEST;
181        requestTokenStore.put(token, rToken);
182
183        return rToken;
184    }
185
186    @Override
187    public OAuthToken getAccessToken(String token) {
188
189        try {
190            return getTokenFromDirectory(token);
191        } catch (DirectoryException e) {
192            log.error("Error while accessing Token SQL storage", e);
193            return null;
194        }
195    }
196
197    @Override
198    public OAuthToken getRequestToken(String token) {
199        return requestTokenStore.get(token);
200    }
201
202    @Override
203    public List<OAuthToken> listAccessTokenForConsumer(String consumerKey) {
204        List<OAuthToken> result = new ArrayList<OAuthToken>();
205
206        DirectoryService ds = Framework.getService(DirectoryService.class);
207        try (Session session = ds.open(DIRECTORY_NAME)) {
208            Map<String, Serializable> filter = new HashMap<String, Serializable>();
209            filter.put("consumerKey", consumerKey);
210            filter.put("clientToken", 0);
211            DocumentModelList entries = session.query(filter);
212            for (DocumentModel entry : entries) {
213                result.add(new NuxeoOAuthToken(entry));
214            }
215        } catch (DirectoryException e) {
216            log.error("Error during token listing", e);
217        }
218        return result;
219    }
220
221    @Override
222    public List<OAuthToken> listAccessTokenForUser(String login) {
223        List<OAuthToken> result = new ArrayList<OAuthToken>();
224        DirectoryService ds = Framework.getService(DirectoryService.class);
225        try (Session session = ds.open(DIRECTORY_NAME)) {
226            Map<String, Serializable> filter = new HashMap<>();
227            filter.put("nuxeoLogin", login);
228            filter.put("clientToken", 0);
229            DocumentModelList entries = session.query(filter);
230            for (DocumentModel entry : entries) {
231                result.add(new NuxeoOAuthToken(entry));
232            }
233        } catch (DirectoryException e) {
234            log.error("Error during token listing", e);
235        }
236        return result;
237    }
238
239    @Override
240    public void removeAccessToken(String token) {
241        DirectoryService ds = Framework.getService(DirectoryService.class);
242        try (Session session = ds.open(DIRECTORY_NAME)) {
243            session.deleteEntry(token);
244        }
245    }
246
247    @Override
248    public void removeRequestToken(String token) {
249        requestTokenStore.remove(token);
250    }
251
252}