package vault

import (
	"testing"

	"github.com/hashicorp/vault/logical"
)

func TestACL_Root(t *testing.T) {
	// Create the root policy ACL
	policy := []*Policy{&Policy{Name: "root"}}
	acl, err := NewACL(policy)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !acl.RootPrivilege("sys/mount/foo") {
		t.Fatalf("expected root")
	}
	if !acl.AllowOperation(logical.WriteOperation, "sys/mount/foo") {
		t.Fatalf("expected permission")
	}
}

func TestACL_Single(t *testing.T) {
	policy, err := Parse(aclPolicy)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	acl, err := NewACL([]*Policy{policy})
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if acl.RootPrivilege("sys/mount/foo") {
		t.Fatalf("unexpected root")
	}

	type tcase struct {
		op     logical.Operation
		path   string
		expect bool
	}
	tcases := []tcase{
		{logical.ReadOperation, "root", false},
		{logical.HelpOperation, "root", true},

		{logical.ReadOperation, "dev/foo", true},
		{logical.WriteOperation, "dev/foo", true},

		{logical.DeleteOperation, "stage/foo", true},
		{logical.WriteOperation, "stage/aws/foo", false},
		{logical.WriteOperation, "stage/aws/policy/foo", true},

		{logical.DeleteOperation, "prod/foo", false},
		{logical.WriteOperation, "prod/foo", false},
		{logical.ReadOperation, "prod/foo", true},
		{logical.ListOperation, "prod/foo", true},
		{logical.ReadOperation, "prod/aws/foo", false},
	}

	for _, tc := range tcases {
		out := acl.AllowOperation(tc.op, tc.path)
		if out != tc.expect {
			t.Fatalf("bad: case %#v: %v", tc, out)
		}
	}
}

func TestACL_Layered(t *testing.T) {
	policy1, err := Parse(aclPolicy)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	policy2, err := Parse(aclPolicy2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	acl, err := NewACL([]*Policy{policy1, policy2})
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	testLayeredACL(t, acl)
}

func testLayeredACL(t *testing.T, acl *ACL) {
	if acl.RootPrivilege("sys/mount/foo") {
		t.Fatalf("unexpected root")
	}

	type tcase struct {
		op     logical.Operation
		path   string
		expect bool
	}
	tcases := []tcase{
		{logical.ReadOperation, "root", false},
		{logical.HelpOperation, "root", true},

		{logical.ReadOperation, "dev/hide/foo", false},
		{logical.WriteOperation, "dev/hide/foo", false},

		{logical.DeleteOperation, "stage/foo", true},
		{logical.WriteOperation, "stage/aws/foo", false},
		{logical.WriteOperation, "stage/aws/policy/foo", true},

		{logical.DeleteOperation, "prod/foo", true},
		{logical.WriteOperation, "prod/foo", true},
		{logical.ReadOperation, "prod/foo", true},
		{logical.ListOperation, "prod/foo", true},
		{logical.ReadOperation, "prod/aws/foo", false},
	}

	for _, tc := range tcases {
		out := acl.AllowOperation(tc.op, tc.path)
		if out != tc.expect {
			t.Fatalf("bad: case %#v: %v", tc, out)
		}
	}
}

var aclPolicy = `
name = "dev"
path "dev/" {
	policy = "sudo"
}
path "stage/" {
	policy = "write"
}
path "stage/aws/" {
	policy = "read"
}
path "stage/aws/policy/" {
	policy = "sudo"
}
path "prod/" {
	policy = "read"
}
path "prod/aws/" {
	policy = "deny"
}
`

var aclPolicy2 = `
name = "ops"
path "dev/hide/" {
	policy = "deny"
}
path "stage/aws/policy/" {
	policy = "deny"
}
path "prod/" {
	policy = "write"
}
`
