support oidc identity provider

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-12-25 19:14:04 +08:00
parent 1f4d5cb686
commit ac2bdf2509
82 changed files with 13355 additions and 196 deletions

View File

@@ -37,7 +37,7 @@ type aliyunIDaaS struct {
ClientID string `json:"clientID" yaml:"clientID"`
// ClientSecret is the application's secret.
ClientSecret string `json:"-" yaml:"clientSecret"`
ClientSecret string `json:"clientSecret" yaml:"clientSecret"`
// Endpoint contains the resource server's token endpoint
// URLs. These are constants specific to each server and are
@@ -51,6 +51,8 @@ type aliyunIDaaS struct {
// 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
@@ -58,7 +60,7 @@ type aliyunIDaaS struct {
type endpoint struct {
AuthURL string `json:"authURL" yaml:"authURL"`
TokenURL string `json:"tokenURL" yaml:"tokenURL"`
UserInfoURL string `json:"user_info_url" yaml:"userInfoUrl"`
UserInfoURL string `json:"userInfoURL" yaml:"userInfoURL"`
}
type idaasIdentity struct {
@@ -81,15 +83,26 @@ type userInfoResp struct {
type idaasProviderFactory struct {
}
func (g *idaasProviderFactory) Type() string {
return "AliyunIDaasProvider"
func (f *idaasProviderFactory) Type() string {
return "AliyunIDaaSProvider"
}
func (g *idaasProviderFactory) Create(options *oauth.DynamicOptions) (identityprovider.OAuthProvider, error) {
func (f *idaasProviderFactory) Create(options oauth.DynamicOptions) (identityprovider.OAuthProvider, error) {
var idaas aliyunIDaaS
if err := mapstructure.Decode(options, &idaas); err != nil {
return nil, err
}
idaas.Config = &oauth2.Config{
ClientID: idaas.ClientID,
ClientSecret: idaas.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: idaas.Endpoint.AuthURL,
TokenURL: idaas.Endpoint.TokenURL,
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: idaas.RedirectURL,
Scopes: idaas.Scopes,
}
return &idaas, nil
}
@@ -105,28 +118,13 @@ func (a idaasIdentity) GetEmail() string {
return a.Email
}
func (a idaasIdentity) GetDisplayName() string {
return a.Nickname
}
func (a *aliyunIDaaS) IdentityExchange(code string) (identityprovider.Identity, error) {
config := oauth2.Config{
ClientID: a.ClientID,
ClientSecret: a.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: a.Endpoint.AuthURL,
TokenURL: a.Endpoint.TokenURL,
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: a.RedirectURL,
Scopes: a.Scopes,
}
token, err := config.Exchange(context.Background(), code)
token, err := a.Config.Exchange(context.TODO(), code)
if err != nil {
return nil, err
}
resp, err := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(token)).Get(a.Endpoint.UserInfoURL)
resp, err := oauth2.NewClient(context.TODO(), oauth2.StaticTokenSource(token)).Get(a.Endpoint.UserInfoURL)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,96 @@
/*
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 aliyunidaas
import (
"golang.org/x/oauth2"
"gopkg.in/yaml.v3"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"reflect"
"testing"
)
func Test_idaasProviderFactory_Create(t *testing.T) {
type args struct {
options oauth.DynamicOptions
}
mustUnmarshalYAML := func(data string) oauth.DynamicOptions {
var dynamicOptions oauth.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{options: mustUnmarshalYAML(`
clientID: xxxx
clientSecret: xxxx
endpoint:
userInfoUrl: "https://xxxxx.login.aliyunidaas.com/api/bff/v1.2/oauth2/userinfo"
authURL: "https://xxxx.login.aliyunidaas.com/oauth/authorize"
tokenURL: "https://xxxx.login.aliyunidaas.com/oauth/token"
redirectURL: "http://ks-console/oauth/redirect"
scopes:
- read
`)},
want: &aliyunIDaaS{
ClientID: "xxxx",
ClientSecret: "xxxx",
Endpoint: endpoint{
AuthURL: "https://xxxx.login.aliyunidaas.com/oauth/authorize",
TokenURL: "https://xxxx.login.aliyunidaas.com/oauth/token",
UserInfoURL: "https://xxxxx.login.aliyunidaas.com/api/bff/v1.2/oauth2/userinfo",
},
RedirectURL: "http://ks-console/oauth/redirect",
Scopes: []string{"read"},
Config: &oauth2.Config{
ClientID: "xxxx",
ClientSecret: "xxxx",
Endpoint: oauth2.Endpoint{
AuthURL: "https://xxxx.login.aliyunidaas.com/oauth/authorize",
TokenURL: "https://xxxx.login.aliyunidaas.com/oauth/token",
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: "http://ks-console/oauth/redirect",
Scopes: []string{"read"},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &idaasProviderFactory{}
got, err := f.Create(tt.args.options)
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

@@ -1,17 +1,19 @@
/*
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
Copyright 2020 The KubeSphere Authors.
http://www.apache.org/licenses/LICENSE-2.0
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.
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
@@ -20,10 +22,6 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
)
var (
builtinGenericProviders = make(map[string]GenericProviderFactory)
)
type GenericProvider interface {
// Authenticate from remote server
Authenticate(username string, password string) (Identity, error)
@@ -33,16 +31,5 @@ type GenericProviderFactory interface {
// Type unique type of the provider
Type() string
// Apply the dynamic options from kubesphere-config
Create(options *oauth.DynamicOptions) (GenericProvider, error)
}
func CreateGenericProvider(providerType string, options *oauth.DynamicOptions) (GenericProvider, error) {
if factory, ok := builtinGenericProviders[providerType]; ok {
return factory.Create(options)
}
return nil, identityProviderNotFound
}
func RegisterGenericProvider(factory GenericProviderFactory) {
builtinGenericProviders[factory.Type()] = factory
Create(options oauth.DynamicOptions) (GenericProvider, error)
}

View File

@@ -18,21 +18,25 @@ package github
import (
"context"
"crypto/tls"
"encoding/json"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"net/http"
"time"
)
const (
UserInfoURL = "https://api.github.com/user"
userInfoURL = "https://api.github.com/user"
authURL = "https://github.com/login/oauth/authorize"
tokenURL = "https://github.com/login/oauth/access_token"
)
func init() {
identityprovider.RegisterOAuthProvider(&githubProviderFactory{})
identityprovider.RegisterOAuthProvider(&ldapProviderFactory{})
}
type github struct {
@@ -52,15 +56,21 @@ type github struct {
// 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"`
AuthURL string `json:"authURL" yaml:"authURL"`
TokenURL string `json:"tokenURL" yaml:"tokenURL"`
UserInfoURL string `json:"userInfoURL" yaml:"userInfoURL"`
}
type githubIdentity struct {
@@ -102,18 +112,44 @@ type githubIdentity struct {
Collaborators int `json:"collaborators"`
}
type githubProviderFactory struct {
type ldapProviderFactory struct {
}
func (g *githubProviderFactory) Type() string {
func (g *ldapProviderFactory) Type() string {
return "GitHubIdentityProvider"
}
func (g *githubProviderFactory) Create(options *oauth.DynamicOptions) (identityprovider.OAuthProvider, error) {
func (g *ldapProviderFactory) Create(options oauth.DynamicOptions) (identityprovider.OAuthProvider, error) {
var github github
if err := mapstructure.Decode(options, &github); err != nil {
return nil, err
}
if github.Endpoint.AuthURL == "" {
github.Endpoint.AuthURL = authURL
}
if github.Endpoint.TokenURL == "" {
github.Endpoint.TokenURL = tokenURL
}
if github.Endpoint.UserInfoURL == "" {
github.Endpoint.UserInfoURL = userInfoURL
}
// fixed options
options["endpoint"] = oauth.DynamicOptions{
"authURL": github.Endpoint.AuthURL,
"tokenURL": github.Endpoint.TokenURL,
"userInfoURL": github.Endpoint.UserInfoURL,
}
github.Config = &oauth2.Config{
ClientID: github.ClientID,
ClientSecret: github.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: github.Endpoint.AuthURL,
TokenURL: github.Endpoint.TokenURL,
},
RedirectURL: github.RedirectURL,
Scopes: github.Scopes,
}
return &github, nil
}
@@ -129,29 +165,23 @@ func (g githubIdentity) GetEmail() string {
return g.Email
}
func (g githubIdentity) GetDisplayName() string {
return ""
}
func (g *github) IdentityExchange(code string) (identityprovider.Identity, error) {
config := oauth2.Config{
ClientID: g.ClientID,
ClientSecret: g.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: g.Endpoint.AuthURL,
TokenURL: g.Endpoint.TokenURL,
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: g.RedirectURL,
Scopes: g.Scopes,
ctx := context.TODO()
if g.InsecureSkipVerify {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
}
token, err := config.Exchange(context.Background(), code)
token, err := g.Config.Exchange(ctx, code)
if err != nil {
return nil, err
}
resp, err := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(token)).Get(UserInfoURL)
resp, err := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)).Get(g.Endpoint.UserInfoURL)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,160 @@
/*
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 github
import (
"encoding/json"
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"golang.org/x/oauth2"
"gopkg.in/yaml.v3"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"net/http"
"net/http/httptest"
"testing"
"time"
)
var githubServer *httptest.Server
func TestGithub(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "GitHub Identity Provider Suite")
}
var _ = BeforeSuite(func(done Done) {
githubServer = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
switch r.RequestURI {
case "/login/oauth/access_token":
data = map[string]interface{}{
"access_token": "e72e16c7e42f292c6912e7710c838347ae178b4a",
"scope": "user,repo,gist",
"token_type": "bearer",
}
case "/user":
data = map[string]interface{}{
"login": "test",
"email": "test@kubesphere.io",
}
default:
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("not implemented"))
return
}
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}))
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
githubServer.Close()
})
var _ = Describe("GitHub", func() {
Context("GitHub", func() {
var (
provider identityprovider.OAuthProvider
err error
)
It("should configure successfully", func() {
configYAML := `
clientID: de6ff8bed0304e487b6e
clientSecret: 2b70536f79ec8d2939863509d05e2a71c268b9af
redirectURL: "http://ks-console/oauth/redirect"
scopes:
- user
`
config := mustUnmarshalYAML(configYAML)
factory := ldapProviderFactory{}
provider, err = factory.Create(config)
Expect(err).Should(BeNil())
expected := &github{
ClientID: "de6ff8bed0304e487b6e",
ClientSecret: "2b70536f79ec8d2939863509d05e2a71c268b9af",
Endpoint: endpoint{
AuthURL: authURL,
TokenURL: tokenURL,
UserInfoURL: userInfoURL,
},
RedirectURL: "http://ks-console/oauth/redirect",
Scopes: []string{"user"},
Config: &oauth2.Config{
ClientID: "de6ff8bed0304e487b6e",
ClientSecret: "2b70536f79ec8d2939863509d05e2a71c268b9af",
Endpoint: oauth2.Endpoint{
AuthURL: authURL,
TokenURL: tokenURL,
},
RedirectURL: "http://ks-console/oauth/redirect",
Scopes: []string{"user"},
},
}
Expect(provider).Should(Equal(expected))
})
It("should configure successfully", func() {
config := oauth.DynamicOptions{
"clientID": "de6ff8bed0304e487b6e",
"clientSecret": "2b70536f79ec8d2939863509d05e2a71c268b9af",
"redirectURL": "http://ks-console/oauth/redirect",
"insecureSkipVerify": true,
"endpoint": oauth.DynamicOptions{
"authURL": fmt.Sprintf("%s/login/oauth/authorize", githubServer.URL),
"tokenURL": fmt.Sprintf("%s/login/oauth/access_token", githubServer.URL),
"userInfoURL": fmt.Sprintf("%s/user", githubServer.URL),
},
}
factory := ldapProviderFactory{}
provider, err = factory.Create(config)
Expect(err).Should(BeNil())
expected := oauth.DynamicOptions{
"clientID": "de6ff8bed0304e487b6e",
"clientSecret": "2b70536f79ec8d2939863509d05e2a71c268b9af",
"redirectURL": "http://ks-console/oauth/redirect",
"insecureSkipVerify": true,
"endpoint": oauth.DynamicOptions{
"authURL": fmt.Sprintf("%s/login/oauth/authorize", githubServer.URL),
"tokenURL": fmt.Sprintf("%s/login/oauth/access_token", githubServer.URL),
"userInfoURL": fmt.Sprintf("%s/user", githubServer.URL),
},
}
Expect(config).Should(Equal(expected))
})
It("should login successfully", func() {
identity, err := provider.IdentityExchange("3389")
Expect(err).Should(BeNil())
Expect(identity.GetUserID()).Should(Equal("test"))
Expect(identity.GetUsername()).Should(Equal("test"))
Expect(identity.GetEmail()).Should(Equal("test@kubesphere.io"))
})
})
})
func mustUnmarshalYAML(data string) oauth.DynamicOptions {
var dynamicOptions oauth.DynamicOptions
_ = yaml.Unmarshal([]byte(data), &dynamicOptions)
return dynamicOptions
}

View File

@@ -1,28 +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
type Identity interface {
// required
GetUserID() string
// optional
GetUsername() string
// optional
GetDisplayName() string
// optional
GetEmail() string
}

View File

@@ -0,0 +1,104 @@
/*
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"
"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 {
// required
// Identifier for the End-User at the Issuer.
GetUserID() string
// optional
// The username which the End-User wishes to be referred to kubesphere.
GetUsername() string
// optional
GetEmail() string
}
// SetupWithOptions will verify the configuration and initialize the identityProviders
func SetupWithOptions(options []oauth.IdentityProviderOptions) error {
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
}
// GetGenericProvider 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
}
// RegisterOAuthProvider register GenericProviderFactory with the specified type
func RegisterGenericProvider(factory GenericProviderFactory) {
genericProviderFactories[factory.Type()] = factory
}

View File

@@ -0,0 +1,135 @@
/*
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 (
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"testing"
)
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) IdentityExchange(code string) (Identity, error) {
return emptyIdentity{}, nil
}
func (e emptyOAuthProviderFactory) Create(options oauth.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 oauth.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: oauth.DynamicOptions{},
},
}},
wantErr: false,
},
{
name: "conflict",
args: args{options: []oauth.IdentityProviderOptions{
{
Name: "ldap",
MappingMethod: "auto",
Type: "LDAPIdentityProvider",
Provider: oauth.DynamicOptions{},
},
}},
wantErr: true,
},
{
name: "not supported",
args: args{options: []oauth.IdentityProviderOptions{
{
Name: "test",
MappingMethod: "auto",
Type: "NotSupported",
Provider: oauth.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

@@ -72,9 +72,8 @@ type ldapProvider struct {
GroupMemberAttribute string `json:"groupMemberAttribute,omitempty" yaml:"groupMemberAttribute"`
// The following three fields are direct mappings of attributes on the user entry.
// login attribute used for comparing user entries.
LoginAttribute string `json:"loginAttribute" yaml:"loginAttribute"`
MailAttribute string `json:"mailAttribute" yaml:"mailAttribute"`
DisplayNameAttribute string `json:"displayNameAttribute" yaml:"displayNameAttribute"`
LoginAttribute string `json:"loginAttribute" yaml:"loginAttribute"`
MailAttribute string `json:"mailAttribute" yaml:"mailAttribute"`
}
type ldapProviderFactory struct {
@@ -84,7 +83,7 @@ func (l *ldapProviderFactory) Type() string {
return ldapIdentityProvider
}
func (l *ldapProviderFactory) Create(options *oauth.DynamicOptions) (identityprovider.GenericProvider, error) {
func (l *ldapProviderFactory) Create(options oauth.DynamicOptions) (identityprovider.GenericProvider, error) {
var ldapProvider ldapProvider
if err := mapstructure.Decode(options, &ldapProvider); err != nil {
return nil, err
@@ -96,9 +95,8 @@ func (l *ldapProviderFactory) Create(options *oauth.DynamicOptions) (identitypro
}
type ldapIdentity struct {
Username string
Email string
DisplayName string
Username string
Email string
}
func (l *ldapIdentity) GetUserID() string {
@@ -113,10 +111,6 @@ func (l *ldapIdentity) GetEmail() string {
return l.Email
}
func (l *ldapIdentity) GetDisplayName() string {
return l.DisplayName
}
func (l ldapProvider) Authenticate(username string, password string) (identityprovider.Identity, error) {
conn, err := l.newConn()
if err != nil {
@@ -141,7 +135,7 @@ func (l ldapProvider) Authenticate(username string, password string) (identitypr
TimeLimit: 0,
TypesOnly: false,
Filter: filter,
Attributes: []string{l.LoginAttribute, l.MailAttribute, l.DisplayNameAttribute},
Attributes: []string{l.LoginAttribute, l.MailAttribute},
})
if err != nil {
klog.Error(err)
@@ -161,11 +155,9 @@ func (l ldapProvider) Authenticate(username string, password string) (identitypr
return nil, err
}
email := entry.GetAttributeValue(l.MailAttribute)
displayName := entry.GetAttributeValue(l.DisplayNameAttribute)
return &ldapIdentity{
Username: username,
DisplayName: displayName,
Email: email,
Username: username,
Email: email,
}, nil
}

View File

@@ -40,7 +40,7 @@ mailAttribute: mail
if err != nil {
t.Fatal(err)
}
got, err := new(ldapProviderFactory).Create(&dynamicOptions)
got, err := new(ldapProviderFactory).Create(dynamicOptions)
if err != nil {
t.Fatal(err)
}
@@ -61,7 +61,6 @@ mailAttribute: mail
GroupMemberAttribute: "",
LoginAttribute: "uid",
MailAttribute: "mail",
DisplayNameAttribute: "",
}
if diff := cmp.Diff(got, expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", expected, diff)
@@ -81,7 +80,7 @@ func TestLdapProvider_Authenticate(t *testing.T) {
if err = yaml.Unmarshal(options, &dynamicOptions); err != nil {
t.Fatal(err)
}
ldapProvider, err := new(ldapProviderFactory).Create(&dynamicOptions)
ldapProvider, err := new(ldapProviderFactory).Create(dynamicOptions)
if err != nil {
t.Fatal(err)
}

View File

@@ -16,15 +16,9 @@ limitations under the License.
package identityprovider
import (
"errors"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
)
var (
builtinOAuthProviders = make(map[string]OAuthProviderFactory)
identityProviderNotFound = errors.New("identity provider not found")
)
type OAuthProvider interface {
// IdentityExchange exchange identity from remote server
IdentityExchange(code string) (Identity, error)
@@ -34,16 +28,5 @@ type OAuthProviderFactory interface {
// Type unique type of the provider
Type() string
// Apply the dynamic options from kubesphere-config
Create(options *oauth.DynamicOptions) (OAuthProvider, error)
}
func CreateOAuthProvider(providerType string, options *oauth.DynamicOptions) (OAuthProvider, error) {
if provider, ok := builtinOAuthProviders[providerType]; ok {
return provider.Create(options)
}
return nil, identityProviderNotFound
}
func RegisterOAuthProvider(factory OAuthProviderFactory) {
builtinOAuthProviders[factory.Type()] = factory
Create(options oauth.DynamicOptions) (OAuthProvider, error)
}

View File

@@ -0,0 +1,282 @@
/*
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 oidc
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"github.com/coreos/go-oidc"
"github.com/dgrijalva/jwt-go"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"net/http"
)
func init() {
identityprovider.RegisterOAuthProvider(&oidcProviderFactory{})
}
type oidcProvider struct {
// Defines how Clients dynamically discover information about OpenID Providers
// See also, https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
Issuer string `json:"issuer,omitempty" yaml:"issuer,omitempty"`
// 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 github.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"`
// Scope specifies optional requested permissions.
Scopes []string `json:"scopes" yaml:"scopes"`
// GetUserInfo uses the userinfo endpoint to get additional claims for the token.
// This is especially useful where upstreams return "thin" id tokens
// See also, https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
GetUserInfo bool `json:"getUserInfo" yaml:"getUserInfo"`
// Used to turn off TLS certificate checks
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"`
// Configurable key which contains the email claims
EmailKey string `json:"emailKey" yaml:"emailKey"`
// Configurable key which contains the preferred username claims
PreferredUsernameKey string `json:"preferredUsernameKey" yaml:"preferredUsernameKey"`
Provider *oidc.Provider `json:"-" yaml:"-"`
OAuth2Config *oauth2.Config `json:"-" yaml:"-"`
Verifier *oidc.IDTokenVerifier `json:"-" yaml:"-"`
}
// endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs.
type endpoint struct {
// URL of the OP's OAuth 2.0 Authorization Endpoint [OpenID.Core](https://openid.net/specs/openid-connect-discovery-1_0.html#OpenID.Core).
AuthURL string `json:"authURL" yaml:"authURL"`
// URL of the OP's OAuth 2.0 Token Endpoint [OpenID.Core](https://openid.net/specs/openid-connect-discovery-1_0.html#OpenID.Core).
// This is REQUIRED unless only the Implicit Flow is used.
TokenURL string `json:"tokenURL" yaml:"tokenURL"`
// URL of the OP's UserInfo Endpoint [OpenID.Core](https://openid.net/specs/openid-connect-discovery-1_0.html#OpenID.Core).
// This URL MUST use the https scheme and MAY contain port, path, and query parameter components.
UserInfoURL string `json:"userInfoURL" yaml:"userInfoURL"`
// URL of the OP's JSON Web Key Set [JWK](https://openid.net/specs/openid-connect-discovery-1_0.html#JWK) document.
JWKSURL string `json:"jwksURL"`
}
type oidcIdentity struct {
// Subject - Identifier for the End-User at the Issuer.
Sub string `json:"sub"`
// Shorthand name by which the End-User wishes to be referred to at the RP,
// such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace.
// The RP MUST NOT rely upon this value being unique
PreferredUsername string `json:"preferred_username"`
// End-User's preferred e-mail address.
// Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax.
// The RP MUST NOT rely upon this value being unique.
Email string `json:"email"`
}
func (o oidcIdentity) GetUserID() string {
return o.Sub
}
func (o oidcIdentity) GetUsername() string {
return o.PreferredUsername
}
func (o oidcIdentity) GetEmail() string {
return o.Email
}
type oidcProviderFactory struct {
}
func (f *oidcProviderFactory) Type() string {
return "OIDCIdentityProvider"
}
func (f *oidcProviderFactory) Create(options oauth.DynamicOptions) (identityprovider.OAuthProvider, error) {
var oidcProvider oidcProvider
if err := mapstructure.Decode(options, &oidcProvider); err != nil {
return nil, err
}
// dynamically discover
if oidcProvider.Issuer != "" {
ctx := context.TODO()
if oidcProvider.InsecureSkipVerify {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
ctx = oidc.ClientContext(ctx, client)
}
provider, err := oidc.NewProvider(ctx, oidcProvider.Issuer)
if err != nil {
return nil, fmt.Errorf("failed to create oidc provider: %v", err)
}
var providerJSON map[string]interface{}
if err = provider.Claims(&providerJSON); err != nil {
return nil, fmt.Errorf("failed to decode oidc provider claims: %v", err)
}
oidcProvider.Endpoint.AuthURL, _ = providerJSON["authorization_endpoint"].(string)
oidcProvider.Endpoint.TokenURL, _ = providerJSON["token_endpoint"].(string)
oidcProvider.Endpoint.UserInfoURL, _ = providerJSON["userinfo_endpoint"].(string)
oidcProvider.Endpoint.JWKSURL, _ = providerJSON["jwks_uri"].(string)
oidcProvider.Provider = provider
oidcProvider.Verifier = provider.Verifier(&oidc.Config{
// TODO: support HS256
ClientID: oidcProvider.ClientID,
})
options["endpoint"] = oauth.DynamicOptions{
"authURL": oidcProvider.Endpoint.AuthURL,
"tokenURL": oidcProvider.Endpoint.TokenURL,
"userInfoURL": oidcProvider.Endpoint.UserInfoURL,
"jwksURL": oidcProvider.Endpoint.JWKSURL,
}
}
scopes := []string{oidc.ScopeOpenID}
if len(oidcProvider.Scopes) > 0 {
scopes = append(scopes, oidcProvider.Scopes...)
} else {
scopes = append(scopes, "openid", "profile", "email")
}
oidcProvider.Scopes = scopes
oidcProvider.OAuth2Config = &oauth2.Config{
ClientID: oidcProvider.ClientID,
ClientSecret: oidcProvider.ClientSecret,
Endpoint: oauth2.Endpoint{
TokenURL: oidcProvider.Endpoint.TokenURL,
AuthURL: oidcProvider.Endpoint.AuthURL,
},
RedirectURL: oidcProvider.RedirectURL,
Scopes: oidcProvider.Scopes,
}
return &oidcProvider, nil
}
func (o *oidcProvider) IdentityExchange(code string) (identityprovider.Identity, error) {
ctx := context.TODO()
if o.InsecureSkipVerify {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
}
token, err := o.OAuth2Config.Exchange(ctx, code)
if err != nil {
return nil, fmt.Errorf("oidc: failed to get token: %v", err)
}
rawIDToken, ok := token.Extra("id_token").(string)
if !ok {
return nil, errors.New("no id_token in token response")
}
var claims jwt.MapClaims
if o.Verifier != nil {
idToken, err := o.Verifier.Verify(ctx, rawIDToken)
if err != nil {
return nil, fmt.Errorf("failed to verify id token: %v", err)
}
if err := idToken.Claims(&claims); err != nil {
return nil, fmt.Errorf("failed to decode id token claims: %v", err)
}
} else {
_, _, err := new(jwt.Parser).ParseUnverified(rawIDToken, &claims)
if err != nil {
return nil, fmt.Errorf("failed to decode id token claims: %v", err)
}
if err := claims.Valid(); err != nil {
return nil, fmt.Errorf("failed to verify id token: %v", err)
}
}
if o.GetUserInfo {
if o.Provider != nil {
userInfo, err := o.Provider.UserInfo(ctx, oauth2.StaticTokenSource(token))
if err != nil {
return nil, fmt.Errorf("failed to fetch userinfo: %v", err)
}
if err := userInfo.Claims(&claims); err != nil {
return nil, fmt.Errorf("failed to decode userinfo claims: %v", err)
}
} else {
resp, err := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)).Get(o.Endpoint.UserInfoURL)
if err != nil {
return nil, fmt.Errorf("failed to fetch userinfo: %v", err)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to fetch userinfo: %v", err)
}
_ = resp.Body.Close()
if err := json.Unmarshal(data, &claims); err != nil {
return nil, fmt.Errorf("failed to decode userinfo claims: %v", err)
}
}
}
subject, ok := claims["sub"].(string)
if !ok {
return nil, errors.New("missing required claim \"sub\"")
}
var email string
emailKey := "email"
if o.EmailKey != "" {
emailKey = o.EmailKey
}
email, _ = claims[emailKey].(string)
var preferredUsername string
preferredUsernameKey := "preferred_username"
if o.PreferredUsernameKey != "" {
preferredUsernameKey = o.PreferredUsernameKey
}
preferredUsername, _ = claims[preferredUsernameKey].(string)
if preferredUsername == "" {
preferredUsername, _ = claims["name"].(string)
}
return &oidcIdentity{
Sub: subject,
PreferredUsername: preferredUsername,
Email: email,
}, nil
}

View File

@@ -0,0 +1,214 @@
/*
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 oidc
import (
"bytes"
cryptorand "crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/dgrijalva/jwt-go"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"gopkg.in/square/go-jose.v2"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
var (
oidcServer *httptest.Server
)
func TestOIDC(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "OIDC Identity Provider Suite")
}
var _ = BeforeSuite(func(done Done) {
privateKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
Expect(err).Should(BeNil())
jwk := jose.JSONWebKey{
Key: privateKey,
KeyID: "keyID",
Algorithm: "RSA",
}
oidcServer = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data interface{}
switch r.RequestURI {
case "/.well-known/openid-configuration":
data = map[string]interface{}{
"issuer": oidcServer.URL,
"token_endpoint": fmt.Sprintf("%s/token", oidcServer.URL),
"authorization_endpoint": fmt.Sprintf("%s/authorize", oidcServer.URL),
"userinfo_endpoint": fmt.Sprintf("%s/userinfo", oidcServer.URL),
"jwks_uri": fmt.Sprintf("%s/keys", oidcServer.URL),
"response_types_supported": []string{
"code",
"token",
"id_token",
"none",
},
"id_token_signing_alg_values_supported": []string{
"RS256",
},
"scopes_supported": []string{
"openid",
"email",
"profile",
},
"token_endpoint_auth_methods_supported": []string{
"client_secret_post",
"client_secret_basic",
},
"claims_supported": []string{
"aud",
"email",
"email_verified",
"exp",
"iat",
"iss",
"name",
"sub",
},
"code_challenge_methods_supported": []string{
"plain",
"S256",
},
"grant_types_supported": []string{
"authorization_code",
"refresh_token",
},
}
case "/user":
data = map[string]interface{}{
"login": "test",
"email": "test@kubesphere.io",
}
case "/keys":
data = map[string]interface{}{
"keys": []map[string]interface{}{{
"alg": jwk.Algorithm,
"kty": jwk.Algorithm,
"kid": jwk.KeyID,
"n": n(&privateKey.PublicKey),
"e": e(&privateKey.PublicKey),
}},
}
case "/token":
claims := jwt.MapClaims{
"iss": oidcServer.URL,
"sub": "110169484474386276334",
"aud": "kubesphere",
"email": "test@kubesphere.io",
"email_verified": "true",
"name": "test",
"iat": time.Now().Unix(),
"exp": time.Now().Add(10 * time.Hour).Unix(),
}
idToken, _ := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(privateKey)
data = map[string]interface{}{
"access_token": "e72e16c7e42f292c6912e7710c838347ae178b4a",
"id_token": idToken,
"token_type": "Bearer",
"expires_in": 3600,
}
default:
fmt.Println(r.URL)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("not implemented"))
return
}
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}))
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
oidcServer.Close()
})
var _ = Describe("OIDC", func() {
Context("OIDC", func() {
var (
provider identityprovider.OAuthProvider
err error
)
It("should configure successfully", func() {
config := oauth.DynamicOptions{
"issuer": oidcServer.URL,
"clientID": "kubesphere",
"clientSecret": "c53e80ab92d48ab12f4e7f1f6976d1bdc996e0d7",
"redirectURL": "http://ks-console/oauth/redirect",
"insecureSkipVerify": true,
}
factory := oidcProviderFactory{}
provider, err = factory.Create(config)
Expect(err).Should(BeNil())
expected := oauth.DynamicOptions{
"issuer": oidcServer.URL,
"clientID": "kubesphere",
"clientSecret": "c53e80ab92d48ab12f4e7f1f6976d1bdc996e0d7",
"redirectURL": "http://ks-console/oauth/redirect",
"insecureSkipVerify": true,
"endpoint": oauth.DynamicOptions{
"authURL": fmt.Sprintf("%s/authorize", oidcServer.URL),
"tokenURL": fmt.Sprintf("%s/token", oidcServer.URL),
"userInfoURL": fmt.Sprintf("%s/userinfo", oidcServer.URL),
"jwksURL": fmt.Sprintf("%s/keys", oidcServer.URL),
},
}
Expect(config).Should(Equal(expected))
})
It("should login successfully", func() {
identity, err := provider.IdentityExchange("3389")
Expect(err).Should(BeNil())
Expect(identity.GetUserID()).Should(Equal("110169484474386276334"))
Expect(identity.GetUsername()).Should(Equal("test"))
Expect(identity.GetEmail()).Should(Equal("test@kubesphere.io"))
})
})
})
func n(pub *rsa.PublicKey) string {
return encode(pub.N.Bytes())
}
func e(pub *rsa.PublicKey) string {
data := make([]byte, 8)
binary.BigEndian.PutUint64(data, uint64(pub.E))
return encode(bytes.TrimLeft(data, "\x00"))
}
func encode(payload []byte) string {
result := base64.URLEncoding.EncodeToString(payload)
return strings.TrimRight(result, "=")
}

View File

@@ -145,7 +145,7 @@ type IdentityProviderOptions struct {
Type string `json:"type" yaml:"type"`
// The options of identify provider
Provider *DynamicOptions `json:"provider" yaml:"provider"`
Provider DynamicOptions `json:"provider" yaml:"provider"`
}
type Token struct {
@@ -231,6 +231,7 @@ func (o *Options) OAuthClient(name string) (Client, error) {
}
return Client{}, ErrorClientNotFound
}
func (o *Options) IdentityProviderOptions(name string) (*IdentityProviderOptions, error) {
for _, found := range o.IdentityProviders {
if found.Name == name {

View File

@@ -49,7 +49,6 @@ func TestDefaultAuthOptions(t *testing.T) {
}
func TestClientResolveRedirectURL(t *testing.T) {
options := NewOptions()
defaultClient, err := options.OAuthClient("default")
if err != nil {

View File

@@ -19,9 +19,11 @@ package options
import (
"fmt"
"github.com/spf13/pflag"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
_ "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider/aliyunidaas"
_ "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider/github"
_ "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider/ldap"
_ "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider/oidc"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
"time"
)
@@ -67,6 +69,9 @@ func (options *AuthenticationOptions) Validate() []error {
if len(options.JwtSecret) == 0 {
errs = append(errs, fmt.Errorf("jwt secret is empty"))
}
if err := identityprovider.SetupWithOptions(options.OAuthOptions.IdentityProviders); err != nil {
errs = append(errs, err)
}
return errs
}

View File

@@ -41,7 +41,7 @@ func (o *AuthorizationOptions) AddFlags(fs *pflag.FlagSet, s *AuthorizationOptio
fs.StringVar(&o.Mode, "authorization", s.Mode, "Authorization setting, allowed values: AlwaysDeny, AlwaysAllow, RBAC.")
}
func (o AuthorizationOptions) Validate() []error {
func (o *AuthorizationOptions) Validate() []error {
errs := make([]error, 0)
if !sliceutil.HasString([]string{AlwaysAllow, AlwaysDeny, RBAC}, o.Mode) {
err := fmt.Errorf("authorization mode %s not support", o.Mode)

View File

@@ -171,7 +171,7 @@ func (h *handler) Authorize(req *restful.Request, resp *restful.Response) {
http.Redirect(resp, req.Request, redirectURL, http.StatusFound)
}
func (h *handler) oauthCallBack(req *restful.Request, resp *restful.Response) {
func (h *handler) oauthCallback(req *restful.Request, resp *restful.Response) {
code := req.QueryParameter("code")
provider := req.PathParameter("callback")

View File

@@ -104,7 +104,7 @@ func AddToContainer(c *restful.Container, im im.IdentityManagementInterface,
"otherwise, REQUIRED. The scope of the access token as described by [RFC6479] Section 3.3.").Required(false)).
Param(ws.QueryParameter("state", "if the \"state\" parameter was present in the client authorization request."+
"The exact value received from the client.").Required(true)).
To(handler.oauthCallBack).
To(handler.oauthCallback).
Returns(http.StatusOK, api.StatusOK, oauth.Token{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AuthenticationTag}))

View File

@@ -99,7 +99,7 @@ func (p *passwordAuthenticator) Authenticate(username, password string) (authuse
if username == constants.AdminUserName {
break
}
if genericProvider, _ := identityprovider.CreateGenericProvider(providerOptions.Type, providerOptions.Provider); genericProvider != nil {
if genericProvider, _ := identityprovider.GetGenericProvider(providerOptions.Name); genericProvider != nil {
authenticated, err := genericProvider.Authenticate(username, password)
if err != nil {
if errors.IsUnauthorized(err) {
@@ -173,7 +173,6 @@ func preRegistrationUser(idp string, identity identityprovider.Identity) authuse
iamv1alpha2.ExtraUID: {identity.GetUserID()},
iamv1alpha2.ExtraUsername: {identity.GetUsername()},
iamv1alpha2.ExtraEmail: {identity.GetEmail()},
iamv1alpha2.ExtraDisplayName: {identity.GetDisplayName()},
},
Groups: []string{iamv1alpha2.PreRegistrationUserGroup},
}
@@ -186,7 +185,7 @@ func (o oauth2Authenticator) Authenticate(provider, code string) (authuser.Info,
klog.Error(err)
return nil, "", err
}
oauthIdentityProvider, err := identityprovider.CreateOAuthProvider(providerOptions.Type, providerOptions.Provider)
oauthIdentityProvider, err := identityprovider.GetOAuthProvider(providerOptions.Name)
if err != nil {
klog.Error(err)
return nil, "", err