/**
 * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipse.hawkbit.repository;

import static org.fest.assertions.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.junit.Test;
import org.springframework.security.access.prepost.PreAuthorize;

import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;

@Features("Unit Tests - Repository")
@Stories("Security Test")
public class RepositoryManagementMethodPreAuthorizeAnnotatedTest {

    private static final Set<Method> METHOD_SECURITY_EXCLUSION = new HashSet<>();

    static {
        METHOD_SECURITY_EXCLUSION.add(getMethod(SystemManagement.class, "currentTenant"));
    }

    @Test
    @Description("Verfies that repository methods are @PreAuthorize annotated")
    public void repositoryManagementMethodsArePreAuthorizedAnnotated()
            throws ClassNotFoundException, URISyntaxException, IOException {
        final List<Class<?>> findInterfacesInPackage = findInterfacesInPackage(getClass().getPackage(),
                Pattern.compile(".*Management"));

        assertThat(findInterfacesInPackage).isNotEmpty();
        for (final Class<?> interfaceToCheck : findInterfacesInPackage) {
            assertDeclaredMethodsContainsPreAuthorizeAnnotaions(interfaceToCheck);
        }
    }

    /**
     * asserts that the given methods are annotated with the
     * {@link PreAuthorize} annotation for security. Inherited methods are not
     * checked. The following methods are excluded due inherited from
     * {@link Object}, like equals() or toString().
     * 
     * @param clazz
     *            the class to retrieve the public declared methods
     */
    private static void assertDeclaredMethodsContainsPreAuthorizeAnnotaions(final Class<?> clazz) {
        final Method[] declaredMethods = clazz.getDeclaredMethods();
        for (final Method method : declaredMethods) {
            if (!METHOD_SECURITY_EXCLUSION.contains(method) && !method.isSynthetic()
                    && Modifier.isPublic(method.getModifiers())) {
                final PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
                assertThat(annotation).as("The public method " + method.getName() + " in class " + clazz.getName()
                        + " is not annoated with @PreAuthorize, security leak?").isNotNull();
            }
        }
    }

    /**
     * Finds all interfaces in a given packages which matches the given filter.
     * 
     * @param p
     *            the package to search for interfaces in
     * @param includeFilter
     *            the pattern which interfaces class names should be included
     * @return a list of loaded interfaces in a specific package and matches the
     *         given filter
     * @throws URISyntaxException
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private List<Class<?>> findInterfacesInPackage(final Package p, final Pattern includeFilter)
            throws URISyntaxException, IOException, ClassNotFoundException {
        final List<Class<?>> interfacesToReturn = new ArrayList<>();
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        final Enumeration<URL> resources = classLoader.getResources(p.getName().replace(".", "/"));
        while (resources.hasMoreElements()) {
            final String uriPath = new URI(resources.nextElement().toString()).getPath();
            if (uriPath != null) {
                final File packageDirectory = new File(uriPath);
                final File[] filesInPackage = packageDirectory.listFiles();
                for (final File classFile : filesInPackage) {
                    final String classNameWithExtension = classFile.getName();
                    final int indexOfExtension = classNameWithExtension.indexOf(".class");
                    if (indexOfExtension > 0) {
                        final String classNameWithoutExtension = classNameWithExtension.substring(0, indexOfExtension);
                        if (includeFilter.matcher(classNameWithoutExtension).matches()) {
                            final Class<?> classInPackage = Class
                                    .forName(p.getName() + "." + classNameWithoutExtension);
                            if (classInPackage.isInterface()) {
                                interfacesToReturn.add(classInPackage);
                            }
                        }
                    }
                }
            }
        }
        return interfacesToReturn;
    }

    private static Method getMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterTypes) {
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException | SecurityException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
}

@@ -536,29 +536,8 @@
 er;%0A
-import java.net.URI;%0A
 impo
@@ -4025,32 +4025,40 @@
 nal 
-String uriPath
+File packageDirectory
  = new 
-URI
+File
 (res
@@ -4082,135 +4082,20 @@
 t().
-toString()).getPath();%0A            if (uriPath != null) %7B%0A                final File packageDirectory = new File(uriPath);%0A    
+getFile());%0A
     
@@ -4170,28 +4170,24 @@
             
-    
 for (final F
@@ -4212,36 +4212,32 @@
 lesInPackage) %7B%0A
-    
                 
@@ -4307,28 +4307,24 @@
             
-    
 final int in
@@ -4382,20 +4382,16 @@
 lass%22);%0A
-    
         
@@ -4438,36 +4438,32 @@
                 
-    
 final String cla
@@ -4534,36 +4534,32 @@
 exOfExtension);%0A
-    
                 
@@ -4648,28 +4648,24 @@
             
-    
 final Class%3C
@@ -4693,45 +4693,8 @@
 lass
-%0A                                    
 .for
@@ -4766,28 +4766,24 @@
             
-    
 if (classInP
@@ -4834,20 +4834,16 @@
         
-   
- 
 interfac
@@ -4878,38 +4878,8 @@
 e);%0A
-                            %7D%0A
     

fix unit test