001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core.authz.support;
021    
022    
023    import java.util.Collection;
024    import java.util.Collections;
025    import java.util.HashSet;
026    import java.util.Iterator;
027    
028    import javax.naming.directory.SearchControls;
029    
030    import org.apache.directory.server.core.authn.AuthenticationInterceptor;
031    import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
032    import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
033    import org.apache.directory.server.core.event.EventInterceptor;
034    import org.apache.directory.server.core.filtering.EntryFilteringCursor;
035    import org.apache.directory.server.core.interceptor.context.OperationContext;
036    import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
037    import org.apache.directory.server.core.normalization.NormalizationInterceptor;
038    import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
039    import org.apache.directory.server.core.schema.SchemaInterceptor;
040    import org.apache.directory.server.core.subtree.SubentryInterceptor;
041    import org.apache.directory.shared.ldap.aci.ACITuple;
042    import org.apache.directory.shared.ldap.aci.MicroOperation;
043    import org.apache.directory.shared.ldap.aci.ProtectedItem;
044    import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
045    import org.apache.directory.shared.ldap.constants.SchemaConstants;
046    import org.apache.directory.shared.ldap.entry.ServerEntry;
047    import org.apache.directory.shared.ldap.entry.Value;
048    import org.apache.directory.shared.ldap.filter.ExprNode;
049    import org.apache.directory.shared.ldap.filter.PresenceNode;
050    import org.apache.directory.shared.ldap.message.AliasDerefMode;
051    import org.apache.directory.shared.ldap.name.DN;
052    import org.apache.directory.shared.ldap.schema.SchemaManager;
053    
054    
055    
056    /**
057     * An {@link ACITupleFilter} that discards all tuples that doesn't satisfy
058     * {@link org.apache.directory.shared.ldap.aci.ProtectedItem.MaxImmSub} constraint if available. (18.8.3.3, X.501)
059     *
060     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
061     * @version $Rev: 927146 $, $Date: 2010-03-24 19:39:54 +0100 (Wed, 24 Mar 2010) $
062     */
063    public class MaxImmSubFilter implements ACITupleFilter
064    {
065        private final ExprNode childrenFilter;
066        private final SearchControls childrenSearchControls;
067    
068    
069        public MaxImmSubFilter()
070        {
071            childrenFilter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
072            childrenSearchControls = new SearchControls();
073            childrenSearchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
074        }
075    
076    
077        public Collection<ACITuple> filter( 
078                SchemaManager schemaManager, 
079                Collection<ACITuple> tuples, 
080                OperationScope scope, 
081                OperationContext opContext,
082                Collection<DN> userGroupNames, 
083                DN userName, 
084                ServerEntry userEntry, 
085                AuthenticationLevel authenticationLevel,
086                DN entryName, 
087                String attrId, 
088                Value<?> attrValue, 
089                ServerEntry entry, 
090                Collection<MicroOperation> microOperations,
091                ServerEntry entryView )
092            throws Exception
093        {
094            if ( entryName.size() == 0 )
095            {
096                return tuples;
097            }
098    
099            if ( tuples.size() == 0 )
100            {
101                return tuples;
102            }
103    
104            if ( scope != OperationScope.ENTRY )
105            {
106                return tuples;
107            }
108    
109            int immSubCount = -1;
110    
111            for ( Iterator<ACITuple> i = tuples.iterator(); i.hasNext(); )
112            {
113                ACITuple tuple = i.next();
114                if ( !tuple.isGrant() )
115                {
116                    continue;
117                }
118    
119                for ( ProtectedItem item : tuple.getProtectedItems() )
120                {
121                    if ( item instanceof ProtectedItem.MaxImmSub )
122                    {
123                        if ( immSubCount < 0 )
124                        {
125                            immSubCount = getImmSubCount( schemaManager, opContext, entryName );
126                        }
127    
128                        ProtectedItem.MaxImmSub mis = ( ProtectedItem.MaxImmSub ) item;
129                        if ( immSubCount >= mis.getValue() )
130                        {
131                            i.remove();
132                            break;
133                        }
134                    }
135                }
136            }
137    
138            return tuples;
139        }
140    
141        public static final Collection<String> SEARCH_BYPASS;
142        static
143        {
144            Collection<String> c = new HashSet<String>();
145            c.add( NormalizationInterceptor.class.getName() );
146            c.add( AuthenticationInterceptor.class.getName() );
147            c.add( AciAuthorizationInterceptor.class.getName() );
148            c.add( DefaultAuthorizationInterceptor.class.getName() );
149            c.add( OperationalAttributeInterceptor.class.getName() );
150            c.add( SchemaInterceptor.class.getName() );
151            c.add( SubentryInterceptor.class.getName() );
152            c.add( EventInterceptor.class.getName() );
153            SEARCH_BYPASS = Collections.unmodifiableCollection( c );
154        }
155    
156    
157        private int getImmSubCount( SchemaManager schemaManager, OperationContext opContext, DN entryName ) throws Exception
158        {
159            int cnt = 0;
160            EntryFilteringCursor results = null;
161            
162            try
163            {
164                SearchOperationContext searchContext = new SearchOperationContext( opContext.getSession(), 
165                    ( DN ) entryName.getPrefix( 1 ), childrenFilter, childrenSearchControls );
166                searchContext.setByPassed( SEARCH_BYPASS );
167                searchContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS );
168                
169                results = opContext.getSession().getDirectoryService().getOperationManager().search( searchContext );
170    
171                while ( results.next() )
172                {
173                    results.get();
174                    cnt++;
175                }
176    
177            }
178            finally
179            {
180                if ( results != null )
181                {
182                    results.close();
183                }
184            }
185    
186            return cnt;
187        }
188    }