0001: /***************************************************************
0002: * This file is part of the [fleXive](R) project.
0003: *
0004: * Copyright (c) 1999-2008
0005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
0006: * All rights reserved
0007: *
0008: * The [fleXive](R) project is free software; you can redistribute
0009: * it and/or modify it under the terms of the GNU General Public
0010: * License as published by the Free Software Foundation;
0011: * either version 2 of the License, or (at your option) any
0012: * later version.
0013: *
0014: * The GNU General Public License can be found at
0015: * http://www.gnu.org/copyleft/gpl.html.
0016: * A copy is found in the textfile GPL.txt and important notices to the
0017: * license from the author are found in LICENSE.txt distributed with
0018: * these libraries.
0019: *
0020: * This library is distributed in the hope that it will be useful,
0021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0023: * GNU General Public License for more details.
0024: *
0025: * For further information about UCS - unique computing solutions gmbh,
0026: * please see the company website: http://www.ucs.at
0027: *
0028: * For further information about [fleXive](R), please see the
0029: * project website: http://www.flexive.org
0030: *
0031: *
0032: * This copyright notice MUST APPEAR in all copies of the file!
0033: ***************************************************************/package com.flexive.ejb.beans;
0034:
0035: import com.flexive.core.Database;
0036: import static com.flexive.core.DatabaseConst.*;
0037: import com.flexive.core.LifeCycleInfoImpl;
0038: import com.flexive.core.security.LoginLogoutHandler;
0039: import com.flexive.core.security.UserTicketStore;
0040: import com.flexive.shared.*;
0041: import com.flexive.shared.content.FxContent;
0042: import com.flexive.shared.content.FxPK;
0043: import com.flexive.shared.content.FxPermissionUtils;
0044: import com.flexive.shared.exceptions.*;
0045: import com.flexive.shared.interfaces.*;
0046: import com.flexive.shared.scripting.FxScriptBinding;
0047: import com.flexive.shared.scripting.FxScriptEvent;
0048: import com.flexive.shared.security.*;
0049: import com.flexive.shared.structure.FxType;
0050: import com.flexive.shared.value.FxString;
0051: import org.apache.commons.lang.ArrayUtils;
0052: import org.apache.commons.lang.RandomStringUtils;
0053: import org.apache.commons.logging.Log;
0054: import org.apache.commons.logging.LogFactory;
0055:
0056: import javax.annotation.Resource;
0057: import javax.ejb.*;
0058: import javax.sql.DataSource;
0059: import java.sql.*;
0060: import java.util.ArrayList;
0061: import java.util.Arrays;
0062: import java.util.Date;
0063: import java.util.List;
0064:
0065: /**
0066: * Account management
0067: *
0068: * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
0069: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
0070: */
0071: @Stateless(name="AccountEngine")
0072: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0073: @TransactionManagement(TransactionManagementType.CONTAINER)
0074: public class AccountEngineBean implements AccountEngine,
0075: AccountEngineLocal {
0076:
0077: private static transient Log LOG = LogFactory
0078: .getLog(AccountEngineBean.class);
0079: @Resource
0080: javax.ejb.SessionContext ctx;
0081:
0082: // Constant for the function checkPermissions(..)
0083: private static final int MAY_SET_ROLES = 1;
0084: // Constant for the function checkPermissions(..)
0085: private static final int MAY_SET_GROUPS = 2;
0086: // Constant for the function checkPermissions(..)
0087: private static final int MAY_UPDATE = 0;
0088:
0089: @EJB
0090: private UserGroupEngineLocal group;
0091: @EJB
0092: private LanguageEngineLocal language;
0093: @EJB
0094: private SequencerEngineLocal seq;
0095: @EJB
0096: private ContentEngineLocal co;
0097: @EJB
0098: private ScriptingEngineLocal scripting;
0099:
0100: /**
0101: * {@inheritDoc}
0102: */
0103: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0104: public List<UserTicket> getActiveUserTickets() {
0105: return UserTicketStore.getTickets();
0106: }
0107:
0108: /**
0109: * {@inheritDoc}
0110: */
0111: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0112: public void login(String username, String password, boolean takeOver)
0113: throws FxLoginFailedException, FxAccountInUseException {
0114: DataSource ds;
0115: try {
0116: ds = Database.getDataSource();
0117: } catch (Exception exc) {
0118: throw new FxLoginFailedException(exc.getMessage(),
0119: FxLoginFailedException.TYPE_SQL_ERROR);
0120: }
0121: LoginLogoutHandler.doLogin(username, password, takeOver, ctx,
0122: ds);
0123: }
0124:
0125: /**
0126: * {@inheritDoc}
0127: */
0128: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0129: public void logout() throws FxLogoutFailedException {
0130: LoginLogoutHandler.doLogout();
0131: }
0132:
0133: /**
0134: * Loads the account data from the database.
0135: *
0136: * @param loginName the unique name of the user, if null the accountId is used
0137: * @param accountId the unique id of the user to load
0138: * @param contactId the contact id
0139: * @return the Account object
0140: * @throws FxApplicationException if the user account could not be loaded
0141: */
0142: private Account load(String loginName, Long accountId,
0143: Long contactId) throws FxApplicationException {
0144: Statement stmt = null;
0145: String curSql;
0146: Connection con = null;
0147: try {
0148:
0149: con = Database.getDbConnection();
0150: // Load the account data
0151: curSql = "SELECT "
0152: +
0153: // 1, 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9
0154: "ID,EMAIL,CONTACT_ID,MANDATOR,VALID_FROM,VALID_TO,DESCRIPTION,USERNAME,LOGIN_NAME,"
0155: +
0156: //10 , 11 , 12 , 13 14 15 16
0157: "IS_ACTIVE,IS_VALIDATED,LANG,ALLOW_MULTILOGIN,UPDATETOKEN,CREATED_BY,CREATED_AT,"
0158: +
0159: //17 18
0160: "MODIFIED_BY,MODIFIED_AT FROM "
0161: + TBL_ACCOUNTS
0162: + " WHERE "
0163: + (loginName != null ? "LOGIN_NAME='" + loginName
0164: + "'" : "")
0165: + (accountId != null ? ((loginName != null) ? " AND "
0166: : "")
0167: + "ID=" + accountId
0168: : "")
0169: + (contactId != null ? ((loginName != null || accountId != null) ? " AND "
0170: : "")
0171: + "CONTACT_ID=" + contactId
0172: : "");
0173: stmt = con.createStatement();
0174: ResultSet rs = stmt.executeQuery(curSql);
0175: if (rs == null || !rs.next())
0176: throw new FxNotFoundException(LOG,
0177: "ex.account.notFound",
0178: (loginName != null ? loginName : accountId));
0179: long id = rs.getLong(1);
0180: String email = rs.getString(2);
0181: long contactDataId = rs.getLong(3);
0182: long mandator = rs.getLong(4);
0183: Date validFrom = new Date(rs.getLong(5));
0184: Date validTo = new Date(rs.getLong(6));
0185: String description = rs.getString(7);
0186: String name = rs.getString(8);
0187: String _loginName = rs.getString(9);
0188: boolean active = rs.getBoolean(10);
0189: boolean validated = rs.getBoolean(11);
0190: FxLanguage lang = language.load(rs.getInt(12));
0191: boolean bAllowMultiLogin = rs.getBoolean(13);
0192: String updateToken = rs.getString(14);
0193: Account ad = new Account(id, name, _loginName, mandator,
0194: email, lang, active, validated, validFrom, validTo,
0195: -1, description, contactDataId, bAllowMultiLogin,
0196: updateToken, LifeCycleInfoImpl.load(rs, 15, 16, 17,
0197: 18));
0198: if (LOG.isDebugEnabled())
0199: LOG.debug(ad.toString());
0200: return ad;
0201: } catch (SQLException exc) {
0202: throw new FxLoadException(LOG, "ex.account.loadFailed.sql",
0203: (loginName != null ? loginName : accountId), exc
0204: .getMessage());
0205: } finally {
0206: Database.closeObjects(AccountEngineBean.class, con, stmt);
0207: }
0208: }
0209:
0210: /**
0211: * {@inheritDoc}
0212: */
0213: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0214: public Account load(final long id) throws FxApplicationException {
0215: return load(null, id, null);
0216: }
0217:
0218: /**
0219: * {@inheritDoc}
0220: */
0221: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0222: public Account load(final String loginName)
0223: throws FxApplicationException {
0224: return load(loginName, null, null);
0225: }
0226:
0227: /**
0228: * {@inheritDoc}
0229: */
0230: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0231: public Account loadForContactData(FxPK contactDataPK)
0232: throws FxApplicationException {
0233: return load(null, null, contactDataPK.getId());
0234: }
0235:
0236: /**
0237: * {@inheritDoc}
0238: */
0239: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0240: public UserTicket getUserTicket() {
0241: return UserTicketStore.getTicket();
0242: }
0243:
0244: /**
0245: * {@inheritDoc}
0246: */
0247: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0248: public ArrayList<UserGroup> getGroupList(long accountId)
0249: throws FxApplicationException {
0250: UserGroupList gl = getGroups(accountId);
0251: ArrayList<UserGroup> result = new ArrayList<UserGroup>(gl
0252: .size());
0253: for (long id : gl.toLongArray())
0254: result.add(group.load(id));
0255: return result;
0256: }
0257:
0258: /**
0259: * {@inheritDoc}
0260: */
0261: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0262: public UserGroupList getGroups(long accountId)
0263: throws FxApplicationException {
0264:
0265: Connection con = null;
0266: Statement stmt = null;
0267: String curSql;
0268: final UserTicket ticket = UserTicketStore.getTicket(false);
0269: try {
0270: // Obtain a database connection
0271: con = Database.getDbConnection();
0272:
0273: // Check the user
0274: long mandator = getMandatorForAccount(con, accountId);
0275: // Permission checks (2)
0276: if (!ticket.isGlobalSupervisor()) {
0277: // Read access for all within a mandator
0278: if (mandator != ticket.getMandatorId())
0279: throw new FxNoAccessException(LOG,
0280: "ex.account.groups.wrongMandator",
0281: accountId);
0282: }
0283:
0284: // Load the groups the account is assigned to
0285: curSql = "SELECT DISTINCT ass.USERGROUP,grp.NAME,grp.MANDATOR,grp.COLOR"
0286: + " FROM "
0287: + TBL_ASSIGN_GROUPS
0288: + " ass, "
0289: + TBL_GROUP
0290: + " grp"
0291: + " WHERE grp.ID=ass.USERGROUP AND ass.ACCOUNT="
0292: + accountId;
0293: stmt = con.createStatement();
0294: ResultSet rs = stmt.executeQuery(curSql);
0295: ArrayList<UserGroup> tmp = new ArrayList<UserGroup>(10);
0296: while (rs != null && rs.next()) {
0297: long id = rs.getLong(1);
0298: String grpName = rs.getString(2);
0299: long grpMandator = rs.getLong(3);
0300: String grpColor = rs.getString(4);
0301: final UserGroup aGroup = new UserGroup(id, grpName,
0302: grpMandator, grpColor);
0303: if (LOG.isDebugEnabled())
0304: LOG.debug("Found group for user [" + accountId
0305: + "]: " + aGroup);
0306: tmp.add(aGroup);
0307: }
0308: // Return as array
0309: return new UserGroupList(tmp.toArray(new UserGroup[tmp
0310: .size()]));
0311: } catch (SQLException exc) {
0312: throw new FxLoadException(LOG, exc,
0313: "ex.account.groups.loadFailed.sql", accountId, exc
0314: .getMessage());
0315: } finally {
0316: Database.closeObjects(AccountEngineBean.class, con, stmt);
0317: }
0318:
0319: }
0320:
0321: /**
0322: * Internal helper function, returns the mandator of an account or throws a FxNotFoundException if the user does
0323: * not exist.
0324: *
0325: * @param con a already opened connection, will not be closed by the function
0326: * @param accountId the account to check for
0327: * @return the mandator of the given account
0328: * @throws FxNotFoundException if the account does not exist
0329: * @throws SQLException if the function failed because od a sql error
0330: */
0331: private long getMandatorForAccount(final Connection con,
0332: final long accountId) throws FxNotFoundException,
0333: SQLException {
0334: Statement stmt = null;
0335: try {
0336: String curSql = "SELECT MANDATOR FROM " + TBL_ACCOUNTS
0337: + " WHERE ID=" + accountId;
0338: stmt = con.createStatement();
0339: ResultSet rs = stmt.executeQuery(curSql);
0340: long mandator = -1;
0341: if (rs != null && rs.next())
0342: mandator = rs.getLong(1);
0343: if (mandator == -1)
0344: throw new FxNotFoundException(LOG,
0345: "ex.account.notFound", accountId);
0346: return mandator;
0347: } finally {
0348: Database.closeObjects(AccountEngineBean.class, null, stmt);
0349: }
0350: }
0351:
0352: /**
0353: * {@inheritDoc}
0354: */
0355: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0356: public List<Role> getRoleList(long accountId, RoleLoadMode mode)
0357: throws FxApplicationException {
0358: return Arrays.asList(getRoles(accountId, mode));
0359: }
0360:
0361: /**
0362: * {@inheritDoc}
0363: */
0364: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
0365: public Role[] getRoles(long accountId, RoleLoadMode mode)
0366: throws FxApplicationException {
0367: Connection con = null;
0368: Statement stmt = null;
0369: String curSql;
0370: final UserTicket ticket = UserTicketStore.getTicket(false);
0371:
0372: try {
0373: // Obtain a database connection
0374: con = Database.getDbConnection();
0375:
0376: // Check the user
0377: long mandator = getMandatorForAccount(con, accountId);
0378: // Permission checks (2)
0379: if (!ticket.isGlobalSupervisor()) {
0380: if (mandator != ticket.getMandatorId())
0381: throw new FxNoAccessException(LOG,
0382: "ex.account.roles.wrongMandator", accountId);
0383: }
0384:
0385: // Load the roles
0386: curSql = "SELECT DISTINCT ROLE FROM "
0387: + TBL_ASSIGN_ROLES
0388: + " WHERE "
0389: + ((mode == RoleLoadMode.ALL || mode == RoleLoadMode.FROM_USER_ONLY) ? "ACCOUNT="
0390: + accountId
0391: : "")
0392: + ((mode == RoleLoadMode.ALL) ? " OR " : "")
0393: + ((mode == RoleLoadMode.ALL || mode == RoleLoadMode.FROM_GROUPS_ONLY) ? " USERGROUP IN (SELECT USERGROUP FROM "
0394: + TBL_ASSIGN_GROUPS
0395: + " WHERE ACCOUNT="
0396: + accountId + " )"
0397: : "");
0398: stmt = con.createStatement();
0399: ResultSet rs = stmt.executeQuery(curSql);
0400: ArrayList<Byte> tmp = new ArrayList<Byte>(15);
0401: while (rs != null && rs.next())
0402: tmp.add(rs.getByte(1));
0403: Role[] roles = new Role[tmp.size()];
0404: for (int i = 0; i < tmp.size(); i++)
0405: roles[i] = Role.getById(tmp.get(i));
0406: if (LOG.isDebugEnabled())
0407: LOG.debug("Role for user [" + accountId + "]: "
0408: + FxArrayUtils.toSeparatedList(roles, ','));
0409: return roles;
0410: } catch (SQLException exc) {
0411: throw new FxLoadException(LOG, exc,
0412: "ex.account.roles.loadFailed.sql", accountId, exc
0413: .getMessage());
0414: } finally {
0415: Database.closeObjects(AccountEngineBean.class, con, stmt);
0416: }
0417: }
0418:
0419: /**
0420: * {@inheritDoc}
0421: */
0422: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0423: public long create(String userName, String loginName,
0424: String password, String email, long lang, long mandatorId,
0425: boolean isActive, boolean isConfirmed, Date validFrom,
0426: Date validTo, long defaultNode, String description,
0427: boolean allowMultiLogin, boolean checkUserRoles)
0428: throws FxApplicationException {
0429:
0430: final UserTicket ticket = FxContext.get().getTicket();
0431: // Security
0432: if (checkUserRoles && !ticket.isGlobalSupervisor()) {
0433: if (!ticket.isInRole(Role.MandatorSupervisor))
0434: FxPermissionUtils.checkRole(ticket,
0435: Role.AccountManagement);
0436: if (ticket.getMandatorId() != mandatorId)
0437: throw new FxNoAccessException(LOG,
0438: "ex.account.create.wrongMandator");
0439: }
0440:
0441: // Parameter checks
0442: try {
0443: CacheAdmin.getEnvironment().getMandator(mandatorId);
0444: loginName = checkLoginName(loginName);
0445: userName = checkUserName(userName);
0446: checkDates(validFrom, validTo);
0447: if (description == null)
0448: description = "";
0449: email = FxFormatUtils.isEmail(email);
0450: if (!language.isValid(lang))
0451: throw new FxInvalidParameterException("LANGUAGE",
0452: "ex.account.languageInvalid", lang);
0453: } catch (FxInvalidParameterException pe) {
0454: if (LOG.isInfoEnabled())
0455: LOG.info(pe);
0456: throw pe;
0457: }
0458:
0459: Connection con = null;
0460: PreparedStatement stmt = null;
0461: String curSql;
0462: FxPK contactDataPK;
0463: try {
0464: final FxContext ri = FxContext.get();
0465: final FxContent contactData;
0466: try {
0467: if (!checkUserRoles) {
0468: // we're probably running in unprivileged code, but application logic
0469: // already determined that a user account should be created
0470: ri.runAsSystem();
0471: }
0472: contactData = co.initialize(CacheAdmin.getEnvironment()
0473: .getType(FxType.CONTACTDATA).getId());
0474: } finally {
0475: if (!checkUserRoles) {
0476: ri.stopRunAsSystem();
0477: }
0478: }
0479: contactData.setAclId(ACL.ACL_CONTACTDATA);
0480: contactData.setValue("/SURNAME", new FxString(false,
0481: userName));
0482: contactData.setValue("/EMAIL", new FxString(false, email));
0483: contactDataPK = co.save(contactData);
0484:
0485: // Obtain a database connection
0486: con = Database.getDbConnection();
0487:
0488: // Get a new Id
0489: long newId = seq.getId(SequencerEngine.System.ACCOUNT);
0490:
0491: password = password == null ? "" : FxFormatUtils
0492: .encodePassword(newId, password);
0493: curSql = "INSERT INTO "
0494: + TBL_ACCOUNTS
0495: + "("
0496: +
0497: //1 2 3 4 5 6 7 8 9
0498: "ID,MANDATOR,USERNAME,LOGIN_NAME,PASSWORD,EMAIL,CONTACT_ID,LANG,VALID_FROM,"
0499: +
0500: //10 11 12 13 14
0501: "VALID_TO,DESCRIPTION,CREATED_BY,CREATED_AT,MODIFIED_BY,"
0502: +
0503: //15 16 17 18 19 20
0504: "MODIFIED_AT,IS_ACTIVE,IS_VALIDATED,DEFAULT_NODE,ALLOW_MULTILOGIN,UPDATETOKEN) values "
0505: +
0506: //1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
0507: "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
0508:
0509: final long NOW = System.currentTimeMillis();
0510: stmt = con.prepareStatement(curSql);
0511: stmt.setLong(1, newId);
0512: stmt.setLong(2, mandatorId);
0513: stmt.setString(3, userName);
0514: stmt.setString(4, loginName);
0515: stmt.setString(5, password);
0516: stmt.setString(6, email);
0517: stmt.setLong(7, contactDataPK.getId());
0518: stmt.setInt(8, (int) lang);
0519: //subtract a second to prevent login-after-immediatly-create problems with databases truncating milliseconds
0520: stmt.setLong(9, validFrom.getTime() - 1000);
0521: stmt.setLong(10, validTo.getTime());
0522: stmt.setString(11, description);
0523: stmt.setLong(12, ticket.getUserId());
0524: stmt.setLong(13, NOW);
0525: stmt.setLong(14, ticket.getUserId());
0526: stmt.setLong(15, NOW);
0527: stmt.setBoolean(16, isActive);
0528: stmt.setBoolean(17, isConfirmed);
0529: stmt.setLong(18, (defaultNode < 0) ? 0 : defaultNode);
0530: stmt.setBoolean(19, allowMultiLogin);
0531: stmt
0532: .setString(20, RandomStringUtils
0533: .randomAlphanumeric(64));
0534: stmt.executeUpdate();
0535:
0536: //make sure the user is the owner of his contact data
0537: stmt.close();
0538: stmt = con.prepareStatement("UPDATE " + TBL_CONTENT
0539: + " SET CREATED_BY=? WHERE ID=?");
0540: stmt.setLong(1, newId);
0541: stmt.setLong(2, contactDataPK.getId());
0542: stmt.executeUpdate();
0543:
0544: // call scripts
0545: final List<Long> scriptIds = scripting
0546: .getByScriptType(FxScriptEvent.AfterAccountCreate);
0547: final FxScriptBinding binding = new FxScriptBinding();
0548: binding.setVariable("accountId", newId);
0549: binding.setVariable("pk", contactDataPK);
0550: for (long scriptId : scriptIds)
0551: scripting.runScript(scriptId, binding);
0552:
0553: // EVERY users is a member of group EVERYONE and his mandator
0554: final long[] groups = { UserGroup.GROUP_EVERYONE,
0555: group.loadMandatorGroup(mandatorId).getId() };
0556: ri.runAsSystem();
0557: try {
0558: setGroups(newId, groups);
0559: } catch (Exception exc) {
0560: throw new FxCreateException(exc,
0561: "ex.account.create.everyoneAssignFailed", exc
0562: .getMessage());
0563: } finally {
0564: ri.stopRunAsSystem();
0565: }
0566:
0567: // Return the id
0568: return newId;
0569: } catch (SQLException exc) {
0570: final boolean uniqueConstraintViolation = Database
0571: .isUniqueConstraintViolation(exc);
0572: ctx.setRollbackOnly();
0573: if (uniqueConstraintViolation) {
0574: throw new FxEntryExistsException(LOG, exc,
0575: "ex.account.userExists", loginName, userName);
0576: } else {
0577: throw new FxCreateException(LOG, exc,
0578: "ex.account.createFailed.sql", loginName, exc
0579: .getMessage());
0580: }
0581: } finally {
0582: Database.closeObjects(AccountEngineBean.class, con, stmt);
0583: }
0584: }
0585:
0586: private static String checkLoginName(String name)
0587: throws FxInvalidParameterException {
0588: if (name == null || name.length() == 0)
0589: throw new FxInvalidParameterException("loginName",
0590: "ex.account.login.noName");
0591: if (name.length() > 255)
0592: throw new FxInvalidParameterException("loginName",
0593: "ex.account.login.nameTooLong", 255);
0594: return name.trim();
0595: }
0596:
0597: private static String checkUserName(String name)
0598: throws FxInvalidParameterException {
0599: if (name == null || name.length() == 0)
0600: throw new FxInvalidParameterException("name",
0601: "ex.account.name.noName");
0602: if (name.length() > 255)
0603: throw new FxInvalidParameterException("name",
0604: "ex.account.name.nameTooLong", 255);
0605: return name.trim();
0606: }
0607:
0608: /**
0609: * Checks the dates.
0610: *
0611: * @param validFrom the from date
0612: * @param validTo the to date
0613: * @throws FxInvalidParameterException if the dates are invalid
0614: */
0615: private static void checkDates(final Date validFrom,
0616: final Date validTo) throws FxInvalidParameterException {
0617: if (validFrom == null)
0618: throw new FxInvalidParameterException("VALID_FROM",
0619: "ex.account.login.validFrom.missing");
0620: if (validTo == null)
0621: throw new FxInvalidParameterException("VALID_FROM",
0622: "ex.account.login.validTo.missing");
0623: if (validFrom.getTime() > validTo.getTime())
0624: throw new FxInvalidParameterException("VALID_FROM",
0625: "ex.account.login.dateMismatch");
0626: if (validTo.getTime() < java.lang.System.currentTimeMillis())
0627: throw new FxInvalidParameterException("VALID_TO",
0628: "ex.account.login.dateExpired");
0629: }
0630:
0631: /**
0632: * {@inheritDoc}
0633: */
0634: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0635: public void remove(long accountId) throws FxApplicationException {
0636: // Handle protected users
0637: if (accountId == Account.USER_GUEST)
0638: throw new FxNoAccessException("ex.account.delete.guest");
0639: if (accountId == Account.USER_GLOBAL_SUPERVISOR)
0640: throw new FxNoAccessException(
0641: "ex.account.delete.supervisor");
0642: if (accountId == Account.NULL_ACCOUNT)
0643: throw new FxNoAccessException(
0644: "ex.account.delete.nullAccount");
0645:
0646: Account account = load(accountId);
0647:
0648: // Permission checks
0649: final UserTicket ticket = FxContext.get().getTicket();
0650: if (!ticket.isGlobalSupervisor()) {
0651: if (!ticket.isInRole(Role.MandatorSupervisor))
0652: FxPermissionUtils.checkRole(ticket,
0653: Role.AccountManagement);
0654: if (account.getMandatorId() != ticket.getMandatorId())
0655: throw new FxNoAccessException(LOG,
0656: "ex.account.delete.wrongMandator");
0657: }
0658:
0659: Connection con = null;
0660: Statement stmt = null;
0661: String curSql = null;
0662: try {
0663: con = Database.getDbConnection();
0664: // Delete all group assignments ..
0665: stmt = con.createStatement();
0666: curSql = "DELETE FROM " + TBL_ASSIGN_GROUPS
0667: + " WHERE ACCOUNT=" + accountId;
0668: stmt.executeUpdate(curSql);
0669: stmt.close();
0670:
0671: // Delete all role assigments ..
0672: stmt = con.createStatement();
0673: curSql = "DELETE FROM " + TBL_ASSIGN_ROLES
0674: + " WHERE ACCOUNT=" + accountId;
0675: stmt.executeUpdate(curSql);
0676: stmt.close();
0677:
0678: // TODO: delete user specific configurations
0679: // TODO: delete user specific locks
0680:
0681: // Finally remove the user itself
0682: stmt = con.createStatement();
0683: curSql = "DELETE FROM " + TBL_ACCOUNTS + " WHERE ID="
0684: + accountId;
0685: stmt.executeUpdate(curSql);
0686:
0687: // Log the user out of the system (if he is active)
0688: // This is done after the commit, since a failure should not block the user delete action
0689: UserTicketStore.removeUserId(accountId, null);
0690:
0691: // delete contact data
0692: co.remove(account.getContactData());
0693: } catch (SQLException exc) {
0694: ctx.setRollbackOnly();
0695: throw new FxRemoveException(LOG, exc,
0696: "ex.account.delete.failed.sql", accountId, exc
0697: .getMessage(), curSql);
0698: } finally {
0699: Database.closeObjects(AccountEngineBean.class, con, stmt);
0700: }
0701: }
0702:
0703: /**
0704: * Returns wether the caller may use the operations 'edit','setRoles','setGroups'
0705: * on a specific user.
0706: *
0707: * @param accountId the id of the user to check for
0708: * @return a boolean array indicating if a action may be called<br>
0709: * position <code>MAY_UPDATE</code>: update the user<br>
0710: * position <code>MAY_SET_ROLES</code>: set roles for the user<br>
0711: * position <code>MAY_SET_GROUPS</code>: set groups for the user
0712: * @throws FxApplicationException on errors
0713: */
0714: private boolean[] checkPermissions(long accountId)
0715: throws FxApplicationException {
0716: return _checkPermissions(load(accountId));
0717: }
0718:
0719: /**
0720: * Returns wether the caller may use the operations 'edit','setRoles','setGroups'
0721: * on a specific account.
0722: *
0723: * @param account the user to check the operations for
0724: * @return a boolean array indicating if a action may be called<br>
0725: * position MAY_UPDATE: update the user<br>
0726: * position MAY_SET_ROLES: set roles for the user<br>
0727: * position MAY_SET_GROUPS: set groups for the user
0728: */
0729: private static boolean[] _checkPermissions(Account account) {
0730: final UserTicket ticket = FxContext.get().getTicket();
0731: if (ticket.isGlobalSupervisor()
0732: || (ticket.getMandatorId() == account.getMandatorId() && ticket
0733: .isMandatorSupervisor())) {
0734: return new boolean[] { true, true, true }; // full access
0735: }
0736:
0737: boolean edit = true;
0738: boolean roles = true;
0739: boolean groups = true;
0740:
0741: // Edit access
0742: if (!ticket.isInRole(Role.AccountManagement))
0743: edit = false;
0744: if (ticket.getMandatorId() != account.getMandatorId())
0745: edit = false;
0746:
0747: // Update roles access
0748: if (!ticket.isInRole(Role.AccountManagement))
0749: roles = false;
0750: if (ticket.getMandatorId() != account.getMandatorId())
0751: roles = false;
0752:
0753: // Update groups access. Specified via AccountManagement
0754: if (!ticket.isInRole(Role.AccountManagement))
0755: groups = false;
0756: if (ticket.getMandatorId() != account.getMandatorId())
0757: groups = false;
0758:
0759: boolean result[] = new boolean[3];
0760: result[MAY_SET_GROUPS] = groups;
0761: result[MAY_SET_ROLES] = roles;
0762: result[MAY_UPDATE] = edit;
0763: return result;
0764: }
0765:
0766: /**
0767: * {@inheritDoc}
0768: */
0769: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0770: public void setGroupList(long accountId, List<UserGroup> groups)
0771: throws FxApplicationException {
0772: if (groups != null && groups.size() > 0) {
0773: long groupIds[] = new long[groups.size()];
0774: int pos = 0;
0775: for (UserGroup group : groups) {
0776: groupIds[pos++] = group.getId();
0777: }
0778: setGroups(accountId, groupIds);
0779: } else {
0780: setGroups(accountId, new long[0]);
0781: }
0782: }
0783:
0784: /**
0785: * {@inheritDoc}
0786: */
0787: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0788: public void setGroups(long accountId, long[] groups)
0789: throws FxApplicationException {
0790: // Handle null params
0791: if (groups == null)
0792: groups = new long[0];
0793:
0794: final UserTicket ticket = FxContext.get().getTicket();
0795:
0796: // Permission checks
0797: try {
0798: if (!checkPermissions(accountId)[MAY_SET_GROUPS])
0799: throw new FxNoAccessException(LOG,
0800: "ex.account.groups.noAssignPermission",
0801: accountId);
0802: } catch (FxLoadException le) {
0803: throw new FxUpdateException(le.getMessage());
0804: }
0805:
0806: Account account = load(accountId);
0807:
0808: // Remove duplicated entries and add group everyone
0809: groups = FxArrayUtils.removeDuplicates(groups);
0810:
0811: // Ensure group EVERYONE and mandator group is part of the list, since every user is assigned to it
0812: groups = FxArrayUtils.addElement(groups,
0813: UserGroup.GROUP_EVERYONE);
0814: groups = FxArrayUtils.addElement(groups, group
0815: .loadMandatorGroup(account.getMandatorId()).getId());
0816:
0817: // Check the groups
0818: UserGroupList currentlyAssigned;
0819: try {
0820: currentlyAssigned = getGroups(accountId);
0821: } catch (FxLoadException exc) {
0822: throw new FxUpdateException(exc);
0823: }
0824:
0825: for (long grp : groups) {
0826: // Do not check the special gropups
0827: if (grp == UserGroup.GROUP_EVERYONE)
0828: continue;
0829: if (grp == UserGroup.GROUP_OWNER)
0830: continue;
0831: if (grp == UserGroup.GROUP_UNDEFINED)
0832: continue;
0833: if (currentlyAssigned.contains(grp))
0834: continue;
0835: // Perform the check for all regular groups, loading an inaccessible
0836: // group will throw an exception which is what we want here
0837: UserGroup g = group.load(grp);
0838: if (g.isSystem())
0839: continue;
0840: if (!ticket.isGlobalSupervisor()) {
0841: if (g.getMandatorId() != account.getMandatorId())
0842: throw new FxNoAccessException(LOG,
0843: "ex.account.group.assign.wrongMandator", g
0844: .getName(), accountId);
0845: }
0846: }
0847:
0848: // Write group assignments to the database
0849: Connection con = null;
0850: PreparedStatement ps = null;
0851:
0852: try {
0853: // Obtain a database connection
0854: con = Database.getDbConnection();
0855:
0856: // Delete the old assignments of the user
0857: ps = con.prepareStatement("DELETE FROM "
0858: + TBL_ASSIGN_GROUPS + " WHERE ACCOUNT=?");
0859: ps.setLong(1, accountId);
0860: ps.executeUpdate();
0861: if (groups.length > 0) {
0862: ps.close();
0863: ps = con.prepareStatement("INSERT INTO "
0864: + TBL_ASSIGN_GROUPS
0865: + " (ACCOUNT,USERGROUP) VALUES (?,?)");
0866: }
0867: // Store the new assignments of the user
0868: for (long group : groups) {
0869: // Skipp 'null' group
0870: if (group == UserGroup.GROUP_UNDEFINED)
0871: continue;
0872: ps.setLong(1, accountId);
0873: ps.setLong(2, group);
0874: ps.executeUpdate();
0875: }
0876: LifeCycleInfoImpl.updateLifeCycleInfo(TBL_ACCOUNTS, "ID",
0877: accountId);
0878: // Ensure any active ticket of the updated user are refreshed
0879: UserTicketStore.flagDirtyHavingUserId(accountId);
0880: } catch (SQLException exc) {
0881: ctx.setRollbackOnly();
0882: throw new FxUpdateException(LOG, exc,
0883: "ex.account.roles.updateFailed.sql", accountId, exc
0884: .getMessage());
0885: } finally {
0886: Database.closeObjects(AccountEngineBean.class, con, ps);
0887: }
0888:
0889: }
0890:
0891: /**
0892: * {@inheritDoc}
0893: */
0894: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0895: public void setRoleList(long accountId, List<Role> roles)
0896: throws FxApplicationException {
0897: if (roles != null && roles.size() > 0) {
0898: setRoles(accountId, roles.toArray(new Role[roles.size()]));
0899: } else {
0900: setRoles(accountId);
0901: }
0902: }
0903:
0904: /**
0905: * {@inheritDoc}
0906: */
0907: @TransactionAttribute(TransactionAttributeType.REQUIRED)
0908: public void setRoles(long accountId, long... roles)
0909: throws FxApplicationException {
0910: // Handle null params
0911: if (roles == null)
0912: roles = new long[0];
0913:
0914: // Permission checks
0915: try {
0916: if (!checkPermissions(accountId)[MAY_SET_ROLES])
0917: throw new FxNoAccessException(LOG,
0918: "ex.account.roles.noAssignPermission",
0919: accountId);
0920: } catch (FxLoadException le) {
0921: throw new FxUpdateException(le);
0922: }
0923:
0924: // Write roles to database
0925: Connection con = null;
0926: PreparedStatement ps = null;
0927:
0928: try {
0929: // Remove duplicated role entries
0930: roles = FxArrayUtils.removeDuplicates(roles);
0931: // Obtain a database connection
0932: con = Database.getDbConnection();
0933:
0934: UserTicket ticket = FxContext.get().getTicket();
0935: //only allow to assign roles which the calling user is a member of (unless it is a global supervisor)
0936: if (!ticket.isGlobalSupervisor()) {
0937: Role[] orgRoles = getRoles(accountId,
0938: RoleLoadMode.FROM_USER_ONLY);
0939: long[] orgRoleIds = new long[orgRoles.length];
0940: for (int i = 0; i < orgRoles.length; i++)
0941: orgRoleIds[i] = orgRoles[i].getId();
0942: //check removed roles
0943: for (long check : orgRoleIds) {
0944: if (!FxArrayUtils.containsElement(roles, check)) {
0945: if (ticket.isInRole(Role.getById(check)))
0946: throw new FxNoAccessException(
0947: "ex.account.roles.assign.noMember.remove",
0948: Role.getById(check).getName());
0949: }
0950: }
0951: //check added roles
0952: for (long check : roles) {
0953: if (!FxArrayUtils
0954: .containsElement(orgRoleIds, check)) {
0955: if (ticket.isInRole(Role.getById(check)))
0956: throw new FxNoAccessException(
0957: "ex.account.roles.assign.noMember.add",
0958: Role.getById(check).getName());
0959: }
0960: }
0961: }
0962:
0963: // Delete the old assignments of the user
0964: ps = con.prepareStatement("DELETE FROM " + TBL_ASSIGN_ROLES
0965: + " WHERE ACCOUNT=?");
0966: ps.setLong(1, accountId);
0967: ps.executeUpdate();
0968:
0969: if (roles.length > 0) {
0970: ps.close();
0971: ps = con.prepareStatement("INSERT INTO "
0972: + TBL_ASSIGN_ROLES
0973: + " (ACCOUNT,USERGROUP,ROLE) VALUES (?,?,?)");
0974: }
0975:
0976: // Store the new assignments of the account
0977: for (long role : roles) {
0978: if (Role.isUndefined(role))
0979: continue;
0980: ps.setLong(1, accountId);
0981: ps.setLong(2, UserGroup.GROUP_NULL);
0982: ps.setLong(3, role);
0983: ps.executeUpdate();
0984: }
0985: LifeCycleInfoImpl.updateLifeCycleInfo(TBL_ACCOUNTS, "ID",
0986: accountId);
0987: // Ensure any active ticket of the updated account are refreshed
0988: UserTicketStore.flagDirtyHavingUserId(accountId);
0989:
0990: } catch (SQLException exc) {
0991: ctx.setRollbackOnly();
0992: throw new FxUpdateException(LOG, exc,
0993: "ex.account.roles.updateFailed.sql", accountId, exc
0994: .getMessage());
0995: } finally {
0996: Database.closeObjects(AccountEngineBean.class, con, ps);
0997: }
0998: }
0999:
1000: /**
1001: * Helper function to set the roles for an account.
1002: *
1003: * @param accountId the account id
1004: * @param roles the roles to set
1005: * @throws FxApplicationException on errors
1006: */
1007: private void setRoles(long accountId, Role[] roles)
1008: throws FxApplicationException {
1009: long[] roleIds = new long[roles.length];
1010: for (int i = 0; i < roles.length; i++) {
1011: roleIds[i] = roles[i].getId();
1012: }
1013: setRoles(accountId, roleIds);
1014: }
1015:
1016: /**
1017: * {@inheritDoc}
1018: */
1019: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
1020: public Account[] loadAll(String name, String loginName,
1021: String email, Boolean isActive, Boolean isConfirmed,
1022: Long mandatorId, int[] isInRole, long[] isInGroup,
1023: int startIdx, int maxEntries) throws FxApplicationException {
1024:
1025: Connection con = null;
1026: Statement stmt = null;
1027: final UserTicket ticket = FxContext.get().getTicket();
1028:
1029: String curSql = _buildSearchStmt(ticket, name, loginName,
1030: email, isActive, isConfirmed, mandatorId, isInRole,
1031: isInGroup, false);
1032:
1033: try {
1034: // Obtain a database connection
1035: con = Database.getDbConnection();
1036:
1037: // Load the users
1038: stmt = con.createStatement();
1039: ResultSet rs = stmt.executeQuery(curSql);
1040: ArrayList<Account> alResult = new ArrayList<Account>(1000);
1041: int counter = 0;
1042: if (startIdx < 0)
1043: startIdx = 0;
1044: if (rs != null) {
1045: while (rs.next()) {
1046:
1047: // jump to startIndex
1048: if (startIdx > 0) {
1049: startIdx--;
1050: continue;
1051: }
1052:
1053: // return only maxEntries
1054: if (counter > -1) {
1055: if (counter == maxEntries)
1056: break;
1057: counter++;
1058: }
1059:
1060: // add to result
1061: long id = rs.getLong(1);
1062: String _email = rs.getString(2);
1063: long contactDataId = rs.getLong(3);
1064: if (rs.wasNull())
1065: contactDataId = -1;
1066: long mandator = rs.getLong(4);
1067: FxLanguage lang = language.load(rs.getInt(5));
1068: Date validFrom = new Date(rs.getLong(6));
1069: Date validTo = new Date(rs.getLong(7));
1070: String description = rs.getString(8);
1071: String _name = rs.getString(9);
1072: String _loginName = rs.getString(10);
1073: boolean active = rs.getBoolean(11);
1074: boolean validated = rs.getBoolean(12);
1075: boolean multiLogin = rs.getBoolean(13);
1076: String updateToken = rs.getString(14);
1077:
1078: alResult
1079: .add(new Account(id, _name, _loginName,
1080: mandator, _email, lang, active,
1081: validated, validFrom, validTo, -1,
1082: description, contactDataId,
1083: multiLogin, updateToken,
1084: LifeCycleInfoImpl.load(rs, 15, 16,
1085: 17, 18)));
1086: }
1087: }
1088:
1089: return alResult.toArray(new Account[alResult.size()]);
1090: } catch (FxInvalidLanguageException exc) {
1091: throw new FxLoadException(exc);
1092: } catch (SQLException exc) {
1093: throw new FxLoadException(LOG, exc,
1094: "ex.account.loadAll.failed.sql", exc.getMessage(),
1095: curSql);
1096: } finally {
1097: Database.closeObjects(AccountEngineBean.class, con, stmt);
1098: }
1099: }
1100:
1101: /**
1102: * {@inheritDoc}
1103: */
1104: public Account[] loadAll(long mandatorId)
1105: throws FxApplicationException {
1106: return loadAll(null, null, null, null, null, mandatorId, null,
1107: null, 0, Integer.MAX_VALUE);
1108: }
1109:
1110: /**
1111: * Helper function.
1112: *
1113: * @param ticket the ticket of the calling user
1114: * @param name the name filter
1115: * @param loginName the login name filter
1116: * @param email the email filter
1117: * @param isActive the is active filter
1118: * @param isConfirmed the is confirmed filter
1119: * @param mandatorId the mandator id filter
1120: * @param isInRole the role filter
1121: * @param isInGroup the group filter
1122: * @param isCountOnly the "count only" option
1123: * @return the sql filter String
1124: * @throws FxNoAccessException if the user does not have access
1125: */
1126: private static String _buildSearchStmt(UserTicket ticket,
1127: String name, String loginName, String email,
1128: Boolean isActive, Boolean isConfirmed, Long mandatorId,
1129: int[] isInRole, long[] isInGroup, boolean isCountOnly)
1130: throws FxNoAccessException {
1131:
1132: // Do no filter GROUP_UNDEFINED (=null value)
1133: if (isInGroup != null) {
1134: isInGroup = ArrayUtils.removeElement(isInGroup,
1135: UserGroup.GROUP_UNDEFINED);
1136: }
1137:
1138: // Determine if group/role filter options are enabled
1139: final boolean filterByGrp = (isInGroup != null && isInGroup.length > 0);
1140: final boolean filterByRole = (isInRole != null && isInRole.length > 0);
1141:
1142: // Determine the mandator, and check its security
1143: long _mandatorId = (mandatorId == null) ? ticket
1144: .getMandatorId() : mandatorId;
1145: if (_mandatorId < 0 && !ticket.isGlobalSupervisor())
1146: throw new FxNoAccessException(LOG,
1147: "ex.account.loadFailed.wrongMandator");
1148:
1149: // Do case insensitive search
1150: if (email != null)
1151: email = email.toUpperCase();
1152: if (name != null)
1153: name = name.toUpperCase();
1154: if (loginName != null)
1155: loginName = loginName.toUpperCase();
1156:
1157: String curSql = "SELECT "
1158: + ""
1159: + (isCountOnly ? "COUNT(*)" :
1160: // 1 2 3 4 5 6
1161: "usr.ID,usr.EMAIL,usr.CONTACT_ID,usr.MANDATOR,usr.LANG,usr.VALID_FROM,"
1162: +
1163: // 7 8 9 10 11
1164: "usr.VALID_TO,usr.DESCRIPTION,usr.USERNAME,usr.LOGIN_NAME,usr.IS_ACTIVE,"
1165: +
1166: // 12 13 14
1167: "usr.IS_VALIDATED,usr.ALLOW_MULTILOGIN,usr.UPDATETOKEN,"
1168: +
1169: // 15 16 17 18
1170: "usr.CREATED_BY,usr.CREATED_AT,usr.MODIFIED_BY,usr.MODIFIED_AT")
1171: + " FROM "
1172: + TBL_ACCOUNTS
1173: + " usr WHERE ID!="
1174: + Account.NULL_ACCOUNT
1175: + " "
1176: + ((_mandatorId == -1) ? "" : " AND (usr.MANDATOR="
1177: + _mandatorId + " OR usr.ID<="
1178: + Account.USER_GLOBAL_SUPERVISOR + ")")
1179: + ((name != null && name.length() > 0) ? " AND UPPER(usr.USERNAME) LIKE '%"
1180: + name + "%'"
1181: : "")
1182: + ((loginName != null && loginName.length() > 0) ? " AND UPPER(usr.LOGIN_NAME) LIKE '%"
1183: + loginName + "%'"
1184: : "")
1185: + ((email != null && email.length() > 0) ? " AND UPPER(usr.EMAIL) LIKE '%"
1186: + email + "%'"
1187: : "")
1188: + ((isActive != null) ? " AND usr.IS_ACTIVE"
1189: + (isActive ? "=" : "<>") + "TRUE" : "")
1190: + ((isConfirmed != null) ? " AND usr.IS_VALIDATED"
1191: + (isConfirmed ? "=" : "<>") + "TRUE" : "") +
1192:
1193: // Group link
1194: ((!filterByGrp) ? ""
1195: : " AND EXISTS (SELECT 1 FROM "
1196: + TBL_ASSIGN_GROUPS
1197: + " grp WHERE grp.ACCOUNT=usr.ID AND grp.USERGROUP IN ("
1198: + FxArrayUtils.toSeparatedList(
1199: isInGroup, ',') + "))") +
1200:
1201: // Role link
1202: ((!filterByRole) ? ""
1203: : " AND (EXISTS (SELECT 1 FROM "
1204: + TBL_ASSIGN_ROLES
1205: + " rol WHERE rol.ACCOUNT=usr.ID and rol.ROLE IN ("
1206: + FxArrayUtils.toSeparatedList(
1207: isInRole, ',')
1208: + ")) OR "
1209: + "EXISTS (SELECT 1 FROM "
1210: + TBL_ASSIGN_ROLES
1211: + " rol, "
1212: + TBL_ASSIGN_GROUPS
1213: + " grp WHERE "
1214: + "grp.ACCOUNT=usr.ID AND rol.USERGROUP=grp.USERGROUP AND rol.ROLE IN ("
1215: + FxArrayUtils.toSeparatedList(
1216: isInRole, ',') + ")))") +
1217:
1218: // Order
1219: (isCountOnly ? "" : " ORDER by usr.LOGIN_NAME");
1220: if (LOG.isDebugEnabled())
1221: LOG.debug(curSql);
1222: return curSql;
1223: }
1224:
1225: /**
1226: * {@inheritDoc}
1227: */
1228: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
1229: public int getAccountMatches(String name, String loginName,
1230: String email, Boolean isActive, Boolean isConfirmed,
1231: Long mandatorId, int[] isInRole, long[] isInGroup)
1232: throws FxApplicationException {
1233:
1234: Connection con = null;
1235: Statement stmt = null;
1236: final UserTicket ticket = FxContext.get().getTicket();
1237: String curSql = _buildSearchStmt(ticket, name, loginName,
1238: email, isActive, isConfirmed, mandatorId, isInRole,
1239: isInGroup, true);
1240: try {
1241: // Obtain a database connection
1242: con = Database.getDbConnection();
1243:
1244: // Load the users
1245: stmt = con.createStatement();
1246: ResultSet rs = stmt.executeQuery(curSql);
1247: if (rs != null && rs.next())
1248: return rs.getInt(1);
1249: return -1;
1250:
1251: } catch (SQLException exc) {
1252: throw new FxLoadException(LOG, exc,
1253: "ex.account.loadAll.failed.sql", exc.getMessage(),
1254: curSql);
1255: } finally {
1256: Database.closeObjects(AccountEngineBean.class, con, stmt);
1257: }
1258: }
1259:
1260: /**
1261: * {@inheritDoc}
1262: */
1263: @TransactionAttribute(TransactionAttributeType.REQUIRED)
1264: public void update(long accountId, String password,
1265: Long defaultNode, String name, String loginName,
1266: String email, Boolean isConfirmed, Boolean isActive,
1267: Date validFrom, Date validTo, Long lang,
1268: String description, Boolean allowMultiLogin,
1269: Long contactDataId) throws FxApplicationException {
1270:
1271: // Load the account to update
1272: Account account = load(accountId);
1273:
1274: final UserTicket ticket = FxContext.get().getTicket();
1275: // Determine if only fields are accessed that the use may alter for himself
1276: final boolean protectedFields = (name != null
1277: || loginName != null || isConfirmed != null
1278: || isActive != null || validTo != null
1279: || validFrom != null || description != null);
1280: if (!protectedFields && ticket.getUserId() == accountId) {
1281: // passed
1282: } else {
1283: if (!_checkPermissions(account)[MAY_UPDATE])
1284: throw new FxNoAccessException(LOG,
1285: "ex.account.update.noPermission", account
1286: .getName());
1287: }
1288:
1289: // Parameter checks
1290: try {
1291: if (loginName != null)
1292: loginName = checkLoginName(loginName);
1293: if (email != null)
1294: email = FxFormatUtils.isEmail(email);
1295: if (password != null) {
1296: password = FxFormatUtils.encodePassword(accountId,
1297: password.trim());
1298: }
1299: if (lang != null && !language.isValid(lang))
1300: throw new FxInvalidParameterException("LANGUAGE",
1301: "ex.account.languageInvalid", lang);
1302: } catch (FxInvalidParameterException pe) {
1303: if (LOG.isInfoEnabled())
1304: LOG.info(pe);
1305: throw pe;
1306: }
1307:
1308: Connection con = null;
1309: PreparedStatement stmt = null;
1310: String curSql;
1311:
1312: if (name == null)
1313: name = account.getName();
1314: if (loginName == null)
1315: loginName = account.getLoginName();
1316: if (email == null)
1317: email = account.getEmail();
1318: if (lang == null)
1319: lang = account.getLanguage().getId();
1320: if (isActive == null)
1321: isActive = account.isActive();
1322: if (isConfirmed == null)
1323: isConfirmed = account.isValidated();
1324: if (description == null)
1325: description = account.getDescription();
1326: if (defaultNode == null)
1327: defaultNode = account.getDefaultNode();
1328: if (allowMultiLogin == null)
1329: allowMultiLogin = account.isAllowMultiLogin();
1330: // Assign and check dates
1331: if (validFrom == null)
1332: validFrom = account.getValidFrom();
1333: if (validTo == null)
1334: validTo = account.getValidTo();
1335: checkDates(validFrom, validTo);
1336: if (defaultNode < 0)
1337: defaultNode = (long) 0;
1338:
1339: try {
1340:
1341: // Obtain a database connection
1342: con = Database.getDbConnection();
1343:
1344: curSql = "UPDATE "
1345: + TBL_ACCOUNTS
1346: + " SET "
1347: +
1348: // 1 2 3 4 5 6
1349: "EMAIL=?,LANG=?,VALID_FROM=?,VALID_TO=?,DESCRIPTION=?,"
1350: +
1351: // 6 7 8 9 10
1352: "MODIFIED_BY=?,MODIFIED_AT=?,IS_ACTIVE=?,IS_VALIDATED=?,DEFAULT_NODE=?,"
1353: +
1354: // 11, 12 , 13 , 14
1355: "USERNAME=?,LOGIN_NAME=?,ALLOW_MULTILOGIN=?"
1356: + ((password != null) ? ",PASSWORD=?" : "")
1357: + ((contactDataId != null) ? ",CONTACT_ID=?" : "")
1358: + " WHERE ID=" + accountId;
1359: stmt = con.prepareStatement(curSql);
1360: stmt.setString(1, email);
1361: stmt.setInt(2, lang.intValue());
1362: stmt.setLong(3, validFrom.getTime());
1363: stmt.setLong(4, validTo.getTime());
1364: stmt.setString(5, description);
1365: stmt.setLong(6, ticket.getUserId());
1366: stmt.setLong(7, System.currentTimeMillis());
1367: stmt.setBoolean(8, isActive);
1368: stmt.setBoolean(9, isConfirmed);
1369: stmt.setLong(10, defaultNode);
1370: stmt.setString(11, name);
1371: stmt.setString(12, loginName);
1372: stmt.setBoolean(13, allowMultiLogin);
1373: int pos = 14;
1374: if (password != null)
1375: stmt.setString(pos++, password);
1376: if (contactDataId != null)
1377: stmt.setLong(pos/*++*/, contactDataId);
1378: stmt.executeUpdate();
1379: if (contactDataId != null) {
1380: //make sure the user is the owner of his contact data
1381: stmt.close();
1382: stmt = con.prepareStatement("UPDATE " + TBL_CONTENT
1383: + " SET CREATED_BY=? WHERE ID=?");
1384: stmt.setLong(1, accountId);
1385: stmt.setLong(2, contactDataId);
1386: stmt.executeUpdate();
1387: }
1388:
1389: // Log the user out of the system if he was made active
1390: if (!isActive || !isConfirmed) {
1391: UserTicketStore.removeUserId(accountId, null);
1392: } else {
1393: // Ensure any active ticket of the updated user are refreshed
1394: UserTicketStore.flagDirtyHavingUserId(account.getId());
1395: }
1396: } catch (SQLException exc) {
1397: final boolean uniqueConstraintViolation = Database
1398: .isUniqueConstraintViolation(exc);
1399: ctx.setRollbackOnly();
1400: if (uniqueConstraintViolation) {
1401: throw new FxEntryExistsException(LOG,
1402: "ex.account.userExists", name, loginName);
1403: } else {
1404: throw new FxUpdateException(LOG,
1405: "ex.account.update.failed.sql", account
1406: .getLoginName(), exc.getMessage());
1407: }
1408: } finally {
1409: Database.closeObjects(AccountEngineBean.class, con, stmt);
1410: }
1411: }
1412:
1413: /**
1414: * {@inheritDoc}
1415: */
1416: @TransactionAttribute(TransactionAttributeType.REQUIRED)
1417: public void updateUser(long accountId, String password,
1418: String name, String loginName, String email, Long lang)
1419: throws FxApplicationException {
1420: // Load the account to update
1421: Account account = load(accountId);
1422:
1423: final UserTicket ticket = FxContext.get().getTicket();
1424: if (ticket.getUserId() != accountId) {
1425: if (!ticket.isGlobalSupervisor()
1426: || !(ticket.isMandatorSupervisor() && account
1427: .getMandatorId() == ticket.getMandatorId()))
1428: throw new FxNoAccessException(LOG,
1429: "ex.account.update.noPermission", account
1430: .getName());
1431: }
1432:
1433: // Parameter checks
1434: if (loginName != null)
1435: loginName = checkLoginName(loginName);
1436: if (email != null)
1437: email = FxFormatUtils.isEmail(email);
1438: if (password != null) {
1439: password = FxFormatUtils.encodePassword(accountId, password
1440: .trim());
1441: }
1442:
1443: if (lang != null && !language.isValid(lang))
1444: throw new FxInvalidParameterException("LANGUAGE",
1445: "ex.account.languageInvalid", lang);
1446:
1447: Connection con = null;
1448: PreparedStatement stmt = null;
1449: String curSql;
1450:
1451: if (name == null)
1452: name = account.getName();
1453: if (loginName == null)
1454: loginName = account.getLoginName();
1455: if (email == null)
1456: email = account.getEmail();
1457: if (lang == null)
1458: lang = account.getLanguage().getId();
1459:
1460: try {
1461: // Obtain a database connection
1462: con = Database.getDbConnection();
1463:
1464: curSql = "UPDATE " + TBL_ACCOUNTS
1465: + " SET "
1466: +
1467: // 1 2 3 4 5 6 7
1468: "EMAIL=?,LANG=?, USERNAME=?,LOGIN_NAME=?, MODIFIED_BY=?,MODIFIED_AT=?"
1469: + ((password != null) ? ",PASSWORD=?" : "")
1470: + " WHERE ID=" + accountId;
1471: stmt = con.prepareStatement(curSql);
1472: stmt.setString(1, email);
1473: stmt.setInt(2, lang.intValue());
1474: stmt.setString(3, name);
1475: stmt.setString(4, loginName);
1476:
1477: stmt.setLong(5, ticket.getUserId());
1478: stmt.setLong(6, System.currentTimeMillis());
1479:
1480: if (password != null)
1481: stmt.setString(7, password);
1482: stmt.executeUpdate();
1483:
1484: // Ensure any active ticket of the updated user are refreshed
1485: UserTicketStore.flagDirtyHavingUserId(account.getId());
1486: } catch (SQLException exc) {
1487: final boolean uniqueConstraintViolation = Database
1488: .isUniqueConstraintViolation(exc);
1489: ctx.setRollbackOnly();
1490: if (uniqueConstraintViolation) {
1491: throw new FxEntryExistsException(LOG,
1492: "ex.account.userExists", name, loginName);
1493: } else {
1494: throw new FxUpdateException(LOG,
1495: "ex.account.update.failed.sql", account
1496: .getLoginName(), exc.getMessage());
1497: }
1498: } finally {
1499: Database.closeObjects(AccountEngineBean.class, con, stmt);
1500: }
1501: }
1502:
1503: /**
1504: * {@inheritDoc}
1505: */
1506: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
1507: public Account[] getAssignedUsers(long groupId, int startIdx,
1508: int maxEntries) throws FxApplicationException {
1509:
1510: final UserTicket ticket = FxContext.get().getTicket();
1511:
1512: // Load the requested group
1513: final UserGroup theGroup = group.load(groupId);
1514:
1515: long[] groups = { theGroup.getId() };
1516:
1517: // Return the users from ALL mandators assigned to the group if the caller is GLOBAL_SUPERVISOR.
1518: // Else only look for users within the callers mandator.
1519: Long mandatorId = ticket.isGlobalSupervisor() ? -1 : ticket
1520: .getMandatorId();
1521:
1522: // Load the users
1523:
1524: return loadAll(null, null, null, null, null, mandatorId, null,
1525: groups, startIdx, maxEntries);
1526: }
1527:
1528: /**
1529: * {@inheritDoc}
1530: */
1531: @TransactionAttribute(TransactionAttributeType.SUPPORTS)
1532: public long getAssignedUsersCount(long groupId,
1533: boolean includeInvisible) throws FxApplicationException {
1534:
1535: if (groupId < 0 || groupId == UserGroup.GROUP_UNDEFINED)
1536: return 0;
1537: final UserTicket ticket = FxContext.get().getTicket();
1538: Connection con = null;
1539: Statement stmt = null;
1540: String sCurSql = null;
1541: try {
1542:
1543: // Obtain a database connection
1544: con = Database.getDbConnection();
1545:
1546: // Nothing is invisible for the GLOBAL_SUPERVISOR
1547: if (ticket.isGlobalSupervisor())
1548: includeInvisible = true;
1549:
1550: // Load the count
1551: stmt = con.createStatement();
1552:
1553: if (includeInvisible) {
1554: sCurSql = "SELECT COUNT(*) FROM " + TBL_ASSIGN_GROUPS
1555: + " WHERE USERGROUP=" + groupId;
1556: } else {
1557: sCurSql = "SELECT COUNT(*) FORM "
1558: + TBL_ASSIGN_GROUPS
1559: + " ln, "
1560: + TBL_ACCOUNTS
1561: + " usr WHERE ln.ACCOUNT=usr.ID AND usr.MANDATOR="
1562: + ticket.getMandatorId() + " AND ln.USERGROUP="
1563: + groupId;
1564: }
1565:
1566: ResultSet rs = stmt.executeQuery(sCurSql);
1567: long result = 0;
1568: if (rs != null && rs.next())
1569: result = rs.getLong(1);
1570:
1571: if (LOG.isDebugEnabled())
1572: LOG.debug("Users in group [" + groupId + "]:" + result
1573: + ", caller=" + ticket);
1574: return result;
1575: } catch (SQLException exc) {
1576: FxLoadException ce = new FxLoadException(
1577: "Database error! Last stmt was [" + sCurSql + "]:"
1578: + exc.getMessage(), exc);
1579: LOG.error(ce);
1580: throw ce;
1581: } finally {
1582: Database.closeObjects(UserGroupEngineBean.class, con, stmt);
1583: }
1584:
1585: }
1586:
1587: /**
1588: * {@inheritDoc}
1589: */
1590: @TransactionAttribute(TransactionAttributeType.REQUIRED)
1591: public ACLAssignment[] loadAccountAssignments(long accountId)
1592: throws FxApplicationException {
1593: Connection con = null;
1594: Statement stmt = null;
1595: String curSql;
1596: UserTicket ticket = UserTicketStore.getTicket(false);
1597:
1598: // Security checks
1599: if (!ticket.isGlobalSupervisor()
1600: && (!(accountId == ticket.getUserId() || accountId == Account.USER_GUEST))) {
1601: try {
1602: Account usr = load(accountId);
1603: if (ticket.isMandatorSupervisor()
1604: && ticket.getMandatorId() == usr
1605: .getMandatorId()) {
1606: // MandatorSupervisor may access all users within his domain
1607: } else {
1608: FxNoAccessException nae = new FxNoAccessException(
1609: "You may not access the ACLAssignment of user ["
1610: + accountId + "]");
1611: if (LOG.isInfoEnabled())
1612: LOG.info(nae);
1613: throw nae;
1614: }
1615: } catch (FxNotFoundException exc) {
1616: return new ACLAssignment[0];
1617: }
1618: }
1619:
1620: try {
1621:
1622: // Obtain a database connection
1623: con = Database.getDbConnection();
1624:
1625: // Delete any old assignments
1626: // 1 2 3 4 5 6 7
1627: curSql = "SELECT ass.USERGROUP,ass.ACL,ass.PREAD,ass.PEDIT,ass.PREMOVE,ass.PEXPORT,ass.PREL,"
1628: +
1629: // 8 9 10 11 12 13
1630: "ass.PCREATE, acl.CAT_TYPE,ass.CREATED_BY,ass.CREATED_AT,ass.MODIFIED_BY,ass.MODIFIED_AT "
1631: + "FROM "
1632: + TBL_ASSIGN_ACLS
1633: + " ass, "
1634: + TBL_ASSIGN_GROUPS
1635: + " grp, "
1636: + TBL_ACLS
1637: + " acl "
1638: + "WHERE acl.ID=ass.ACL AND ((ass.USERGROUP=grp.USERGROUP AND grp.ACCOUNT="
1639: + accountId
1640: + ")OR(ass.USERGROUP="
1641: + UserGroup.GROUP_OWNER + "))";
1642:
1643: stmt = con.createStatement();
1644: ResultSet rs = stmt.executeQuery(curSql);
1645:
1646: // Read the data
1647: ArrayList<ACLAssignment> result = new ArrayList<ACLAssignment>(
1648: 20);
1649: while (rs != null && rs.next()) {
1650: long groupId = rs.getLong(1);
1651: /*if (groupId == UserGroup.GROUP_OWNER) {
1652: // skip
1653: continue;
1654: }*/
1655: result.add(new ACLAssignment(rs.getLong(2), groupId, rs
1656: .getBoolean(3), rs.getBoolean(4), rs
1657: .getBoolean(7), rs.getBoolean(5), rs
1658: .getBoolean(6), rs.getBoolean(8), ACL.Category
1659: .getById(rs.getByte(9)), LifeCycleInfoImpl
1660: .load(rs, 10, 11, 12, 13)));
1661: }
1662: // Return the found entries
1663: return result.toArray(new ACLAssignment[result.size()]);
1664: } catch (SQLException exc) {
1665: FxLoadException dbe = new FxLoadException(
1666: "Failed to load the ACL assignments for user ["
1667: + accountId + "]: " + exc.getMessage(), exc);
1668: LOG.error(dbe);
1669: throw dbe;
1670: } finally {
1671: Database.closeObjects(ACLEngineBean.class, con, stmt);
1672: }
1673: }
1674:
1675: /**
1676: * {@inheritDoc}
1677: */
1678: @TransactionAttribute(TransactionAttributeType.REQUIRED)
1679: public void fixContactData() throws FxApplicationException {
1680: Account[] acct = loadAll(null, null, null, null, null, null,
1681: null, null, 0, Integer.MAX_VALUE);
1682: for (Account a : acct) {
1683: if (a.getContactData().getId() == -1) {
1684: FxContent contactData = co.initialize(CacheAdmin
1685: .getEnvironment().getType(FxType.CONTACTDATA)
1686: .getId());
1687: contactData.setValue("/SURNAME", new FxString(false, a
1688: .getName()));
1689: contactData.setValue("/EMAIL", new FxString(false, a
1690: .getEmail()));
1691: FxPK contactDataPK = co.save(contactData);
1692: update(a.getId(), null, null, null, null, null, null,
1693: null, null, null, null, null, null,
1694: contactDataPK.getId());
1695: }
1696: }
1697: }
1698: }
|