package authorizer

import (
	"testing"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/auth/user"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/util/uuid"

	authorizationapi "github.com/openshift/origin/pkg/authorization/api"

	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
)

func TestClusterAdminUseGroup(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "root", Groups: []string{bootstrappolicy.ClusterAdminGroup}}),
		attributes: &DefaultAuthorizationAttributes{
			APIGroup: "extensions",
			Verb:     "create",
			Resource: "jobs",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.clusterBindings = newDefaultClusterPolicyBindings()

	test.test(t)
}

func TestClusterReaderUseGroup(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "root", Groups: []string{bootstrappolicy.ClusterReaderGroup}}),
		attributes: &DefaultAuthorizationAttributes{
			APIGroup: "extensions",
			Verb:     "list",
			Resource: "jobs",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.clusterBindings = newDefaultClusterPolicyBindings()

	test.test(t)
}

func TestInvalidRole(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Brad"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "buildConfigs",
		},
		expectedAllowed: false,
		expectedError:   "unable to interpret:",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newInvalidExtensionPolicies()
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newInvalidExtensionBindings()

	test.test(t)
}
func TestInvalidRoleButRuleNotUsed(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Brad"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "buildConfigs",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newInvalidExtensionPolicies()
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newInvalidExtensionBindings()

	test.test(t)
}
func TestViewerGetAllowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Victor"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "pods",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestViewerGetAllowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Victor"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "pods",
		},
		expectedAllowed: false,
		expectedReason:  `User "Victor" cannot get pods in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestViewerGetDisallowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Victor"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "policies",
		},
		expectedAllowed: false,
		expectedReason:  `User "Victor" cannot get policies in project "mallet"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestViewerGetDisallowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Victor"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "policies",
		},
		expectedAllowed: false,
		expectedReason:  `User "Victor" cannot get policies in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestViewerCreateAllowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Victor"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "create",
			Resource: "pods",
		},
		expectedAllowed: false,
		expectedReason:  `User "Victor" cannot create pods in project "mallet"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestViewerCreateAllowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Victor"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "create",
			Resource: "pods",
		},
		expectedAllowed: false,
		expectedReason:  `User "Victor" cannot create pods in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestEditorUpdateAllowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Edgar"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "pods",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestEditorUpdateAllowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Edgar"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "pods",
		},
		expectedAllowed: false,
		expectedReason:  `User "Edgar" cannot update pods in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestEditorUpdateDisallowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Edgar"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "roleBindings",
		},
		expectedAllowed: false,
		expectedReason:  `User "Edgar" cannot update roleBindings in project "mallet"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestEditorUpdateDisallowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Edgar"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "roleBindings",
		},
		expectedAllowed: false,
		expectedReason:  `User "Edgar" cannot update roleBindings in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestEditorGetAllowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Edgar"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "pods",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestEditorGetAllowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Edgar"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "pods",
		},
		expectedAllowed: false,
		expectedReason:  `User "Edgar" cannot get pods in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestAdminUpdateAllowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "roleBindings",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestAdminUpdateAllowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "roleBindings",
		},
		expectedAllowed: false,
		expectedReason:  `User "Matthew" cannot update roleBindings in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestAdminUpdateStatusInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "pods/status",
		},
		expectedAllowed: false,
		expectedReason:  `User "Matthew" cannot update pods/status in project "mallet"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestAdminGetStatusInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "pods/status",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestAdminUpdateDisallowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "policies",
		},
		expectedAllowed: false,
		expectedReason:  `User "Matthew" cannot update policies in project "mallet"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestAdminUpdateDisallowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "update",
			Resource: "roles",
		},
		expectedAllowed: false,
		expectedReason:  `User "Matthew" cannot update roles in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func TestAdminGetAllowedKindInMallet(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "mallet"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "policies",
		},
		expectedAllowed: true,
		expectedReason:  "allowed by rule in mallet",
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}
func TestAdminGetAllowedKindInAdze(t *testing.T) {
	test := &authorizeTest{
		context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: "Matthew"}),
		attributes: &DefaultAuthorizationAttributes{
			Verb:     "get",
			Resource: "policies",
		},
		expectedAllowed: false,
		expectedReason:  `User "Matthew" cannot get policies in project "adze"`,
	}
	test.clusterPolicies = newDefaultClusterPolicies()
	test.policies = newAdzePolicies()
	test.policies = append(test.policies, newMalletPolicies()...)
	test.clusterBindings = newDefaultClusterPolicyBindings()
	test.bindings = newAdzeBindings()
	test.bindings = append(test.bindings, newMalletBindings()...)

	test.test(t)
}

func newMalletPolicies() []authorizationapi.Policy {
	return []authorizationapi.Policy{
		{
			ObjectMeta: kapi.ObjectMeta{
				Name:      authorizationapi.PolicyName,
				Namespace: "mallet",
			},
			Roles: map[string]*authorizationapi.Role{},
		}}
}
func newMalletBindings() []authorizationapi.PolicyBinding {
	return []authorizationapi.PolicyBinding{
		{
			ObjectMeta: kapi.ObjectMeta{
				Name:      authorizationapi.ClusterPolicyBindingName,
				Namespace: "mallet",
			},
			RoleBindings: map[string]*authorizationapi.RoleBinding{
				"projectAdmins": {
					ObjectMeta: kapi.ObjectMeta{
						Name:      "projectAdmins",
						Namespace: "mallet",
					},
					RoleRef: kapi.ObjectReference{
						Name: bootstrappolicy.AdminRoleName,
					},
					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Matthew"}},
				},
				"viewers": {
					ObjectMeta: kapi.ObjectMeta{
						Name:      "viewers",
						Namespace: "mallet",
					},
					RoleRef: kapi.ObjectReference{
						Name: bootstrappolicy.ViewRoleName,
					},
					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Victor"}},
				},
				"editors": {
					ObjectMeta: kapi.ObjectMeta{
						Name:      "editors",
						Namespace: "mallet",
					},
					RoleRef: kapi.ObjectReference{
						Name: bootstrappolicy.EditRoleName,
					},
					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Edgar"}},
				},
			},
		},
	}
}
func newInvalidExtensionPolicies() []authorizationapi.Policy {
	return []authorizationapi.Policy{
		{
			ObjectMeta: kapi.ObjectMeta{
				Name:      authorizationapi.PolicyName,
				Namespace: "mallet",
			},
			Roles: map[string]*authorizationapi.Role{
				"badExtension": {
					ObjectMeta: kapi.ObjectMeta{
						Name:      "failure",
						Namespace: "mallet",
					},
					Rules: []authorizationapi.PolicyRule{
						{
							APIGroups:             []string{""},
							Verbs:                 sets.NewString("watch", "list", "get"),
							Resources:             sets.NewString("buildConfigs"),
							AttributeRestrictions: &authorizationapi.Role{},
						},
						{
							APIGroups: []string{""},
							Verbs:     sets.NewString("update"),
							Resources: sets.NewString("buildConfigs"),
						},
					},
				},
			},
		}}
}
func newInvalidExtensionBindings() []authorizationapi.PolicyBinding {
	return []authorizationapi.PolicyBinding{
		{
			ObjectMeta: kapi.ObjectMeta{
				Name:      "mallet",
				Namespace: "mallet",
			},
			RoleBindings: map[string]*authorizationapi.RoleBinding{
				"borked": {
					ObjectMeta: kapi.ObjectMeta{
						Name:      "borked",
						Namespace: "mallet",
					},
					RoleRef: kapi.ObjectReference{
						Name:      "badExtension",
						Namespace: "mallet",
					},
					Subjects: []kapi.ObjectReference{{Kind: authorizationapi.UserKind, Name: "Brad"}},
				},
			},
		},
	}
}

func GetBootstrapPolicy() *authorizationapi.ClusterPolicy {
	policy := &authorizationapi.ClusterPolicy{
		ObjectMeta: kapi.ObjectMeta{
			Name:              authorizationapi.PolicyName,
			CreationTimestamp: unversioned.Now(),
			UID:               uuid.NewUUID(),
		},
		LastModified: unversioned.Now(),
		Roles:        make(map[string]*authorizationapi.ClusterRole),
	}

	roles := bootstrappolicy.GetBootstrapClusterRoles()
	for i := range roles {
		policy.Roles[roles[i].Name] = &roles[i]
	}

	return policy
}

func GetBootstrapPolicyBinding() *authorizationapi.ClusterPolicyBinding {
	policyBinding := &authorizationapi.ClusterPolicyBinding{
		ObjectMeta: kapi.ObjectMeta{
			Name:              ":Default",
			CreationTimestamp: unversioned.Now(),
			UID:               uuid.NewUUID(),
		},
		LastModified: unversioned.Now(),
		RoleBindings: make(map[string]*authorizationapi.ClusterRoleBinding),
	}

	bindings := bootstrappolicy.GetBootstrapClusterRoleBindings()
	for i := range bindings {
		policyBinding.RoleBindings[bindings[i].Name] = &bindings[i]
	}

	return policyBinding
}