/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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
 *
 *    https://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 grails.plugin.springsecurity

import spock.lang.Ignore
import spock.lang.Specification

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator

import grails.testing.web.taglib.TagLibUnitTest
import org.grails.web.mapping.DefaultLinkGenerator

class SecurityTagLibUnitSpec extends Specification implements TagLibUnitTest<SecurityTagLib> {

    static final String CONTEXT_PATH = '/context-path'
    static final String CONTROLLER = 'admin'
    static final String ACTION = 'index'
    static final String TAG_BODY = 'body'
    String currentUri = null
    boolean isAllowed = true

    def setup() {
        defineBeans({
            linkGenerator(DefaultLinkGenerator, '', CONTEXT_PATH)
        })
        currentUri = null
        tagLib.webInvocationPrivilegeEvaluator = new WebInvocationPrivilegeEvaluator() {

            @Override
            boolean isAllowed(String uri, Authentication authentication) {
                currentUri = uri
                return isAllowed
            }

            @Override
            boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
                currentUri = uri
                return isAllowed
            }
        }
        def mockSpringSecurityService = Mock(SpringSecurityService)
        mockSpringSecurityService.getAuthentication() >> {
            Authentication auth = new UsernamePasswordAuthenticationToken("user", "credentials",
                    [new SimpleGrantedAuthority("ROLE_ADMIN")])
            return auth
        }
        tagLib.springSecurityService = mockSpringSecurityService
    }

    def cleanup() {
    }

    void "test no access if not authorized"() {
        setup:
        def mockSpringSecurityService = Mock(SpringSecurityService)
        mockSpringSecurityService.getAuthentication() >> {
            return new UsernamePasswordAuthenticationToken("user", "credentials")
        }
        tagLib.springSecurityService = mockSpringSecurityService

        when:
        String rendered = accessTag()

        then:
        !rendered
    }

    void "test no access if disallowed by webInvocationPrivilegeEvaluator"() {
        setup:
        isAllowed = false

        when:
        String rendered = accessTag()

        then:
        !rendered
    }

    void "test access if authenticated"() {
        when:
        String rendered = accessTag()

        then:
        rendered == TAG_BODY
    }

    void "test remove context path from uri generated by linkGenerator"() {
        given:
        tagLib.serverContextPath = CONTEXT_PATH

        when:
        String rendered = accessTag()

        then:
        rendered == TAG_BODY
        currentUri == "/${CONTROLLER}/${ACTION}"
    }

    @Ignore
    // https://github.com/apache/grails-core/issues/13690
    void "test no removal of context path from uri generated by linkGenerator with no contextPath config"() {
        given:
        tagLib.serverContextPath = null

        when:
        accessTag()

        then:
        currentUri == "${CONTEXT_PATH}/${CONTROLLER}/${ACTION}"
    }

    void "test remove context path from uri generated by linkGenerator if contextPath is defined in request"() {
        given:
        tagLib.serverContextPath = null

        and:
        request.contextPath = CONTEXT_PATH

        when:
        String rendered = accessTag()

        then:
        rendered == TAG_BODY
        currentUri == "/${CONTROLLER}/${ACTION}"
    }

    private String accessTag() {
        return tagLib.access(controller: CONTROLLER, action: ACTION, TAG_BODY)
    }
}