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.collectionspace.services.account.AccountsCommon;
31 import org.collectionspace.services.authentication.User;
32 import org.collectionspace.services.common.context.ServiceContext;
33 import org.collectionspace.services.common.document.BadRequestException;
34 import org.collectionspace.services.common.document.DocumentException;
35 import org.collectionspace.services.common.document.DocumentHandler;
36 import org.collectionspace.services.common.document.DocumentHandler.Action;
37 import org.collectionspace.services.common.document.DocumentNotFoundException;
38 import org.collectionspace.services.common.document.DocumentWrapper;
39 import org.collectionspace.services.common.document.DocumentWrapperImpl;
40 import org.collectionspace.services.common.security.SecurityUtils;
41 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
42 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
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,
65 throw new IllegalArgumentException(
66 "AccountStorageClient.create : ctx is missing");
68 if (handler == null) {
69 throw new IllegalArgumentException(
70 "AccountStorageClient.create: handler is missing");
72 EntityManagerFactory emf = null;
73 EntityManager em = null;
75 handler.prepare(Action.CREATE);
76 AccountsCommon account = (AccountsCommon) handler.getCommonPart();
77 DocumentWrapper<AccountsCommon> wrapDoc =
78 new DocumentWrapperImpl<AccountsCommon>(account);
79 handler.handle(Action.CREATE, wrapDoc);
80 emf = JpaStorageUtils.getEntityManagerFactory();
81 em = emf.createEntityManager();
82 em.getTransaction().begin();
83 //if userid and password are given, add to default id provider
84 if (account.getUserId() != null && account.getPassword() != null) {
85 User user = createUser(account);
88 // if (account.getTenant() != null) {
89 // UserTenant ut = createTenantAssoc(account);
92 account.setCreatedAtItem(new Date());
94 em.getTransaction().commit();
95 handler.complete(Action.CREATE, wrapDoc);
96 return (String) getValue(account, "getCsid");
97 } catch (BadRequestException bre) {
98 if (em != null && em.getTransaction().isActive()) {
99 em.getTransaction().rollback();
102 } catch (Exception e) {
103 if (em != null && em.getTransaction().isActive()) {
104 em.getTransaction().rollback();
106 if (logger.isDebugEnabled()) {
107 logger.debug("Caught exception ", e);
109 throw new DocumentException(e);
112 JpaStorageUtils.releaseEntityManagerFactory(emf);
118 public void update(ServiceContext ctx, String id, DocumentHandler handler)
119 throws BadRequestException, DocumentNotFoundException,
122 throw new IllegalArgumentException(
123 "AccountStorageClient.update : ctx is missing");
125 if (handler == null) {
126 throw new IllegalArgumentException(
127 "AccountStorageClient.update: handler is missing");
129 EntityManagerFactory emf = null;
130 EntityManager em = null;
132 handler.prepare(Action.UPDATE);
133 AccountsCommon account = (AccountsCommon) handler.getCommonPart();
134 DocumentWrapper<AccountsCommon> wrapDoc =
135 new DocumentWrapperImpl<AccountsCommon>(account);
136 setCsid(account, id); //set id just in case it was not populated by consumer
137 handler.handle(Action.UPDATE, wrapDoc);
138 emf = JpaStorageUtils.getEntityManagerFactory();
139 em = emf.createEntityManager();
140 em.getTransaction().begin();
141 AccountsCommon accountFound = getAccount(em, id);
142 Date now = new Date();
143 checkAllowedUpdates(account, accountFound);
144 //if userid and password are given, add to default id provider
145 if (account.getUserId() != null && hasPassword(account.getPassword())) {
146 updateUser(em, account);
148 account = em.merge(account);
149 account.setUpdatedAtItem(now);
150 if (logger.isDebugEnabled()) {
151 logger.debug("merged account=" + account.toString());
153 em.getTransaction().commit();
154 handler.complete(Action.UPDATE, wrapDoc);
155 } catch (BadRequestException bre) {
156 if (em != null && em.getTransaction().isActive()) {
157 em.getTransaction().rollback();
160 } catch (DocumentException de) {
161 if (em != null && em.getTransaction().isActive()) {
162 em.getTransaction().rollback();
165 } catch (Exception e) {
166 if (logger.isDebugEnabled()) {
167 logger.debug("Caught exception ", e);
169 throw new DocumentException(e);
172 JpaStorageUtils.releaseEntityManagerFactory(emf);
178 public void delete(ServiceContext ctx, String id)
179 throws DocumentNotFoundException,
182 if (logger.isDebugEnabled()) {
183 logger.debug("deleting entity with id=" + id);
186 throw new IllegalArgumentException(
187 "AccountStorageClient.delete : ctx is missing");
189 EntityManagerFactory emf = null;
190 EntityManager em = null;
192 emf = JpaStorageUtils.getEntityManagerFactory();
193 em = emf.createEntityManager();
194 //TODO investigate if deep delete is possible
195 //query an delete is inefficient
196 AccountsCommon accountFound = getAccount(em, id);
198 //TODO: add tenant id
200 //if userid gives any indication about the id provider, it should
201 //be used to avoid the following approach
203 User userLocal = getUser(em, accountFound);
204 if (userLocal != null) {
205 StringBuilder usrDelStr = new StringBuilder("DELETE FROM ");
206 usrDelStr.append(User.class.getCanonicalName());
207 usrDelStr.append(" WHERE username = :username");
208 //TODO: add tenant id
209 usrDel = em.createQuery(usrDelStr.toString());
210 usrDel.setParameter("username", accountFound.getUserId());
212 em.getTransaction().begin();
214 if (userLocal != null) {
215 int usrDelCount = usrDel.executeUpdate();
216 if (usrDelCount != 1) {
217 if (em != null && em.getTransaction().isActive()) {
218 em.getTransaction().rollback();
221 if (usrDelCount != 1) {
222 String msg = "could not find user with username=" + accountFound.getUserId();
224 throw new DocumentNotFoundException(msg);
227 em.remove(accountFound);
228 em.getTransaction().commit();
230 } catch (DocumentException de) {
231 if (em != null && em.getTransaction().isActive()) {
232 em.getTransaction().rollback();
235 } catch (Exception e) {
236 if (logger.isDebugEnabled()) {
237 logger.debug("Caught exception ", e);
239 if (em != null && em.getTransaction().isActive()) {
240 em.getTransaction().rollback();
242 throw new DocumentException(e);
245 JpaStorageUtils.releaseEntityManagerFactory(emf);
250 private AccountsCommon getAccount(EntityManager em, String id) throws DocumentNotFoundException {
251 AccountsCommon accountFound = em.find(AccountsCommon.class, id);
252 if (accountFound == null) {
253 if (em != null && em.getTransaction().isActive()) {
254 em.getTransaction().rollback();
256 String msg = "could not find account with id=" + id;
258 throw new DocumentNotFoundException(msg);
263 private boolean checkAllowedUpdates(AccountsCommon toAccount, AccountsCommon fromAccount) throws BadRequestException {
264 if (!fromAccount.getUserId().equals(toAccount.getUserId())) {
265 String msg = "User id " + toAccount.getUserId() + " does not match " +
266 "for given account with csid=" + fromAccount.getCsid();
268 logger.debug(msg + " found userid=" + fromAccount.getUserId());
269 throw new BadRequestException(msg);
274 private User createUser(AccountsCommon account) {
275 User user = new User();
276 user.setUsername(account.getUserId());
277 if (hasPassword(account.getPassword())) {
278 user.setPasswd(getEncPassword(account));
280 user.setCreatedAtItem(new Date());
284 private User getUser(EntityManager em, AccountsCommon account) throws DocumentNotFoundException {
285 User userFound = em.find(User.class, account.getUserId());
286 if (userFound == null) {
287 if (em != null && em.getTransaction().isActive()) {
288 em.getTransaction().rollback();
290 String msg = "could not find user with id=" + account.getUserId();
292 throw new DocumentNotFoundException(msg);
297 private void updateUser(EntityManager em, AccountsCommon account) throws Exception {
298 User userFound = getUser(em, account);
299 if (userFound != null) {
300 userFound.setPasswd(getEncPassword(account));
301 userFound.setUpdatedAtItem(new Date());
302 if (logger.isDebugEnabled()) {
303 logger.debug("updated user=" + userFound.toString());
305 em.persist(userFound);
309 private String getEncPassword(AccountsCommon account) {
310 //jaxb unmarshaller already unmarshal xs:base64Binary, no need to b64 decode
311 //byte[] bpass = Base64.decodeBase64(account.getPassword());
312 SecurityUtils.validatePassword(new String(account.getPassword()));
313 String secEncPasswd = SecurityUtils.createPasswordHash(
314 account.getUserId(), new String(account.getPassword()));
318 private boolean hasPassword(byte[] bpass) {
319 return bpass != null && bpass.length > 0;
321 // private UserTenant createTenantAssoc(AccountsCommon account) {
322 // UserTenant userTenant = new UserTenant();
323 // userTenant.setUserId(account.getUserId());
324 // List<AccountsCommon.Tenant> atl = account.getTenant();
325 // List<UserTenant.Tenant> utl =
326 // new ArrayList<UserTenant.Tenant>();
327 // for (AccountsCommon.Tenant at : atl) {
328 // UserTenant.Tenant ut = new UserTenant.Tenant();
329 // ut.setId(at.getId());
330 // ut.setName(at.getName());
333 // userTenant.setTenant(utl);
334 // return userTenant;