
package de.ivu.elect.jee.security;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.ArrayList;

import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.jee.faces.Messages;

public abstract class AbstractSecureInterceptor<UseCasePerm extends Permission, Secure extends Annotation>
    implements
      Serializable {

  private static final long serialVersionUID = 1L;

  private final Logger log = LoggerFactory.getLogger(getClass());

  @Inject
  transient AbstractPrincipal<UseCasePerm, ? , ? , ?> principal;

  @Inject
  private transient Messages messages;

  @AroundInvoke
  public Object secure(InvocationContext ctx) throws Exception {
    Secure secure = getAnnotation(ctx);

    checkPermission(secure, ctx.getTarget().getClass().getName(), ctx.getMethod().getName());

    return ctx.proceed();
  }

  protected void checkPermission(Secure secure, String className, String methodeName) {
    try {
      checkUseCasePermission(secure, className, methodeName);
    } catch (SecurityException se) {
      throw se;
    } catch (RuntimeException e) {
      log.warn("Fehler beim Prüfen der Permissions, aufgerufene Klasse: " + className + ", Methode:" + methodeName
          + ", exception: " + e.getMessage() + "- " + e.getClass());
      throw e;
    }
  }

  private void checkUseCasePermission(Secure secure, String className, String methodeName) {
    UseCasePerm useCasePermission = getUseCasePermission(secure);
    if (useCasePermission != null && !principal.hasUseCasePermission(useCasePermission)) {
      throw new SecurityException(messages, useCasePermission, new ArrayList<>(principal.getUseCasePermissions()), className,
          methodeName);
    }
  }

  protected abstract UseCasePerm getUseCasePermission(Secure secure);

  Secure getAnnotation(InvocationContext ctx) {
    Secure annotation = ctx.getMethod().getAnnotation(getSecureAnnotationClass());
    if (annotation == null) {
      annotation = ctx.getTarget().getClass().getAnnotation(getSecureAnnotationClass());
    }
    return annotation;
  }

  protected abstract Class<Secure> getSecureAnnotationClass();
}
