package org.zalando.zhewbacca

import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import play.api.Configuration
import play.api.test.FakeRequest

import scala.concurrent.ExecutionContext

class SecurityRulesRepositorySpec extends Specification with Mockito {

  "SecurityRulesRepository" should {

    "load rules from default file" in {
      val provider = mock[AuthProvider]
      val repository = new SecurityRulesRepository(Configuration(), provider)
      val expectedRule = ValidateTokenRule(provider, "GET", "/foo", Scope(Set("uid", "entity.read")))

      repository.get(FakeRequest("GET", "/foo")) must beSome(expectedRule)
    }

    "load rules from custom file" in {
      val provider = mock[AuthProvider]
      val config = Configuration("authorisation.rules.file" -> "security_custom-security.conf")
      val repository = new SecurityRulesRepository(config, provider)
      val expectedRule = ValidateTokenRule(provider, "POST", "/bar.*", Scope(Set("uid")))

      repository.get(FakeRequest("POST", "/bar.*")) must beSome(expectedRule)
    }

    "raise an error when custom file is not available" in {
      val authProvider = mock[AuthProvider]
      val config = Configuration("authorisation.rules.file" -> "this-file-does-not-exist.conf")

      new SecurityRulesRepository(config, authProvider) must
        throwA[RuntimeException]("configuration file this-file-does-not-exist.conf for security rules not found")
    }

    "allow comments in security rules configuration file" in {
      val provider = mock[AuthProvider]
      val config = Configuration("authorisation.rules.file" -> "security_commented.conf")
      val repository = new SecurityRulesRepository(config, provider)
      val expectedRule = ValidateTokenRule(provider, "OPTIONS", "/", Scope(Set("app.resource.read")))

      repository.get(FakeRequest("OPTIONS", "/")) must beSome(expectedRule)
    }

    "raise an error when it cannot parse a configuration file" in {
      val authProvider = mock[AuthProvider]
        def config(fileName: String): Configuration = Configuration("authorisation.rules.file" -> fileName)

      new SecurityRulesRepository(config("security_unknown-http-method.conf"), authProvider) must throwA[RuntimeException]
      new SecurityRulesRepository(config("security_no-scopes.conf"), authProvider) must throwA[RuntimeException]
    }

    "return None if there is no configured rules for given request" in {
      val authProvider = mock[AuthProvider]
      val repository = new SecurityRulesRepository(Configuration(), authProvider)

      repository.get(FakeRequest("GET", "/unknown-uri")) must beNone
    }

    "allow explicitly to pass-through or deny a request for a specific URI" in {
      val authProvider = mock[AuthProvider]
      val configuration = Configuration("authorisation.rules.file" -> "security_pass-through.conf")
      val repository = new SecurityRulesRepository(configuration, authProvider)

      repository.get(FakeRequest("GET", "/foo")).get must beAnInstanceOf[ExplicitlyAllowedRule]
      repository.get(FakeRequest("GET", "/bar")).get must beAnInstanceOf[ExplicitlyDeniedRule]
    }

  }

}