[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