feat: kubesphere 4.0 (#6115)

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

---------

Signed-off-by: ci-bot <ci-bot@kubesphere.io>
Co-authored-by: ks-ci-bot <ks-ci-bot@example.com>
Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
KubeSphere CI Bot
2024-09-06 11:05:52 +08:00
committed by GitHub
parent b5015ec7b9
commit 447a51f08b
8557 changed files with 546695 additions and 1146174 deletions

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package aliyunidaas
@@ -23,7 +12,6 @@ import (
"net/http"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
@@ -31,7 +19,7 @@ import (
)
func init() {
identityprovider.RegisterOAuthProvider(&idaasProviderFactory{})
identityprovider.RegisterOAuthProviderFactory(&idaasProviderFactory{})
}
type aliyunIDaaS struct {

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package aliyunidaas

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package cas
@@ -30,7 +19,7 @@ import (
)
func init() {
identityprovider.RegisterOAuthProvider(&casProviderFactory{})
identityprovider.RegisterOAuthProviderFactory(&casProviderFactory{})
}
type cas struct {

View File

@@ -0,0 +1,120 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package identityprovider
import (
"context"
"errors"
"gopkg.in/yaml.v3"
v1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/options"
)
const (
MappingMethodManual MappingMethod = "manual"
MappingMethodAuto MappingMethod = "auto"
// MappingMethodLookup Looks up an existing identity, user identity mapping, and user, but does not automatically
// provision users or identities. Using this method requires you to manually provision users.
MappingMethodLookup MappingMethod = "lookup"
ConfigTypeIdentityProvider = "identityprovider"
SecretTypeIdentityProvider = "config.kubesphere.io/" + ConfigTypeIdentityProvider
SecretDataKey = "configuration.yaml"
)
var ErrorIdentityProviderNotFound = errors.New("the Identity provider was not found")
type MappingMethod string
type Configuration struct {
// The provider name.
Name string `json:"name" yaml:"name"`
// Defines how new identities are mapped to users when they login. Allowed values are:
// - manual: The user needs to confirm the mapped username on the onboarding page.
// - auto: Skip the onboarding screen, so the user cannot change its username.
// Fails if a user with that username is already mapped to another identity.
// - lookup: Looks up an existing identity, user identity mapping, and user, but does not automatically
// provision users or identities. Using this method requires you to manually provision users.
MappingMethod MappingMethod `json:"mappingMethod" yaml:"mappingMethod"`
// The type of identity provider
Type string `json:"type" yaml:"type"`
// The options of identify provider
ProviderOptions options.DynamicOptions `json:"provider" yaml:"provider"`
}
type ConfigurationGetter interface {
GetConfiguration(ctx context.Context, name string) (*Configuration, error)
ListConfigurations(ctx context.Context) ([]*Configuration, error)
}
func NewConfigurationGetter(client client.Client) ConfigurationGetter {
return &configurationGetter{client}
}
type configurationGetter struct {
client.Client
}
func (o *configurationGetter) ListConfigurations(ctx context.Context) ([]*Configuration, error) {
configurations := make([]*Configuration, 0)
secrets := &v1.SecretList{}
if err := o.List(ctx, secrets, client.InNamespace(constants.KubeSphereNamespace), client.MatchingLabels{constants.GenericConfigTypeLabel: ConfigTypeIdentityProvider}); err != nil {
klog.Errorf("failed to list secrets: %v", err)
return nil, err
}
for _, secret := range secrets.Items {
if secret.Type != SecretTypeIdentityProvider {
continue
}
if c, err := UnmarshalFrom(&secret); err != nil {
klog.Errorf("failed to unmarshal secret data: %s", err)
continue
} else {
configurations = append(configurations, c)
}
}
return configurations, nil
}
func (o *configurationGetter) GetConfiguration(ctx context.Context, name string) (*Configuration, error) {
configurations, err := o.ListConfigurations(ctx)
if err != nil {
klog.Errorf("failed to list identity providers: %v", err)
return nil, err
}
for _, c := range configurations {
if c.Name == name {
return c, nil
}
}
return nil, ErrorIdentityProviderNotFound
}
func UnmarshalFrom(secret *v1.Secret) (*Configuration, error) {
c := &Configuration{}
if err := yaml.Unmarshal(secret.Data[SecretDataKey], c); err != nil {
return nil, err
}
return c, nil
}
func IsIdentityProviderConfiguration(secret *v1.Secret) bool {
if secret.Namespace != constants.KubeSphereNamespace {
return false
}
return secret.Type == SecretTypeIdentityProvider
}

View File

@@ -1,20 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package identityprovider
@@ -30,6 +17,6 @@ type GenericProvider interface {
type GenericProviderFactory interface {
// Type unique type of the provider
Type() string
// Apply the dynamic options from kubesphere-config
// Create generic identity provider
Create(options options.DynamicOptions) (GenericProvider, error)
}

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package github
@@ -38,7 +27,7 @@ const (
)
func init() {
identityprovider.RegisterOAuthProvider(&ldapProviderFactory{})
identityprovider.RegisterOAuthProviderFactory(&ldapProviderFactory{})
}
type github struct {

View File

@@ -1,20 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package github
@@ -29,7 +16,7 @@ import (
"kubesphere.io/kubesphere/pkg/server/options"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"golang.org/x/oauth2"
@@ -45,7 +32,7 @@ func TestGithub(t *testing.T) {
RunSpecs(t, "GitHub Identity Provider Suite")
}
var _ = BeforeSuite(func(done Done) {
var _ = BeforeSuite(func() {
githubServer = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
switch r.RequestURI {
@@ -69,8 +56,7 @@ var _ = BeforeSuite(func(done Done) {
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}))
close(done)
}, 60)
})
var _ = AfterSuite(func() {
By("tearing down the test environment")

View File

@@ -1,177 +0,0 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gitlab
import (
"context"
"crypto/tls"
"encoding/json"
"io"
"net/http"
"strconv"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/server/options"
)
const (
userInfoURL = "https://gitlab.com/api/v4/user"
authURL = "https://gitlab.com/oauth/authorize"
tokenURL = "https://gitlab.com/oauth/token"
)
func init() {
identityprovider.RegisterOAuthProvider(&gitlabProviderFactory{})
}
type gitlab struct {
// ClientID is the application's ID.
ClientID string `json:"clientID" yaml:"clientID"`
// ClientSecret is the application's secret.
ClientSecret string `json:"-" yaml:"clientSecret"`
// Endpoint contains the resource server's token endpoint
// URLs. These are constants specific to each server and are
// often available via site-specific packages, such as
// google.Endpoint or sso.endpoint.
Endpoint endpoint `json:"endpoint" yaml:"endpoint"`
// RedirectURL is the URL to redirect users going through
// the OAuth flow, after the resource owner's URLs.
RedirectURL string `json:"redirectURL" yaml:"redirectURL"`
// Used to turn off TLS certificate checks
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"`
// Scope specifies optional requested permissions.
Scopes []string `json:"scopes" yaml:"scopes"`
Config *oauth2.Config `json:"-" yaml:"-"`
}
// endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs.
type endpoint struct {
AuthURL string `json:"authURL" yaml:"authURL"`
TokenURL string `json:"tokenURL" yaml:"tokenURL"`
UserInfoURL string `json:"userInfoURL" yaml:"userInfoURL"`
}
type gitlabIdentity struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
}
type gitlabProviderFactory struct {
}
func (g *gitlabProviderFactory) Type() string {
return "GitlabIdentityProvider"
}
func (g *gitlabProviderFactory) Create(opts options.DynamicOptions) (identityprovider.OAuthProvider, error) {
var gitlab gitlab
if err := mapstructure.Decode(opts, &gitlab); err != nil {
return nil, err
}
if gitlab.Endpoint.AuthURL == "" {
gitlab.Endpoint.AuthURL = authURL
}
if gitlab.Endpoint.TokenURL == "" {
gitlab.Endpoint.TokenURL = tokenURL
}
if gitlab.Endpoint.UserInfoURL == "" {
gitlab.Endpoint.UserInfoURL = userInfoURL
}
// fixed options
opts["endpoint"] = options.DynamicOptions{
"authURL": gitlab.Endpoint.AuthURL,
"tokenURL": gitlab.Endpoint.TokenURL,
"userInfoURL": gitlab.Endpoint.UserInfoURL,
}
gitlab.Config = &oauth2.Config{
ClientID: gitlab.ClientID,
ClientSecret: gitlab.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: gitlab.Endpoint.AuthURL,
TokenURL: gitlab.Endpoint.TokenURL,
},
RedirectURL: gitlab.RedirectURL,
Scopes: gitlab.Scopes,
}
return &gitlab, nil
}
func (g gitlabIdentity) GetUserID() string {
return strconv.FormatInt(g.ID, 10)
}
func (g gitlabIdentity) GetUsername() string {
return g.Username
}
func (g gitlabIdentity) GetEmail() string {
return g.Email
}
func (g *gitlab) IdentityExchangeCallback(req *http.Request) (identityprovider.Identity, error) {
// OAuth2 callback, see also https://tools.ietf.org/html/rfc6749#section-4.1.2
code := req.URL.Query().Get("code")
ctx := req.Context()
if g.InsecureSkipVerify {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
}
token, err := g.Config.Exchange(ctx, code)
if err != nil {
return nil, err
}
resp, err := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)).Get(g.Endpoint.UserInfoURL)
if err != nil {
return nil, err
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var gitlabIdentity gitlabIdentity
err = json.Unmarshal(data, &gitlabIdentity)
if err != nil {
return nil, err
}
return gitlabIdentity, nil
}

View File

@@ -1,82 +0,0 @@
package gitlab
import (
"reflect"
"testing"
"golang.org/x/oauth2"
"gopkg.in/yaml.v3"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/server/options"
)
func Test_gitlabProviderFactory_Create(t *testing.T) {
type args struct {
opts options.DynamicOptions
}
mustUnmarshalYAML := func(data string) options.DynamicOptions {
var dynamicOptions options.DynamicOptions
_ = yaml.Unmarshal([]byte(data), &dynamicOptions)
return dynamicOptions
}
tests := []struct {
name string
args args
want identityprovider.OAuthProvider
wantErr bool
}{
{
name: "should create successfully",
args: args{opts: mustUnmarshalYAML(`
clientID: 035c18fc229c686e4652d7034
clientSecret: 75c82b42e54aaf25186140f5
endpoint:
userInfoUrl: "https://gitlab.com/api/v4/user"
authURL: "https://gitlab.com/oauth/authorize"
tokenURL: "https://gitlab.com/oauth/token"
redirectURL: "https://ks-console.kubesphere-system.svc/oauth/redirect/gitlab"
scopes:
- read
`)},
want: &gitlab{
ClientID: "035c18fc229c686e4652d7034",
ClientSecret: "75c82b42e54aaf25186140f5",
Endpoint: endpoint{
AuthURL: "https://gitlab.com/oauth/authorize",
TokenURL: "https://gitlab.com/oauth/token",
UserInfoURL: "https://gitlab.com/api/v4/user",
},
RedirectURL: "https://ks-console.kubesphere-system.svc/oauth/redirect/gitlab",
Scopes: []string{"read"},
Config: &oauth2.Config{
ClientID: "035c18fc229c686e4652d7034",
ClientSecret: "75c82b42e54aaf25186140f5",
Endpoint: oauth2.Endpoint{
AuthURL: "https://gitlab.com/oauth/authorize",
TokenURL: "https://gitlab.com/oauth/token",
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: "https://ks-console.kubesphere-system.svc/oauth/redirect/gitlab",
Scopes: []string{"read"},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := &gitlabProviderFactory{}
got, err := g.Create(tt.args.opts)
if (err != nil) != tt.wantErr {
t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Create() got = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,18 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package identityprovider
// Identity represents the account mapped to kubesphere
type Identity interface {
// GetUserID required
// Identifier for the End-User at the Issuer.
GetUserID() string
// GetUsername optional
// The username which the End-User wishes to be referred to kubesphere.
GetUsername() string
// GetEmail optional
GetEmail() string
}

View File

@@ -1,110 +0,0 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package identityprovider
import (
"errors"
"fmt"
"k8s.io/klog/v2"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
)
var (
oauthProviderFactories = make(map[string]OAuthProviderFactory)
genericProviderFactories = make(map[string]GenericProviderFactory)
identityProviderNotFound = errors.New("identity provider not found")
oauthProviders = make(map[string]OAuthProvider)
genericProviders = make(map[string]GenericProvider)
)
// Identity represents the account mapped to kubesphere
type Identity interface {
// GetUserID required
// Identifier for the End-User at the Issuer.
GetUserID() string
// GetUsername optional
// The username which the End-User wishes to be referred to kubesphere.
GetUsername() string
// GetEmail optional
GetEmail() string
}
// SetupWithOptions will verify the configuration and initialize the identityProviders
func SetupWithOptions(options []oauth.IdentityProviderOptions) error {
// Clear all providers when reloading configuration
oauthProviders = make(map[string]OAuthProvider)
genericProviders = make(map[string]GenericProvider)
for _, o := range options {
if oauthProviders[o.Name] != nil || genericProviders[o.Name] != nil {
err := fmt.Errorf("duplicate identity provider found: %s, name must be unique", o.Name)
klog.Error(err)
return err
}
if genericProviderFactories[o.Type] == nil && oauthProviderFactories[o.Type] == nil {
err := fmt.Errorf("identity provider %s with type %s is not supported", o.Name, o.Type)
klog.Error(err)
return err
}
if factory, ok := oauthProviderFactories[o.Type]; ok {
if provider, err := factory.Create(o.Provider); err != nil {
// dont return errors, decoupling external dependencies
klog.Error(fmt.Sprintf("failed to create identity provider %s: %s", o.Name, err))
} else {
oauthProviders[o.Name] = provider
klog.V(4).Infof("create identity provider %s successfully", o.Name)
}
}
if factory, ok := genericProviderFactories[o.Type]; ok {
if provider, err := factory.Create(o.Provider); err != nil {
klog.Error(fmt.Sprintf("failed to create identity provider %s: %s", o.Name, err))
} else {
genericProviders[o.Name] = provider
klog.V(4).Infof("create identity provider %s successfully", o.Name)
}
}
}
return nil
}
// GetGenericProvider returns GenericProvider with given name
func GetGenericProvider(providerName string) (GenericProvider, error) {
if provider, ok := genericProviders[providerName]; ok {
return provider, nil
}
return nil, identityProviderNotFound
}
// GetOAuthProvider returns OAuthProvider with given name
func GetOAuthProvider(providerName string) (OAuthProvider, error) {
if provider, ok := oauthProviders[providerName]; ok {
return provider, nil
}
return nil, identityProviderNotFound
}
// RegisterOAuthProvider register OAuthProviderFactory with the specified type
func RegisterOAuthProvider(factory OAuthProviderFactory) {
oauthProviderFactories[factory.Type()] = factory
}
// RegisterGenericProvider registers GenericProviderFactory with the specified type
func RegisterGenericProvider(factory GenericProviderFactory) {
genericProviderFactories[factory.Type()] = factory
}

View File

@@ -0,0 +1,21 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package identityprovider
var (
oauthProviderFactories = make(map[string]OAuthProviderFactory)
genericProviderFactories = make(map[string]GenericProviderFactory)
)
// RegisterOAuthProviderFactory register OAuthProviderFactory with the specified type
func RegisterOAuthProviderFactory(factory OAuthProviderFactory) {
oauthProviderFactories[factory.Type()] = factory
}
// RegisterGenericProviderFactory registers GenericProviderFactory with the specified type
func RegisterGenericProviderFactory(factory GenericProviderFactory) {
genericProviderFactories[factory.Type()] = factory
}

View File

@@ -1,145 +0,0 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package identityprovider
import (
"net/http"
"testing"
"kubesphere.io/kubesphere/pkg/server/options"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
)
type emptyOAuthProviderFactory struct {
typeName string
}
func (e emptyOAuthProviderFactory) Type() string {
return e.typeName
}
type emptyOAuthProvider struct {
}
type emptyIdentity struct {
}
func (e emptyIdentity) GetUserID() string {
return "test"
}
func (e emptyIdentity) GetUsername() string {
return "test"
}
func (e emptyIdentity) GetEmail() string {
return "test@test.com"
}
func (e emptyOAuthProvider) IdentityExchangeCallback(req *http.Request) (Identity, error) {
return emptyIdentity{}, nil
}
func (e emptyOAuthProviderFactory) Create(options options.DynamicOptions) (OAuthProvider, error) {
return emptyOAuthProvider{}, nil
}
type emptyGenericProviderFactory struct {
typeName string
}
func (e emptyGenericProviderFactory) Type() string {
return e.typeName
}
type emptyGenericProvider struct {
}
func (e emptyGenericProvider) Authenticate(username string, password string) (Identity, error) {
return emptyIdentity{}, nil
}
func (e emptyGenericProviderFactory) Create(options options.DynamicOptions) (GenericProvider, error) {
return emptyGenericProvider{}, nil
}
func TestSetupWith(t *testing.T) {
RegisterOAuthProvider(emptyOAuthProviderFactory{typeName: "GitHubIdentityProvider"})
RegisterOAuthProvider(emptyOAuthProviderFactory{typeName: "OIDCIdentityProvider"})
RegisterGenericProvider(emptyGenericProviderFactory{typeName: "LDAPIdentityProvider"})
type args struct {
options []oauth.IdentityProviderOptions
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "ldap",
args: args{options: []oauth.IdentityProviderOptions{
{
Name: "ldap",
MappingMethod: "auto",
Type: "LDAPIdentityProvider",
Provider: options.DynamicOptions{},
},
}},
wantErr: false,
},
{
name: "conflict",
args: args{options: []oauth.IdentityProviderOptions{
{
Name: "ldap",
MappingMethod: "auto",
Type: "LDAPIdentityProvider",
Provider: options.DynamicOptions{},
},
{
Name: "ldap",
MappingMethod: "auto",
Type: "LDAPIdentityProvider",
Provider: options.DynamicOptions{},
},
}},
wantErr: true,
},
{
name: "not supported",
args: args{options: []oauth.IdentityProviderOptions{
{
Name: "test",
MappingMethod: "auto",
Type: "NotSupported",
Provider: options.DynamicOptions{},
},
}},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := SetupWithOptions(tt.args.options); (err != nil) != tt.wantErr {
t.Errorf("SetupWithOptions() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@@ -0,0 +1,131 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package identityprovider
import (
"context"
"fmt"
"sync"
v1 "k8s.io/api/core/v1"
toolscache "k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
runtimecache "sigs.k8s.io/controller-runtime/pkg/cache"
)
var SharedIdentityProviderController = NewController()
type Controller struct {
identityProviders *sync.Map
identityProviderConfigs *sync.Map
}
func NewController() *Controller {
return &Controller{identityProviders: &sync.Map{}, identityProviderConfigs: &sync.Map{}}
}
func (c *Controller) WatchConfigurationChanges(ctx context.Context, cache runtimecache.Cache) error {
informer, err := cache.GetInformer(ctx, &v1.Secret{})
if err != nil {
return fmt.Errorf("get informer failed: %w", err)
}
_, err = informer.AddEventHandler(toolscache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
return IsIdentityProviderConfiguration(obj.(*v1.Secret))
},
Handler: &toolscache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
c.OnConfigurationChange(obj.(*v1.Secret))
},
UpdateFunc: func(old, new interface{}) {
c.OnConfigurationChange(new.(*v1.Secret))
},
DeleteFunc: func(obj interface{}) {
c.OnConfigurationDelete(obj.(*v1.Secret))
},
},
})
if err != nil {
return fmt.Errorf("add event handler failed: %w", err)
}
return nil
}
func (c *Controller) OnConfigurationDelete(secret *v1.Secret) {
configuration, err := UnmarshalFrom(secret)
if err != nil {
klog.Errorf("failed to unmarshal secret data: %s", err)
return
}
c.identityProviders.Delete(configuration.Name)
c.identityProviderConfigs.Delete(configuration.Name)
}
func (c *Controller) OnConfigurationChange(secret *v1.Secret) {
configuration, err := UnmarshalFrom(secret)
if err != nil {
klog.Errorf("failed to unmarshal secret data: %s", err)
return
}
if genericProviderFactories[configuration.Type] == nil && oauthProviderFactories[configuration.Type] == nil {
klog.Errorf("identity provider %s with type %s is not supported", configuration.Name, configuration.Type)
return
}
if factory, ok := oauthProviderFactories[configuration.Type]; ok {
if provider, err := factory.Create(configuration.ProviderOptions); err != nil {
// dont return errors, decoupling external dependencies
klog.Error(fmt.Sprintf("failed to create identity provider %s: %s", configuration.Name, err))
} else {
c.identityProviders.Store(configuration.Name, provider)
c.identityProviderConfigs.Store(configuration.Name, configuration)
klog.Infof("create identity provider %s successfully", configuration.Name)
}
}
if factory, ok := genericProviderFactories[configuration.Type]; ok {
if provider, err := factory.Create(configuration.ProviderOptions); err != nil {
klog.Error(fmt.Sprintf("failed to create identity provider %s: %s", configuration.Name, err))
} else {
c.identityProviders.Store(configuration.Name, provider)
c.identityProviderConfigs.Store(configuration.Name, configuration)
klog.V(4).Infof("create identity provider %s successfully", configuration.Name)
}
}
}
func (c *Controller) GetGenericProvider(providerName string) (GenericProvider, bool) {
if obj, ok := c.identityProviders.Load(providerName); ok {
if provider, ok := obj.(GenericProvider); ok {
return provider, true
}
}
return nil, false
}
func (c *Controller) GetOAuthProvider(providerName string) (OAuthProvider, bool) {
if obj, ok := c.identityProviders.Load(providerName); ok {
if provider, ok := obj.(OAuthProvider); ok {
return provider, true
}
}
return nil, false
}
func (c *Controller) ListConfigurations() []*Configuration {
configurations := make([]*Configuration, 0)
c.identityProviderConfigs.Range(func(key, value any) bool {
if configuration, ok := value.(*Configuration); ok {
configurations = append(configurations, configuration)
}
return true
})
return configurations
}

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package ldap
@@ -39,7 +28,7 @@ const (
)
func init() {
identityprovider.RegisterGenericProvider(&ldapProviderFactory{})
identityprovider.RegisterGenericProviderFactory(&ldapProviderFactory{})
}
type ldapProvider struct {

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package ldap

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package identityprovider

View File

@@ -1,18 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package oidc
@@ -36,7 +25,7 @@ import (
)
func init() {
identityprovider.RegisterOAuthProvider(&oidcProviderFactory{})
identityprovider.RegisterOAuthProviderFactory(&oidcProviderFactory{})
}
type oidcProvider struct {

View File

@@ -1,20 +1,7 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package oidc
@@ -36,7 +23,7 @@ import (
"kubesphere.io/kubesphere/pkg/server/options"
"github.com/golang-jwt/jwt/v4"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"gopkg.in/square/go-jose.v2"
@@ -53,7 +40,7 @@ func TestOIDC(t *testing.T) {
RunSpecs(t, "OIDC Identity Provider Suite")
}
var _ = BeforeSuite(func(done Done) {
var _ = BeforeSuite(func() {
privateKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
Expect(err).Should(BeNil())
jwk := jose.JSONWebKey{
@@ -152,8 +139,7 @@ var _ = BeforeSuite(func(done Done) {
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}))
close(done)
}, 60)
})
var _ = AfterSuite(func() {
By("tearing down the test environment")