Package org.exist.security.internal

Source Code of org.exist.security.internal.SecurityManagerImpl

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-2011 The eXist Project
*  http://exist-db.org
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*  $Id$
*/
package org.exist.security.internal;

import org.exist.scheduler.JobDescription;
import org.exist.security.AbstractRealm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.apache.log4j.Logger;
import org.exist.Database;
import org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.config.Configuration;
import org.exist.config.Configurator;
import org.exist.config.ConfigurationException;
import org.exist.config.annotation.*;
import org.exist.dom.DocumentImpl;
import org.exist.security.AuthenticationException;
import org.exist.security.Group;
import org.exist.security.PermissionDeniedException;
import org.exist.security.PermissionFactory;
import org.exist.security.SecurityManager;
import org.exist.security.Session;
import org.exist.security.Subject;
import org.exist.security.Account;
import org.exist.security.Permission;
import org.exist.security.Principal;
import org.exist.security.SchemaType;
import org.exist.security.internal.aider.GroupAider;
import org.exist.security.realm.Realm;
import org.exist.security.xacml.ExistPDP;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.util.hashtable.Int2ObjectHashMap;
import org.exist.xmldb.XmldbURI;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SimpleTrigger;

/**
* SecurityManager is responsible for managing users and groups.
*
* There's only one SecurityManager for each database instance, which
* may be obtained by {@link BrokerPool#getSecurityManager()}.
*
* Users and groups are stored in the system collection, in document
* users.xml. While it is possible to edit this file by hand, it
* may lead to unexpected results, since SecurityManager reads
* users.xml only during database startup and shutdown.
*/
//<!-- Central user configuration. Editing this document will cause the security to reload and update its internal database. Please handle with care! -->
@ConfigurationClass("security-manager")
public class SecurityManagerImpl implements SecurityManager {


    public final static int MAX_USER_ID = 1048571//1 less than RealmImpl.UNKNOWN_ACCOUNT_ID
    public final static int MAX_GROUP_ID = 1048572; //1 less than RealmImpl.UNKNOWN_GROUP_ID

    public static final String PROPERTY_PERMISSIONS_COLLECTIONS = "indexer.permissions.collection";
    public static final String PROPERTY_PERMISSIONS_RESOURCES = "indexer.permissions.resource"

    public final static Logger LOG = Logger.getLogger(SecurityManager.class);

    private Database pool;

    protected PrincipalDbById<Group> groupsById = new PrincipalDbById<Group>();
    protected PrincipalDbById<Account> usersById = new PrincipalDbById<Account>();

    private final PrincipalLocks<Account> accountLocks = new PrincipalLocks<Account>();
    private final PrincipalLocks<Group> groupLocks = new PrincipalLocks<Group>();

    //TODO: validate & remove if session timeout
    private SessionDb sessions = new SessionDb();

    @ConfigurationFieldAsAttribute("last-account-id")
    protected int lastUserId = 0;

    @ConfigurationFieldAsAttribute("last-group-id")
    protected int lastGroupId = 0;

    @ConfigurationFieldAsAttribute("version")
    private String version = "2.0";

    @ConfigurationFieldAsElement("authentication-entry-point")
    public final static String authenticationEntryPoint = "/authentication/login";
   
    //@ConfigurationField("enableXACML")
    private Boolean enableXACML = false;

    private ExistPDP pdp;
   
    private RealmImpl defaultRealm;
   
    @ConfigurationFieldAsElement("realm")
    @ConfigurationFieldClassMask("org.exist.security.realm.%1$s.%2$sRealm")
    private List<Realm> realms = new ArrayList<Realm>();
   
    @ConfigurationFieldAsElement("events")
    //@ConfigurationFieldClassMask("org.exist.security.internal.SMEvents")
    private SMEvents events = null;
   
    private Collection collection = null;
   
    private Configuration configuration = null;
   
    public SecurityManagerImpl(Database db) throws ConfigurationException {
      this.pool = db;
     
      defaultRealm = new RealmImpl(this, null); //TODO: in-memory configuration???
      realms.add(defaultRealm);

      PermissionFactory.sm = this;
     
        final Properties params = new Properties();
        params.put(getClass().getName(), this);
      pool.getScheduler().createPeriodicJob(TIMEOUT_CHECK_PERIOD, new SessionsCheck(), TIMEOUT_CHECK_PERIOD, params, SimpleTrigger.REPEAT_INDEFINITELY, false);
    }

    /**
     * Initialize the security manager.
     *
     * Checks if the file users.xml exists in the system collection of the database.
     * If not, it is created with two default users: admin and guest.
     * 
     * @param pool
     * @param broker
     */
    @Override
    public void attach(BrokerPool pool, DBBroker broker) throws EXistException {
//      groups = new Int2ObjectHashMap<Group>(65);
//      users = new Int2ObjectHashMap<User>(65);

      this.pool = pool;
     
        final TransactionManager transaction = pool.getTransactionManager();
        Txn txn = null;
   
        Collection systemCollection = null;
        try {
          systemCollection = broker.getCollection(XmldbURI.SYSTEM_COLLECTION_URI);
                if(systemCollection == null) {
                    txn = transaction.beginTransaction();
                    systemCollection = broker.getOrCreateCollection(txn, XmldbURI.SYSTEM_COLLECTION_URI);
                    if (systemCollection == null)
                            {return;}
                    systemCollection.setPermissions(Permission.DEFAULT_SYSTEM_COLLECTION_PERM);
                    broker.saveCollection(txn, systemCollection);

                    transaction.commit(txn);
                }
        } catch (final Exception e) {
            transaction.abort(txn);
            e.printStackTrace();
            LOG.debug("loading acl failed: " + e.getMessage());
        } finally {
            transaction.close(txn);
        }

        try {
            collection = broker.getCollection(SECURITY_COLLECTION_URI);
            if (collection == null) {
                txn = transaction.beginTransaction();
                collection = broker.getOrCreateCollection(txn, SECURITY_COLLECTION_URI);
                if (collection == null){
                    return;
                }
                //if db corrupted it can lead to unrunnable issue
                //throw new ConfigurationException("Collection '/db/system/security' can't be created.");

                collection.setPermissions(Permission.DEFAULT_SYSTEM_SECURITY_COLLECTION_PERM);
                broker.saveCollection(txn, collection);

                transaction.commit(txn);
            }
        } catch (final Exception e) {
            transaction.abort(txn);
            e.printStackTrace();
            LOG.debug("loading configuration failed: " + e.getMessage());
        } finally {
            transaction.close(txn);
        }
     
        final Configuration _config_ = Configurator.parse(this, broker, collection, CONFIG_FILE_URI);
        configuration = Configurator.configure(this, _config_);


        for (final Realm realm : realms) {
            realm.start(broker);
        }
      
        enableXACML = (Boolean)broker.getConfiguration().getProperty("xacml.enable");
        if(enableXACML != null && enableXACML.booleanValue()) {
            pdp = new ExistPDP(pool);
            LOG.debug("XACML enabled");
        }
    }
   
    @Override
    public boolean isXACMLEnabled() {
        return pdp != null;
    }
   
    @Override
    public ExistPDP getPDP() {
        return pdp;
    }
 
    @Override
    public boolean updateAccount(Account account) throws PermissionDeniedException, EXistException {
        if (account == null) {
            return false;
        }

        if (account.getRealmId() == null) {
            throw new ConfigurationException("Account must have realm id.");
        }
       
        accountLocks.getWriteLock(account).lock();
        try {
            return findRealmForRealmId(account.getRealmId()).updateAccount(account);
        } finally {
            accountLocks.getWriteLock(account).unlock();
        }
    }

    @Override
    public boolean updateGroup(Group group) throws PermissionDeniedException, EXistException {
        if (group == null) {
            return false;
        }

        if (group.getRealmId() == null) {
            throw new ConfigurationException("Group must have realm id.");
        }

        groupLocks.getWriteLock(group).lock();
        try {
            return findRealmForRealmId(group.getRealmId()).updateGroup(group);
        } finally {
            groupLocks.getWriteLock(group).unlock();
        }
       
    }


    @Override
    public boolean deleteGroup(String name) throws PermissionDeniedException, EXistException {

        final Group group = getGroup(name);
        if (group == null) {
            return false;
        }

        if (group.getRealmId() == null) {
            throw new ConfigurationException("Group must have realm id.");
        }
       
        groupLocks.getWriteLock(group).lock();
        try {
            return findRealmForRealmId(group.getRealmId()).deleteGroup(group);
        } finally {
            groupLocks.getWriteLock(group).unlock();
        }
    }

    @Override
    public boolean deleteAccount(String name) throws PermissionDeniedException, EXistException {
        return deleteAccount(getAccount(name));
    }
 
    @Override
    public boolean deleteAccount(Account account) throws PermissionDeniedException, EXistException {

        if (account == null)
            {return false;}

        if (account.getRealmId() == null) {
            throw new ConfigurationException("Account must have realm id.");
        }
       
        accountLocks.getWriteLock(account).lock();
        try {
            return findRealmForRealmId(account.getRealmId()).deleteAccount(account);
        } finally {
            accountLocks.getWriteLock(account).unlock();
        }
    }

    @Override
    public Account getAccount(String name) {
//      if (SYSTEM.equals(name))
//        return defaultRealm.ACCOUNT_SYSTEM;
     
        for(final Realm realm : realms) {
            final Account account = realm.getAccount(name);
            if (account != null) {
                return account;
            }
        }
       
        LOG.debug("Account for '" + name + "' not found!");
        return null;
    }

    @Override
    public final Account getAccount(final int id) {

        return usersById.read(new PrincipalDbRead<Account, Account>(){
            @Override
            public Account execute(final Int2ObjectHashMap<Account> principalDb) {
                return principalDb.get(id);
            }
        });
    }

    @Override
    public boolean hasGroup(String name) {
      for (final Realm realm : realms) {
            if(realm.hasGroup(name)) {
                return true;
            }
      }
      return false;
    }

    @Override
    public boolean hasGroup(Group group) {
      return hasGroup(group.getName());
    }

    @Override
    public Group getGroup(String name) {
      for(final Realm realm : realms) {
            final Group group = realm.getGroup(name);
            if(group != null){
                return group;
            }
      }
        return null;
    }

    @Override
    public final Group getGroup(final int id) {
        return groupsById.read(new PrincipalDbRead<Group, Group>(){
            @Override
            public Group execute(final Int2ObjectHashMap<Group> principalDb) {
                return principalDb.get(id);
            }
        });
    }
 
    @Override
    public boolean hasAdminPrivileges(Account user) {
       
        accountLocks.getReadLock(user).lock();
        try {
            return user.hasDbaRole();
        } finally {
            accountLocks.getReadLock(user).unlock();
        }
    }

    @Override
    public boolean hasAccount(String name) {
      for(final Realm realm : realms) {
            if(realm.hasAccount(name)) {
                return true;
            }
      }
      return false;
    }

    @Override
    public Subject authenticate(final String username, final Object credentials) throws AuthenticationException {
        if (LOG.isDebugEnabled())
            {LOG.debug("Authentication try for '"+username+"'.");}

        if (username == null)
          {throw new AuthenticationException(
              AuthenticationException.ACCOUNT_NOT_FOUND, "Account NULL not found");}

        if("jsessionid".equals(username)) {
       
        if (getSystemSubject().getSessionId().equals(credentials))
          {return getSystemSubject();}

        if (getGuestSubject().getSessionId().equals(credentials))
          {return getGuestSubject();}

            final Subject subject = sessions.read(new SessionDbRead<Subject>(){
                @Override
                public Subject execute(final Map<String, Session> db) {
                 
                  Session session = db.get((String)credentials);
                  if (session == null) return null;
                 
                  if (session.isValid())
                    return session.getSubject();
                 
                  return null;
                }
            });

            if(subject == null)
                {throw new AuthenticationException(AuthenticationException.SESSION_NOT_FOUND, "Session [" + credentials + "] not found");}

            if (events != null)
              {events.authenticated(subject);}
           
            //TODO: validate session
            return subject;
        }

        for(final Realm realm : realms) {
            try {
              final Subject subject = realm.authenticate(username, credentials);
             
                if (LOG.isDebugEnabled())
                  {LOG.debug("Authenticated by '"+realm.getId()+"' as '"+subject+"'.");}
               
                if (events != null)
                  {events.authenticated(subject);}

                return subject;
            } catch(final AuthenticationException e) {
                if(e.getType() != AuthenticationException.ACCOUNT_NOT_FOUND) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Realm '"+realm.getId()+"' threw exception for account '"+username+"'. ["+e.getMessage()+"]");
                    }

                    throw e;
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Account '"+username+"' not found, throw error");
        }

        throw new AuthenticationException(
        AuthenticationException.ACCOUNT_NOT_FOUND,
        "Account [" + username + "] not found");
    }
   
    protected Subject systemSubject = null;
    protected Subject guestSubject = null;
 
    @Override
    public Subject getSystemSubject() {
      if (systemSubject == null)
        {systemSubject = new SubjectAccreditedImpl((AccountImpl) defaultRealm.ACCOUNT_SYSTEM, this);}
     
        return systemSubject;
    }

    @Override
    public Subject getGuestSubject() {
      if (guestSubject == null)
        {guestSubject = new SubjectAccreditedImpl((AccountImpl)defaultRealm.getAccount(SecurityManager.GUEST_USER), this);}
     
      return guestSubject;
    }

    @Override
    public Group getDBAGroup() {
        return defaultRealm.GROUP_DBA;
    }

    @Override
    public Database getDatabase() {
        return pool;
    }

    private synchronized int getNextGroupId() {
        if(lastGroupId + 1 == MAX_GROUP_ID) {
            throw new RuntimeException("System has no more group-ids available");           
        }
        return ++lastGroupId;
    }

    private synchronized int getNextAccountId() {
        if(lastUserId +1 == MAX_USER_ID) {
            throw new RuntimeException("System has no more user-ids available");
        }
        return ++lastUserId;
    }

    @Override
    public List<Account> getGroupMembers(String groupName) {

        final List<Account> groupMembers = new ArrayList<Account>();

        for(final Realm realm : realms) {
            for(final Account account : realm.getAccounts()) {
                if(account.hasGroup(groupName)) {
                    groupMembers.add(account);
                }
            }
        }

        return groupMembers;
    }

    @Override
    public List<String> findAllGroupMembers(String groupName) {
        final List<String> userNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            userNames.addAll(realm.findAllGroupMembers(groupName));
        }
        return userNames;
    }

    @Deprecated //use realm's getAccounts
    @Override
    public java.util.Collection<Account> getUsers() {
        return defaultRealm.getAccounts();
    }

    @Deprecated //use realm's getGroups
    @Override
    public java.util.Collection<Group> getGroups() {
        return defaultRealm.getGroups();
    }
   
    @Override
    public void addGroup(String name) throws PermissionDeniedException, EXistException {
        addGroup(new GroupAider(name));
    }

    @Override
    public Group addGroup(Group group) throws PermissionDeniedException, EXistException {
        if(group.getRealmId() == null) {
            throw new ConfigurationException("Group must have realm id.");
        }

        if(group.getName() == null || group.getName().isEmpty()) {
            throw new ConfigurationException("Group must have name.");
        }

        final int id;
        if(group.getId() != Group.UNDEFINED_ID) {
            id = group.getId();
        } else {
            id = getNextGroupId();
        }
       
        final AbstractRealm registeredRealm = (AbstractRealm)findRealmForRealmId(group.getRealmId());
        if (registeredRealm.hasGroup(group.getName())) {
            throw new ConfigurationException("The group '"+group.getName()+"' at realm '" + group.getRealmId() + "' already exist.");
        }
       
        final GroupImpl newGroup = new GroupImpl(registeredRealm, id, group.getName(), group.getManagers());
        for(final SchemaType metadataKey : group.getMetadataKeys()) {
            final String metadataValue = group.getMetadataValue(metadataKey);
            newGroup.setMetadataValue(metadataKey, metadataValue);
        }
       
        groupLocks.getWriteLock(newGroup).lock();
        try {
            groupsById.modify(new PrincipalDbModify<Group>(){
                @Override
                public void execute(final Int2ObjectHashMap<Group> principalDb) {
                    principalDb.put(id, newGroup);
                }
            });
           
            registeredRealm.registerGroup(newGroup);

            save();
            newGroup.save();

            return newGroup;
        } finally {
            groupLocks.getWriteLock(newGroup).unlock();
        }
    }

    @Override
    public final Account addAccount(Account account) throws  PermissionDeniedException, EXistException{
        if(account.getRealmId() == null) {
          LOG.debug("Account must have realm id.");
            throw new ConfigurationException("Account must have realm id.");
        }
   
        if(account.getName() == null || account.getName().isEmpty()) {
          LOG.debug("Account must have name.");
            throw new ConfigurationException("Account must have name.");
        }
   
        final int id;
        if(account.getId() != Account.UNDEFINED_ID) {
            id = account.getId();
        } else {
            id = getNextAccountId();
        }

    final AbstractRealm registeredRealm = (AbstractRealm) findRealmForRealmId(account.getRealmId());
    final AccountImpl newAccount = new AccountImpl(registeredRealm, id, account);
 
        accountLocks.getWriteLock(newAccount).lock();
        try {
            usersById.modify(new PrincipalDbModify<Account>(){
                @Override
                public void execute(final Int2ObjectHashMap<Account> principalDb) {
                    principalDb.put(id, newAccount);
                }
            });
           
            registeredRealm.registerAccount(newAccount);

            //XXX: one transaction?
            save();
            newAccount.save();

            return newAccount;
        } finally {
            accountLocks.getWriteLock(newAccount).unlock();
        }
    }
   
    @Override
    public final Account addAccount(DBBroker broker, Account account) throws  PermissionDeniedException, EXistException{
        if(account.getRealmId() == null) {
            throw new ConfigurationException("Account must have realm id.");
        }
   
        if(account.getName() == null || account.getName().isEmpty()) {
            throw new ConfigurationException("Account must have name.");
        }
   
        final int id;
        if(account.getId() != Account.UNDEFINED_ID) {
            id = account.getId();
        } else {
            id = getNextAccountId();
        }

        final AbstractRealm registeredRealm = (AbstractRealm) findRealmForRealmId(account.getRealmId());
        final AccountImpl newAccount = new AccountImpl(broker, registeredRealm, id, account);

        accountLocks.getWriteLock(newAccount).lock();
        try {
            usersById.modify(new PrincipalDbModify<Account>(){
                @Override
                public void execute(final Int2ObjectHashMap<Account> principalDb) {
                    principalDb.put(id, newAccount);
                }
            });
           
            registeredRealm.registerAccount(newAccount);

            //XXX: one transaction?
            save(broker);
            newAccount.save(broker);

            return newAccount;
        } finally {
            accountLocks.getWriteLock(newAccount).unlock();
        }
    }
 
    private void save() throws PermissionDeniedException, EXistException {
        if (configuration != null) {
            configuration.save();
        }
    }
       
    private void save(DBBroker broker) throws PermissionDeniedException, EXistException {
        if (configuration != null) {
            configuration.save(broker);
        }
    }

    @Override
    public boolean isConfigured() {
            return configuration != null;
    }

    @Override
    public Configuration getConfiguration() {
    return configuration;
    }
   
    //Session management part 
   
    public final static long TIMEOUT_CHECK_PERIOD = 20000; //20 sec

    public static class SessionsCheck implements JobDescription, org.quartz.Job {
     
      boolean firstRun = true;
     
      public SessionsCheck() {
    }

        public String getGroup() {
          return "eXist.Security";
        }

      @Override
        public String getName() {
            return "Sessions.Check";
        }

        @Override
        public void setName(String name) {
        }

        @Override
        public final void execute( JobExecutionContext jec ) throws JobExecutionException {
            final JobDataMap jobDataMap = jec.getJobDetail().getJobDataMap();

            final Properties params = (Properties) jobDataMap.get("params");
            if (params == null) {
                return;
            }
            final SecurityManagerImpl sm = ( SecurityManagerImpl )params.get( SecurityManagerImpl.class.getName() );

            if (sm == null)
              {return;}
           
            sm.sessions.modify(new SessionDbModify(){
              @Override
              public void execute(final Map<String, Session> db) {
                final Iterator<Map.Entry<String, Session>> iter = db.entrySet().iterator();
                while (iter.hasNext()) {
                  final Map.Entry<String, Session> entry = iter.next();
                  if (entry == null || !entry.getValue().isValid()) {
                    iter.remove();
                  }
                }
              }
          });
      }
    }

    @Override
    public void registerSession(final Session session) {
        sessions.modify(new SessionDbModify(){
            @Override
            public void execute(final Map<String, Session> db) {
                db.put(session.getId(), session);
            }
        });
    }

    @Override
    public Subject getSubjectBySessionId(final String sessionId) {
        return sessions.read(new SessionDbRead<Subject>(){
            @Override
            public Subject execute(final Map<String, Session> db) {
                return db.get(sessionId).getSubject();
            }
        });
    }
       
    private Realm findRealmForRealmId(String realmId) throws ConfigurationException {
        for(final Realm realm : realms) {
            if(realm.getId().equals(realmId)) {
                return realm;
            }
        }
        throw new ConfigurationException("The realm id = '" + realmId + "' not found.");
    }
   
    @Override
    public void addGroup(final int id, final Group group) {
        groupsById.modify(new PrincipalDbModify<Group>(){
            @Override
            public void execute(final Int2ObjectHashMap<Group> principalDb) {
               principalDb.put(id, group);
            }
        });
    }

    @Override
    public void addUser(final int id, final Account account) {
        usersById.modify(new PrincipalDbModify<Account>(){
            @Override
            public void execute(final Int2ObjectHashMap<Account> principalDb) {
               principalDb.put(id, account);
            }
        });
    }

    @Override
    public boolean hasGroup(final int id) {
        return groupsById.read(new PrincipalDbRead<Group, Boolean>(){
            @Override
            public Boolean execute(Int2ObjectHashMap<Group> principalDb) {
                return principalDb.containsKey(id);
            }
        });
    }

    @Override
    public boolean hasUser(final int id) {
        return usersById.read(new PrincipalDbRead<Account, Boolean>(){
            @Override
            public Boolean execute(Int2ObjectHashMap<Account> principalDb) {
                return principalDb.containsKey(id);
            }
        });
    }

    @Override
    public List<String> findUsernamesWhereNameStarts(String startsWith) {
        final List<String> userNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            userNames.addAll(realm.findUsernamesWhereNameStarts(startsWith));
        }
        return userNames;
    }

    @Override
    public List<String> findUsernamesWhereUsernameStarts(String startsWith) {
        final List<String> userNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            userNames.addAll(realm.findUsernamesWhereUsernameStarts(startsWith));
        }
        return userNames;
    }
   
    @Override
    public List<String> findUsernamesWhereNamePartStarts(String startsWith) {
        final List<String> userNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            userNames.addAll(realm.findUsernamesWhereNamePartStarts(startsWith));
        }
        return userNames;
    }

    @Override
    public List<String> findGroupnamesWhereGroupnameContains(String fragment) {
        final List<String> groupNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            groupNames.addAll(realm.findGroupnamesWhereGroupnameContains(fragment));
        }
        return groupNames;
    }
   
    @Override
    public List<String> findGroupnamesWhereGroupnameStarts(String startsWith) {
        final List<String> groupNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            groupNames.addAll(realm.findGroupnamesWhereGroupnameStarts(startsWith));
        }
        return groupNames;
    }

    @Override
    public List<String> findAllGroupNames() {
        final List<String> groupNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            groupNames.addAll(realm.findAllGroupNames());
        }
        return groupNames;
    }
   
    @Override
    public List<String> findAllUserNames() {
        final List<String> userNames = new ArrayList<String>();
        for(final Realm realm : realms) {
            userNames.addAll(realm.findAllUserNames());
        }
        return userNames;
    }
   
    private Map<XmldbURI, Integer> saving = new HashMap<XmldbURI, Integer>();
   
    @Override
    public void processPramatterBeforeSave(DBBroker broker, DocumentImpl document) throws ConfigurationException {
        XmldbURI uri = document.getCollection().getURI();
       
        final boolean isRemoved = uri.endsWith(SecurityManager.REMOVED_COLLECTION_URI);
        if(isRemoved) {
            uri = uri.removeLastSegment();
        }
   
        final boolean isAccount = uri.endsWith(SecurityManager.ACCOUNTS_COLLECTION_URI);
        final boolean isGroup = uri.endsWith(SecurityManager.GROUPS_COLLECTION_URI);
   
        if(isAccount || isGroup) {
            //uri = uri.removeLastSegment();
            //String realmId = uri.lastSegment().toString();
            //AbstractRealm realm = (AbstractRealm)findRealmForRealmId(realmId);
            final Configuration conf = Configurator.parse(document);

          saving.put(document.getURI(), conf.getPropertyInteger("id"));
        }
    }

    @Override
    public void processPramatter(DBBroker broker, DocumentImpl document) throws ConfigurationException {

        XmldbURI uri = document.getCollection().getURI();
       
        //System.out.println(document);

        final boolean isRemoved = uri.endsWith(SecurityManager.REMOVED_COLLECTION_URI);
        if(isRemoved) {
            uri = uri.removeLastSegment();
        }
   
        final boolean isAccount = uri.endsWith(SecurityManager.ACCOUNTS_COLLECTION_URI);
        final boolean isGroup = uri.endsWith(SecurityManager.GROUPS_COLLECTION_URI);
   
        if(isAccount || isGroup) {
            uri = uri.removeLastSegment();

            final String realmId = uri.lastSegment().toString();
     
            final AbstractRealm realm = (AbstractRealm)findRealmForRealmId(realmId);
            final Configuration conf = Configurator.parse(document);

            Integer id = -1;
            if(isRemoved) {
                id = conf.getPropertyInteger("id");
            }

            final String name = conf.getProperty("name");

            if(isAccount) {
                if (isRemoved && id > 2 && !hasUser(id)) {
                    final AccountImpl account = new AccountImpl( realm, conf );
                    account.removed = true;
                    addUser(account.getId(), account);
                } else if(name != null) {
                  if (realm.hasAccount(name)) {
                    final Integer oldId = saving.get(document.getURI());
                   
                  final Integer newId = conf.getPropertyInteger("id");
                 
                  //XXX: resolve conflicts on ids!!!
                 
                  if (!newId.equals(oldId)) {
                        final Account current = realm.getAccount(name);
                        accountLocks.getWriteLock(current).lock();
                        try {
                            usersById.modify(new PrincipalDbModify<Account>(){
                                @Override
                                public void execute(final Int2ObjectHashMap<Account> principalDb) {
                                    principalDb.remove(oldId);
                                    principalDb.put(newId, current);
                                }
                            });
                        } finally {
                            accountLocks.getWriteLock(current).unlock();
                        }
                  }
                  } else {
                    final Account account = new AccountImpl( realm, conf );
                    addUser(account.getId(), account);
                    realm.registerAccount(account);
                  }
                } else {
                    //this can't be! log any way
                    LOG.error("Account '"+name+"' pressent at '"+realmId+"' realm, but get event that new one created.");
                }
           
            } else if(isGroup) {
                if (isRemoved && id > 2 && !hasGroup(id)) {
                    final GroupImpl group = new GroupImpl( realm, conf );
                    group.removed = true;
                    addGroup(group.getId(), group);
                } else if (name != null && !realm.hasGroup(name)) {
                    final GroupImpl group = new GroupImpl( realm, conf );
                    addGroup(group.getId(), group);
                    realm.registerGroup(group);
                } else {
                    //this can't be! log any way
                    LOG.error("Group '"+name+"' pressent at '"+realmId+"' realm, but get event that new one created.");
                }
                           
            }
            saving.remove(document.getURI());
        }
    }

    @Override
    public String getAuthenticationEntryPoint() {
            return authenticationEntryPoint;
    }

    private class PrincipalLocks<T extends Principal> {
        private final Map<Integer, ReentrantReadWriteLock> locks = new HashMap<Integer, ReentrantReadWriteLock>();
        private synchronized ReentrantReadWriteLock getLock(T principal) {
            ReentrantReadWriteLock lock = locks.get(principal.getId());
            if(lock == null) {
                lock = new ReentrantReadWriteLock();
                locks.put(principal.getId(), lock);
            }
            return lock;
        }

        public ReadLock getReadLock(T principal) {
            return getLock(principal).readLock();
        }

        public WriteLock getWriteLock(T principal) {
            return getLock(principal).writeLock();
        }
    }
  
    protected class SessionDb {
        private final Map<String, Session> db = new HashMap<String, Session>();
        private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        private final ReadLock readLock = lock.readLock();
        private final WriteLock writeLock = lock.writeLock();

        public <R> R read(final SessionDbRead<R> readOp) {
            readLock.lock();
            try {
                return readOp.execute(db);
            } finally {
                readLock.unlock();
            }
        }

        public final void modify(final SessionDbModify writeOp) {
            writeLock.lock();
            try {
                writeOp.execute(db);
            } finally {
                writeLock.unlock();
            }
        }
    }
   
    protected interface SessionDbRead<R> {
       public R execute(final Map<String, Session> db);
    }

    protected interface SessionDbModify {
        public void execute(final Map<String, Session> db);
    }
  
    protected class PrincipalDbById<V extends Principal> {
   
        private final Int2ObjectHashMap<V> db = new Int2ObjectHashMap<V>(65);
        private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        private final ReadLock readLock = lock.readLock();
        private final WriteLock writeLock = lock.writeLock();

        public <R> R read(final PrincipalDbRead<V, R> readOp) {
            readLock.lock();
            try {
                return readOp.execute(db);
            } finally {
                readLock.unlock();
            }
        }

        public final void modify(final PrincipalDbModify<V> writeOp) {
            writeLock.lock();
            try {
                writeOp.execute(db);
            } finally {
                writeLock.unlock();
            }
        }

        public final <E extends Exception> void modifyE(final PrincipalDbModifyE<V, E> writeOp) throws E {
            writeLock.lock();
            try {
                writeOp.execute(db);
            } finally {
                writeLock.unlock();
            }
        }

        public final <E extends Exception, E2 extends Exception> void modify2E(final PrincipalDbModify2E<V, E, E2> writeOp) throws E, E2 {
            writeLock.lock();
            try {
                writeOp.execute(db);
            } finally {
                writeLock.unlock();
            }
        }
    }
   
    protected interface PrincipalDbRead<V extends Principal, R> {
       public R execute(final Int2ObjectHashMap<V> principalDb);
    }

    protected interface PrincipalDbModify<V extends Principal> {
        public void execute(final Int2ObjectHashMap<V> principalDb);
    }

    protected interface PrincipalDbModifyE<V extends Principal, E extends Exception> {
        public void execute(final Int2ObjectHashMap<V> principalDb) throws E;
    }

    protected interface PrincipalDbModify2E<V extends Principal, E extends Exception, E2 extends Exception> {
        public void execute(final Int2ObjectHashMap<V> principalDb) throws E, E2;
    }

  @Override
  public Subject getCurrentSubject() {
    return pool.getSubject();
  }

    @Override
    public final synchronized void preAllocateAccountId(final PrincipalIdReceiver receiver) throws PermissionDeniedException, EXistException {
        final int id = getNextAccountId();
        save();
        receiver.allocate(id);
    }

    @Override
    public final synchronized void preAllocateGroupId(final PrincipalIdReceiver receiver) throws PermissionDeniedException, EXistException {
        final int id = getNextGroupId();
        save();
        receiver.allocate(id);
    }
}
TOP

Related Classes of org.exist.security.internal.SecurityManagerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.