AclAssignerAspect.java

/*
 * Copyright 2018 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.genesys.blocks.security.component;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * Using aspects to generate default ACL permissions when entities are persisted
 * and remove permissions on delete.
 *
 * @author Maxym Borodenko
 * @author Matija Obreza
 */
@Aspect
@Component
@Slf4j
public class AclAssignerAspect {

	/** The acl service. */
	@Autowired
	private CustomAclService aclService;
	
	/**
	 * Instantiates a new acl assigner aspect.
	 */
	public AclAssignerAspect() {
		log.warn("Enabling {}", getClass().getName());
	}

	/**
	 * Ensure owner permissions on persist or update
	 *
	 * @param result the result
	 * @return the object
	 */
	@AfterReturning(pointcut = "execution(* org.springframework.data.repository.*.save(..)) || execution(* org.springframework.data.repository.*.saveAll(..)) || execution(* org.springframework.data.jpa.repository.*.save(..)) || execution(* org.springframework.data.jpa.repository.*.saveAndFlush(..))", returning = "result")
	public Object afterSaveAclObject(final Object result) {
		final Authentication auth = SecurityContextHolder.getContext().getAuthentication();

		if (auth != null) {
			if (result instanceof AclAwareModel) {
				maybeUpdatePermissions(result);
			} else if (result instanceof Iterable) {
				// Handle collections of AclAwareModel
				final Iterable<?> i = (Iterable<?>) result;
				for (final Object o : i) {
					maybeUpdatePermissions(o);
				}
			} else {
				log.trace("{} is not instance of AclAwareModel", result);
			}
		}

		return result;
	}

	/**
	 * Maybe add creator permissions.
	 *
	 * @param obj the obj
	 */
	private void maybeUpdatePermissions(final Object obj) {
		if (obj instanceof AclAwareModel) {
			log.trace("Creating or updating permissions for {} ", obj);
			aclService.createOrUpdatePermissions((AclAwareModel) obj);
		} else {
			log.trace("{} is not instance of AclAwareModel", obj);
		}
	}

	/**
	 * Remove permissions on delete.
	 *
	 * @param joinPoint the join point
	 */
	@Before("execution(* org.springframework.data.repository.*.delete(..)) || execution(* org.springframework.data.repository.*.deleteAll(..)) || execution(* org.springframework.data.jpa.repository.*.deleteInBatch(..))")
	public void afterDeleteAclObject(final JoinPoint joinPoint) {
		if (joinPoint.getArgs().length == 0) {
			return;
		}
		final Object arg0 = joinPoint.getArgs()[0];

		try {
			if (arg0 instanceof Long) {
				// NOOP
			} else if (arg0 instanceof AclSid) {
				final AclSid aclSid = (AclSid) arg0;
				// Remove permissions owned by SID
				aclService.removePermissionsFor(aclSid);
				// Remove permissions on SID
				maybeRemoveAclAwareModel(aclSid);
			} else if (arg0 instanceof AclAwareModel) {
				final AclAwareModel aclModel = (AclAwareModel) arg0;
				maybeRemoveAclAwareModel(aclModel);
			} else if (arg0 instanceof Iterable) {
				// Handle collections of AclAwareModel
				final Iterable<?> i = (Iterable<?>) arg0;
				for (final Object o : i) {
					maybeRemoveAclAwareModel(o);
				}
			} else {
				log.trace("{} is not instance of AclAwareModel", arg0);
			}
		} finally {
			// Nothing to do here
		}
	}

	/**
	 * Maybe remove permissions.
	 *
	 * @param obj the obj
	 */
	private void maybeRemoveAclAwareModel(final Object obj) {
		if (obj instanceof AclAwareModel) {
			log.debug("Removing ACL entries for model={}", obj);
			aclService.removeAclAwareModel((AclAwareModel) obj);
		} else {
			log.trace("{} is not instance of AclAwareModel", obj);
		}
	}
}