001    package org.apache.turbine.services.security.torque;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.beans.PropertyDescriptor;
023    
024    import java.util.ArrayList;
025    import java.util.Iterator;
026    import java.util.Enumeration;
027    import java.util.List;
028    import java.util.Vector;
029    
030    import org.apache.commons.configuration.Configuration;
031    
032    import org.apache.commons.logging.Log;
033    import org.apache.commons.logging.LogFactory;
034    
035    import org.apache.turbine.om.security.Permission;
036    import org.apache.turbine.om.security.Role;
037    import org.apache.turbine.services.InitializationException;
038    import org.apache.turbine.services.security.TurbineSecurity;
039    import org.apache.turbine.services.security.torque.om.TurbineRolePermissionPeer;
040    import org.apache.turbine.util.security.DataBackendException;
041    import org.apache.turbine.util.security.PermissionSet;
042    
043    import org.apache.torque.TorqueException;
044    import org.apache.torque.om.Persistent;
045    import org.apache.torque.util.BasePeer;
046    import org.apache.torque.util.Criteria;
047    
048    /**
049     * This class capsulates all direct Peer access for the Permission entities.
050     * It allows the exchange of the default Turbine supplied TurbinePermissionPeer
051     * class against a custom class.
052     *
053     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
054     * @version $Id: PermissionPeerManager.java 1096130 2011-04-23 10:37:19Z ludwig $
055     *
056     */
057    
058    public class PermissionPeerManager
059        implements PermissionPeerManagerConstants
060    {
061        /** The class of the Peer the TorqueSecurityService uses */
062        private static Class persistentPeerClass = null;
063    
064        /** The class name of the objects returned by the configured peer. */
065        private static Class permissionObject = null;
066    
067        /** The name of the Table used for Permission Object queries  */
068        private static String tableName = null;
069    
070        /** The name of the column used as "Name" Column */
071        private static String nameColumn = null;
072    
073        /** The name of the column used as "Id" Column */
074        private static String idColumn = null;
075    
076        /** The "Name" property descriptor */
077        private static PropertyDescriptor namePropDesc = null;
078    
079        /** The "Id" property descriptor */
080        private static PropertyDescriptor idPropDesc = null;
081    
082        /** Logging */
083        static Log log = LogFactory.getLog(PermissionPeerManager.class);
084    
085        /**
086         * Initializes the PermissionPeerManager, loading the class object for the
087         * Peer used to retrieve Permission objects
088         *
089         * @param conf The configuration object used to configure the Manager
090         *
091         * @exception InitializationException A problem occured during initialization
092         */
093    
094        public static void init(Configuration conf)
095            throws InitializationException
096        {
097            String persistentPeerClassName =
098                conf.getString(PERMISSION_PEER_CLASS_KEY,
099                               PERMISSION_PEER_CLASS_DEFAULT);
100    
101            String permissionObjectName = null;
102    
103            try
104            {
105                persistentPeerClass = Class.forName(persistentPeerClassName);
106    
107                tableName  =
108                    (String) persistentPeerClass.getField("TABLE_NAME").get(null);
109    
110                //
111                // We have either an user configured Object class or we use the
112                // default as supplied by the Peer class
113                //
114                // Default from Peer, can be overridden
115    
116                permissionObject = getPersistenceClass();
117    
118                permissionObjectName = conf.getString(PERMISSION_CLASS_KEY,
119                                            permissionObject.getName());
120    
121                // Maybe the user set a new value...
122                permissionObject = Class.forName(permissionObjectName);
123    
124                /* If any of the following Field queries fails, the permission
125                 * subsystem is unusable. So check this right here at init time,
126                 * which saves us much time and hassle if it fails...
127                 */
128    
129                nameColumn = (String) persistentPeerClass.getField(
130                        conf.getString(PERMISSION_NAME_COLUMN_KEY,
131                                       PERMISSION_NAME_COLUMN_DEFAULT)
132                        ).get(null);
133    
134                idColumn = (String) persistentPeerClass.getField(
135                        conf.getString(PERMISSION_ID_COLUMN_KEY,
136                                       PERMISSION_ID_COLUMN_DEFAULT)
137                        ).get(null);
138    
139                namePropDesc = new PropertyDescriptor(
140                        conf.getString(PERMISSION_NAME_PROPERTY_KEY,
141                                       PERMISSION_NAME_PROPERTY_DEFAULT),
142                        permissionObject);
143    
144                idPropDesc = new PropertyDescriptor(
145                        conf.getString(PERMISSION_ID_PROPERTY_KEY,
146                                       PERMISSION_ID_PROPERTY_DEFAULT),
147                        permissionObject);
148            }
149            catch (Exception e)
150            {
151                if (persistentPeerClassName == null || persistentPeerClass == null)
152                {
153                    throw new InitializationException(
154                        "Could not find PermissionPeer class ("
155                        + persistentPeerClassName + ")", e);
156                }
157                if (tableName == null)
158                {
159                    throw new InitializationException(
160                        "Failed to get the table name from the Peer object", e);
161                }
162    
163                if (permissionObject == null || permissionObjectName == null)
164                {
165                    throw new InitializationException(
166                        "Failed to get the object type from the Peer object", e);
167                }
168    
169    
170                if (nameColumn == null || namePropDesc == null)
171                {
172                    throw new InitializationException(
173                        "PermissionPeer " + persistentPeerClassName +
174                        " has no name column information!", e);
175                }
176                if (idColumn == null || idPropDesc == null)
177                {
178                    throw new InitializationException(
179                        "PermissionPeer " + persistentPeerClassName +
180                        " has no id column information!", e);
181                }
182            }
183        }
184    
185        /**
186         * Get the name of this table.
187         *
188         * @return A String with the name of the table.
189         */
190        public static String getTableName()
191        {
192            return tableName;
193        }
194    
195        /**
196         * Returns the fully qualified name of the Column to
197         * use as the Name Column for a permission
198         *
199         * @return A String containing the column name
200         */
201        public static String getNameColumn()
202        {
203            return nameColumn;
204        }
205    
206        /**
207         * Returns the fully qualified name of the Column to
208         * use as the Id Column for a permission
209         *
210         * @return A String containing the column id
211         *
212         */
213        public static String getIdColumn()
214        {
215            return idColumn;
216        }
217    
218        /**
219         * Returns the full name of a column.
220         *
221         * @param name The column to fully qualify
222         *
223         * @return A String with the full name of the column.
224         */
225        public static String getColumnName(String name)
226        {
227            StringBuffer sb = new StringBuffer();
228            sb.append(getTableName());
229            sb.append(".");
230            sb.append(name);
231            return sb.toString();
232        }
233    
234        /**
235         * Returns a new, empty object for the underlying peer.
236         * Used to create a new underlying object
237         *
238         * @return A new object which is compatible to the Peer
239         *         and can be used as a User object
240         *
241         */
242    
243        public static Persistent newPersistentInstance()
244        {
245            Persistent obj = null;
246    
247            if(permissionObject == null)
248            {
249                // This can happen if the Turbine wants to determine the
250                // name of the anonymous user before the security service
251                // has been initialized. In this case, the Peer Manager
252                // has not yet been inited and the permissionObject is still
253                // null. Return null in this case.
254                //
255                return obj;
256            }
257    
258            try
259            {
260                obj = (Persistent) permissionObject.newInstance();
261            }
262            catch (Exception e)
263            {
264                log.error("Could not instantiate a permission object", e);
265                obj = null;
266            }
267            return obj;
268        }
269    
270        /**
271         * Checks if a Permission is defined in the system. The name
272         * is used as query criteria.
273         *
274         * @param permission The Permission to be checked.
275         * @return <code>true</code> if given Permission exists in the system.
276         * @throws DataBackendException when more than one Permission with
277         *         the same name exists.
278         * @throws Exception A generic exception.
279         */
280        public static boolean checkExists(Permission permission)
281            throws DataBackendException, Exception
282        {
283            Criteria criteria = new Criteria();
284    
285            criteria.addSelectColumn(getIdColumn());
286    
287            criteria.add(getNameColumn(), permission.getName());
288    
289            List results = BasePeer.doSelect(criteria);
290    
291            if (results.size() > 1)
292            {
293                throw new DataBackendException("Multiple permissions named '" +
294                                               permission.getName() + "' exist!");
295            }
296            return (results.size() == 1);
297        }
298    
299        /**
300         * Retrieves/assembles a PermissionSet
301         *
302         * @param criteria The criteria to use.
303         * @return A PermissionSet.
304         * @exception Exception A generic Exception.
305         */
306        public static PermissionSet retrieveSet(Criteria criteria)
307            throws Exception
308        {
309            List results = doSelect(criteria);
310            PermissionSet ps = new PermissionSet();
311    
312            for(Iterator it = results.iterator(); it.hasNext(); )
313            {
314                ps.add((Permission) it.next());
315            }
316            return ps;
317        }
318    
319        /**
320         * Retrieves a set of Permissions associated with a particular Role.
321         *
322         * @param role The role to query permissions of.
323         * @return A set of permissions associated with the Role.
324         * @exception Exception A generic Exception.
325         */
326        public static PermissionSet retrieveSet(Role role)
327            throws Exception
328        {
329            Criteria criteria = new Criteria();
330            criteria.add(TurbineRolePermissionPeer.ROLE_ID,
331                         ((Persistent) role).getPrimaryKey());
332    
333            criteria.addJoin(TurbineRolePermissionPeer.PERMISSION_ID,
334                             getIdColumn());
335    
336            return retrieveSet(criteria);
337        }
338    
339        /**
340         * Pass in two Vector's of Permission Objects.  It will return a
341         * new Vector with the difference of the two Vectors: C = (A - B).
342         *
343         * @param some Vector B in C = (A - B).
344         * @param all Vector A in C = (A - B).
345         * @return Vector C in C = (A - B).
346         */
347        public static final Vector getDifference(Vector some, Vector all)
348        {
349            Vector clone = (Vector) all.clone();
350            for (Enumeration e = some.elements() ; e.hasMoreElements() ;)
351            {
352                Permission tmp = (Permission) e.nextElement();
353                for (Enumeration f = clone.elements() ; f.hasMoreElements() ;)
354                {
355                    Permission tmp2 = (Permission) f.nextElement();
356                    if (((Persistent) tmp).getPrimaryKey() ==
357                        ((Persistent) tmp2).getPrimaryKey())
358                    {
359                        clone.removeElement(tmp2);
360                        break;
361                    }
362                }
363            }
364            return clone;
365        }
366    
367        /*
368         * ========================================================================
369         *
370         * WARNING! Do not read on if you have a weak stomach. What follows here
371         * are some abominations thanks to the braindead static peers of Torque
372         * and the rigidity of Java....
373         *
374         * ========================================================================
375         *
376         */
377    
378        /**
379         * Calls buildCriteria(Permission permission) in
380         * the configured PermissionPeer. If you get a
381         * ClassCastException in this routine, you put a
382         * Permission object into this method which
383         * can't be cast into an object for the
384         * TorqueSecurityService. This is a configuration error most of
385         * the time.
386         *
387         * @param permission An object which implements
388         *                 the Permission interface
389         *
390         * @return A criteria for the supplied permission object
391         */
392    
393        public static Criteria buildCriteria(Permission permission)
394        {
395            Criteria crit;
396    
397            try
398            {
399                Class[] clazz = new Class[] { permissionObject };
400                Object[] params =
401                  new Object[] { ((TorquePermission) permission).getPersistentObj() };
402    
403                crit =  (Criteria) persistentPeerClass
404                    .getMethod("buildCriteria", clazz)
405                    .invoke(null, params);
406            }
407            catch (Exception e)
408            {
409                crit = null;
410            }
411    
412            return crit;
413        }
414    
415        /**
416         * Invokes doUpdate(Criteria c) on the configured Peer Object
417         *
418         * @param criteria  A Criteria Object
419         *
420         * @exception TorqueException A problem occured.
421         */
422    
423        public static void doUpdate(Criteria criteria)
424            throws TorqueException
425        {
426            try
427            {
428                Class[] clazz = new Class[] { Criteria.class };
429                Object[] params = new Object[] { criteria };
430    
431                persistentPeerClass
432                    .getMethod("doUpdate", clazz)
433                    .invoke(null, params);
434            }
435            catch (Exception e)
436            {
437                throw new TorqueException("doUpdate failed", e);
438            }
439        }
440    
441        /**
442         * Invokes doInsert(Criteria c) on the configured Peer Object
443         *
444         * @param criteria  A Criteria Object
445         *
446         * @exception TorqueException A problem occured.
447         */
448    
449        public static void doInsert(Criteria criteria)
450            throws TorqueException
451        {
452            try
453            {
454                Class[] clazz = new Class[] { Criteria.class };
455                Object[] params = new Object[] { criteria };
456    
457                persistentPeerClass
458                    .getMethod("doInsert", clazz)
459                    .invoke(null, params);
460            }
461            catch (Exception e)
462            {
463                throw new TorqueException("doInsert failed", e);
464            }
465        }
466    
467        /**
468         * Invokes doSelect(Criteria c) on the configured Peer Object
469         *
470         * @param criteria  A Criteria Object
471         *
472         * @return A List of Permission Objects selected by the Criteria
473         *
474         * @exception TorqueException A problem occured.
475         */
476        public static List doSelect(Criteria criteria)
477            throws TorqueException
478        {
479            List list;
480    
481            try
482            {
483                Class[] clazz =
484                  new Class[] { Criteria.class };
485                Object[] params = new Object[] { criteria };
486    
487                list = (List) persistentPeerClass
488                    .getMethod("doSelect", clazz)
489                    .invoke(null, params);
490            }
491            catch (Exception e)
492            {
493                throw new TorqueException("doSelect failed", e);
494            }
495    
496            List newList = new ArrayList(list.size());
497    
498            //
499            // Wrap the returned Objects into TorquePermissions.
500            //
501            for (Iterator it = list.iterator(); it.hasNext(); )
502            {
503                Permission p = getNewPermission((Persistent) it.next());
504                newList.add(p);
505            }
506    
507            return newList;
508        }
509    
510        /**
511         * Invokes doDelete(Criteria c) on the configured Peer Object
512         *
513         * @param criteria  A Criteria Object
514         *
515         * @exception TorqueException A problem occured.
516         */
517        public static void doDelete(Criteria criteria)
518            throws TorqueException
519        {
520            try
521            {
522                Class[] clazz = new Class[] { Criteria.class };
523                Object[] params = new Object[] { criteria };
524    
525                persistentPeerClass
526                    .getMethod("doDelete", clazz)
527                    .invoke(null, params);
528            }
529            catch (Exception e)
530            {
531                throw new TorqueException("doDelete failed", e);
532            }
533        }
534    
535        /**
536         * Invokes setName(String s) on the supplied base object
537         *
538         * @param obj The object to use for setting the name
539         * @param name The Name to set
540         */
541        public static void setPermissionName(Persistent obj, String name)
542        {
543            if(obj == null)
544            {
545                return;
546            }
547    
548            try
549            {
550                Object[] params = new Object[] { name };
551                namePropDesc.getWriteMethod().invoke(obj, params);
552            }
553            catch (ClassCastException cce)
554            {
555                String msg = obj.getClass().getName() + " does not seem to be a Permission Object!";
556                log.error(msg);
557                throw new RuntimeException(msg);
558            }
559            catch (Exception e)
560            {
561                log.error(e, e);
562            }
563        }
564    
565        /**
566         * Invokes getName() on the supplied base object
567         *
568         * @param obj The object to use for getting the name
569         *
570         * @return A string containing the name
571         */
572        public static String getPermissionName(Persistent obj)
573        {
574            String name = null;
575    
576            if(obj == null)
577            {
578                return null;
579            }
580    
581            try
582            {
583                name = (String) namePropDesc
584                    .getReadMethod()
585                    .invoke(obj, new Object[] {});
586            }
587            catch (ClassCastException cce)
588            {
589                String msg = obj.getClass().getName() + " does not seem to be a Permission Object!";
590                log.error(msg);
591                throw new RuntimeException(msg);
592            }
593            catch (Exception e)
594            {
595                log.error(e, e);
596            }
597            return name;
598        }
599    
600        /**
601         * Invokes setId(int n) on the supplied base object
602         *
603         * @param obj The object to use for setting the name
604         * @param id The new Id
605         */
606        public static void setId(Persistent obj, int id)
607        {
608            if(obj == null)
609            {
610                return;
611            }
612    
613            try
614            {
615                Object[] params = new Object[] { Integer.TYPE };
616                idPropDesc.getWriteMethod().invoke(obj, params);
617            }
618            catch (ClassCastException cce)
619            {
620                String msg = obj.getClass().getName() + " does not seem to be a Permission Object!";
621                log.error(msg);
622                throw new RuntimeException(msg);
623            }
624            catch (Exception e)
625            {
626                log.error(e, e);
627            }
628        }
629    
630        /**
631         * Invokes getId() on the supplied base object
632         *
633         * @param obj The object to use for getting the id
634         *
635         * @return The Id of this object
636         */
637        public static Integer getIdAsObj(Persistent obj)
638        {
639            Integer id = null;
640    
641            if(obj == null)
642            {
643                return new Integer(0);
644            }
645    
646            try
647            {
648                id = (Integer) idPropDesc
649                    .getReadMethod()
650                    .invoke(obj, new Object[] {});
651            }
652            catch (ClassCastException cce)
653            {
654                String msg = obj.getClass().getName() + " does not seem to be a Permission Object!";
655                log.error(msg);
656                throw new RuntimeException(msg);
657            }
658            catch (Exception e)
659            {
660                log.error(e, e);
661            }
662            return id;
663        }
664    
665        /**
666         * Returns the Class of the configured Object class
667         * from the peer
668         *
669         * @return The class of the objects returned by the configured peer
670         *
671         */
672    
673        private static Class getPersistenceClass()
674        {
675            Class persistenceClass = null;
676    
677            try
678            {
679                Object[] params = new Object[0];
680    
681                persistenceClass =  (Class) persistentPeerClass
682                    .getMethod("getOMClass", (Class[])null)
683                    .invoke(null, params);
684            }
685            catch (Exception e)
686            {
687                persistenceClass = null;
688            }
689    
690            return persistenceClass;
691        }
692    
693        /**
694         * Returns a new, configured Permission Object with
695         * a supplied Persistent object at its core
696         *
697         * @param p The persistent object
698         *
699         * @return a new, configured Permission Object
700         *
701         * @exception Exception Could not create a new Object
702         *
703         */
704    
705        public static Permission getNewPermission(Persistent p)
706        {
707            Permission perm = null;
708            try
709            {
710                Class permissionWrapperClass = TurbineSecurity.getPermissionClass();
711    
712                Class [] clazz = new Class [] { Persistent.class };
713                Object [] params = new Object [] { p };
714    
715                perm = (Permission) permissionWrapperClass
716                  .getConstructor(clazz)
717                  .newInstance(params);
718            }
719            catch (Exception e)
720            {
721                log.error("Could not instantiate a new permission from supplied persistent: ", e);
722            }
723    
724            return perm;
725        }
726    }
727