package authz

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/RafayLabs/rcloud-base/internal/constants"
	"github.com/RafayLabs/rcloud-base/pkg/controller/runtime"
	"github.com/RafayLabs/rcloud-base/pkg/log"
	"github.com/RafayLabs/rcloud-base/pkg/query"
	"github.com/RafayLabs/rcloud-base/pkg/sentry/kubeconfig"
	"github.com/RafayLabs/rcloud-base/pkg/service"
	sentryrpc "github.com/RafayLabs/rcloud-base/proto/rpc/sentry"
	commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
	"github.com/RafayLabs/rcloud-base/proto/types/controller"
	"github.com/RafayLabs/rcloud-base/proto/types/sentry"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
)

var _log = log.GetLogger()

var permissions = []string{
	sentry.KubectlFullAccessPermission,
	sentry.KubectlNamespaceReadPermission,
	sentry.KubectlNamespaceWritePermission,
	sentry.KubectlClusterReadPermission,
	sentry.KubectlClusterWritePermission,
}

var clusterScopePermissions = []string{
	sentry.KubectlClusterReadPermission,
	sentry.KubectlClusterWritePermission,
	sentry.KubectlFullAccessPermission,
}

var namespaceScopePermissions = []string{
	sentry.KubectlNamespaceReadPermission,
	sentry.KubectlNamespaceWritePermission,
}

const (
	rafayRelayLabel     = "rafay-relay"
	relayUserLabel      = "relay-user"
	authzRefreshedLabel = "authz-refreshed"
	systemUsername      = "admin@rafay.co"
)

type roleBindExclusionList struct {
	exclude   bool
	namespace string
}

func getCurrentEpoch() string {
	return strconv.FormatInt(time.Now().Unix(), 10)
}

func getAuthzLabels(userName string) map[string]string {
	return map[string]string{
		rafayRelayLabel:     "true",
		relayUserLabel:      userName,
		authzRefreshedLabel: getCurrentEpoch(),
	}
}

/*TODO: pending along with namespaces
func getAccountProjectNamespace(ctx context.Context, projectID, accountID, orgID int64, apn service.AccountProjectNamespaceService) ([]string, error) {
	var ns []string

	apns, err := apn.GetAccountProjectNamesapce(ctx, orgID, accountID, projectID)
	if err != nil {
		return nil, err
	}

	for _, apn := range apns {
		ns = append(ns, apn.NamespaceName)
	}

	return ns, nil
}

func getSSOAccountProjectNamespace(ctx context.Context, projectID, accountID, orgID int64, apn service.AccountProjectNamespaceService) ([]string, error) {
	var ns []string

	apns, err := apn.GetSSOAccountProjectNamesapce(ctx, orgID, accountID, projectID)
	if err != nil {
		return nil, err
	}

	for _, apn := range apns {
		ns = append(ns, apn.NamespaceName)
	}

	return ns, nil
}

func getGroupAccountProjectNamespace(ctx context.Context, projectID, accountID, orgID int64, apn service.AccountProjectNamespaceService) ([]string, error) {
	var ns []string

	apns, err := apn.GetGroupProjectNamesapce(ctx, orgID, accountID, projectID)
	if err != nil {
		return nil, err
	}

	for _, apn := range apns {
		ns = append(ns, apn.NamespaceName)
	}

	return ns, nil
}
*/

func getProjectPermissions(ctx context.Context, projects []string, accountID, orgID, partnerID string, aps service.AccountPermissionService) (map[string][]string, string, error) {
	projects = append(projects, "")
	accountPermissions, err := aps.GetAccountPermissionsByProjectIDPermissions(ctx, accountID, orgID, partnerID, projects, permissions)
	if err != nil {
		return nil, "", err
	}

	accountData, err := aps.GetAccount(ctx, accountID)
	if err != nil {
		return nil, "", err
	}

	projectPermissions := make(map[string][]string)
OUTER:
	for _, accountPermission := range accountPermissions {
		p := accountPermission.ProjectID
		if projectPermissions[p] == nil {
			projectPermissions[p] = []string{}
		}
		for _, permission := range projectPermissions[p] {
			if permission == accountPermission.PermissionName {
				continue OUTER
			}
		}
		projectPermissions[p] = append(projectPermissions[p], accountPermission.PermissionName)
	}
	return projectPermissions, accountData.Username, nil
}

func getSSOProjectPermissions(ctx context.Context, projects []string, orgID, partnerID, accountID string, aps service.AccountPermissionService, gps service.GroupPermissionService) (map[string][]string, string, []string, error) {
	acc, err := aps.GetAccount(ctx, accountID)
	if err != nil {
		return nil, "", nil, err
	}
	groups, err := aps.GetAccountGroups(ctx, accountID)
	if err != nil {
		return nil, "", nil, err
	}
	projects = append(projects, "")
	groupPermissions, err := gps.GetGroupPermissionsByProjectIDPermissions(ctx, groups, orgID, partnerID, projects, permissions)
	if err != nil {
		return nil, "", nil, err
	}

	projectPermissions := make(map[string][]string)
OUTER:
	for _, groupPermission := range groupPermissions {
		p := groupPermission.ProjectID
		if projectPermissions[p] == nil {
			projectPermissions[p] = []string{}
		}
		for _, permission := range projectPermissions[p] {
			if permission == groupPermission.PermissionName {
				continue OUTER
			}
		}
		projectPermissions[p] = append(projectPermissions[p], groupPermission.PermissionName)
	}
	return projectPermissions, acc.Username, groups, nil
}

func getClusterRole(permission string) (cr *rbacv1.ClusterRole, err error) {
	switch permission {
	case sentry.KubectlFullAccessPermission:
		cr, err = GetFullAccessClusterRole()
	case sentry.KubectlNamespaceWritePermission:
		cr, err = GetWriteNamespaceClusterRole()
	case sentry.KubectlNamespaceReadPermission:
		cr, err = GetReadNamespaceClusterRole()
	case sentry.KubectlClusterWritePermission:
		cr, err = GetWriteClusterScopeClusterRole()
	case sentry.KubectlClusterReadPermission:
		cr, err = GetReadClusterScopeClusterRole()
	default:
		err = fmt.Errorf("permission not valid - %s", permission)
	}

	if cr != nil {

	}

	return
}

func getRole(permission string) (r *rbacv1.Role, err error) {
	switch permission {
	case sentry.KubectlNamespaceWritePermission:
		r, err = GetWriteNamespaceRole()
	case sentry.KubectlNamespaceReadPermission:
		r, err = GetReadNamespaceRole()
	default:
		err = fmt.Errorf("permission not valid - %s", permission)
	}

	return
}

func getRoleName(nsName, permission string) string {
	switch permission {
	case sentry.KubectlNamespaceWritePermission:
		return "rafay-ns-role-write-" + nsName
	case sentry.KubectlNamespaceReadPermission:
		return "rafay-ns-role-read-" + nsName
	default:
		_log.Infow("getRoleName invalid namespace", "permission", permission)
	}
	return ""
}

func setRoleValues(r *rbacv1.Role, nsName, permission string) {
	r.Name = getRoleName(nsName, permission)
	r.Namespace = nsName
}

func getClusterRoleBindingName(saName, clusterRole string) string {
	return clusterRole + "-" + saName + "-cr-binding"
}

func getClusterRoleBinding(sa *corev1.ServiceAccount, clusterRole string) *rbacv1.ClusterRoleBinding {
	crb := &rbacv1.ClusterRoleBinding{}
	crb.APIVersion = "rbac.authorization.k8s.io/v1"
	crb.Kind = "ClusterRoleBinding"
	crb.Name = getClusterRoleBindingName(sa.Name, clusterRole)
	// crb.Labels = map[string]string{
	// 	"rafay-relay": "true",
	// 	"relay-user":  sa.Name,
	// }
	subject := rbacv1.Subject{}
	subject.Kind = "ServiceAccount"
	subject.Name = sa.Name
	subject.Namespace = sa.Namespace
	crb.Subjects = append(crb.Subjects, subject)

	crb.RoleRef.Kind = "ClusterRole"
	crb.RoleRef.APIGroup = "rbac.authorization.k8s.io"
	crb.RoleRef.Name = clusterRole

	return crb
}

func getDeleteClusterRoleBinding(name string) *rbacv1.ClusterRoleBinding {
	crb := &rbacv1.ClusterRoleBinding{}
	crb.APIVersion = "rbac.authorization.k8s.io/v1"
	crb.Kind = "ClusterRoleBinding"
	crb.Name = name
	return crb
}

func getRoleBindingName(saName, roleName string) string {
	return roleName + "-" + saName + "-r-binding"
}

func getRoleBinding(sa *corev1.ServiceAccount, roleName, namespace string) *rbacv1.RoleBinding {
	rb := &rbacv1.RoleBinding{}
	rb.APIVersion = "rbac.authorization.k8s.io/v1"
	rb.Kind = "RoleBinding"
	rb.Name = getRoleBindingName(sa.Name, roleName)
	rb.Namespace = namespace
	// rb.Labels = map[string]string{
	// 	"rafay-relay": "true",
	// 	"relay-user":  sa.Name,
	// }
	subject := rbacv1.Subject{}
	subject.Kind = "ServiceAccount"
	subject.Name = sa.Name
	subject.Namespace = sa.Namespace
	rb.Subjects = append(rb.Subjects, subject)

	rb.RoleRef.Kind = "Role"
	rb.RoleRef.APIGroup = "rbac.authorization.k8s.io"
	rb.RoleRef.Name = roleName

	return rb
}

func getDeleteRoleBinding(name, namespace string) *rbacv1.RoleBinding {
	rb := &rbacv1.RoleBinding{}
	rb.APIVersion = "rbac.authorization.k8s.io/v1"
	rb.Kind = "RoleBinding"
	rb.Name = name
	rb.Namespace = namespace
	return rb
}

func getProjectsFromLabels(labels map[string]string) ([]string, error) {
	projects := make([]string, 0)
	for key := range labels {
		if !strings.HasPrefix(key, "project/") {
			continue
		}
		s := strings.Split(key, "/")
		if len(s) != 2 {
			continue
		}
		projectID := s[1]
		projects = append(projects, projectID)
	}
	return projects, nil
}

// GetAuthorization returns authorization for user, cluster
// The RBAC model mapped to the existing role
// PROJECT_ADMIN:
//   -   Read/Write access to all cluster scoped resources
//   -   Read/Write access to all namespace scoped resources
// PROJECT_READ:
//   -   Read access to all cluster scoped resources
//   -   Read access to all namespace scoped resources
// INFRA_ADMIN:
//   -   Read/Write access to all cluster scoped resources
//   -   Read/Write access to all namespace scoped resources
// INFRA_READ:
//   -   Read access to all cluster scoped resources
//   -   Read access to all namespace scoped resources
// ENV_ADMIN
//   -   NO Access to cluster scoped resources
//   -   Read/Write Access to namespace scoped resources (only within the environment)
// ENV_READ
//   -   NO Access to cluster scoped resources
//   -   Read Access to namespace scoped resources (only within the environment)
func GetAuthorization(ctx context.Context, req *sentryrpc.GetUserAuthorizationRequest, bs service.BootstrapService, aps service.AccountPermissionService, gps service.GroupPermissionService, krs service.KubeconfigRevocationService, kcs service.KubectlClusterSettingsService, kss service.KubeconfigSettingService) (resp *sentryrpc.GetUserAuthorizationResponse, err error) {
	var userName string
	var groups []string
	var rolePrevilage int
	var highestRole string
	var enforceOrgAdminOnlySecretAccess, isOrgAdmin bool

	resp = new(sentryrpc.GetUserAuthorizationResponse)

	// get attributes from user CN
	cnAttr := kubeconfig.GetCNAttributes(req.UserCN)
	accountID := cnAttr.AccountID
	if err != nil {
		_log.Errorw("error getting accountID from user CN", "userCN", req.UserCN)
		return nil, err
	}
	orgID := cnAttr.OrganizationID
	if err != nil {
		_log.Errorw("error getting orgID from user CN", "userCN", req.UserCN)
		return nil, err
	}
	partnerID := cnAttr.PartnerID
	if err != nil {
		_log.Errorw("error getting partnerID from user CN", "userCN", req.UserCN)
		return nil, err
	}

	if cnAttr.SystemUser {
		return getSystemUserAuthz(cnAttr)
	}

	isOrgAdmin, _ = aps.IsOrgAdmin(ctx, accountID, partnerID)

	// Check user is partner / super admin to bypass cluster/user checks.
	// Partner Super admins has full access.
	isPartnerAdmin, isSuperAdmin, err := aps.IsPartnerSuperAdmin(ctx, accountID, partnerID)
	if err != nil {
		_log.Infow("Error getting partner/super admin permission info", "accountID", accountID, "orgID", orgID, "partnerID", partnerID, "error", err)
	}
	_log.Infow("check for partner/super admin", " isPartnerAdmin ", isPartnerAdmin, " isSuperAdmin ", isSuperAdmin)
	if !isSuperAdmin && !isPartnerAdmin {
		existUserLevel := true
		// get org level setting if exist
		ksOrg, errOrg := kss.Get(ctx, orgID, "", cnAttr.IsSSO)
		if errOrg != nil && errOrg != constants.ErrNotFound {
			_log.Errorw("failed to fetch organization level kubectl settings for", "userCN", req.UserCN)
			return nil, errOrg
		}
		if errOrg == nil && ksOrg != nil {
			// check for kubectl org settings
			err = verifyKubectlSettings(cnAttr, ksOrg, "organization")
			if err != nil {
				_log.Errorw("kubectl denied as per org level kubectl settings for", "userCN", req.UserCN)
				return nil, err
			}
			enforceOrgAdminOnlySecretAccess = ksOrg.EnforceOrgAdminSecretAccess
		}

		// check for kubectl cluster settings
		err = verifyClusterKubectlSettings(ctx, bs, kcs, cnAttr, req.ClusterID, orgID)
		if err != nil {
			_log.Errorw("failed to verify kubectl cluster settings for", "userCN", req.UserCN)
			return nil, err
		}

		// get user level settings if exist
		ks, errUser := kss.Get(ctx, orgID, accountID, cnAttr.IsSSO)
		if errUser == constants.ErrNotFound {
			existUserLevel = false
			// set org settings if exist
			errUser = errOrg
			ks = ksOrg
		} else if errUser != nil && errUser != constants.ErrNotFound {
			return nil, errUser
		}

		if existUserLevel && ks != nil {
			// check for kubectl user settings
			errVerify := verifyKubectlSettings(cnAttr, ks, "user")
			if errVerify != nil {
				_log.Errorw("kubectl denied as per user level kubectl settings for", "userCN", req.UserCN)
				return nil, errVerify
			}
		}

		if errUser == nil && ks != nil && ks.EnableSessionCheck {
			// check the last login timestamp
			var lastLogin time.Time
			if cnAttr.IsSSO {
				accountData, err := aps.GetAccount(ctx, accountID)
				if err != nil {
					return nil, err
				}
				lastLogin = accountData.LastLogin
			} else {
				accountData, err := aps.GetAccount(ctx, accountID)
				if err != nil {
					return nil, err
				}
				lastLogin = accountData.LastLogin
			}
			t1 := time.Now()
			if t1.Sub(lastLogin) > time.Hour*12 {
				_log.Infow("get kubectl authorization block access. user did not login to portal in last 12 Hour")
				return nil, fmt.Errorf("enforce session enabled. user did not login to portal in last 12 Hour")
			}
		}

		// is user active
		if !cnAttr.IsSSO {
			active, err := aps.IsAccountActive(ctx, accountID, orgID)
			if err != nil {
				return nil, err
			}
			if !active {
				return nil, fmt.Errorf("Error: kubeconfig user deactivated")
			}
		}

		// get revocation timestamp
		kr, err := krs.Get(ctx, orgID, accountID, cnAttr.IsSSO)
		if err != nil && err != constants.ErrNotFound {
			return nil, err
		} else if err == nil && kr.RevokedAt.AsTime().Unix() >= req.CertIssueSeconds {
			return nil, fmt.Errorf("Error: kubeconfig revoked")
		}
	}

	opts := commonv3.QueryOptions{
		Name:         req.ClusterID,
		Organization: orgID,
		Partner:      partnerID,
	}
	bal, err := bs.GetBootstrapAgents(ctx, "-",
		query.WithOptions(&opts),
		// ignore project id because kubeconfig is not project scoped
		query.WithIgnoreScopeDefault(),
	)
	if err != nil {
		return nil, err
	}
	if bal.Metadata.Count <= 0 {
		return nil, fmt.Errorf("no bootstrap agents found")
	}
	ba := bal.Items[0]
	labels := ba.Metadata.GetLabels()

	// get projects
	projects, err := getProjectsFromLabels(labels)
	if err != nil {
		_log.Errorw("error getting projects from bootstrap agents labels", "labels", labels, "error", err.Error())
		return nil, err
	}

	// get permissions in the cluster's projects
	var projectPermissions map[string][]string
	if !cnAttr.IsSSO {
		projectPermissions, userName, err = getProjectPermissions(ctx, projects, accountID, orgID, partnerID, aps)
	} else {
		projectPermissions, userName, groups, err = getSSOProjectPermissions(ctx, projects, orgID, partnerID, accountID, aps, gps)
	}
	if err != nil {
		_log.Errorw("error getting project permission", "projects", projects, "userCN", req.UserCN, "error", err.Error())
		return nil, err
	}

	// get sa, clusterroles, roles, bindings
	sa := &corev1.ServiceAccount{}
	sa.APIVersion = "v1"
	sa.Kind = "ServiceAccount"
	sa.Name = cnAttr.Username
	sa.Namespace = "rafay-system"

	crMap := make(map[string]*rbacv1.ClusterRole)
	crbMap := make(map[string]*rbacv1.ClusterRoleBinding)
	rMap := make(map[string]*rbacv1.Role)
	rbMap := make(map[string]*rbacv1.RoleBinding)
	nsMap := make(map[string]*corev1.Namespace)
	crbExclusionMap := make(map[string]bool)
	rbExclusionMap := make(map[string]*roleBindExclusionList)

	projectNamespaces := make([]string, 0)
	/*TODO: pending with namespaces
	projectNamespaces, err := func() ([]string, error) {
		var nsl []string
		configClient, err := cPool.NewClient(ctx)
		if err != nil {
			err = errors.Wrap(err, "unable to get config client")
			return nil, err
		}
		defer configClient.Close()

		for _, project := range projects {
			namespaces, err := configClient.GetNamespaces(ctx, &configrpc.GetAllNamespacesRequest{
				QueryOptions: commonv3.QueryOptions{
					Project:      project,
					Organization: orgID,
					Partner:      partnerID,
				},
			})

			if err == nil {
				_log.Debugw("Get namespaces ", "orgID", orgID, "partnerID", partnerID, "project", project, "namespaces", namespaces.Items, "itemslen", len(namespaces.Items))
				for _, namespace := range namespaces.Items {
					nsl = append(nsl, namespace.Name)
				}
			}
		}
		return nsl, nil
	}()

	if err != nil {
		_log.Infow("unable to get project namespaces", "error", err)
		return nil, err
	}

	_log.Debugw("projectNamespaces", "names", projectNamespaces)
	*/

	for _, pm := range sentry.GetKubeConfigClusterPermissions() {
		cr, err := getClusterRole(pm)
		if err != nil {
			continue
		}
		crbName := getClusterRoleBindingName(sa.Name, cr.Name)
		crbExclusionMap[crbName] = true
	}

	for _, pm := range sentry.GetKubeConfigNameSpacePermissions() {
		if len(projectNamespaces) > 0 {
			for _, nsName := range projectNamespaces {
				roleName := getRoleName(nsName, pm)
				rbName := getRoleBindingName(sa.Name, roleName)
				rbExclusionMap[rbName] = &roleBindExclusionList{true, nsName}
			}
		}
	}

	rolePrevilage = -1
	for project, permissions := range projectPermissions {
		var namespaces []string
		_log.Infow("authorization", "project", project, "user", sa.Name, "permissions", permissions)
		groups = append(groups, permissions...)
		/* TODO: pending with namespaces
		// need to get the namesapces assigned to this user.
		if !cnAttr.IsSSO {
			ns1, _ := getAccountProjectNamespace(ctx, project, accountID, orgID, apn)
			ns2, _ := getGroupAccountProjectNamespace(ctx, project, accountID, orgID, apn)
			if len(ns1) > 0 {
				namespaces = append(namespaces, ns1...)
			}
			if len(ns2) > 0 {
				namespaces = append(namespaces, ns2...)
			}
			_log.Infow("namespaces", "project", project, "accountID", accountID, "orgID", orgID, "namespaces", namespaces)
		} else {
			namespaces, _ = getSSOAccountProjectNamespace(ctx, project, accountID, orgID, apn)
			_log.Infow("namespacesSSO", "project", project, "accountID", accountID, "orgID", orgID, "namespaces", namespaces)
		}
		*/
		// org scope
		if project == "" {
			for _, permission := range permissions {
				cr, err := getClusterRole(permission)
				if err != nil {
					return nil, err
				}

				rp := sentry.GetKubeConfigPermissionPrivilage(permission)
				if rp > rolePrevilage {
					rolePrevilage = rp
					highestRole = permission
				}

				crb := getClusterRoleBinding(sa, cr.Name)
				crMap[cr.Name] = cr
				crbMap[crb.Name] = crb
				crbExclusionMap[crb.Name] = false
			}
			break
		}
		for _, permission := range permissions {

			rp := sentry.GetKubeConfigPermissionPrivilage(permission)
			if rp > rolePrevilage {
				rolePrevilage = rp
				highestRole = permission
			}

			if isClusterScopePermission(permission) {
				cr, err := getClusterRole(permission)
				if err != nil {
					return nil, err
				}
				crb := getClusterRoleBinding(sa, cr.Name)
				crMap[cr.Name] = cr
				crbMap[crb.Name] = crb
				crbExclusionMap[crb.Name] = false
			} else if isNamespaceScopePermission(permission) {
				for _, namespace := range namespaces {
					ns, err := GetNamespace()
					if err != nil {
						return nil, err
					}
					ns.Name = namespace
					nsMap[namespace] = ns

					r, err := getRole(permission)
					if err != nil {
						return nil, err
					}
					setRoleValues(r, namespace, permission)
					rb := getRoleBinding(sa, r.Name, namespace)
					rMap[r.Name] = r
					rbMap[rb.Name] = rb
					rbExclusionMap[rb.Name] = &roleBindExclusionList{false, namespace}
				}
			}
		}

	}

	// add authz labels
	authzLabels := getAuthzLabels(cnAttr.Username)

	sa.Labels = authzLabels
	for k := range crMap {
		crMap[k].Labels = authzLabels
	}
	for k := range crbMap {
		crbMap[k].Labels = authzLabels
	}

	for k := range rMap {
		rMap[k].Labels = authzLabels
	}
	for k := range rbMap {
		rbMap[k].Labels = authzLabels
	}

	// convert to step objects
	saObject, err := runtime.FromObject(sa)
	if err != nil {
		return nil, err
	}
	resp.ServiceAccount = saObject

	for _, cr := range crMap {
		crObject, err := runtime.FromObject(cr)
		if err != nil {
			return nil, err
		}
		resp.ClusterRoles = append(resp.ClusterRoles, crObject)
	}

	for _, crb := range crbMap {
		crbObject, err := runtime.FromObject(crb)
		if err != nil {
			return nil, err
		}
		resp.ClusterRoleBindings = append(resp.ClusterRoleBindings, crbObject)
	}

	for _, r := range rMap {
		rObject, err := runtime.FromObject(r)
		if err != nil {
			return nil, err
		}
		resp.Roles = append(resp.Roles, rObject)
	}

	for _, ns := range nsMap {
		nObject, err := runtime.FromObject(ns)
		if err != nil {
			return nil, err
		}
		resp.Namespaces = append(resp.Namespaces, nObject)
	}

	for _, rb := range rbMap {
		rbObject, err := runtime.FromObject(rb)
		if err != nil {
			return nil, err
		}
		resp.RoleBindings = append(resp.RoleBindings, rbObject)
	}

	for dcrbName, val := range crbExclusionMap {
		if val {
			crbObject, err := runtime.FromObject(getDeleteClusterRoleBinding(dcrbName))
			if err == nil {
				resp.DeleteClusterRoleBindings = append(resp.DeleteClusterRoleBindings, crbObject)
			}
		}
	}

	for drbName, val := range rbExclusionMap {
		if val.exclude {
			rbObject, err := runtime.FromObject(getDeleteRoleBinding(drbName, val.namespace))
			if err == nil {
				resp.DeleteRoleBindings = append(resp.DeleteRoleBindings, rbObject)
			}
		}
	}

	resp.UserName = cnAttr.Username
	resp.RoleName = highestRole
	resp.IsRead = sentry.GetKubeConfigPermissionIsRead(highestRole)
	resp.EnforceOrgAdminOnlySecretAccess = enforceOrgAdminOnlySecretAccess
	resp.IsOrgAdmin = isOrgAdmin

	//to be removed along with events
	_log.Infow("username", userName)
	/*TODO: pending with events
	// system audit log event to notify success authz
	clusterName := labels["rafay.dev/clusterName"]
	kubectlAuthzEvent("user.login.success", req.ClusterID, clusterName, cnAttr.OrganizationID, cnAttr.PartnerID, userName, cnAttr.AccountID, groups)
	*/

	return resp, nil
}

func getSystemUserAuthz(cnAttrs kubeconfig.CNAttributes) (resp *sentryrpc.GetUserAuthorizationResponse, err error) {
	resp = new(sentryrpc.GetUserAuthorizationResponse)

	authzLabels := getAuthzLabels(cnAttrs.Username)
	sa := &corev1.ServiceAccount{}
	sa.APIVersion = "v1"
	sa.Kind = "ServiceAccount"
	sa.Name = cnAttrs.Username
	sa.Namespace = "rafay-system"
	sa.Labels = authzLabels

	cr, err := getClusterRole(sentry.KubectlFullAccessPermission)
	if err != nil {
		return nil, err
	}
	cr.Labels = authzLabels
	crb := getClusterRoleBinding(sa, cr.Name)
	crb.Labels = authzLabels

	saObject, err := runtime.FromObject(sa)
	if err != nil {
		return nil, err
	}

	crObject, err := runtime.FromObject(cr)
	if err != nil {
		return nil, err
	}

	crbObject, err := runtime.FromObject(crb)
	if err != nil {
		return nil, err
	}

	resp.UserName = cnAttrs.Username
	resp.ServiceAccount = saObject
	resp.ClusterRoles = []*controller.StepObject{crObject}
	resp.ClusterRoleBindings = []*controller.StepObject{crbObject}
	return
}

func isClusterScopePermission(permission string) bool {
	for _, p := range clusterScopePermissions {
		if permission == p {
			return true
		}
	}
	return false
}

func isNamespaceScopePermission(permission string) bool {
	for _, p := range namespaceScopePermissions {
		if permission == p {
			return true
		}
	}
	return false
}

func verifyClusterKubectlSettings(ctx context.Context, bs service.BootstrapService, kcs service.KubectlClusterSettingsService, cnAttr kubeconfig.CNAttributes, clusterID string, orgID string) error {
	if cnAttr.SessionType == kubeconfig.RafaySystem {
		// internal system sessions are always allowed
		return nil
	}

	_, err := bs.GetBootstrapAgentCountForClusterID(ctx, clusterID, orgID)
	if err != nil {
		_log.Infow("verify cluster kubectl settings invalid clusterid or orgid", "cluster", clusterID, "orgID", orgID)
		return err
	}

	/*
		if cnAttr.RelayNetwork {
			_log.Debugw("skip verify cluster kubectl settings for relaynetwork sessions")
			return nil // allow
		}
	*/

	kc, err := kcs.Get(ctx, orgID, clusterID)
	if err == constants.ErrNotFound {
		// no settings found, hence there is no restriction.
		return nil //allow
	} else if err != nil {
		return err
	}

	if cnAttr.SessionType == "" || cnAttr.SessionType == kubeconfig.TerminalShell {
		// backward combatibility treat "" as terminal session for old kubeconfigs
		if kc.DisableCLIKubectl {
			_log.Infow("kubectl cli is not authorized for ", "cnAttr", cnAttr)
			return fmt.Errorf("kubectl cli is not authorized") //deny
		}
		return nil // allow
	}

	if cnAttr.SessionType == kubeconfig.WebShell {
		if kc.DisableWebKubectl {
			_log.Infow("browser based kubectl is not authorized for ", "cnAttr", cnAttr)
			return fmt.Errorf("browser based kubectl is not authorized") //deny
		}
		return nil // allow
	}

	_log.Infow("unknown kubectl ", "SessionType", cnAttr.SessionType)

	return fmt.Errorf("unknown kubectl session type is not authorized")
}

func verifyKubectlSettings(cnAttr kubeconfig.CNAttributes, ks *sentry.KubeconfigSetting, level string) error {
	if cnAttr.SessionType == kubeconfig.RafaySystem {
		// internal system sessions are always allowed
		return nil
	}

	if cnAttr.SessionType == "" || cnAttr.SessionType == kubeconfig.TerminalShell {
		// backward combatibility treat "" as terminal session for old kubeconfigs
		if ks.DisableCLIKubectl {
			_log.Infow("kubectl cli is not authorized for ", "cnAttr", cnAttr, " by ", level, "config")
			return fmt.Errorf("kubectl cli is not authorized" + " by " + level + "config") //deny
		}
		return nil // allow
	}

	if cnAttr.SessionType == kubeconfig.WebShell {
		if ks.DisableWebKubectl {
			_log.Infow("browser based kubectl is not authorized for ", "cnAttr", cnAttr, " by ", level, "config")
			return fmt.Errorf("browser based kubectl is not authorized" + " by " + level + "config") //deny
		}
		return nil // allow
	}

	_log.Infow("unknown kubectl ", "SessionType", cnAttr.SessionType)

	return fmt.Errorf("unknown kubectl session type is not authorized")
}
