package providerrefresh

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"testing"

	"github.com/rancher/norman/types"
	ext "github.com/rancher/rancher/pkg/apis/ext.cattle.io/v1"
	apiv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/auth/accessor"
	"github.com/rancher/rancher/pkg/auth/providers"
	"github.com/rancher/rancher/pkg/auth/providers/common"
	"github.com/rancher/rancher/pkg/auth/providers/saml"
	"github.com/rancher/rancher/pkg/auth/tokens"
	exttokens "github.com/rancher/rancher/pkg/ext/stores/tokens"
	"github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3/fakes"
	"github.com/rancher/wrangler/v3/pkg/generic/fake"
	"github.com/stretchr/testify/assert"
	"go.uber.org/mock/gomock"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	ktypes "k8s.io/apimachinery/pkg/types"
)

func TestRefreshAttributes(t *testing.T) {
	var tokenUpdateCalled bool
	var tokenDeleteCalled bool

	userLocal := apiv3.User{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde"},
		Username:   "admin",
		PrincipalIDs: []string{
			"local://user-abcde",
		},
	}

	userShibboleth := apiv3.User{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde"},
		Username:   "admin",
		PrincipalIDs: []string{
			"shibboleth_user://user1",
		},
	}

	attribsIn := apiv3.UserAttribute{
		ObjectMeta:      metav1.ObjectMeta{Name: "user-abcde"},
		GroupPrincipals: map[string]apiv3.Principals{},
		ExtraByProvider: map[string]map[string][]string{},
	}

	wantNoExtra := apiv3.UserAttribute{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde"},
		GroupPrincipals: map[string]apiv3.Principals{
			"local":      {},
			"shibboleth": {},
		},
		ExtraByProvider: map[string]map[string][]string{},
	}

	wantLocal := apiv3.UserAttribute{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde"},
		GroupPrincipals: map[string]apiv3.Principals{
			"local":      {},
			"shibboleth": {},
		},
		ExtraByProvider: map[string]map[string][]string{
			providers.LocalProvider: {
				common.UserAttributePrincipalID: {"local://user-abcde"},
				common.UserAttributeUserName:    {"admin"},
			},
		},
	}

	wantShibboleth := apiv3.UserAttribute{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde"},
		GroupPrincipals: map[string]apiv3.Principals{
			"local":      {},
			"shibboleth": {},
		},
		ExtraByProvider: map[string]map[string][]string{
			saml.ShibbolethName: {
				common.UserAttributePrincipalID: {"shibboleth_user://user1"},
				common.UserAttributeUserName:    {"user1"},
			},
		},
	}

	loginTokenLocal := apiv3.Token{
		UserID:       "user-abcde",
		IsDerived:    false,
		AuthProvider: providers.LocalProvider,
		UserPrincipal: apiv3.Principal{
			ObjectMeta: metav1.ObjectMeta{
				Name: "local://user-abcde",
			},
			LoginName: "admin",
			Provider:  providers.LocalProvider,
			ExtraInfo: map[string]string{
				common.UserAttributePrincipalID: "local://user-abcde",
				common.UserAttributeUserName:    "admin",
			},
		},
	}

	derivedTokenLocal := loginTokenLocal
	derivedTokenLocal.IsDerived = true

	derivedTokenShibboleth := apiv3.Token{
		UserID:       "user-abcde",
		IsDerived:    true,
		AuthProvider: saml.ShibbolethName,
		UserPrincipal: apiv3.Principal{
			ObjectMeta: metav1.ObjectMeta{
				Name: "shibboleth_user://user1",
			},
			LoginName: "user1",
			Provider:  saml.ShibbolethName,
			ExtraInfo: map[string]string{
				common.UserAttributePrincipalID: "shibboleth_user://user1",
				common.UserAttributeUserName:    "user1",
			},
		},
	}

	// BEWARE: for the ext tokens we see here into the internals, in
	// particular into the field structure of the backing secret.  Using the
	// exported constants for field names should help detecting changes
	// breaking things.

	eLoginTokenLocal := ext.Token{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde-login-local"},
		Spec: ext.TokenSpec{
			UserID: "user-abcde",
			Kind:   exttokens.IsLogin,
			UserPrincipal: ext.TokenPrincipal{
				Name:      "local://user-abcde",
				Provider:  providers.LocalProvider,
				LoginName: "admin",
				ExtraInfo: map[string]string{
					common.UserAttributePrincipalID: "local://user-abcde",
					common.UserAttributeUserName:    "admin",
				},
			},
		},
	}

	localPrincipalBytes, _ := json.Marshal(eLoginTokenLocal.Spec.UserPrincipal)
	eLoginSecretLocal := corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde-login-local"},
		Data: map[string][]byte{
			exttokens.FieldEnabled:        []byte("true"),
			exttokens.FieldHash:           []byte("kla9jkdmj"),
			exttokens.FieldKind:           []byte(exttokens.IsLogin),
			exttokens.FieldLastUpdateTime: []byte("13:00:05"),
			exttokens.FieldPrincipal:      localPrincipalBytes,
			exttokens.FieldTTL:            []byte("4000"),
			exttokens.FieldUID:            []byte("2905498-kafld-lkad"),
			exttokens.FieldUserID:         []byte("user-abcde"),
		},
	}

	eDerivedTokenLocal := eLoginTokenLocal
	eDerivedTokenLocal.ObjectMeta.Name = "user-abcde-derived-local"
	eDerivedTokenLocal.Spec.Kind = ""

	eDerivedSecretLocal := *eLoginSecretLocal.DeepCopy() // copy the map
	eDerivedSecretLocal.ObjectMeta.Name = "user-abcde-derived-local"
	eDerivedSecretLocal.Data[exttokens.FieldKind] = []byte("")

	eDerivedTokenShibboleth := ext.Token{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde-derived-shibboleth"},
		Spec: ext.TokenSpec{
			UserID: "user-abcde",
			Kind:   "",
			UserPrincipal: ext.TokenPrincipal{
				Name:      "shibboleth_user://user1",
				Provider:  saml.ShibbolethName,
				LoginName: "user1",
				ExtraInfo: map[string]string{
					common.UserAttributePrincipalID: "shibboleth_user://user1",
					common.UserAttributeUserName:    "user1",
				},
			},
		},
	}

	shibbolethPrincipalBytes, _ := json.Marshal(eDerivedTokenShibboleth.Spec.UserPrincipal)
	eDerivedSecretShibboleth := corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{Name: "user-abcde-derived-shibboleth"},
		Data: map[string][]byte{
			exttokens.FieldEnabled:        []byte("true"),
			exttokens.FieldHash:           []byte("kla9jkdmj"),
			exttokens.FieldKind:           []byte(""),
			exttokens.FieldLastUpdateTime: []byte("13:00:05"),
			exttokens.FieldPrincipal:      shibbolethPrincipalBytes,
			exttokens.FieldTTL:            []byte("4000"),
			exttokens.FieldUID:            []byte("2905498-kafld-lkad"),
			exttokens.FieldUserID:         []byte("user-abcde"),
		},
	}

	eTokenSetupEmpty := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{}, nil).
			AnyTimes()
	}

	eTokenSetupLoginLocal := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{
				&eLoginSecretLocal,
			}, nil).
			AnyTimes()
	}

	eTokenSetupDerivedLocal := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{
				&eDerivedSecretLocal,
			}, nil).
			AnyTimes()
		scache.EXPECT().
			Get("cattle-tokens", gomock.Any()).
			Return(&eDerivedSecretLocal, nil).
			AnyTimes()
	}

	eTokenSetupLocal := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{
				&eLoginSecretLocal,
				&eDerivedSecretLocal,
			}, nil).
			AnyTimes()
		scache.EXPECT().
			Get("cattle-tokens", "user-abcde-login-local").
			Return(&eLoginSecretLocal, nil).
			AnyTimes()
		scache.EXPECT().
			Get("cattle-tokens", "user-abcde-derived-local").
			Return(&eDerivedSecretLocal, nil).
			AnyTimes()
	}

	eTokenSetupLocalPatch := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		secrets.EXPECT().
			Patch("cattle-tokens",
				"user-abcde-login-local",
				ktypes.JSONPatchType,
				[]byte(`[{"op":"replace","path":"/data/enabled","value":"ZmFsc2U="}]`)).
			DoAndReturn(func(ns, name string, pt ktypes.PatchType, patch []byte, subresources ...string) (*corev1.Secret, error) {
				tokenUpdateCalled = true
				return nil, nil
			}).
			AnyTimes()
		secrets.EXPECT().
			Patch("cattle-tokens",
				"user-abcde-derived-local",
				ktypes.JSONPatchType,
				[]byte(`[{"op":"replace","path":"/data/enabled","value":"ZmFsc2U="}]`)).
			DoAndReturn(func(ns, name string, pt ktypes.PatchType, patch []byte, subresources ...string) (*corev1.Secret, error) {
				tokenUpdateCalled = true
				return nil, nil
			}).
			AnyTimes()
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{
				&eLoginSecretLocal,
				&eDerivedSecretLocal,
			}, nil).
			AnyTimes()
	}

	eTokenSetupDerivedLocalPatch := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		secrets.EXPECT().
			Patch("cattle-tokens",
				gomock.Any(),
				ktypes.JSONPatchType,
				[]byte(`[{"op":"replace","path":"/data/enabled","value":"ZmFsc2U="}]`)).
			DoAndReturn(func(ns, name string, pt ktypes.PatchType, patch []byte, subresources ...string) (*corev1.Secret, error) {
				tokenUpdateCalled = true
				return nil, nil
			}).
			AnyTimes()
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{
				&eDerivedSecretLocal,
			}, nil).
			AnyTimes()
	}

	eTokenSetupDerivedShibboleth := func(
		secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
		scache *fake.MockCacheInterface[*corev1.Secret]) {
		scache.EXPECT().
			List("cattle-tokens", gomock.Any()).
			Return([]*corev1.Secret{
				&eDerivedSecretShibboleth,
			}, nil).
			AnyTimes()
		scache.EXPECT().
			Get("cattle-tokens", gomock.Any()).
			Return(&eDerivedSecretShibboleth, nil).
			AnyTimes()
	}

	tests := []struct {
		name                  string
		user                  *apiv3.User
		attribs               *apiv3.UserAttribute // argument to refreshAttributes
		providerDisabled      bool
		providerDisabledError error
		tokens                []*apiv3.Token
		eTokens               []*ext.Token
		enabled               bool
		deleted               bool
		want                  *apiv3.UserAttribute // result expected from refreshAttributes
		eTokenSetup           func(
			secrets *fake.MockControllerInterface[*corev1.Secret, *corev1.SecretList],
			scache *fake.MockCacheInterface[*corev1.Secret])
	}{
		{
			name:        "local user no tokens",
			user:        &userLocal,
			attribs:     &attribsIn,
			tokens:      []*apiv3.Token{},
			eTokens:     []*ext.Token{},
			enabled:     true,
			want:        &wantNoExtra,
			eTokenSetup: eTokenSetupEmpty,
		},
		// from here on out test cases are pairs testing the same thing, one each for v3 and ext tokens
		{
			name:        "local user with login token",
			user:        &userLocal,
			attribs:     &attribsIn,
			tokens:      []*apiv3.Token{&loginTokenLocal},
			enabled:     true,
			want:        &wantLocal,
			eTokenSetup: eTokenSetupEmpty,
		},
		{
			name:        "local user with ext login token",
			user:        &userLocal,
			attribs:     &attribsIn,
			eTokens:     []*ext.Token{&eLoginTokenLocal},
			enabled:     true,
			want:        &wantLocal,
			eTokenSetup: eTokenSetupLoginLocal,
		},
		{
			name:        "local user with derived token",
			user:        &userLocal,
			attribs:     &attribsIn,
			tokens:      []*apiv3.Token{&derivedTokenLocal},
			enabled:     true,
			want:        &wantLocal,
			eTokenSetup: eTokenSetupEmpty,
		},
		{
			name:        "local user with derived ext token",
			user:        &userLocal,
			attribs:     &attribsIn,
			eTokens:     []*ext.Token{&eDerivedTokenLocal},
			enabled:     true,
			want:        &wantLocal,
			eTokenSetup: eTokenSetupDerivedLocal,
		},
		{
			name:        "user with derived token disabled in provider",
			user:        &userLocal,
			attribs:     &attribsIn,
			tokens:      []*apiv3.Token{&derivedTokenLocal},
			enabled:     false,
			want:        &wantNoExtra,
			eTokenSetup: eTokenSetupEmpty,
		},
		{
			name:        "user with derived ext token disabled in provider",
			user:        &userLocal,
			attribs:     &attribsIn,
			eTokens:     []*ext.Token{&eDerivedTokenLocal},
			enabled:     false,
			want:        &wantNoExtra,
			eTokenSetup: eTokenSetupDerivedLocalPatch,
		},
		{
			name:        "user with login and derived tokens",
			user:        &userLocal,
			attribs:     &attribsIn,
			tokens:      []*apiv3.Token{&loginTokenLocal, &derivedTokenLocal},
			enabled:     true,
			want:        &wantLocal,
			eTokenSetup: eTokenSetupEmpty,
		},
		{
			name:        "user with ext login and derived tokens",
			user:        &userLocal,
			attribs:     &attribsIn,
			eTokens:     []*ext.Token{&eLoginTokenLocal, &eDerivedTokenLocal},
			enabled:     true,
			want:        &wantLocal,
			eTokenSetup: eTokenSetupLocal,
		},
		{
			name:        "shibboleth user",
			user:        &userShibboleth,
			attribs:     &attribsIn,
			tokens:      []*apiv3.Token{&derivedTokenShibboleth},
			enabled:     true,
			want:        &wantShibboleth,
			eTokenSetup: eTokenSetupEmpty,
		},
		{
			name:        "shibboleth user, ext",
			user:        &userShibboleth,
			attribs:     &attribsIn,
			eTokens:     []*ext.Token{&eDerivedTokenShibboleth},
			enabled:     true,
			want:        &wantShibboleth,
			eTokenSetup: eTokenSetupDerivedShibboleth,
		},
		{
			name:             "disabled provider, disabled/deleted tokens",
			user:             &userLocal,
			attribs:          &attribsIn,
			tokens:           []*apiv3.Token{&loginTokenLocal, &derivedTokenLocal},
			want:             &wantNoExtra,
			eTokenSetup:      eTokenSetupEmpty,
			providerDisabled: true,
			deleted:          true,
			enabled:          false,
		},
		{
			name:             "disabled provider, disabled/deleted ext tokens",
			user:             &userLocal,
			attribs:          &attribsIn,
			eTokens:          []*ext.Token{&eLoginTokenLocal, &eDerivedTokenLocal},
			want:             &wantNoExtra,
			eTokenSetup:      eTokenSetupLocalPatch,
			providerDisabled: true,
			deleted:          true,
			enabled:          false,
		},
		{
			name:                  "error in determining if provider is disabled, tokens left unchanged",
			user:                  &userLocal,
			attribs:               &attribsIn,
			tokens:                []*apiv3.Token{&loginTokenLocal, &derivedTokenLocal},
			want:                  &wantLocal,
			eTokenSetup:           eTokenSetupEmpty,
			providerDisabled:      true,
			providerDisabledError: fmt.Errorf("unable to determine if provider was disabled"),
			deleted:               false,
			enabled:               true,
		},
		{
			name:                  "error in determining if provider is disabled, ext tokens left unchanged",
			user:                  &userLocal,
			attribs:               &attribsIn,
			eTokens:               []*ext.Token{&eLoginTokenLocal, &eDerivedTokenLocal},
			want:                  &wantLocal,
			eTokenSetup:           eTokenSetupLocal,
			providerDisabled:      true,
			providerDisabledError: fmt.Errorf("unable to determine if provider was disabled"),
			deleted:               false,
			enabled:               true,
		},
	}

	providers.ProviderNames = map[string]bool{
		providers.LocalProvider: true,
		saml.ShibbolethName:     true,
	}

	for _, tt := range tests {
		tokenUpdateCalled = false
		tokenDeleteCalled = false
		t.Run(tt.name, func(t *testing.T) {
			providers.Providers = map[string]common.AuthProvider{
				providers.LocalProvider: &mockLocalProvider{
					canAccess:   tt.enabled,
					disabled:    tt.providerDisabled,
					disabledErr: tt.providerDisabledError,
				},
				saml.ShibbolethName: &mockShibbolethProvider{},
			}

			ctrl := gomock.NewController(t)
			secrets := fake.NewMockControllerInterface[*corev1.Secret, *corev1.SecretList](ctrl)
			scache := fake.NewMockCacheInterface[*corev1.Secret](ctrl)
			users := fake.NewMockNonNamespacedControllerInterface[*apiv3.User, *apiv3.UserList](ctrl)

			users.EXPECT().Cache().Return(nil)
			secrets.EXPECT().Cache().Return(scache)

			// standard capture of delete and update events. See
			// also the `tokens` interface used by the refresher
			// below, same thing for the v3 tokens.
			secrets.EXPECT().
				Update(gomock.Any()).
				DoAndReturn(func(secret *corev1.Secret) (*corev1.Secret, error) {
					tokenUpdateCalled = true
					return secret, nil
				}).AnyTimes()
			secrets.EXPECT().
				Delete("cattle-tokens", gomock.Any(), gomock.Any()).
				DoAndReturn(func(space, name string, opts *metav1.DeleteOptions) error {
					tokenDeleteCalled = true
					return nil
				}).AnyTimes()

			// additional ext token setup
			if tt.eTokenSetup != nil {
				tt.eTokenSetup(secrets, scache)
			}

			tokenClient := fake.NewMockNonNamespacedClientInterface[*apiv3.Token, *apiv3.TokenList](ctrl)
			tokenClient.EXPECT().Update(gomock.Any()).DoAndReturn(func(token *apiv3.Token) (*apiv3.Token, error) {
				tokenUpdateCalled = true
				return token.DeepCopy(), nil
			}).AnyTimes()

			r := &refresher{
				tokenLister: &fakes.TokenListerMock{
					ListFunc: func(_ string, _ labels.Selector) ([]*apiv3.Token, error) {
						return tt.tokens, nil
					},
				},
				userLister: &fakes.UserListerMock{
					GetFunc: func(_, _ string) (*apiv3.User, error) {
						return tt.user, nil
					},
				},
				tokens: &fakes.TokenInterfaceMock{
					DeleteFunc: func(_ string, _ *metav1.DeleteOptions) error {
						tokenDeleteCalled = true
						return nil
					},
				},
				tokenMGR: tokens.NewMockedManager(tokenClient),
				extTokenStore: exttokens.NewSystem(nil, nil, secrets, users, nil, nil,
					exttokens.NewTimeHandler(),
					exttokens.NewHashHandler(),
					exttokens.NewAuthHandler()),
			}
			got, err := r.refreshAttributes(tt.attribs)
			assert.Nil(t, err)
			assert.Equal(t, tt.want, got)
			assert.NotEqual(t, tt.enabled, tokenUpdateCalled)
			assert.Equal(t, tt.deleted, tokenDeleteCalled)
		})
	}
}

func TestGetPrincipalIDForProvider(t *testing.T) {
	const testUserUsername = "tUser"
	tests := []struct {
		name               string
		userPrincipalIds   []string
		providerName       string
		desiredPrincipalId string
	}{
		{
			name:               "basic test",
			userPrincipalIds:   []string{fmt.Sprintf("azure_user://%s", testUserUsername)},
			providerName:       "azure",
			desiredPrincipalId: fmt.Sprintf("azure_user://%s", testUserUsername),
		},
		{
			name:               "no principal for provider",
			userPrincipalIds:   []string{fmt.Sprintf("azure_user://%s", testUserUsername)},
			providerName:       "not-a-provider",
			desiredPrincipalId: "",
		},
		{
			name:               "local provider principal",
			userPrincipalIds:   []string{fmt.Sprintf("local://%s", testUserUsername)},
			providerName:       "local",
			desiredPrincipalId: fmt.Sprintf("local://%s", testUserUsername),
		},
		{
			name:               "local provider missing principal",
			userPrincipalIds:   []string{fmt.Sprintf("local_user://%s", testUserUsername)},
			providerName:       "local",
			desiredPrincipalId: "",
		},
		{
			name: "multiple providers, correct one (first) chosen",
			userPrincipalIds: []string{
				fmt.Sprintf("ldap_user://%s", testUserUsername),
				fmt.Sprintf("azure_user://%s", testUserUsername),
			},
			providerName:       "ldap",
			desiredPrincipalId: fmt.Sprintf("ldap_user://%s", testUserUsername),
		},
		{
			name: "multiple providers, correct one (last) chosen",
			userPrincipalIds: []string{
				fmt.Sprintf("ldap_user://%s", testUserUsername),
				fmt.Sprintf("azure_user://%s", testUserUsername),
			},
			providerName:       "azure",
			desiredPrincipalId: fmt.Sprintf("azure_user://%s", testUserUsername),
		},
		{
			name: "multiple correct providers, first one chosen",
			userPrincipalIds: []string{
				fmt.Sprintf("ldap_user://%s", testUserUsername),
				fmt.Sprintf("ldap_user://%s", "tUser2"),
			},
			providerName:       "ldap",
			desiredPrincipalId: fmt.Sprintf("ldap_user://%s", testUserUsername),
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			user := apiv3.User{
				ObjectMeta: metav1.ObjectMeta{
					Name: testUserUsername,
				},
				PrincipalIDs: test.userPrincipalIds,
			}
			outputPrincipalID := GetPrincipalIDForProvider(test.providerName, &user)
			assert.Equal(t, test.desiredPrincipalId, outputPrincipalID, "got a different principal id than expected")
		})
	}
}

type mockLocalProvider struct {
	canAccess   bool
	disabled    bool
	disabledErr error
}

func (p *mockLocalProvider) IsDisabledProvider() (bool, error) {
	return p.disabled, p.disabledErr
}

func (p *mockLocalProvider) Logout(w http.ResponseWriter, r *http.Request, token accessor.TokenAccessor) error {
	panic("not implemented")
}

func (p *mockLocalProvider) LogoutAll(w http.ResponseWriter, r *http.Request, token accessor.TokenAccessor) error {
	panic("not implemented")
}

func (p *mockLocalProvider) GetName() string {
	panic("not implemented")
}

func (p *mockLocalProvider) AuthenticateUser(http.ResponseWriter, *http.Request, any) (apiv3.Principal, []apiv3.Principal, string, error) {
	panic("not implemented")
}

func (p *mockLocalProvider) SearchPrincipals(name, principalType string, myToken accessor.TokenAccessor) ([]apiv3.Principal, error) {
	panic("not implemented")
}

func (p *mockLocalProvider) GetPrincipal(principalID string, token accessor.TokenAccessor) (apiv3.Principal, error) {
	return token.GetUserPrincipal(), nil
}

func (p *mockLocalProvider) CustomizeSchema(schema *types.Schema) {
	panic("not implemented")
}

func (p *mockLocalProvider) TransformToAuthProvider(authConfig map[string]any) (map[string]any, error) {
	panic("not implemented")
}

func (p *mockLocalProvider) RefetchGroupPrincipals(principalID string, secret string) ([]apiv3.Principal, error) {
	return []apiv3.Principal{}, nil
}

func (p *mockLocalProvider) CanAccessWithGroupProviders(userPrincipalID string, groups []apiv3.Principal) (bool, error) {
	return p.canAccess, nil
}

func (p *mockLocalProvider) GetUserExtraAttributes(userPrincipal apiv3.Principal) map[string][]string {
	return map[string][]string{
		common.UserAttributePrincipalID: {userPrincipal.ExtraInfo[common.UserAttributePrincipalID]},
		common.UserAttributeUserName:    {userPrincipal.ExtraInfo[common.UserAttributeUserName]},
	}
}

func (p *mockLocalProvider) CleanupResources(*apiv3.AuthConfig) error {
	return nil
}

type mockShibbolethProvider struct {
	enabled    bool
	enabledErr error
}

func (p *mockShibbolethProvider) IsDisabledProvider() (bool, error) {
	return p.enabled, p.enabledErr
}

func (p *mockShibbolethProvider) Logout(w http.ResponseWriter, r *http.Request, token accessor.TokenAccessor) error {
	panic("not implemented")
}

func (p *mockShibbolethProvider) LogoutAll(w http.ResponseWriter, r *http.Request, token accessor.TokenAccessor) error {
	panic("not implemented")
}

func (p *mockShibbolethProvider) GetName() string {
	panic("not implemented")
}

func (p *mockShibbolethProvider) AuthenticateUser(http.ResponseWriter, *http.Request, any) (apiv3.Principal, []apiv3.Principal, string, error) {
	panic("not implemented")
}

func (p *mockShibbolethProvider) SearchPrincipals(name, principalType string, myToken accessor.TokenAccessor) ([]apiv3.Principal, error) {
	panic("not implemented")
}

func (p *mockShibbolethProvider) GetPrincipal(principalID string, token accessor.TokenAccessor) (apiv3.Principal, error) {
	return token.GetUserPrincipal(), nil
}

func (p *mockShibbolethProvider) CustomizeSchema(schema *types.Schema) {
	panic("not implemented")
}

func (p *mockShibbolethProvider) TransformToAuthProvider(authConfig map[string]any) (map[string]any, error) {
	panic("not implemented")
}

func (p *mockShibbolethProvider) RefetchGroupPrincipals(principalID string, secret string) ([]apiv3.Principal, error) {
	return []apiv3.Principal{}, errors.New("Not implemented")
}

func (p *mockShibbolethProvider) CanAccessWithGroupProviders(userPrincipalID string, groups []apiv3.Principal) (bool, error) {
	return true, nil
}

func (p *mockShibbolethProvider) GetUserExtraAttributes(userPrincipal apiv3.Principal) map[string][]string {
	return map[string][]string{
		common.UserAttributePrincipalID: {userPrincipal.ExtraInfo[common.UserAttributePrincipalID]},
		common.UserAttributeUserName:    {userPrincipal.ExtraInfo[common.UserAttributeUserName]},
	}
}

func (p *mockShibbolethProvider) CleanupResources(*apiv3.AuthConfig) error {
	return nil
}
