2 * This document is a part of the source code and related artifacts
3 * for CollectionSpace, an open source collections management system
4 * for museums and related institutions:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.account.storage;
26 import java.util.Date;
27 import javax.persistence.EntityManager;
28 import javax.persistence.EntityManagerFactory;
29 import javax.persistence.Query;
30 import org.apache.commons.codec.binary.Base64;
31 import org.collectionspace.services.account.AccountsCommon;
32 import org.collectionspace.services.authentication.User;
33 import org.collectionspace.services.common.context.ServiceContext;
34 import org.collectionspace.services.common.document.BadRequestException;
35 import org.collectionspace.services.common.document.DocumentException;
36 import org.collectionspace.services.common.document.DocumentHandler;
37 import org.collectionspace.services.common.document.DocumentHandler.Action;
38 import org.collectionspace.services.common.document.DocumentNotFoundException;
39 import org.collectionspace.services.common.document.DocumentWrapper;
40 import org.collectionspace.services.common.document.DocumentWrapperImpl;
41 import org.collectionspace.services.common.security.SecurityUtils;
42 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * AccountStorageClient deals with both Account and Default Identity Provider's
49 * state in persistent storage
52 public class AccountStorageClient extends JpaStorageClientImpl {
54 private final Logger logger = LoggerFactory.getLogger(AccountStorageClient.class);
56 public AccountStorageClient() {
60 public String create(ServiceContext ctx,
61 DocumentHandler handler) throws BadRequestException,
64 String docType = ctx.getDocumentType();
65 if (docType == null) {
66 throw new DocumentNotFoundException(
67 "Unable to find DocumentType for service " + ctx.getServiceName());
69 if (handler == null) {
70 throw new IllegalArgumentException(
71 "AccountStorageClient.create: handler is missing");
73 EntityManagerFactory emf = null;
74 EntityManager em = null;
76 handler.prepare(Action.CREATE);
77 AccountsCommon account = (AccountsCommon) handler.getCommonPart();
78 DocumentWrapper<AccountsCommon> wrapDoc =
79 new DocumentWrapperImpl<AccountsCommon>(account);
80 handler.handle(Action.CREATE, wrapDoc);
81 emf = getEntityManagerFactory();
82 em = emf.createEntityManager();
83 em.getTransaction().begin();
84 //if userid and password are given, add to default id provider
85 if (account.getUserId() != null && account.getPassword() != null) {
86 User user = createUser(account);
89 // if (account.getTenant() != null) {
90 // UserTenant ut = createTenantAssoc(account);
93 account.setCreatedAtItem(new Date());
95 em.getTransaction().commit();
96 handler.complete(Action.CREATE, wrapDoc);
97 return (String) getValue(account, "getCsid");
98 } catch (BadRequestException bre) {
99 if (em != null && em.getTransaction().isActive()) {
100 em.getTransaction().rollback();
103 } catch (Exception e) {
104 if (em != null && em.getTransaction().isActive()) {
105 em.getTransaction().rollback();
107 if (logger.isDebugEnabled()) {
108 logger.debug("Caught exception ", e);
110 throw new DocumentException(e);
113 releaseEntityManagerFactory(emf);
119 public void update(ServiceContext ctx, String id, DocumentHandler handler)
120 throws BadRequestException, DocumentNotFoundException,
122 String docType = ctx.getDocumentType();
123 if (docType == null) {
124 throw new DocumentNotFoundException(
125 "Unable to find DocumentType for service " + ctx.getServiceName());
127 if (handler == null) {
128 throw new IllegalArgumentException(
129 "AccountStorageClient.update: handler is missing");
131 EntityManagerFactory emf = null;
132 EntityManager em = null;
134 handler.prepare(Action.UPDATE);
135 AccountsCommon account = (AccountsCommon) handler.getCommonPart();
136 DocumentWrapper<AccountsCommon> wrapDoc =
137 new DocumentWrapperImpl<AccountsCommon>(account);
138 setCsid(account, id); //set id just in case it was not populated by consumer
139 handler.handle(Action.UPDATE, wrapDoc);
140 emf = getEntityManagerFactory();
141 em = emf.createEntityManager();
142 em.getTransaction().begin();
143 AccountsCommon accountFound = getAccount(em, id);
144 Date now = new Date();
145 checkAllowedUpdates(account, accountFound);
146 //if userid and password are given, add to default id provider
147 if (account.getUserId() != null && hasPassword(account.getPassword())) {
148 updateUser(em, account);
150 account = em.merge(account);
151 account.setUpdatedAtItem(now);
152 if (logger.isDebugEnabled()) {
153 logger.debug("merged account=" + account.toString());
155 em.getTransaction().commit();
156 handler.complete(Action.UPDATE, wrapDoc);
157 } catch (BadRequestException bre) {
158 if (em != null && em.getTransaction().isActive()) {
159 em.getTransaction().rollback();
162 } catch (DocumentException de) {
163 if (em != null && em.getTransaction().isActive()) {
164 em.getTransaction().rollback();
167 } catch (Exception e) {
168 if (logger.isDebugEnabled()) {
169 logger.debug("Caught exception ", e);
171 throw new DocumentException(e);
174 releaseEntityManagerFactory(emf);
180 public void delete(ServiceContext ctx, String id)
181 throws DocumentNotFoundException,
184 if (logger.isDebugEnabled()) {
185 logger.debug("deleting entity with id=" + id);
187 String docType = ctx.getDocumentType();
188 if (docType == null) {
189 throw new DocumentNotFoundException(
190 "Unable to find DocumentType for service " + ctx.getServiceName());
192 EntityManagerFactory emf = null;
193 EntityManager em = null;
195 emf = getEntityManagerFactory();
196 em = emf.createEntityManager();
197 //TODO investigate if deep delete is possible
198 //query an delete is inefficient
199 AccountsCommon accountFound = getAccount(em, id);
201 StringBuilder accDelStr = new StringBuilder("DELETE FROM ");
202 accDelStr.append(getEntityName(ctx));
203 accDelStr.append(" WHERE csid = :csid");
204 //TODO: add tenant id
205 Query accDel = em.createQuery(accDelStr.toString());
206 accDel.setParameter("csid", id);
207 //TODO: add tenant id
209 //if userid gives any indication about the id provider, it should
210 //be used to avoid the following approach
212 User userLocal = getUser(em, accountFound);
213 if (userLocal != null) {
214 StringBuilder usrDelStr = new StringBuilder("DELETE FROM ");
215 usrDelStr.append(User.class.getCanonicalName());
216 usrDelStr.append(" WHERE username = :username");
217 //TODO: add tenant id
218 usrDel = em.createQuery(usrDelStr.toString());
219 usrDel.setParameter("username", accountFound.getUserId());
221 em.getTransaction().begin();
222 // int accDelCount = accDel.executeUpdate();
223 // if (accDelCount != 1) {
224 // if (em != null && em.getTransaction().isActive()) {
225 // em.getTransaction().rollback();
228 if (userLocal != null) {
229 int usrDelCount = usrDel.executeUpdate();
230 if (usrDelCount != 1) {
231 if (em != null && em.getTransaction().isActive()) {
232 em.getTransaction().rollback();
235 if (usrDelCount != 1) {
236 String msg = "could not find user with username=" + accountFound.getUserId();
238 throw new DocumentNotFoundException(msg);
241 em.remove(accountFound);
242 em.getTransaction().commit();
244 } catch (DocumentException de) {
245 if (em != null && em.getTransaction().isActive()) {
246 em.getTransaction().rollback();
249 } catch (Exception e) {
250 if (logger.isDebugEnabled()) {
251 logger.debug("Caught exception ", e);
253 if (em != null && em.getTransaction().isActive()) {
254 em.getTransaction().rollback();
256 throw new DocumentException(e);
259 releaseEntityManagerFactory(emf);
264 private AccountsCommon getAccount(EntityManager em, String id) throws DocumentNotFoundException {
265 AccountsCommon accountFound = em.find(AccountsCommon.class, id);
266 if (accountFound == null) {
267 if (em != null && em.getTransaction().isActive()) {
268 em.getTransaction().rollback();
270 String msg = "could not find account with id=" + id;
272 throw new DocumentNotFoundException(msg);
277 private boolean checkAllowedUpdates(AccountsCommon toAccount, AccountsCommon fromAccount) throws BadRequestException {
278 if (!fromAccount.getUserId().equals(toAccount.getUserId())) {
279 String msg = "User id " + toAccount.getUserId() + " not found!";
281 logger.debug(msg + " found userid=" + fromAccount.getUserId());
282 throw new BadRequestException(msg);
287 private User createUser(AccountsCommon account) {
288 User user = new User();
289 user.setUsername(account.getUserId());
290 if (hasPassword(account.getPassword())) {
291 user.setPasswd(getEncPassword(account));
293 user.setCreatedAtItem(new Date());
297 private User getUser(EntityManager em, AccountsCommon account) throws DocumentNotFoundException {
298 User userFound = em.find(User.class, account.getUserId());
299 if (userFound == null) {
300 if (em != null && em.getTransaction().isActive()) {
301 em.getTransaction().rollback();
303 String msg = "could not find user with id=" + account.getUserId();
305 throw new DocumentNotFoundException(msg);
310 private void updateUser(EntityManager em, AccountsCommon account) throws Exception {
311 User userFound = getUser(em, account);
312 if (userFound != null) {
313 userFound.setPasswd(getEncPassword(account));
314 userFound.setUpdatedAtItem(new Date());
315 if (logger.isDebugEnabled()) {
316 logger.debug("updated user=" + userFound.toString());
318 em.persist(userFound);
322 private String getEncPassword(AccountsCommon account) {
323 byte[] bpass = Base64.decodeBase64(account.getPassword());
324 SecurityUtils.validatePassword(new String(bpass));
325 String secEncPasswd = SecurityUtils.createPasswordHash(
326 account.getUserId(), new String(bpass));
330 private boolean hasPassword(byte[] bpass) {
331 return bpass != null && bpass.length > 0;
333 // private UserTenant createTenantAssoc(AccountsCommon account) {
334 // UserTenant userTenant = new UserTenant();
335 // userTenant.setUserId(account.getUserId());
336 // List<AccountsCommon.Tenant> atl = account.getTenant();
337 // List<UserTenant.Tenant> utl =
338 // new ArrayList<UserTenant.Tenant>();
339 // for (AccountsCommon.Tenant at : atl) {
340 // UserTenant.Tenant ut = new UserTenant.Tenant();
341 // ut.setId(at.getId());
342 // ut.setName(at.getName());
345 // userTenant.setTenant(utl);
346 // return userTenant;