/** * Copyright (C) 2011 JTalks.org Team * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jtalks.jcommune.service.security.acl; import com.google.common.base.Predicate; import org.jtalks.common.model.entity.Entity; import org.springframework.security.acls.model.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.validation.constraints.Min; import java.util.List; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Lists.newArrayList; import static org.jtalks.jcommune.service.security.acl.TypeConvertingObjectIdentityGenerator.createDefaultGenerator; /** * The fine grained utilities to work with Spring ACL. * * @author stanislav bashkirtsev */ public class AclUtil { private TypeConvertingObjectIdentityGenerator objectIdentityGenerator = createDefaultGenerator(); private final MutableAclService mutableAclService; /** * Use this constructor if you need a full blown ACL utilities. * * @param mutableAclService the acl service that is required for some operations related to DB. If you use factory * methods, then you don't need to specify it working with object identities only for * example. */ public AclUtil(@Nonnull MutableAclService mutableAclService) { this.mutableAclService = mutableAclService; } /** * Need to be able to create it without {@link MutableAclService} if it's not required. This was created mainly for * the purpose of factory methods. */ AclUtil() { this.mutableAclService = null; } /** * {@inheritDoc} */ public ExtendedMutableAcl getAclFor(Entity entity) { ObjectIdentity oid = createIdentityFor(entity); return getAclFor(oid); } /** * {@inheritDoc} */ public ExtendedMutableAcl getAclFor(ObjectIdentity oid) { try { return ExtendedMutableAcl.castAndCreate(mutableAclService.readAclById(oid)); } catch (NotFoundException nfe) { return ExtendedMutableAcl.castAndCreate(mutableAclService.createAcl(oid)); } } /** * {@inheritDoc} */ public ObjectIdentity createIdentityFor(Entity securedObject) { return objectIdentityGenerator.getObjectIdentity(securedObject); } /** * {@inheritDoc} */ public ObjectIdentity createIdentity(long id, @Nonnull String type) { return objectIdentityGenerator.createObjectIdentity(id, type); } /** * {@inheritDoc} */ public ExtendedMutableAcl grant(List<? extends Sid> sids, List<Permission> permissions, Entity target) { return applyPermissionsToSids(sids, permissions, target, true); } /** * {@inheritDoc} */ public ExtendedMutableAcl restrict(List<? extends Sid> sids, List<Permission> permissions, Entity target) { return applyPermissionsToSids(sids, permissions, target, false); } /** * {@inheritDoc} */ public ExtendedMutableAcl delete(List<? extends Sid> sids, List<Permission> permissions, Entity target) { ObjectIdentity oid = createIdentityFor(target); ExtendedMutableAcl acl = ExtendedMutableAcl.castAndCreate(mutableAclService.readAclById(oid)); deletePermissionsFromAcl(acl, sids, permissions); return acl; } /** * {@inheritDoc} */ public void deletePermissionsFromAcl( ExtendedMutableAcl acl, List<? extends Sid> sids, List<Permission> permissions) { List<AccessControlEntry> allEntries = acl.getEntries(); // it's a copy List<AccessControlEntry> filtered = newArrayList(filter(allEntries, new BySidAndPermissionFilter(sids, permissions))); acl.delete(filtered); } public Acl aclFromObjectIdentity(@Min(1) long id, @Nonnull String type) { ObjectIdentity identity = this.objectIdentityGenerator.createObjectIdentity(id, type); return getAclFor(identity); } public void setObjectIdentityGenerator(TypeConvertingObjectIdentityGenerator objectIdentityGenerator) { this.objectIdentityGenerator = objectIdentityGenerator; } /** * Apply every permission from list to every sid from list. * * @param sids list of sids * @param permissions list of permissions * @param target securable object * @param granting grant if true, restrict if false * @return the ACL that manages the specified {@code target} and its Sids & Permissions */ private ExtendedMutableAcl applyPermissionsToSids( List<? extends Sid> sids, List<Permission> permissions, Entity target, boolean granting) { ExtendedMutableAcl acl = getAclFor(target); deletePermissionsFromAcl(acl, sids, permissions); acl.addPermissions(sids, permissions, granting); return acl; } /** * Gets the list of Sids and Permissions into the constructor and filters out those {@link AccessControlEntry} whose * Sid & Permission is not in the specified lists. * * @see com.google.common.collect.Iterators#filter(java.util.Iterator, Predicate) */ private static class BySidAndPermissionFilter implements Predicate<AccessControlEntry> { private final List<? extends Sid> sids; private final List<Permission> permissions; /** * @param sids to find {@link AccessControlEntry}s that contain them * @param permissions to find the {@link AccessControlEntry}s where specified {@code sids} have these * permissions */ private BySidAndPermissionFilter(@Nonnull List<? extends Sid> sids, @Nonnull List<Permission> permissions) { this.sids = sids; this.permissions = permissions; } /** * {@inheritDoc} */ @Override public boolean apply(@Nullable AccessControlEntry input) { if (input == null) { return false; } return sids.contains(input.getSid()) && permissions.contains(input.getPermission()); } } }