core/identityprovider/identityprovider_test.go
Rama main · réplica de lectura del repositorio original.
package identityprovider
import (
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
storepb "github.com/usememos/memos/proto/gen/store"
)
func validProvider() *storepb.IdentityProvider {
return &storepb.IdentityProvider{
Uid: "company-sso",
Name: "Company SSO",
Type: storepb.IdentityProvider_OAUTH2,
Config: &storepb.IdentityProviderConfig{
Config: &storepb.IdentityProviderConfig_Oauth2Config{
Oauth2Config: &storepb.OAuth2Config{
ClientId: "memos",
ClientSecret: "secret",
AuthUrl: "https://idp.example.com/authorize",
TokenUrl: "https://idp.example.com/token",
UserInfoUrl: "http://idp.internal/userinfo",
Scopes: []string{"openid"},
FieldMapping: &storepb.FieldMapping{Identifier: "sub"},
},
},
},
}
}
func TestValidate(t *testing.T) {
require.NoError(t, Validate(validProvider()))
public := validProvider()
public.Config.GetOauth2Config().ClientSecret = ""
public.Config.GetOauth2Config().Scopes = nil
require.NoError(t, Validate(public), "a public client without a secret or scopes is allowed")
tests := map[string]struct {
mutate func(*storepb.IdentityProvider)
want string
}{
"invalid uid": {func(p *storepb.IdentityProvider) { p.Uid = "-sso" }, "uid must be"},
"blank title": {func(p *storepb.IdentityProvider) { p.Name = " " }, "title is required"},
"unspecified type": {func(p *storepb.IdentityProvider) { p.Type = storepb.IdentityProvider_TYPE_UNSPECIFIED }, "type must be OAUTH2"},
"invalid filter": {func(p *storepb.IdentityProvider) { p.IdentifierFilter = "(" }, "identifier_filter"},
"missing config": {func(p *storepb.IdentityProvider) { p.Config = nil }, "config.oauth2_config is required"},
"missing client id": {func(p *storepb.IdentityProvider) { p.Config.GetOauth2Config().ClientId = "" }, "client_id is required"},
"relative auth url": {func(p *storepb.IdentityProvider) { p.Config.GetOauth2Config().AuthUrl = "/authorize" }, "auth_url must be"},
"non-http token url": {func(p *storepb.IdentityProvider) { p.Config.GetOauth2Config().TokenUrl = "ftp://idp/token" }, "token_url must be"},
"empty scope": {func(p *storepb.IdentityProvider) { p.Config.GetOauth2Config().Scopes = []string{"openid", " "} }, "scopes[1]"},
"missing mapping": {func(p *storepb.IdentityProvider) { p.Config.GetOauth2Config().FieldMapping = nil }, "field_mapping.identifier"},
"missing identifier": {func(p *storepb.IdentityProvider) { p.Config.GetOauth2Config().FieldMapping.Identifier = "" }, "field_mapping.identifier"},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
provider := proto.CloneOf(validProvider())
test.mutate(provider)
err := Validate(provider)
require.Error(t, err)
require.Contains(t, err.Error(), test.want)
})
}
require.Error(t, Validate(nil))
}
func TestConversionRoundTrips(t *testing.T) {
stored := validProvider()
stored.IdentifierFilter = "^.+@example\\.com$"
stored.Config.GetOauth2Config().FieldMapping = &storepb.FieldMapping{Identifier: "sub", DisplayName: "name", Email: "email", AvatarUrl: "picture"}
api := FromStore(stored)
require.Equal(t, NamePrefix+"company-sso", api.GetName())
require.Equal(t, "secret", api.GetConfig().GetOauth2Config().GetClientSecret(), "FromStore keeps the secret; responses clear it")
require.True(t, proto.Equal(stored, ToStore(api)))
// A missing config or field mapping converts without panicking and then fails validation.
api.Config = nil
require.Error(t, Validate(ToStore(api)))
}