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 java.util.HashMap;
29 import org.collectionspace.services.account.AccountsCommon;
30 import org.collectionspace.services.account.storage.csidp.UserStorageClient;
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.DocumentFilter;
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.document.JaxbUtils;
42 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
43 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
44 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * AccountStorageClient deals with both Account and CSIdP's
51 * state in persistent storage. The rationale behind creating this class is that
52 * this class manages pesistence for both account and CSIP's user. Transactions
53 * are used where possible to permorme the persistence operations atomically.
56 @SuppressWarnings({ "rawtypes", "unchecked" })
57 public class AccountStorageClient extends JpaStorageClientImpl {
59 private final Logger logger = LoggerFactory.getLogger(AccountStorageClient.class);
60 private UserStorageClient userStorageClient = new UserStorageClient();
62 public AccountStorageClient() {
66 public String create(ServiceContext ctx,
67 DocumentHandler handler) throws BadRequestException,
71 AccountsCommon account = null;
72 JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();
74 account = (AccountsCommon) handler.getCommonPart();
75 handler.prepare(Action.CREATE);
76 DocumentWrapper<AccountsCommon> wrapDoc = new DocumentWrapperImpl<AccountsCommon>(account);
77 handler.handle(Action.CREATE, wrapDoc);
78 jpaConnectionContext.beginTransaction();
80 // If userid and password are given, add to default ID provider -i.e., add it to the Spring Security account list
82 if (account.getUserId() != null && isForCSpaceIdentityProvider(account.getPassword())) {
83 User user = userStorageClient.create(account.getUserId(), account.getPassword());
84 jpaConnectionContext.persist(user);
87 // Now add the account to the CSpace list of accounts
89 account.setCreatedAtItem(new Date());
90 jpaConnectionContext.persist(account);
92 // Finish creating related resources -e.g., account-role relationships
94 handler.complete(Action.CREATE, wrapDoc);
95 jpaConnectionContext.commitTransaction();
97 result = (String)JaxbUtils.getValue(account, "getCsid");
98 } catch (BadRequestException bre) {
99 jpaConnectionContext.markForRollback();
101 } catch (Exception e) {
102 if (logger.isDebugEnabled()) {
103 logger.debug("Caught exception ", e);
105 boolean uniqueConstraint = false;
106 if (userStorageClient.get(ctx, account.getUserId()) != null) {
107 //might be unique constraint violation
108 uniqueConstraint = true;
111 jpaConnectionContext.markForRollback();
113 if (uniqueConstraint) {
114 String msg = "UserId exists. Non unique userId=" + account.getUserId();
116 throw new BadRequestException(msg);
118 throw new DocumentException(e);
120 ctx.closeConnection();
127 public void get(ServiceContext ctx, String id, DocumentHandler handler)
128 throws DocumentNotFoundException, DocumentException {
129 DocumentFilter docFilter = handler.getDocumentFilter();
130 if (docFilter == null) {
131 docFilter = handler.createDocumentFilter();
134 JPATransactionContext jpaTransactionContext = (JPATransactionContext)ctx.openConnection();
136 handler.prepare(Action.GET);
138 String whereClause = " JOIN a.tenants as at where csid = :csid and at.tenantId = :tenantId";
139 HashMap<String, Object> params = new HashMap<String, Object>();
140 params.put("csid", id);
141 params.put("tenantId", ctx.getTenantId());
143 o = JpaStorageUtils.getEntity(jpaTransactionContext,
144 "org.collectionspace.services.account.AccountsCommon", whereClause, params);
146 String msg = "could not find entity with id=" + id;
147 throw new DocumentNotFoundException(msg);
149 DocumentWrapper<Object> wrapDoc = new DocumentWrapperImpl<Object>(o);
150 handler.handle(Action.GET, wrapDoc);
151 handler.complete(Action.GET, wrapDoc);
152 } catch (DocumentException de) {
154 } catch (Exception e) {
155 if (logger.isDebugEnabled()) {
156 logger.debug("Caught exception ", e);
158 throw new DocumentException(e);
160 ctx.closeConnection();
166 public void update(ServiceContext ctx, String id, DocumentHandler handler)
167 throws BadRequestException, DocumentNotFoundException,
170 JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();
172 jpaConnectionContext.beginTransaction();
174 handler.prepare(Action.UPDATE);
175 AccountsCommon accountReceived = (AccountsCommon) handler.getCommonPart();
176 AccountsCommon accountFound = getAccount(jpaConnectionContext, id);
177 checkAllowedUpdates(accountReceived, accountFound);
178 //if userid and password are given, add to default id provider
179 // Note that this ignores the immutable flag, as we allow
181 if (accountReceived.getUserId() != null && isForCSpaceIdentityProvider(accountReceived.getPassword())) {
182 userStorageClient.update(jpaConnectionContext,
183 accountReceived.getUserId(),
184 accountReceived.getPassword());
186 DocumentWrapper<AccountsCommon> wrapDoc =
187 new DocumentWrapperImpl<AccountsCommon>(accountFound);
188 handler.handle(Action.UPDATE, wrapDoc);
189 handler.complete(Action.UPDATE, wrapDoc);
190 jpaConnectionContext.commitTransaction();
192 // Don't sanitize until we've committed changes to the DB
194 handler.sanitize(wrapDoc);
195 } catch (BadRequestException bre) {
196 jpaConnectionContext.markForRollback();
198 } catch (DocumentException de) {
199 jpaConnectionContext.markForRollback();
201 } catch (Exception e) {
202 jpaConnectionContext.markForRollback();
203 throw new DocumentException(e);
205 ctx.closeConnection();
210 public void delete(ServiceContext ctx, String id)
211 throws DocumentNotFoundException,
214 if (logger.isDebugEnabled()) {
215 logger.debug("deleting entity with id=" + id);
218 JPATransactionContext jpaConnectionContext = (JPATransactionContext)ctx.openConnection();
220 AccountsCommon accountFound = getAccount(jpaConnectionContext, id);
221 jpaConnectionContext.beginTransaction();
222 //if userid gives any indication about the id provider, it should
223 //be used to avoid delete
224 userStorageClient.delete(jpaConnectionContext, accountFound.getUserId());
225 jpaConnectionContext.remove(accountFound);
226 jpaConnectionContext.commitTransaction();
227 } catch (DocumentException de) {
228 jpaConnectionContext.markForRollback();
230 } catch (Exception e) {
231 if (logger.isDebugEnabled()) {
232 logger.debug("Caught exception ", e);
234 jpaConnectionContext.markForRollback();
235 throw new DocumentException(e);
237 ctx.closeConnection();
241 private AccountsCommon getAccount(JPATransactionContext jpaConnectionContext, String id) throws DocumentNotFoundException {
242 AccountsCommon accountFound = (AccountsCommon) jpaConnectionContext.find(AccountsCommon.class, id);
243 if (accountFound == null) {
244 String msg = "could not find account with id=" + id;
246 throw new DocumentNotFoundException(msg);
252 private boolean checkAllowedUpdates(AccountsCommon toAccount, AccountsCommon fromAccount) throws BadRequestException {
253 if (!fromAccount.getUserId().equals(toAccount.getUserId())) {
254 String msg = "userId=" + toAccount.getUserId() + " of existing account does not match "
255 + "the userId=" + fromAccount.getUserId()
256 + " with csid=" + fromAccount.getCsid();
258 if (logger.isDebugEnabled()) {
259 logger.debug(msg + " found userid=" + fromAccount.getUserId());
261 throw new BadRequestException(msg);
267 * isForCSIdP deteremines if the create/update is also needed for CS IdP
271 private boolean isForCSpaceIdentityProvider(byte[] bpass) {
272 return bpass != null && bpass.length > 0;
274 // private UserTenant createTenantAssoc(AccountsCommon accountReceived) {
275 // UserTenant userTenant = new UserTenant();
276 // userTenant.setUserId(accountReceived.getUserId());
277 // List<AccountsCommon.Tenant> atl = accountReceived.getTenant();
278 // List<UserTenant.Tenant> utl =
279 // new ArrayList<UserTenant.Tenant>();
280 // for (AccountsCommon.Tenant at : atl) {
281 // UserTenant.Tenant ut = new UserTenant.Tenant();
282 // ut.setId(at.getId());
283 // ut.setName(at.getName());
286 // userTenant.setTenant(utl);
287 // return userTenant;