[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao ...

Christian Bauer christian at hibernate.org
Sun Mar 18 11:44:36 EDT 2007


  User: cbauer  
  Date: 07/03/18 11:44:36

  Modified:    examples/wiki/src/main/org/jboss/seam/wiki/core/dao      
                        NodeDAO.java UserDAO.java
  Added:       examples/wiki/src/main/org/jboss/seam/wiki/core/dao      
                        UserRoleAccessFactory.java WikiNodeFactory.java
  Removed:     examples/wiki/src/main/org/jboss/seam/wiki/core/dao      
                        WikiRoot.java Roles.java
  Log:
  Basic access level/role security, automatic home page for activated users
  
  Revision  Changes    Path
  1.2       +65 -45    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/NodeDAO.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: NodeDAO.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/NodeDAO.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -b -r1.1 -r1.2
  --- NodeDAO.java	7 Mar 2007 18:37:34 -0000	1.1
  +++ NodeDAO.java	18 Mar 2007 15:44:36 -0000	1.2
  @@ -17,13 +17,10 @@
   import java.util.List;
   
   /**
  - * DAO for Nodes.
  + * DAO for nodes, transparently respects security access levels.
    * <p>
  - * The primary reason why this DAO exists is the broken JPA specification. There is no reason
  - * why query.getSingleResult() should throw any exception if the query result is empty. It should
  - * just return <tt>null</tt>, like Hibernates query.uniqueResult() method. So instead of using
  - * the EntityManager directly, most users, like me, will outsource this exception wrapping into
  - * a DAO. Hey, this sounds like a job for Spring? Or maybe we should fix the specification...
  + * All node access should go through this component, this component knows
  + * about access levels because it relies on a restricted (filtered) Entitymanager.
    *
    * @author Christian Bauer
    *
  @@ -33,26 +30,25 @@
   @Transactional
   public class NodeDAO {
   
  -    @In
  -    protected EntityManager entityManager;
  +    // Most of the DAO methods use this
  +    @In protected EntityManager restrictedEntityManager;
   
  -    public Node findNode(Long nodeId) {
  -        entityManager.joinTransaction();
  -        try {
  -            return entityManager.find(Node.class, nodeId);
  -        } catch (EntityNotFoundException ex) {
  -        }
  -        return null;
  -    }
  +    // Some run unrestricted (e.g. internal unique key validation of wiki names)
  +    // Make sure that these methods do not return detached objects!
  +    @In protected EntityManager entityManager;
   
  -    public Node findNodeInArea(Long areaNumber, String wikiname) {
  +
  +    public void makePersistent(Node node) {
           entityManager.joinTransaction();
  +        entityManager.persist(node);
  +    }
   
  +    public Node findNode(Long nodeId) {
  +        restrictedEntityManager.joinTransaction();
           try {
  -            return (Node) entityManager
  -                    .createQuery("select n from Node n where n.areaNumber = :areaNumber and n.wikiname = :wikiname")
  -                    .setParameter("areaNumber", areaNumber)
  -                    .setParameter("wikiname", wikiname)
  +            return (Node) restrictedEntityManager
  +                    .createQuery("select n from Node n where n.id = :nodeId")
  +                    .setParameter("nodeId", nodeId)
                       .getSingleResult();
           } catch (EntityNotFoundException ex) {
           } catch (NoResultException ex) {
  @@ -60,13 +56,17 @@
           return null;
       }
   
  -    public Node findNodeInDirectory(Directory directory, String wikiname) {
  -        entityManager.joinTransaction();
  +    public Node findNodeInArea(Long areaNumber, String wikiname) {
  +        return findNodeInArea(areaNumber, wikiname, restrictedEntityManager);
  +    }
  +
  +    private Node findNodeInArea(Long areaNumber, String wikiname, EntityManager em) {
  +        em.joinTransaction();
   
           try {
  -            return (Node) entityManager
  -                    .createQuery("select n from Node n where n.parent = :parentDir and n.wikiname = :wikiname")
  -                    .setParameter("parentDir", directory)
  +            return (Node) em
  +                    .createQuery("select n from Node n where n.areaNumber = :areaNumber and n.wikiname = :wikiname")
  +                    .setParameter("areaNumber", areaNumber)
                       .setParameter("wikiname", wikiname)
                       .getSingleResult();
           } catch (EntityNotFoundException ex) {
  @@ -76,10 +76,10 @@
       }
   
       public Document findDocumentInArea(Long areaNumber, String wikiname) {
  -        entityManager.joinTransaction();
  +        restrictedEntityManager.joinTransaction();
   
           try {
  -            return (Document) entityManager
  +            return (Document) restrictedEntityManager
                       .createQuery("select d from Document d where d.areaNumber = :areaNumber and d.wikiname = :wikiname")
                       .setParameter("areaNumber", areaNumber)
                       .setParameter("wikiname", wikiname)
  @@ -91,10 +91,10 @@
       }
   
       public Directory findDirectoryInArea(Long areaNumber, String wikiname) {
  -        entityManager.joinTransaction();
  +        restrictedEntityManager.joinTransaction();
   
           try {
  -            return (Directory) entityManager
  +            return (Directory) restrictedEntityManager
                       .createQuery("select d from Directory d where d.areaNumber = :areaNumber and d.wikiname = :wikiname")
                       .setParameter("areaNumber", areaNumber)
                       .setParameter("wikiname", wikiname)
  @@ -106,10 +106,10 @@
       }
   
       public Directory findArea(String wikiname) {
  -        entityManager.joinTransaction();
  +        restrictedEntityManager.joinTransaction();
   
           try {
  -            return (Directory) entityManager
  +            return (Directory) restrictedEntityManager
                       .createQuery("select d from Directory d where d.parent = :root and d.wikiname = :wikiname")
                       .setParameter("root", Component.getInstance("wikiRoot"))
                       .setParameter("wikiname", wikiname)
  @@ -127,7 +127,8 @@
       }
   
       public void persistHistoricalNode(Node historicalNode) {
  -        getSession().persist("HistoricalDocument", historicalNode);
  +        // TODO: Ugh, concatenating class names to get the entity name?!
  +        getSession().persist("Historical"+historicalNode.getClass().getSimpleName(), historicalNode);
           getSession().flush();
           getSession().evict(historicalNode);
       }
  @@ -147,17 +148,21 @@
                               .list();
       }
   
  -    // I need these methods because find() is broken, e.g. find(Document,1) would return a Directory if the
  -    // persistence context contains a directory with id 1... even more annoying, I need to catch NoResultException,
  -    // so there really is no easy and correct way to look for the existence of a row.
  -    // TODO: A new Hibernate version should fix find()/get() - the old JBoss AS 4.0.5 version is broken
  -    // ... or is it not: http://opensource.atlassian.com/projects/hibernate/browse/HHH-2352
  +    // Multi-row constraint validation
  +    public boolean isUniqueWikiname(Node node) {
  +        Node foundNode = findNodeInArea(node.getParent().getAreaNumber(), node.getWikiname(), entityManager);
  +        if (foundNode == null) {
  +            return true;
  +        } else {
  +            return node.getId() != null && node.getId().equals(foundNode.getId());
  +        }
  +    }
   
       public Document findDocument(Long documentId) {
  -        entityManager.joinTransaction();
  +        restrictedEntityManager.joinTransaction();
   
           try {
  -            return (Document) entityManager
  +            return (Document) restrictedEntityManager
                       .createQuery("select d from Document d where d.id = :id")
                       .setParameter("id", documentId)
                       .getSingleResult();
  @@ -168,10 +173,10 @@
       }
   
       public Directory findDirectory(Long directoryId) {
  -        entityManager.joinTransaction();
  +        restrictedEntityManager.joinTransaction();
   
           try {
  -            return (Directory) entityManager
  +            return (Directory) restrictedEntityManager
                       .createQuery("select d from Directory d where d.id = :id")
                       .setParameter("id", directoryId)
                       .getSingleResult();
  @@ -182,10 +187,10 @@
       }
   
       public File findFile(Long fileId) {
  -        entityManager.joinTransaction();
  +        restrictedEntityManager.joinTransaction();
   
           try {
  -            return (File) entityManager
  +            return (File) restrictedEntityManager
                       .createQuery("select f from File f where f.id = :id")
                       .setParameter("id", fileId)
                       .getSingleResult();
  @@ -195,7 +200,22 @@
           return null;
       }
   
  +    public Document findDefaultDocument(Directory directory) {
  +        if (directory == null) return null;
  +        restrictedEntityManager.joinTransaction();
  +        try {
  +            return (Document) restrictedEntityManager
  +                    .createQuery("select doc from Document doc, Directory dir" +
  +                                 " where doc.id = dir.defaultDocument.id and dir.id = :did")
  +                    .setParameter("did", directory.getId())
  +                    .getSingleResult();
  +        } catch (EntityNotFoundException ex) {
  +        } catch (NoResultException ex) {
  +        }
  +        return null;
  +    }
  +
       private Session getSession() {
  -        return ((Session)((org.jboss.seam.persistence.EntityManagerProxy)entityManager).getDelegate());
  +        return ((Session)((org.jboss.seam.persistence.EntityManagerProxy) restrictedEntityManager).getDelegate());
       }
   }
  
  
  
  1.3       +7 -36     jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserDAO.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: UserDAO.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserDAO.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -b -r1.2 -r1.3
  --- UserDAO.java	9 Mar 2007 08:02:09 -0000	1.2
  +++ UserDAO.java	18 Mar 2007 15:44:36 -0000	1.3
  @@ -28,16 +28,20 @@
       @In
       protected EntityManager entityManager;
   
  -    public User findUser(String username, boolean onlyActivated) {
  +    public User findUser(String username, boolean onlyActivated, boolean caseSensitive) {
           entityManager.joinTransaction();
   
  -        StringBuffer query = new StringBuffer("select u from User u where u.username = :username");
  +        StringBuffer query = new StringBuffer("select u from User u where");
  +        if (caseSensitive)
  +            query.append(" u.username = :username");
  +        else
  +            query.append(" lower(u.username) = :username");
           if (onlyActivated) query.append(" and u.activated = true");
   
           try {
               return (User) entityManager
                       .createQuery(query.toString())
  -                    .setParameter("username", username)
  +                    .setParameter("username", caseSensitive ? username : username.toLowerCase())
                       .getSingleResult();
           } catch (EntityNotFoundException ex) {
           } catch (NoResultException ex) {
  @@ -60,39 +64,6 @@
           return null;
       }
   
  -    public Role findRole(String rolename) {
  -        entityManager.joinTransaction();
  -
  -        try {
  -            return (Role) entityManager
  -                    .createQuery("select r from Role r where r.name = :name")
  -                    .setParameter("name", rolename)
  -                    .getSingleResult();
  -        } catch (EntityNotFoundException ex) {
  -        } catch (NoResultException ex) {
  -        }
  -        return null;
  -    }
  -
  -    public Role findRole(int accessLevel) {
  -        entityManager.joinTransaction();
  -
  -        try {
  -            return (Role) entityManager
  -                    .createQuery("select r from Role r where r.accessLevel = :accessLevel")
  -                    .setParameter("accessLevel", accessLevel)
  -                    .getSingleResult();
  -        } catch (EntityNotFoundException ex) {
  -        } catch (NoResultException ex) {
  -        }
  -        return null;
  -    }
  -
  -    public Role findRole(Long roleId) {
  -        entityManager.joinTransaction();
  -        return entityManager.find(Role.class, roleId);
  -    }
  -
       public List<User> findByExample(User exampleUser, String orderByProperty, boolean orderDescending,
                                       int firstResult, int maxResults, String... ignoreProperty) {
           Criteria crit = prepareExampleCriteria(exampleUser, orderByProperty, orderDescending, ignoreProperty);
  
  
  
  1.1      date: 2007/03/18 15:44:36;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserRoleAccessFactory.java
  
  Index: UserRoleAccessFactory.java
  ===================================================================
  package org.jboss.seam.wiki.core.dao;
  
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.wiki.core.model.Role;
  import org.jboss.seam.wiki.core.model.User;
  import org.jboss.seam.wiki.core.model.GlobalPreferences;
  import org.jboss.seam.annotations.*;
  
  import javax.persistence.EntityManager;
  import javax.persistence.NoResultException;
  import java.io.Serializable;
  import java.util.*;
  
  @Name("userRoleAccessFactory")
  @Transactional
  public class UserRoleAccessFactory implements Serializable {
  
      public static final String GUEST_USERNAME = "guest";
      public static final String ADMIN_USERNAME = "admin";
      public static final int GUESTROLE_ACCESSLEVEL = 0;
      public static final int ADMINROLE_ACCESSLEVEL = 1000;
      
      @In
      EntityManager entityManager;
  
      @In
      private GlobalPreferences globalPrefs;
  
      @Factory(value = "guestUser", scope = ScopeType.SESSION)
      public User getGuestUser() {
          try {
              entityManager.joinTransaction();
              User guestUser =
                      (User) entityManager
                              .createQuery("select u from User u left join fetch u.roles where u.username = '"+GUEST_USERNAME+"'")
                              .getSingleResult();
              if (guestUser.getRoles().size() > 1 || guestUser.getRoles().size() == 0) {
                  throw new RuntimeException("Your '"+GUEST_USERNAME+"' user has none or more than one role assigned, illegal database state");
              }
              if (guestUser.getRoles().iterator().next().getAccessLevel() != GUESTROLE_ACCESSLEVEL) {
                  throw new RuntimeException("Your '"+GUEST_USERNAME+"' user isn't assigned to the guest role (access level "+GUESTROLE_ACCESSLEVEL+")");
              }
              return guestUser;
          } catch (NoResultException ex) {
              throw new RuntimeException("You need to INSERT a user with username '"+GUEST_USERNAME+"' into the database");
          }
      }
  
  
      @Factory(value = "adminUser", scope = ScopeType.SESSION)
      public User getAdminUser() {
          try {
              entityManager.joinTransaction();
              User adminUser =
                      (User) entityManager
                              .createQuery("select u from User u left join fetch u.roles where u.username = '"+ADMIN_USERNAME+"'")
                              .getSingleResult();
              if (adminUser.getRoles().size() > 1 || adminUser.getRoles().size() == 0) {
                  throw new RuntimeException("Your '"+ADMIN_USERNAME+"' user has none or more than one role assigned, illegal database state");
              }
              if (adminUser.getRoles().iterator().next().getAccessLevel() != ADMINROLE_ACCESSLEVEL) {
                  throw new RuntimeException("Your '"+ADMIN_USERNAME+"' user isn't assigned to the admin role (access level "+ADMINROLE_ACCESSLEVEL+")");
              }
              return adminUser;
          } catch (NoResultException ex) {
              throw new RuntimeException("You need to INSERT a user with username '"+ADMIN_USERNAME+"' into the database");
          }
      }
  
      @Factory(value = "guestRole", scope = ScopeType.SESSION)
      public Role getGuestRole() {
          try {
              entityManager.joinTransaction();
              return (Role) entityManager
                      .createQuery("select r from Role r where r.accessLevel = '"+GUESTROLE_ACCESSLEVEL+"'")
                              .getSingleResult();
          } catch (NoResultException ex) {
              throw new RuntimeException("You need to INSERT a role with accesslevel '"+GUESTROLE_ACCESSLEVEL+"' (the guest role) into the database");
          }
      }
  
      @Factory(value = "adminRole", scope = ScopeType.SESSION)
      public Role getAdminRole() {
          try {
              entityManager.joinTransaction();
              return (Role) entityManager
                      .createQuery("select r from Role r where r.accessLevel = '"+ADMINROLE_ACCESSLEVEL+"'")
                              .getSingleResult();
          } catch (NoResultException ex) {
              throw new RuntimeException("You need to INSERT a role with accesslevel '"+ADMINROLE_ACCESSLEVEL+"' (the admin role) into the database");
          }
      }
  
      @Factory(value = "newUserDefaultRole", scope = ScopeType.SESSION)
      public Role getDefaultRole() {
          try {
              entityManager.joinTransaction();
              return (Role) entityManager
                      .createQuery("select r from Role r where r.name = '"+globalPrefs.getNewUserInRole()+"'")
                              .getSingleResult();
          } catch (NoResultException ex) {
              throw new RuntimeException("Configured default role for new users '"+globalPrefs.getNewUserInRole()+"' not found");
          }
      }
  
      /**
       * A <tt>List</tt> of all roles in the system.
       */
      @Name("rolesList")
      @Scope(ScopeType.CONVERSATION)
      @AutoCreate
      public static class RoleList {
  
          @In
          protected EntityManager entityManager;
  
          protected List<Role> roles;
  
          @Unwrap
          @SuppressWarnings({"unchecked"})
          @Transactional
          public List<Role> getRoles() {
              if (roles == null) {
                  entityManager.joinTransaction();
                  roles = (List<Role>) entityManager
                          .createQuery("select r from Role r order by r.accessLevel desc, r.displayName asc")
                              .getResultList();
                  if (roles.size() < 2)
                      throw new RuntimeException("You need to INSERT at least two roles into the database, " +
                                                 "with access level '"+GUESTROLE_ACCESSLEVEL+"' and '"+ADMINROLE_ACCESSLEVEL+"'");
                  }
              return roles;
          }
  
      }
  
      /**
       * Aggregates role names with access level integers, e.g.
       * <pre>
       * Access Level, Role Name
       *  1             Foo
       *  2             Bar
       *  2             Baz
       *  1000          Admin
       * </pre>
       * is aggregated for display into
       * <pre>
       * Access Level, Role Name
       *  1             Foo
       *  2             Bar, Baz
       *  1000          Owner, Admin
       * </pre>
       */
      @Name("accessLevelsList")
      @Scope(ScopeType.CONVERSATION)
      @AutoCreate
      public static class AccessLevelsList {
  
          @In
          List<Role> rolesList;
  
          List<Role.AccessLevel> accessLevelsList;
  
          @Unwrap
          public List<Role.AccessLevel> getAccessLevelList() {
              if (accessLevelsList == null) {
                  accessLevelsList = new ArrayList<Role.AccessLevel>(rolesList.size());
                  for (Role role : rolesList) {
  
                      // Create an access level object, append fake role name "Owner" if the
                      // access level is the superuser level
                      Role.AccessLevel newAccessLevel =
                              new Role.AccessLevel(
                                      role.getAccessLevel(),
                                      role.getAccessLevel() == ADMINROLE_ACCESSLEVEL
                                          ? "Owner, " + role.getDisplayName()
                                          : role.getDisplayName()
                              );
  
                      // Put into list, if this level already exists only append the role names
                      if (accessLevelsList.contains(newAccessLevel)) {
                          Role.AccessLevel existingAccessLevel =
                                  accessLevelsList.get(accessLevelsList.indexOf(newAccessLevel));
                          existingAccessLevel.appendRoleName(newAccessLevel.getRoleNames());
                      } else {
                          accessLevelsList.add(newAccessLevel);
                      }
                  }
              }
  
              return accessLevelsList;
          }
      }
  
      /**
       * AccessLevel's that can be assigned by the current user, e.g. when editing a document.
       */
      @Name("assignableAccessLevelsList")
      @Scope(ScopeType.CONVERSATION)
      @AutoCreate
      public static class AssignableAccessLevelsList {
  
          @In
          List<Role.AccessLevel> accessLevelsList;
  
          @In
          Integer currentAccessLevel;
  
          List<Role.AccessLevel> assignableAccessLevelsList;
  
          @Unwrap
          public List<Role.AccessLevel> getAssignableAccessLevelList() {
              if (assignableAccessLevelsList == null) {
                  assignableAccessLevelsList = new ArrayList<Role.AccessLevel>(accessLevelsList.size());
                  for (Role.AccessLevel accessLevel : accessLevelsList) {
                      // Only make access levels assignable if the current user has at least this access level
                      if (accessLevel.getAccessLevel() <= currentAccessLevel)
                          assignableAccessLevelsList.add(accessLevel);
                  }
              }
  
              return assignableAccessLevelsList;
          }
      }
  
  }
  
  
  
  1.1      date: 2007/03/18 15:44:36;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/WikiNodeFactory.java
  
  Index: WikiNodeFactory.java
  ===================================================================
  package org.jboss.seam.wiki.core.dao;
  
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.Component;
  import org.jboss.seam.wiki.core.model.Directory;
  import org.jboss.seam.wiki.core.model.GlobalPreferences;
  
  import javax.persistence.EntityManager;
  import java.io.Serializable;
  
  @Name("wikiNodeFactory")
  public class WikiNodeFactory implements Serializable {
  
      @In
      protected EntityManager entityManager;
  
      @In
      protected EntityManager restrictedEntityManager;
  
      @Factory(value = "wikiRoot", scope = ScopeType.CONVERSATION, autoCreate = true)
      @Transactional
      public Directory loadWikiRoot() {
          entityManager.joinTransaction();
          try {
              return (Directory) entityManager
                      .createQuery("select d from Directory d where d.parent is null")
                      .getSingleResult();
          } catch (RuntimeException ex) {
              throw new RuntimeException("You need to INSERT at least one parentless directory into the database", ex);
          }
      }
  
      // Loads the same instance into a different persistence context
      @Factory(value = "restrictedWikiRoot", scope = ScopeType.CONVERSATION, autoCreate = true)
      @Transactional
      public Directory loadWikiRootRestricted() {
          Directory wikiroot = (Directory) Component.getInstance("wikiRoot");
          restrictedEntityManager.joinTransaction();
          return restrictedEntityManager.find(Directory.class, wikiroot.getId());
      }
  
      @Factory(value = "memberArea", scope = ScopeType.CONVERSATION, autoCreate = true)
      @Transactional
      public Directory loadMemberArea() {
          Long memberAreaId = ((GlobalPreferences)Component.getInstance("globalPrefs")).getMemberAreaId();
          entityManager.joinTransaction();
          try {
              return (Directory) entityManager
                      .createQuery("select d from Directory d where d.id = :dirId and d.parent.parent is null")
                      .setParameter("dirId", memberAreaId)
                      .getSingleResult();
          } catch (RuntimeException ex) {
              throw new RuntimeException("Could not find member area with id " + memberAreaId, ex);
          }
      }
  
  }
  
  
  



More information about the jboss-cvs-commits mailing list