1    /*
2     * Copyright 2006 :torweg free software group
3     * 
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.accesscontrol;
19   
20   import java.util.HashSet;
21   import java.util.List;
22   import java.util.Set;
23   
24   import javax.persistence.CascadeType;
25   import javax.persistence.Column;
26   import javax.persistence.Entity;
27   import javax.persistence.Lob;
28   import javax.persistence.OneToMany;
29   import javax.xml.bind.annotation.XmlAccessOrder;
30   import javax.xml.bind.annotation.XmlAccessType;
31   import javax.xml.bind.annotation.XmlAccessorOrder;
32   import javax.xml.bind.annotation.XmlAccessorType;
33   import javax.xml.bind.annotation.XmlElement;
34   import javax.xml.bind.annotation.XmlElementWrapper;
35   import javax.xml.bind.annotation.XmlRootElement;
36   import javax.xml.bind.annotation.XmlTransient;
37   
38   import net.sf.json.JSONObject;
39   
40   import org.hibernate.LazyInitializationException;
41   import org.hibernate.Session;
42   import org.hibernate.criterion.Projections;
43   import org.jdom.Element;
44   import org.slf4j.Logger;
45   import org.slf4j.LoggerFactory;
46   import org.torweg.pulse.util.INameable;
47   
48   /**
49    * A {@code Permission} is an entity used to restrict the excecution of {@code
50    * Command}s.
51    * 
52    * <p>
53    * This is done by linking {@code Permission}s with {@code CommandMatcher}s,
54    * {@code Group}s and {@code User}s. In order to find out if a {@code User} has
55    * the allowance for a {@code Command}, his {@code Permission}s (including those
56    * he gains through his associated {@code Group}s) have to be compared to the
57    * {@code Permission}s of the {@code CommandMatcher}s matching the {@code
58    * Command}. If the {@code CommandMatcher}s' set of {@code Permission}s is a
59    * subset of the {@code User}'s set of {@code Permission}s, the {@code User} has
60    * the allowance for the {@code Command}.
61    * </p>
62    * 
63    * @author Christian Schatt, Daniel Dietz
64    * @version $Revision: 1479 $
65    */
66   @Entity
67   @XmlRootElement(name = "permission")
68   @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
69   @XmlAccessorType(XmlAccessType.FIELD)
70   public class Permission extends AbstractAccessControlObject implements
71           INameable {
72   
73       /**
74        * The serialVersionUID of the {@code Permission}.
75        */
76       private static final long serialVersionUID = 7102043673242121392L;
77   
78       /**
79        * the logger.
80        */
81       private static final Logger LOGGER = LoggerFactory
82               .getLogger(Permission.class);
83   
84       /**
85        * The description of the {@code Permission}.
86        */
87       @Lob
88       @Column(length = 4096, nullable = false)
89       @XmlElement(name = "description")
90       private String description = "";
91   
92       /**
93        * The {@code CommandMatcher}s associated with the {@code Permission}.
94        */
95       @OneToMany(cascade = CascadeType.ALL)
96       @XmlTransient
97       // getter JAXB-annotated
98       private final Set<CommandMatcher> commandMatchers = new HashSet<CommandMatcher>();
99   
100      /**
101       * used for Hibernate<sup>TM</sup>.
102       */
103      @Deprecated
104      protected Permission() {
105          super();
106      }
107  
108      /**
109       * The {@code Constructor} that sets the name of the {@code Permission}.
110       * 
111       * @param newName
112       *            the name of the {@code Permission}
113       */
114      public Permission(final String newName) {
115          super();
116          this.setName(newName);
117      }
118  
119      /**
120       * returns the description.
121       * 
122       * @return the description.
123       */
124      public final String getDescription() {
125          return this.description;
126      }
127  
128      /**
129       * sets the description.
130       * 
131       * @param d
132       *            the description to set
133       */
134      public final void setDescription(final String d) {
135          this.description = d;
136      }
137  
138      /**
139       * Returns a shallow copy of the {@code Permission}'s {@code Collection} of
140       * {@code CommandMatcher}s.
141       * 
142       * <p>
143       * Attention: Since the returned {@code Set} is not a reference to the
144       * {@code Permission}'s internal {@code Collection}, any modifications to
145       * its state will get lost. However, modifications to the state of the
146       * {@code Set}'s elements will be consistent.
147       * </p>
148       * 
149       * <p>
150       * For example: Adding/Deleting a {@code CommandMatcher} to/from the {@code
151       * Set} will not have any effect, when the {@code Permission} is saved. On
152       * the other hand, modifications to a {@code CommandMatcher} in the {@code
153       * Set} will be made persistent, when the {@code CommandMatcher} is saved.
154       * </p>
155       * 
156       * @return the {@code CommandMatcher}s associated with the {@code
157       *         Permission}
158       */
159      public final Set<CommandMatcher> getCommandMatchers() {
160          return new HashSet<CommandMatcher>(this.commandMatchers);
161      }
162  
163      /**
164       * For JAXB only.
165       * 
166       * @return this.getCommandMatchers()
167       */
168      @XmlElementWrapper(name = "command-matchers")
169      @XmlElement(name = "command-matcher")
170      @SuppressWarnings("unused")
171      @Deprecated
172      private HashSet<CommandMatcher> getCommandMatchersJAXB() { // NOPMD
173          try {
174              return new HashSet<CommandMatcher>(getCommandMatchers());
175          } catch (LazyInitializationException e) {
176              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
177              return null;
178          }
179      }
180  
181      /**
182       * Adds a {@code CommandMatcher} to the {@code Permission}'s {@code Set} of
183       * {@code CommandMatcher}s. If it already is part of the {@code Set}, it
184       * will not be added.
185       * 
186       * @param matcher
187       *            the {@code CommandMatcher} to be added
188       * @return true if the {@code Set} changed as a result of the call
189       */
190      protected final boolean addCommandMatcher(final CommandMatcher matcher) {
191          return this.commandMatchers.add(matcher);
192      }
193  
194      /**
195       * Removes a {@code CommandMatcher} from the {@code Permission}'s {@code
196       * Set} of {@code CommandMatcher}s if it is part of it.
197       * 
198       * @param matcher
199       *            the {@code CommandMatcher} to be removed
200       * @return true if the {@code Set} changed as a result of the call
201       */
202      public final boolean removeCommandMatcher(final CommandMatcher matcher) {
203          return this.commandMatchers.remove(matcher);
204      }
205  
206      /**
207       * Returns the {@code Group}s associated with the {@code Permission} ordered
208       * by name.
209       * 
210       * @param s
211       *            the hibernate<sup>TM</sup>-{@code Session}
212       * 
213       * @return the {@code Group}s associated with the {@code Permission}
214       */
215      @SuppressWarnings("unchecked")
216      public final List<Group> getGroups(final Session s) {
217          return (List<Group>) getAssociatedAbstractAccessControlObjects(s,
218                  Group.class, "permissions", getId());
219      }
220  
221      /**
222       * Returns the {@code Role}s associated with the {@code Permission} ordered
223       * by name.
224       * 
225       * @param s
226       *            the hibernate<sup>TM</sup>-{@code Session}
227       * 
228       * @return the {@code Role}s associated with the {@code Permission}
229       */
230      @SuppressWarnings("unchecked")
231      public final List<Role> getRoles(final Session s) {
232          return (List<Role>) getAssociatedAbstractAccessControlObjects(s,
233                  Role.class, "permissions", getId());
234      }
235  
236      /**
237       * Returns the {@code User}s associated with the {@code Permission} ordered
238       * by name.
239       * 
240       * @param s
241       *            the hibernate<sup>TM</sup>-{@code Session}
242       * 
243       * @return the {@code User}s associated with the {@code Permission}
244       */
245      @SuppressWarnings("unchecked")
246      public final List<User> getUsers(final Session s) {
247          return (List<User>) getAssociatedAbstractAccessControlObjects(s,
248                  User.class, "permissions", getId());
249      }
250  
251      /**
252       * Serializes the state of the {@code Permission} as a JDOM {@code Element}.
253       * 
254       * @return the state of the {@code Permission} as a JDOM {@code Element}
255       */
256      public final Element deserializeToJDOM() {
257          Element element = new Element(getClass().getSimpleName()).setAttribute(
258                  "class", getClass().getCanonicalName());
259          if (getId() != null) {
260              element.setAttribute("id", getId().toString());
261          }
262          element.setAttribute("name", getName());
263          element
264                  .addContent(new Element("description")
265                          .setText(getDescription()));
266          try {
267              for (CommandMatcher matcher : getCommandMatchers()) {
268                  element.addContent(matcher.deserializeToJDOM());
269              }
270          } catch (LazyInitializationException exception) {
271              element.removeChildren(CommandMatcher.class.getSimpleName());
272          }
273  
274          return element;
275      }
276  
277      /**
278       * Serializes the state of the {@code Permission} as a {@code JSONObject}.
279       * 
280       * @return the state of the {@code Permission} as a {@code JSONObject}
281       */
282      @Override
283      public final JSONObject toJSON() {
284          return toJSON(null);
285      }
286  
287      /**
288       * Serializes the state of the {@code Permission} as a {@code JSONObject}.
289       * <p>
290       * If a {@code Session} is given the number of {@code User}s, {@code Group}s
291       * and {@code Role}s associated with the {@code Permission} will be added to
292       * the JSON.
293       * </p>
294       * 
295       * @param s
296       *            the hibernate<sup>TM</sup>-{@code Session}
297       * 
298       * @return the state of the {@code Permission} as a {@code JSONObject}
299       */
300      public final JSONObject toJSON(final Session s) {
301  
302          JSONObject json = super.toJSON();
303  
304          json.put("name", getName());
305          json.put("description", getDescription());
306  
307          // adds info for ext-tree
308          json.put("uiProvider", "AccessControlTreeNodeUI");
309          json.put("leaf", true);
310          json.put("expandable", false);
311          json.put("allowChildren", false);
312          json.put("text", getName());
313  
314          // adds info for overview-grid
315          json.put("noCommandMatchers", getCommandMatchers().size());
316          if (s != null) {
317              json.put("noGroups", (Long) getLoadCriteriaForClassWithAlias(s,
318                      Group.class, "permissions", getId()).setProjection(
319                      Projections.rowCount()).uniqueResult());
320              json.put("noRoles", (Long) getLoadCriteriaForClassWithAlias(s,
321                      Role.class, "permissions", getId()).setProjection(
322                      Projections.rowCount()).uniqueResult());
323              json.put("noUsers", (Long) getLoadCriteriaForClassWithAlias(s,
324                      User.class, "permissions", getId()).setProjection(
325                      Projections.rowCount()).uniqueResult());
326          }
327  
328          return json;
329      }
330  
331  }
332