001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.acl.basic;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020:
021: import org.springframework.util.Assert;
022:
023: import java.util.Arrays;
024:
025: /**
026: * Abstract implementation of {@link BasicAclEntry}.<P>Provides core bit mask handling methods.</p>
027: *
028: * @author Ben Alex
029: * @version $Id: AbstractBasicAclEntry.java 1496 2006-05-23 13:38:33Z benalex $
030: */
031: public abstract class AbstractBasicAclEntry implements BasicAclEntry {
032: //~ Static fields/initializers =====================================================================================
033:
034: private static final Log logger = LogFactory
035: .getLog(AbstractBasicAclEntry.class);
036:
037: //~ Instance fields ================================================================================================
038:
039: private AclObjectIdentity aclObjectIdentity;
040: private AclObjectIdentity aclObjectParentIdentity;
041: private Object recipient;
042: private int[] validPermissions;
043: private int mask = 0; // default means no permissions
044:
045: //~ Constructors ===================================================================================================
046:
047: public AbstractBasicAclEntry(Object recipient,
048: AclObjectIdentity aclObjectIdentity,
049: AclObjectIdentity aclObjectParentIdentity, int mask) {
050: Assert.notNull(recipient, "recipient cannot be null");
051:
052: Assert.notNull(aclObjectIdentity,
053: "aclObjectIdentity cannot be null");
054:
055: validPermissions = getValidPermissions();
056: Arrays.sort(validPermissions);
057:
058: for (int i = 0; i < validPermissions.length; i++) {
059: if (logger.isDebugEnabled()) {
060: logger.debug("Valid permission: "
061: + printPermissionsBlock(validPermissions[i])
062: + " " + printBinary(validPermissions[i]) + " ("
063: + validPermissions[i] + ")");
064: }
065: }
066:
067: this .recipient = recipient;
068: this .aclObjectIdentity = aclObjectIdentity;
069: this .aclObjectParentIdentity = aclObjectParentIdentity;
070: this .mask = mask;
071: }
072:
073: /**
074: * A protected constructor for use by Hibernate.
075: */
076: protected AbstractBasicAclEntry() {
077: validPermissions = getValidPermissions();
078: Arrays.sort(validPermissions);
079: }
080:
081: //~ Methods ========================================================================================================
082:
083: public int addPermission(int permissionToAdd) {
084: return addPermissions(new int[] { permissionToAdd });
085: }
086:
087: public int addPermissions(int[] permissionsToAdd) {
088: if (logger.isDebugEnabled()) {
089: logger.debug("BEFORE Permissions: "
090: + printPermissionsBlock(mask) + " "
091: + printBinary(mask) + " (" + mask + ")");
092: }
093:
094: for (int i = 0; i < permissionsToAdd.length; i++) {
095: if (logger.isDebugEnabled()) {
096: logger.debug("Add permission: "
097: + printPermissionsBlock(permissionsToAdd[i])
098: + " " + printBinary(permissionsToAdd[i]) + " ("
099: + permissionsToAdd[i] + ")");
100: }
101:
102: this .mask |= permissionsToAdd[i];
103: }
104:
105: if (Arrays.binarySearch(validPermissions, this .mask) < 0) {
106: throw new IllegalArgumentException(
107: "Resulting permission set will be invalid.");
108: } else {
109: if (logger.isDebugEnabled()) {
110: logger.debug("AFTER Permissions: "
111: + printPermissionsBlock(mask) + " "
112: + printBinary(mask) + " (" + mask + ")");
113: }
114:
115: return this .mask;
116: }
117: }
118:
119: public int deletePermission(int permissionToDelete) {
120: return deletePermissions(new int[] { permissionToDelete });
121: }
122:
123: public int deletePermissions(int[] permissionsToDelete) {
124: if (logger.isDebugEnabled()) {
125: logger.debug("BEFORE Permissions: "
126: + printPermissionsBlock(mask) + " "
127: + printBinary(mask) + " (" + mask + ")");
128: }
129:
130: for (int i = 0; i < permissionsToDelete.length; i++) {
131: if (logger.isDebugEnabled()) {
132: logger.debug("Delete permission: "
133: + printPermissionsBlock(permissionsToDelete[i])
134: + " " + printBinary(permissionsToDelete[i])
135: + " (" + permissionsToDelete[i] + ")");
136: }
137:
138: this .mask &= ~permissionsToDelete[i];
139: }
140:
141: if (Arrays.binarySearch(validPermissions, this .mask) < 0) {
142: throw new IllegalArgumentException(
143: "Resulting permission set will be invalid.");
144: } else {
145: if (logger.isDebugEnabled()) {
146: logger.debug("AFTER Permissions: "
147: + printPermissionsBlock(mask) + " "
148: + printBinary(mask) + " (" + mask + ")");
149: }
150:
151: return this .mask;
152: }
153: }
154:
155: public AclObjectIdentity getAclObjectIdentity() {
156: return this .aclObjectIdentity;
157: }
158:
159: public AclObjectIdentity getAclObjectParentIdentity() {
160: return this .aclObjectParentIdentity;
161: }
162:
163: public int getMask() {
164: return this .mask;
165: }
166:
167: public Object getRecipient() {
168: return this .recipient;
169: }
170:
171: /**
172: * Subclasses must indicate the permissions they support. Each base permission should be an integer with a
173: * base 2. ie: the first permission is 2^^0 (1), the second permission is 2^^1 (2), the third permission is 2^^2
174: * (4) etc. Each base permission should be exposed by the subclass as a <code>public static final int</code>. It
175: * is further recommended that valid combinations of permissions are also exposed as <code>public static final
176: * int</code>s.<P>This method returns all permission integers that are allowed to be used together. <B>This
177: * must include any combinations of valid permissions</b>. So if the permissions indicated by 2^^2 (4) and 2^^1
178: * (2) can be used together, one of the integers returned by this method must be 6 (4 + 2). Otherwise attempts to
179: * set the permission will be rejected, as the final resulting mask will be rejected.</p>
180: * <P>Whilst it may seem unduly time onerous to return every valid permission <B>combination</B>, doing so
181: * delivers maximum flexibility in ensuring ACLs only reflect logical combinations. For example, it would be
182: * inappropriate to grant a "read" and "write" permission along with an "unrestricted" permission, as the latter
183: * implies the former permissions.</p>
184: *
185: * @return <b>every</b> valid combination of permissions
186: */
187: public abstract int[] getValidPermissions();
188:
189: public boolean isPermitted(int permissionToCheck) {
190: return isPermitted(this .mask, permissionToCheck);
191: }
192:
193: protected boolean isPermitted(int maskToCheck, int permissionToCheck) {
194: return ((maskToCheck & permissionToCheck) == permissionToCheck);
195: }
196:
197: private String printBinary(int i) {
198: String s = Integer.toString(i, 2);
199:
200: String pattern = "................................";
201:
202: String temp1 = pattern.substring(0, pattern.length()
203: - s.length());
204:
205: String temp2 = temp1 + s;
206:
207: return temp2.replace('0', '.');
208: }
209:
210: /**
211: * Outputs the permissions in a human-friendly format. For example, this method may return "CR-D" to
212: * indicate the passed integer permits create, permits read, does not permit update, and permits delete.
213: *
214: * @param i the integer containing the mask which should be printed
215: *
216: * @return the human-friend formatted block
217: */
218: public abstract String printPermissionsBlock(int i);
219:
220: /**
221: * Outputs the permissions in human-friendly format for the current <code>AbstractBasicAclEntry</code>'s
222: * mask.
223: *
224: * @return the human-friendly formatted block for this instance
225: */
226: public String printPermissionsBlock() {
227: return printPermissionsBlock(this .mask);
228: }
229:
230: public void setAclObjectIdentity(AclObjectIdentity aclObjectIdentity) {
231: this .aclObjectIdentity = aclObjectIdentity;
232: }
233:
234: public void setAclObjectParentIdentity(
235: AclObjectIdentity aclObjectParentIdentity) {
236: this .aclObjectParentIdentity = aclObjectParentIdentity;
237: }
238:
239: public void setMask(int mask) {
240: this .mask = mask;
241: }
242:
243: public void setRecipient(Object recipient) {
244: this .recipient = recipient;
245: }
246:
247: public String toString() {
248: StringBuffer sb = new StringBuffer();
249: sb.append(getClass().getName());
250: sb.append("[").append(aclObjectIdentity).append(",").append(
251: recipient);
252: sb.append("=").append(printPermissionsBlock(mask)).append(" ");
253: sb.append(printBinary(mask)).append(" (");
254: sb.append(mask).append(")").append("]");
255:
256: return sb.toString();
257: }
258:
259: public int togglePermission(int permissionToToggle) {
260: this .mask ^= permissionToToggle;
261:
262: if (Arrays.binarySearch(validPermissions, this .mask) < 0) {
263: throw new IllegalArgumentException(
264: "Resulting permission set will be invalid.");
265: } else {
266: return this.mask;
267: }
268: }
269: }
|