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.document.JaxbUtils;
41 import org.collectionspace.services.common.security.SecurityUtils;
42 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
43 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * AccountStorageClient deals with both Account and CSIP's
50 * state in persistent storage. The rationale behind creating this class is that
51 * this class manages pesistence for both account and CSIP's user. Transactions
52 * are used where possible to permorme the persistence operations atomically.
55 public class AccountStorageClient extends JpaStorageClientImpl {
57 private final Logger logger = LoggerFactory.getLogger(AccountStorageClient.class);
59 public AccountStorageClient() {
63 public String create(ServiceContext ctx,
64 DocumentHandler handler) throws BadRequestException,
68 throw new IllegalArgumentException(
69 "AccountStorageClient.create : ctx is missing");
71 if (handler == null) {
72 throw new IllegalArgumentException(
73 "AccountStorageClient.create: handler is missing");
75 EntityManagerFactory emf = null;
76 EntityManager em = null;
78 handler.prepare(Action.CREATE);
79 AccountsCommon account = (AccountsCommon) handler.getCommonPart();
80 DocumentWrapper<AccountsCommon> wrapDoc =
81 new DocumentWrapperImpl<AccountsCommon>(account);
82 handler.handle(Action.CREATE, wrapDoc);
83 emf = JpaStorageUtils.getEntityManagerFactory();
84 em = emf.createEntityManager();
85 em.getTransaction().begin();
86 //if userid and password are given, add to default id provider
87 if (account.getUserId() != null && account.getPassword() != null) {
88 User user = createUser(account);
91 // if (accountReceived.getTenant() != null) {
92 // UserTenant ut = createTenantAssoc(accountReceived);
95 account.setCreatedAtItem(new Date());
97 em.getTransaction().commit();
98 handler.complete(Action.CREATE, wrapDoc);
99 return (String) JaxbUtils.getValue(account, "getCsid");
100 } catch (BadRequestException bre) {
101 if (em != null && em.getTransaction().isActive()) {
102 em.getTransaction().rollback();
105 } catch (Exception e) {
106 if (em != null && em.getTransaction().isActive()) {
107 em.getTransaction().rollback();
109 if (logger.isDebugEnabled()) {
110 logger.debug("Caught exception ", e);
112 throw new DocumentException(e);
115 JpaStorageUtils.releaseEntityManagerFactory(emf);
121 public void update(ServiceContext ctx, String id, DocumentHandler handler)
122 throws BadRequestException, DocumentNotFoundException,
125 throw new IllegalArgumentException(
126 "AccountStorageClient.update : ctx is missing");
128 if (handler == null) {
129 throw new IllegalArgumentException(
130 "AccountStorageClient.update: handler is missing");
132 EntityManagerFactory emf = null;
133 EntityManager em = null;
135 handler.prepare(Action.UPDATE);
136 AccountsCommon accountReceived = (AccountsCommon) handler.getCommonPart();
137 emf = JpaStorageUtils.getEntityManagerFactory();
138 em = emf.createEntityManager();
139 em.getTransaction().begin();
140 AccountsCommon accountFound = getAccount(em, id);
141 checkAllowedUpdates(accountReceived, accountFound);
142 //if userid and password are given, add to default id provider
143 if (accountReceived.getUserId() != null
144 && hasPassword(accountReceived.getPassword())) {
145 updateUser(em, accountReceived);
147 DocumentWrapper<AccountsCommon> wrapDoc =
148 new DocumentWrapperImpl<AccountsCommon>(accountFound);
149 handler.handle(Action.UPDATE, wrapDoc);
150 em.getTransaction().commit();
151 handler.complete(Action.UPDATE, wrapDoc);
152 } catch (BadRequestException bre) {
153 if (em != null && em.getTransaction().isActive()) {
154 em.getTransaction().rollback();
157 } catch (DocumentException de) {
158 if (em != null && em.getTransaction().isActive()) {
159 em.getTransaction().rollback();
162 } catch (Exception e) {
163 if (logger.isDebugEnabled()) {
164 logger.debug("Caught exception ", e);
166 throw new DocumentException(e);
169 JpaStorageUtils.releaseEntityManagerFactory(emf);
175 public void delete(ServiceContext ctx, String id)
176 throws DocumentNotFoundException,
179 if (logger.isDebugEnabled()) {
180 logger.debug("deleting entity with id=" + id);
183 throw new IllegalArgumentException(
184 "AccountStorageClient.delete : ctx is missing");
186 EntityManagerFactory emf = null;
187 EntityManager em = null;
189 emf = JpaStorageUtils.getEntityManagerFactory();
190 em = emf.createEntityManager();
191 //TODO investigate if deep delete is possible
192 //query an delete is inefficient
193 AccountsCommon accountFound = getAccount(em, id);
195 //TODO: add tenant id
197 //if userid gives any indication about the id provider, it should
198 //be used to avoid the following approach
200 User userLocal = getUser(em, accountFound);
201 if (userLocal != null) {
202 StringBuilder usrDelStr = new StringBuilder("DELETE FROM ");
203 usrDelStr.append(User.class.getCanonicalName());
204 usrDelStr.append(" WHERE username = :username");
205 //TODO: add tenant id
206 usrDel = em.createQuery(usrDelStr.toString());
207 usrDel.setParameter("username", accountFound.getUserId());
209 em.getTransaction().begin();
211 if (userLocal != null) {
212 int usrDelCount = usrDel.executeUpdate();
213 if (usrDelCount != 1) {
214 if (em != null && em.getTransaction().isActive()) {
215 em.getTransaction().rollback();
217 String msg = "could not find user with username=" + accountFound.getUserId();
219 throw new DocumentNotFoundException(msg);
222 em.remove(accountFound);
223 em.getTransaction().commit();
225 } catch (DocumentException de) {
226 if (em != null && em.getTransaction().isActive()) {
227 em.getTransaction().rollback();
230 } catch (Exception e) {
231 if (logger.isDebugEnabled()) {
232 logger.debug("Caught exception ", e);
234 if (em != null && em.getTransaction().isActive()) {
235 em.getTransaction().rollback();
237 throw new DocumentException(e);
240 JpaStorageUtils.releaseEntityManagerFactory(emf);
245 private AccountsCommon getAccount(EntityManager em, String id) throws DocumentNotFoundException {
246 AccountsCommon accountFound = em.find(AccountsCommon.class, id);
247 if (accountFound == null) {
248 if (em != null && em.getTransaction().isActive()) {
249 em.getTransaction().rollback();
251 String msg = "could not find account with id=" + id;
253 throw new DocumentNotFoundException(msg);
258 private boolean checkAllowedUpdates(AccountsCommon toAccount, AccountsCommon fromAccount) throws BadRequestException {
259 if (!fromAccount.getUserId().equals(toAccount.getUserId())) {
260 String msg = "User id " + toAccount.getUserId() + " does not match "
261 + "for given account with csid=" + fromAccount.getCsid();
263 logger.debug(msg + " found userid=" + fromAccount.getUserId());
264 throw new BadRequestException(msg);
269 private User createUser(AccountsCommon account) throws Exception {
270 User user = new User();
271 user.setUsername(account.getUserId());
272 if (hasPassword(account.getPassword())) {
273 user.setPasswd(getEncPassword(account));
275 user.setCreatedAtItem(new Date());
279 private User getUser(EntityManager em, AccountsCommon account) throws DocumentNotFoundException {
280 User userFound = em.find(User.class, account.getUserId());
281 if (userFound == null) {
282 if (em != null && em.getTransaction().isActive()) {
283 em.getTransaction().rollback();
285 String msg = "could not find user with id=" + account.getUserId();
287 throw new DocumentNotFoundException(msg);
292 private void updateUser(EntityManager em, AccountsCommon account) throws Exception {
293 User userFound = getUser(em, account);
294 if (userFound != null) {
295 userFound.setPasswd(getEncPassword(account));
296 userFound.setUpdatedAtItem(new Date());
297 if (logger.isDebugEnabled()) {
298 logger.debug("updated user=" + JaxbUtils.toString(userFound, User.class));
300 em.persist(userFound);
304 private String getEncPassword(AccountsCommon account) throws BadRequestException {
305 //jaxb unmarshaller already unmarshal xs:base64Binary, no need to b64 decode
306 //byte[] bpass = Base64.decodeBase64(accountReceived.getPassword());
308 SecurityUtils.validatePassword(new String(account.getPassword()));
309 } catch (Exception e) {
310 throw new BadRequestException(e.getMessage());
312 String secEncPasswd = SecurityUtils.createPasswordHash(
313 account.getUserId(), new String(account.getPassword()));
317 private boolean hasPassword(byte[] bpass) {
318 return bpass != null && bpass.length > 0;
320 // private UserTenant createTenantAssoc(AccountsCommon accountReceived) {
321 // UserTenant userTenant = new UserTenant();
322 // userTenant.setUserId(accountReceived.getUserId());
323 // List<AccountsCommon.Tenant> atl = accountReceived.getTenant();
324 // List<UserTenant.Tenant> utl =
325 // new ArrayList<UserTenant.Tenant>();
326 // for (AccountsCommon.Tenant at : atl) {
327 // UserTenant.Tenant ut = new UserTenant.Tenant();
328 // ut.setId(at.getId());
329 // ut.setName(at.getName());
332 // userTenant.setTenant(utl);
333 // return userTenant;