org.glite.security.voms.admin.persistence.dao.VOMSUserDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.glite.security.voms.admin.persistence.dao.VOMSUserDAO.java

Source

/**
 * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2016
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.glite.security.voms.admin.persistence.dao;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.Validate;
import org.glite.security.voms.admin.apiv2.VOMSUserJSON;
import org.glite.security.voms.admin.configuration.VOMSConfiguration;
import org.glite.security.voms.admin.configuration.VOMSConfigurationConstants;
import org.glite.security.voms.admin.error.NotFoundException;
import org.glite.security.voms.admin.error.NullArgumentException;
import org.glite.security.voms.admin.error.VOMSException;
import org.glite.security.voms.admin.persistence.HibernateFactory;
import org.glite.security.voms.admin.persistence.dao.generic.DAOFactory;
import org.glite.security.voms.admin.persistence.dao.lookup.FindByCertificateDAO;
import org.glite.security.voms.admin.persistence.dao.lookup.LookupPolicyProvider;
import org.glite.security.voms.admin.persistence.error.AlreadyExistsException;
import org.glite.security.voms.admin.persistence.error.AlreadyMemberException;
import org.glite.security.voms.admin.persistence.error.AttributeAlreadyExistsException;
import org.glite.security.voms.admin.persistence.error.AttributeValueAlreadyAssignedException;
import org.glite.security.voms.admin.persistence.error.NoSuchAttributeException;
import org.glite.security.voms.admin.persistence.error.NoSuchCAException;
import org.glite.security.voms.admin.persistence.error.NoSuchGroupException;
import org.glite.security.voms.admin.persistence.error.NoSuchUserException;
import org.glite.security.voms.admin.persistence.error.UserAlreadyExistsException;
import org.glite.security.voms.admin.persistence.error.VOMSDatabaseException;
import org.glite.security.voms.admin.persistence.model.AUP;
import org.glite.security.voms.admin.persistence.model.AUPAcceptanceRecord;
import org.glite.security.voms.admin.persistence.model.AUPVersion;
import org.glite.security.voms.admin.persistence.model.Certificate;
import org.glite.security.voms.admin.persistence.model.VOMSCA;
import org.glite.security.voms.admin.persistence.model.VOMSGroup;
import org.glite.security.voms.admin.persistence.model.VOMSMapping;
import org.glite.security.voms.admin.persistence.model.VOMSRole;
import org.glite.security.voms.admin.persistence.model.VOMSUser;
import org.glite.security.voms.admin.persistence.model.VOMSUser.SuspensionReason;
import org.glite.security.voms.admin.persistence.model.attribute.VOMSAttributeDescription;
import org.glite.security.voms.admin.persistence.model.attribute.VOMSUserAttribute;
import org.glite.security.voms.admin.persistence.model.task.SignAUPTask;
import org.glite.security.voms.admin.util.DNUtil;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class VOMSUserDAO implements FindByCertificateDAO<VOMSUser> {

    private static final Logger log = LoggerFactory.getLogger(VOMSUserDAO.class);

    public static VOMSUserDAO instance() {

        HibernateFactory.beginTransaction();
        return new VOMSUserDAO();
    }

    private VOMSUserDAO() {

    }

    public void requestAUPReacceptance(VOMSUser user, AUP aup) {

        if (user == null)
            throw new NullArgumentException("user cannot be null!");

        if (aup == null)
            throw new NullArgumentException("aup cannot be null!");

        AUPVersion aupVersion = aup.getActiveVersion();

        if (aupVersion == null)
            throw new NotFoundException("No registered version found for AUP '" + aup.getName() + "'.");

        log.debug("User '" + user + "' is request to reaccept aup version '" + aupVersion + "'");

        AUPAcceptanceRecord r = user.getAUPAccceptanceRecord(aupVersion);

        if (r != null) {

            if (r.getValid())
                r.setValid(false);
            else
                log.debug("Ignoring invalidation of already invalid record.");
        }

    }

    public void signAUP(VOMSUser user) {

        signAUP(user, DAOFactory.instance().getAUPDAO().getVOAUP());
    }

    public void signAUP(VOMSUser user, AUP aup) {

        if (user == null)
            throw new NullArgumentException("user cannot be null!");

        if (aup == null)
            throw new NullArgumentException("aup cannot be null!");

        AUPVersion aupVersion = aup.getActiveVersion();

        if (aupVersion == null)
            throw new NotFoundException("No registered version found for AUP '" + aup.getName() + "'.");

        log.debug("User '" + user + "' signing aup version '" + aupVersion + "'");

        AUPAcceptanceRecord r = user.getAUPAccceptanceRecord(aupVersion);

        if (r == null) {

            log.debug("Creating new aup acceptance record!");
            AUPAcceptanceRecord aupRecord = new AUPAcceptanceRecord(user, aupVersion);
            aupRecord.setLastAcceptanceDate(new Date());

            user.getAupAcceptanceRecords().add(aupRecord);

        } else {

            log.debug("Updating existing acceptance record");
            // User has signed but has been prompted to resign
            AUPAcceptanceRecord aupRecord = user.getAUPAccceptanceRecord(aupVersion);
            aupRecord.setLastAcceptanceDate(new Date());
            aupRecord.setValid(true);

        }

        SignAUPTask pendingTask;

        do {

            pendingTask = user.getPendingSignAUPTask(aupVersion.getAup());

            if (pendingTask != null) {
                log.debug("Setting task '" + pendingTask + "' completed");
                pendingTask.setCompleted();
            }

        } while (pendingTask != null);

        if (user.isSuspended() && user.getSuspensionReasonCode().equals(SuspensionReason.FAILED_TO_SIGN_AUP)) {

            log.debug("Restoring user '" + user + "'");
            user.restore(SuspensionReason.FAILED_TO_SIGN_AUP);
        }

        HibernateFactory.getSession().saveOrUpdate(user);

    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findUsersWithPendingSignAUPTask(AUP aup) {

        String queryString = "from VOMSUser u join u.tasks t where t.class = SignAUPTask and t.status != 'COMPLETED' and t.aup = :aup";

        Query q = HibernateFactory.getSession().createQuery(queryString);
        q.setEntity("aup", aup);

        return q.list();

    }

    public Long countExpiredUsers() {

        Date now = new Date();

        String queryString = "select count(*) from VOMSUser u where u.endTime < :now";

        Query q = HibernateFactory.getSession().createQuery(queryString);

        q.setDate("now", now);

        return (Long) q.uniqueResult();

    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findExpiredUsers() {

        Date now = new Date();

        String queryString = "from VOMSUser u where u.endTime is not null and u.endTime < :now";
        Query q = HibernateFactory.getSession().createQuery(queryString);

        q.setDate("now", now);

        return q.list();
    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findAUPFailingUsers(AUP aup) {

        List<VOMSUser> result = new ArrayList<VOMSUser>();

        AUPVersion activeVersion = aup.getActiveVersion();

        // Get users First that do not have any acceptance records for the
        // active aupVersion
        String noAcceptanceRecordForActiveAUPVersionQuery = "select u from VOMSUser u where u not in (select u from VOMSUser u join u.aupAcceptanceRecords r where r.aupVersion.active = true)";

        Query q = HibernateFactory.getSession().createQuery(noAcceptanceRecordForActiveAUPVersionQuery);

        List<VOMSUser> noRecordUsers = q.list();

        result.addAll(noRecordUsers);

        log.debug("Users without acceptance records for currently active aup: {}", result);

        // Add users that have an expired aup acceptance record due to aup
        // update or acceptance retriggering.
        String qString = "select u from VOMSUser u join u.aupAcceptanceRecords r where r.aupVersion.active = true and r.lastAcceptanceDate < :lastUpdateTime";

        Query q2 = HibernateFactory.getSession().createQuery(qString);

        Date aupLastUpdateTime = activeVersion.getLastUpdateTime();

        log.debug("AUP version lastUpdateTime: {}", aupLastUpdateTime);
        q2.setTimestamp("lastUpdateTime", aupLastUpdateTime);

        List<VOMSUser> expiredDueToAUPUpdateUsers = q2.list();
        result.addAll(expiredDueToAUPUpdateUsers);

        log.debug("Users that signed the AUP before it was last updated:" + expiredDueToAUPUpdateUsers);

        // Add users that have a valid aup acceptance record that needs to be
        // checked against
        // the reacceptance period
        Query q3 = HibernateFactory.getSession().createQuery(
                "select u from VOMSUser u join u.aupAcceptanceRecords r where r.aupVersion.active = true"
                        + " and r.lastAcceptanceDate > :lastUpdateTime ");

        q3.setTimestamp("lastUpdateTime", aupLastUpdateTime);

        List<VOMSUser> potentiallyExpiredUsers = q3.list();
        HibernateFactory.getSession().flush();

        log.debug("Users that needs checking since their aup acceptance record could be expired:"
                + potentiallyExpiredUsers);

        for (VOMSUser u : potentiallyExpiredUsers) {

            AUPAcceptanceRecord r = u.getAUPAccceptanceRecord(aup.getActiveVersion());

            if (r.hasExpired()) {
                log.debug("Adding user{} to results due to expired aup acceptance report (aup validity expiration)",
                        u);
                result.add(u);

            }

        }

        // Filter out expired users
        ListIterator<VOMSUser> iter = result.listIterator();
        while (iter.hasNext()) {
            VOMSUser u = iter.next();
            if (u.hasExpired() && u.isSuspended()) {
                log.debug("Removing supended user {} from results since " + "membership expired", u);
                iter.remove();
            }
        }

        return result;

    }

    public Certificate addCertificate(VOMSUser u, String dn, String caDn) {

        Validate.notNull(u, "User must be non-null!");
        Validate.notEmpty(dn, "DN must be non-null & not empty!");
        Validate.notEmpty(caDn, "CA must be non-null & not empty!");

        VOMSCA ca = VOMSCADAO.instance().getByName(caDn);
        if (ca == null)
            throw new NoSuchCAException("CA '" + caDn + "' not found in database.");

        Certificate cert = CertificateDAO.instance().lookup(dn, caDn);

        if (cert != null)
            throw new AlreadyExistsException("Certificate already bound!");

        cert = new Certificate();

        cert.setSubjectString(dn);
        cert.setCa(ca);
        cert.setCreationTime(new Date());
        cert.setSuspended(false);

        cert.setUser(u);
        u.addCertificate(cert);
        if (u.isSuspended()) {
            cert.setSuspended(true);
            cert.setSuspensionReason(u.getSuspensionReason());
        }

        HibernateFactory.getSession().saveOrUpdate(cert);
        HibernateFactory.getSession().saveOrUpdate(u);

        return cert;

    }

    public Certificate addCertificate(VOMSUser u, X509Certificate x509Cert) {

        Validate.notNull(u, "User must be non-null!");
        Validate.notNull(x509Cert, "Certificate must be non-null!");

        // Assume the certificate have been already validated
        // at this stage.

        String caDN = DNUtil.getOpenSSLSubject(x509Cert.getIssuerX500Principal());
        VOMSCA ca = VOMSCADAO.instance().getByName(caDN);

        if (ca == null)
            throw new NoSuchCAException("CA '" + caDN + "' not recognized!");

        Certificate cert = CertificateDAO.instance().find(x509Cert);

        if (cert != null)
            throw new AlreadyExistsException("Certificate already bound!");

        cert = new Certificate();

        String subjectString = DNUtil.getOpenSSLSubject(x509Cert.getSubjectX500Principal());
        cert.setSubjectString(subjectString);
        cert.setCreationTime(new Date());
        cert.setSuspended(false);
        cert.setCa(ca);

        cert.setUser(u);

        if (u.isSuspended()) {
            cert.setSuspended(true);
            cert.setSuspensionReason(u.getSuspensionReason());
        }

        u.addCertificate(cert);

        HibernateFactory.getSession().saveOrUpdate(cert);
        HibernateFactory.getSession().saveOrUpdate(u);

        return cert;

    }

    public void addToGroup(VOMSUser u, VOMSGroup g) {

        log.debug("Adding user \"" + u + "\" to group \"" + g + "\".");

        if (!HibernateFactory.getSession().contains(u)) {

            VOMSUser checkUser = findById(u.getId());

            if (checkUser == null)
                throw new NoSuchUserException("User \"" + u + "\" not found in database.");
        }

        // Check that the group exists
        if (VOMSGroupDAO.instance().findByName(g.getName()) == null)
            throw new NoSuchGroupException("Group \"" + g + "\" is not defined in database.");

        VOMSMapping m = new VOMSMapping(u, g, null);
        if (u.getMappings().contains(m))
            throw new AlreadyMemberException("User \"" + u + "\" is already a member of group \"" + g + "\".");

        u.getMappings().add(m);

        HibernateFactory.getSession().save(u);

    }

    public void assignRole(VOMSUser u, VOMSGroup g, VOMSRole r) {

        u.assignRole(g, r);
        HibernateFactory.getSession().update(u);

    }

    @SuppressWarnings("deprecation")
    private void checkNullFields(VOMSUser usr) {

        if (!VOMSConfiguration.instance().getBoolean("voms.admin.compatibility-mode", true)) {

            if (usr.getName() == null)
                throw new NullArgumentException("Please specify a name for the user!");

            if (usr.getSurname() == null)
                throw new NullArgumentException("Please specify a surname for the user!");

            if (usr.getInstitution() == null)
                throw new NullArgumentException("Please specify an instituion for the user!");

            if (usr.getAddress() == null)
                throw new NullArgumentException("Please specify an address for the user!");

            if (usr.getPhoneNumber() == null)
                throw new NullArgumentException("Please specify a phone number for the user!");

            if (usr.getEmailAddress() == null)
                throw new NullArgumentException("Please specify an email address for the user!");

        } else {

            if (usr.getDn() == null)
                throw new NullArgumentException("Please specify a dn for the user!");

            if (usr.getEmailAddress() == null)
                throw new NullArgumentException("Please specify an email address for the user!");
        }

    }

    public int countMatches(String searchString) {

        String sString = "%" + searchString + "%";

        String countString = "select count(distinct u) from VOMSUser u join u.certificates as cert where lower(u.surname) like lower(:searchString) "
                + "or lower(u.name) like lower(:searchString) or u.emailAddress like :searchString "
                + " or lower(u.institution) like lower(:searchString) "
                + " or cert.subjectString like(:searchString) or cert.ca.subjectString like(:searchString) "
                + " order by u.surname asc";

        Query q = HibernateFactory.getSession().createQuery(countString);
        q.setString("searchString", sString);

        Long count = (Long) q.uniqueResult();

        return count.intValue();

    }

    public Long countUsers() {

        Query q = HibernateFactory.getSession().createQuery("select count(*) from VOMSUser");

        Long count = (Long) q.uniqueResult();

        return count;
    }

    public VOMSUser create(String dn, String caDN, String cn, String certURI, String emailAddress) {

        if (dn == null)
            throw new NullArgumentException("dn must be non-null!");

        if (caDN == null)
            throw new NullArgumentException("ca must be non-null!");

        if (emailAddress == null)
            throw new NullArgumentException("emailAddress must be non-null!");

        dn = DNUtil.normalizeDN(dn);
        caDN = DNUtil.normalizeDN(caDN);

        VOMSUser u = lookup(dn, caDN);

        if (u != null) {
            throw new UserAlreadyExistsException("User " + u + " already exists!");
        }

        VOMSCA ca = VOMSCADAO.instance().getByName(caDN);

        if (ca == null) {
            throw new NoSuchCAException("Unknown ca " + caDN + ". Will not create user " + dn);
        }

        u = new VOMSUser();
        u.setEmailAddress(emailAddress);

        log.debug("Creating user \"" + u + "\".");

        // Add user to default VO group

        VOMSGroup voGroup = VOMSGroupDAO.instance().getVOGroup();
        HibernateFactory.getSession().save(u);

        addToGroup(u, voGroup);

        return u;
    }

    public VOMSUser create(VOMSUserJSON usr, String certificateSubject, String caSubject) {

        VOMSUser u = VOMSUser.fromVOMSUserJSON(usr);

        u.setDn(certificateSubject);
        return create(u, caSubject);

    }

    protected Certificate createCertificate(String certificateSubject, String caSubject) {

        CertificateDAO certDAO = CertificateDAO.instance();

        return certDAO.create(certificateSubject, caSubject);

    }

    protected Certificate lookupCertificate(String certificateSubject, String certificateIssuer) {

        CertificateDAO certDAO = CertificateDAO.instance();
        Certificate cert = certDAO.lookup(certificateSubject, certificateIssuer);

        return cert;
    }

    protected Certificate assertCertificateIsNotBound(String certificateSubject, String caSubject) {

        Certificate cert = lookupCertificate(certificateSubject, caSubject);

        if (cert != null)
            throw new UserAlreadyExistsException("A user holding a certificate with the following subject '"
                    + certificateSubject + "' already exists in this VO.");
        return cert;

    }

    protected VOMSUser initUserMembership(VOMSUser user) {

        Calendar c = Calendar.getInstance();

        // Membership start and end time
        user.setCreationTime(c.getTime());

        // Default lifetime for membership is 12 months
        int lifetime = VOMSConfiguration.instance().getInt(VOMSConfigurationConstants.DEFAULT_MEMBERSHIP_LIFETIME,
                12);

        c.add(Calendar.MONTH, lifetime);
        user.setEndTime(c.getTime());

        return user;

    }

    @SuppressWarnings("deprecation")
    public VOMSUser create(VOMSUser usr, String caDN) {

        checkNullFields(usr);

        Certificate cert = assertCertificateIsNotBound(usr.getDn(), caDN);

        // Initialize user membership
        initUserMembership(usr);

        // Create certificate and link it to the membership
        cert = createCertificate(usr.getDn(), caDN);

        usr.addCertificate(cert);

        HibernateFactory.getSession().save(usr);

        // Add user to VO root group
        VOMSGroup voGroup = VOMSGroupDAO.instance().getVOGroup();
        usr.addToGroup(voGroup);

        return usr;

    }

    public VOMSUser create(VOMSUser usr) {

        return create(usr, null);

    }

    public VOMSUserAttribute createAttribute(VOMSUser u, String attrName, String attrDesc, String value) {

        if (u.getAttributeByName(attrName) != null)
            throw new AttributeAlreadyExistsException(
                    "Attribute \"" + attrName + "\" already defined for user \"" + u + "\".");

        VOMSAttributeDescription desc = VOMSAttributeDAO.instance().getAttributeDescriptionByName(attrName);

        if (desc == null)
            desc = VOMSAttributeDAO.instance().createAttributeDescription(attrName, attrDesc);

        log.debug("Creating attribute \"(" + attrName + "," + value + ")\" for user \"" + u + "\".");
        VOMSUserAttribute val = VOMSUserAttribute.instance(desc, value, u);
        u.addAttribute(val);
        return val;

    }

    public VOMSUser delete(Long userId) {

        VOMSUser u = findById(userId);

        if (u == null)
            throw new NoSuchUserException("User identified by \"" + userId + "\" not found in database!");

        try {

            delete(u);
            return u;

        } catch (ObjectNotFoundException e) {
            // Still don't understand why sometimes findById fails in returnin
            // null...
            throw new NoSuchUserException("User identified by \"" + userId + "\" not found in database!");
        }
    }

    public VOMSUser delete(VOMSUser u) {

        log.debug("Deleting user \"{}\"", u);

        DAOFactory.instance().getRequestDAO().deleteRequestFromUser(u);

        u.getCertificates().clear();
        u.cleanMappings();
        u.getAttributes().clear();
        u.getAupAcceptanceRecords().clear();
        u.getPersonalInformations().clear();
        u.getTasks().clear();

        HibernateFactory.getSession().delete(u);

        return u;

    }

    public void deleteAll() {

        Iterator<VOMSUser> users = findAll().iterator();

        while (users.hasNext()) {
            delete(users.next());
        }

    }

    public VOMSUserAttribute deleteAttribute(VOMSUser u, String attrName) {

        VOMSUserAttribute attribute = u.getAttributeByName(attrName);

        if (attribute == null) {
            throw new NoSuchAttributeException(
                    "Attribute \"" + attrName + "\" not defined for user \"" + u + "\".");
        }

        log.debug("Deleting attribute \"{}\" for user \"{}\".", attrName, u);

        u.deleteAttribute(attribute);
        HibernateFactory.getSession().update(u);
        return attribute;

    }

    public void deleteCertificate(Certificate cert) {

        Validate.notNull(cert, "Cannot delete a null certificate");

        VOMSUser u = cert.getUser();
        deleteCertificate(u, cert);
    }

    public void deleteCertificate(VOMSUser u, Certificate cert) {

        Validate.notNull(u, "Please provide a non-null user.");
        Validate.notNull(cert, "Cannot delete a null certificate.");

        if (u.getCertificates().size() == 1 && u.hasCertificate(cert))
            throw new VOMSException("User has only one certificate registered, so it cannot be removed!");

        if (!u.getCertificates().remove(cert)) {
            // This should never happen
            throw new VOMSDatabaseException(
                    "Inconsistent database! It was not possible to remove certificate '" + cert + "' from user '"
                            + u + "', even if it seemed user actually possessed such certificate.");
        }

    }

    public void dismissRole(VOMSUser u, VOMSGroup g, VOMSRole r) {

        u.dismissRole(g, r);
        HibernateFactory.getSession().update(u);

    }

    public VOMSUser findBySubject(String subject) {

        if (subject == null)
            throw new NullArgumentException("subject cannot be null!");

        String query = "select u from org.glite.security.voms.admin.persistence.model.VOMSUser u join u.certificates c where c.subjectString = :subjectString";

        Query q = HibernateFactory.getSession().createQuery(query);

        q.setString("subjectString", subject);

        VOMSUser u = (VOMSUser) q.uniqueResult();

        return u;

    }

    public VOMSUser findBySubjectAndIssuer(String subject, String issuer) {

        if (subject == null) {
            throw new NullArgumentException("subject cannot be null!");
        }

        if (issuer == null) {
            throw new NullArgumentException("issuer cannot be null!");
        }

        String normalizedSubject = DNUtil.normalizeDN(subject);
        String normalizedIssuer = DNUtil.normalizeDN(issuer);

        String query = "select u from VOMSUser u join u.certificates c where c.subjectString = :subjectString and c.ca.subjectString = :issuerSubjectString";

        Query q = HibernateFactory.getSession().createQuery(query);

        q.setString("subjectString", normalizedSubject);
        q.setString("issuerSubjectString", normalizedIssuer);

        VOMSUser u = (VOMSUser) q.uniqueResult();

        return u;
    }

    public VOMSUser findByEmail(String emailAddress) {

        if (emailAddress == null)
            throw new NullArgumentException("emailAddress must be non-null!");

        String query = "from org.glite.security.voms.admin.persistence.model.VOMSUser as u  where u.emailAddress = :emailAddress";
        Query q = HibernateFactory.getSession().createQuery(query);

        q.setString("emailAddress", emailAddress);

        return (VOMSUser) q.uniqueResult();

    }

    public VOMSUser findById(Long userId) {

        return (VOMSUser) HibernateFactory.getSession().get(VOMSUser.class, userId);
    }

    public List<VOMSUser> findByOrgdbId(Long orgdbId) {
        CriteriaBuilder builder = HibernateFactory.getSession().getCriteriaBuilder();

        CriteriaQuery<VOMSUser> query = builder.createQuery(VOMSUser.class);
        Root<VOMSUser> userRoot = query.from(VOMSUser.class);
        query.where(builder.equal(userRoot.get("orgDbId"), orgdbId));

        return HibernateFactory.getSession().createQuery(query).getResultList();
    }

    public ScrollableResults findAllWithCursor() {

        Query q = HibernateFactory.getSession().createQuery("select u from VOMSUser u order by u.surname asc");

        q.setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY);

        return q.scroll();

    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findAll() {

        Query q = HibernateFactory.getSession().createQuery("select u from VOMSUser u order by u.surname asc");

        List<VOMSUser> result = q.list();

        return result;
    }

    private SearchResults paginatedFind(Query q, Query countQuery, String searchString, int firstResults,
            int maxResults) {

        SearchResults res = SearchResults.instance();

        res.setSearchString(searchString);

        q.setFirstResult(firstResults);
        q.setMaxResults(maxResults);

        res.setResults(q.list());

        Long count = (Long) countQuery.uniqueResult();

        res.setCount(count.intValue());

        res.setFirstResult(firstResults);
        res.setResultsPerPage(maxResults);

        return res;
    }

    private String getUserOrderClause() {

        // If endTime is ignored by configuration, we should not consider
        // it for sorting the search results
        boolean endTimeDisabled = VOMSConfiguration.instance()
                .getBoolean(VOMSConfigurationConstants.DISABLE_MEMBERSHIP_END_TIME, false);

        if (endTimeDisabled) {
            return "order by u.surname asc";
        } else {
            return "order by u.endTime asc, u.surname asc";
        }
    }

    public SearchResults findAll(int firstResults, int maxResults) {

        String query = "from VOMSUser as u " + getUserOrderClause();

        Query q = HibernateFactory.getSession().createQuery(query);

        Query countQ = HibernateFactory.getSession().createQuery("select count(*) from VOMSUser");

        return paginatedFind(q, countQ, null, firstResults, maxResults);
    }

    public List<VOMSRole> getUnAssignedRoles(Long userId, Long groupId) {

        VOMSUser u = findById(userId);

        VOMSGroup g = VOMSGroupDAO.instance().findById(groupId);

        String query = "from VOMSRole r where r not in " + "(select m.role from VOMSMapping m where m.user = :user "
                + "and m.group = :group and m.role is not null)";

        Query<VOMSRole> hq = HibernateFactory.getSession().createQuery(query, VOMSRole.class);
        hq.setParameter("user", u);
        hq.setParameter("group", g);

        List<VOMSRole> result = hq.getResultList();

        return result;
    }

    public List<VOMSGroup> getUnsubscribedGroups(Long userId) {

        VOMSUser u = findById(userId);

        String query = "from VOMSGroup g where g not in "
                + "(select m.group from VOMSMapping m where m.user = :user " + "and m.role is null)";

        Query<VOMSGroup> hq = HibernateFactory.getSession().createQuery(query, VOMSGroup.class);
        hq.setParameter("user", u);
        List<VOMSGroup> result = hq.getResultList();
        return result;
    }

    public void removeFromGroup(VOMSUser u, VOMSGroup g) {

        log.debug("Removing user \"" + u + "\" from group \"" + g + "\".");

        if (VOMSGroupDAO.instance().findByName(g.getName()) == null)
            throw new NoSuchGroupException("Group \"" + g + "\" is not defined in database.");

        u.removeFromGroup(g);

    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findSuspendedUsers() {

        String queryString = "from VOMSUser u where u.suspended = true";
        Query q = HibernateFactory.getSession().createQuery(queryString);

        return q.list();
    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findSuspendedUsers(int firstResult, int maxResults) {

        String queryString = "from VOMSUser u where u.suspended = true";
        Query q = HibernateFactory.getSession().createQuery(queryString);

        return q.list();
    }

    public Long countSuspendedUsers() {

        String queryString = "select count(*) from VOMSUser u where u.suspended = true";
        Query q = HibernateFactory.getSession().createQuery(queryString);

        return (Long) q.uniqueResult();

    }

    public SearchResults searchExpired(String searchString, int firstResults, int maxResults) {

        log.debug("searchString:" + searchString + ",firstResults: " + firstResults + ",maxResults: " + maxResults);

        if (searchString == null || searchString.trim().equals("") || searchString.length() == 0)
            return findAll(firstResults, maxResults);

        SearchResults res = SearchResults.instance();

        String sString = "%" + searchString + "%";

        String queryString = "select distinct u from VOMSUser u join u.certificates as cert where u.expired = true and (lower(u.surname) like lower(:searchString) "
                + "or lower(u.name) like lower(:searchString) or u.emailAddress like :searchString "
                + " or lower(u.institution) like lower(:searchString) "
                + " or cert.subjectString like(:searchString) or cert.ca.subjectString like(:searchString))"
                + getUserOrderClause();

        Query q = HibernateFactory.getSession().createQuery(queryString);

        q.setString("searchString", sString);
        q.setFirstResult(firstResults);
        q.setMaxResults(maxResults);

        res.setCount(countMatches(searchString));
        res.setFirstResult(firstResults);
        res.setResultsPerPage(maxResults);
        res.setResults(q.list());
        res.setSearchString(searchString);

        return res;
    }

    public SearchResults searchWithPendingAUPRequest(String searchString, int firstResults, int maxResults) {

        AUP aup = DAOFactory.instance().getAUPDAO().getVOAUP();

        String queryString = "from VOMSUser u join u.tasks as t join u.certificates as cert "
                + "where t.class = SignAUPTask and t.status != 'COMPLETED' " + "and t.aup = :aup";

        String commonSearchString = "and (lower(u.surname) like lower(:searchString) "
                + "or lower(u.name) like lower(:searchString) " + "or u.emailAddress like :searchString "
                + "or lower(u.institution) like lower(:searchString) "
                + "or cert.subjectString like(:searchString) " + "or cert.ca.subjectString like(:searchString))";

        if (searchStringEmpty(searchString)) {

            Query q = HibernateFactory.getSession().createQuery("select distinct u " + queryString).setEntity("aup",
                    aup);

            String countQuery = String.format("select count(distinct u) %s", queryString);
            Query countQ = HibernateFactory.getSession().createQuery(countQuery).setEntity("aup", aup);

            return paginatedFind(q, countQ, null, firstResults, maxResults);
        } else {
            String sString = "%" + searchString + "%";
            String commonQueryPart = String.format("%s %s", queryString, commonSearchString);

            Query q = HibernateFactory.getSession()
                    .createQuery(String.format("select distinct u %s", commonQueryPart)).setEntity("aup", aup);

            Query count = HibernateFactory.getSession()
                    .createQuery(String.format("select count(distinct u) %s", commonQueryPart))
                    .setEntity("aup", aup);

            q.setString("searchString", sString);
            count.setString("searchString", sString);

            return paginatedFind(q, count, searchString, firstResults, maxResults);
        }

    }

    public SearchResults searchSuspended(String searchString, int firstResults, int maxResults) {

        if (searchStringEmpty(searchString)) {

            Query q = HibernateFactory.getSession().createQuery("from VOMSUser u where u.suspended = true");
            Query countQ = HibernateFactory.getSession()
                    .createQuery("select count(*) from VOMSUser u where u.suspended = true");

            return paginatedFind(q, countQ, null, firstResults, maxResults);

        } else {

            String sString = "%" + searchString + "%";

            String commonQueryPart = "from VOMSUser u join u.certificates as cert where u.suspended = true and (lower(u.surname) like lower(:searchString) "
                    + "or lower(u.name) like lower(:searchString) or u.emailAddress like :searchString "
                    + " or lower(u.institution) like lower(:searchString) "
                    + " or cert.subjectString like(:searchString) or cert.ca.subjectString like(:searchString))"
                    + getUserOrderClause();

            // FIXME: this is *UGLY*, use Criteria API instead
            Query q = HibernateFactory.getSession()
                    .createQuery(String.format("select distinct u %s", commonQueryPart));
            Query count = HibernateFactory.getSession()
                    .createQuery(String.format("select count(*) %s", commonQueryPart));

            q.setString("searchString", sString);
            count.setString("searchString", sString);

            return paginatedFind(q, count, searchString, firstResults, maxResults);

        }

    }

    private boolean searchStringEmpty(String searchString) {

        return (searchString == null || searchString.trim().equals("") || searchString.length() == 0);

    }

    public SearchResults search(String searchString, int firstResults, int maxResults) {

        log.debug("searchString:" + searchString + ",firstResults: " + firstResults + ",maxResults: " + maxResults);

        if (searchStringEmpty(searchString))
            return findAll(firstResults, maxResults);

        String sString = "%" + searchString + "%";

        String commonPart = "from VOMSUser u join u.certificates as cert where lower(u.surname) like lower(:searchString) "
                + "or lower(u.name) like lower(:searchString) or u.emailAddress like :searchString "
                + " or lower(u.institution) like lower(:searchString) "
                + " or cert.subjectString like(:searchString) or cert.ca.subjectString like(:searchString) "
                + getUserOrderClause();

        Query q = HibernateFactory.getSession().createQuery(String.format("select distinct u %s", commonPart));
        Query count = HibernateFactory.getSession().createQuery(String.format("select count(*) %s", commonPart));

        q.setString("searchString", sString);
        count.setString("searchString", sString);

        return paginatedFind(q, count, searchString, firstResults, maxResults);

    }

    public VOMSUserAttribute setAttribute(VOMSUser u, String attrName, String attrValue) {

        VOMSAttributeDescription desc = VOMSAttributeDAO.instance().getAttributeDescriptionByName(attrName);

        log.debug("AttributeDescription:" + desc);

        if (desc == null)
            throw new NoSuchAttributeException("Attribute '" + attrName + "' is not defined in this vo.");

        if (!VOMSAttributeDAO.instance().isAttributeValueAlreadyAssigned(u, desc, attrValue)) {

            VOMSUserAttribute val = u.getAttributeByName(desc.getName());
            if (val == null) {
                val = VOMSUserAttribute.instance(desc, attrValue, u);
                u.addAttribute(val);
            } else
                val.setValue(attrValue);

            return val;
        }

        throw new AttributeValueAlreadyAssignedException("Value '" + attrValue + "' for attribute '" + attrName
                + "' has been already assigned to another user in this vo! Choose a different value.");

    }

    public VOMSUser update(VOMSUser u) {

        HibernateFactory.getSession().update(u);
        return u;

    }

    public Long countExpiringUsers(Integer intervalInDays) {

        Criteria crit = HibernateFactory.getSession().createCriteria(VOMSUser.class);

        Calendar cal = Calendar.getInstance();

        Date now = cal.getTime();
        cal.add(Calendar.DAY_OF_YEAR, intervalInDays);

        Date intervalDate = cal.getTime();

        crit.add(Restrictions.between("endTime", now, intervalDate));
        crit.add(Restrictions.eq("suspended", false));

        crit.setProjection(Projections.rowCount());

        return (Long) crit.list().get(0);

    }

    @SuppressWarnings("unchecked")
    public List<VOMSUser> findExpiringUsers(Integer intervalInDays) {

        Criteria crit = HibernateFactory.getSession().createCriteria(VOMSUser.class);

        Calendar cal = Calendar.getInstance();

        Date now = cal.getTime();
        cal.add(Calendar.DAY_OF_YEAR, intervalInDays);

        Date intervalDate = cal.getTime();

        crit.add(Restrictions.between("endTime", now, intervalDate));
        crit.add(Restrictions.eq("suspended", false));
        crit.addOrder(Order.asc("endTime"));

        return crit.list();

    }

    public VOMSUser lookup(String certificateSubject, String certificateIssuer) {

        return LookupPolicyProvider.instance().lookupStrategy().lookup(this, certificateSubject, certificateIssuer);

    }

}