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        return Framework.doPrivileged(() -> {
146            try (Session session = ds.open(DIRECTORY_NAME)) {
147                DocumentModel entry = session.getEntry(token);
148                if (entry == null) {
149                    return null;
150                }
151                return getTokenFromDirectoryEntry(entry);
152            }
153        });
154    }
155
156    protected NuxeoOAuthToken getTokenFromDirectoryEntry(DocumentModel entry) {
157        return new NuxeoOAuthToken(entry);
158    }
159
160    protected NuxeoOAuthToken storeAccessTokenAsDirectoryEntry(NuxeoOAuthToken aToken) {
161        DirectoryService ds = Framework.getService(DirectoryService.class);
162        return Framework.doPrivileged(() -> {
163            try (Session session = ds.open(DIRECTORY_NAME)) {
164                DocumentModel entry = session.getEntry(aToken.getToken());
165                if (entry == null) {
166                    entry = session.createEntry(Collections.singletonMap("token", aToken.getToken()));
167                }
168
169                aToken.updateEntry(entry);
170                session.updateEntry(entry);
171
172                return getTokenFromDirectoryEntry(session.getEntry(aToken.getToken()));
173            }
174        });
175    }
176
177    @Override
178    public OAuthToken createRequestToken(String consumerKey, String callBack) {
179
180        NuxeoOAuthToken rToken = new NuxeoOAuthToken(consumerKey, callBack);
181        String token = "NX-RT-" + consumerKey + "-" + UUID.randomUUID().toString();
182        rToken.token = token;
183        rToken.tokenSecret = "NX-RTS-" + consumerKey + UUID.randomUUID().toString();
184        rToken.type = OAuthToken.Type.REQUEST;
185        requestTokenStore.put(token, rToken);
186
187        return rToken;
188    }
189
190    @Override
191    public OAuthToken getAccessToken(String token) {
192
193        try {
194            return getTokenFromDirectory(token);
195        } catch (DirectoryException e) {
196            log.error("Error while accessing Token SQL storage", e);
197            return null;
198        }
199    }
200
201    @Override
202    public OAuthToken getRequestToken(String token) {
203        return requestTokenStore.get(token);
204    }
205
206    @Override
207    public List<OAuthToken> listAccessTokenForConsumer(String consumerKey) {
208        List<OAuthToken> result = new ArrayList<OAuthToken>();
209
210        DirectoryService ds = Framework.getService(DirectoryService.class);
211        try (Session session = ds.open(DIRECTORY_NAME)) {
212            Map<String, Serializable> filter = new HashMap<String, Serializable>();
213            filter.put("consumerKey", consumerKey);
214            filter.put("clientToken", 0);
215            DocumentModelList entries = session.query(filter);
216            for (DocumentModel entry : entries) {
217                result.add(new NuxeoOAuthToken(entry));
218            }
219        } catch (DirectoryException e) {
220            log.error("Error during token listing", e);
221        }
222        return result;
223    }
224
225    @Override
226    public List<OAuthToken> listAccessTokenForUser(String login) {
227        List<OAuthToken> result = new ArrayList<OAuthToken>();
228        DirectoryService ds = Framework.getService(DirectoryService.class);
229        try (Session session = ds.open(DIRECTORY_NAME)) {
230            Map<String, Serializable> filter = new HashMap<>();
231            filter.put("nuxeoLogin", login);
232            filter.put("clientToken", 0);
233            DocumentModelList entries = session.query(filter);
234            for (DocumentModel entry : entries) {
235                result.add(new NuxeoOAuthToken(entry));
236            }
237        } catch (DirectoryException e) {
238            log.error("Error during token listing", e);
239        }
240        return result;
241    }
242
243    @Override
244    public void removeAccessToken(String token) {
245        DirectoryService ds = Framework.getService(DirectoryService.class);
246        try (Session session = ds.open(DIRECTORY_NAME)) {
247            session.deleteEntry(token);
248        }
249    }
250
251    @Override
252    public void removeRequestToken(String token) {
253        requestTokenStore.remove(token);
254    }
255
256}