Support skip information reconfirm when using external IDP
Signed-off-by: hongming <hongming@kubesphere.io>
This commit is contained in:
@@ -37,7 +37,8 @@ const (
|
|||||||
GrantHandlerPrompt GrantHandlerType = "prompt"
|
GrantHandlerPrompt GrantHandlerType = "prompt"
|
||||||
// GrantHandlerDeny auto-denies client authorization grant requests
|
// GrantHandlerDeny auto-denies client authorization grant requests
|
||||||
GrantHandlerDeny GrantHandlerType = "deny"
|
GrantHandlerDeny GrantHandlerType = "deny"
|
||||||
// MappingMethodAuto The default value.The user will automatically create and mapping when login successful.
|
// MappingMethodAuto The default value.
|
||||||
|
// The user will automatically create and mapping when login successful.
|
||||||
// Fails if a user with that username is already mapped to another identity.
|
// Fails if a user with that username is already mapped to another identity.
|
||||||
MappingMethodAuto MappingMethod = "auto"
|
MappingMethodAuto MappingMethod = "auto"
|
||||||
// MappingMethodLookup Looks up an existing identity, user identity mapping, and user, but does not automatically
|
// MappingMethodLookup Looks up an existing identity, user identity mapping, and user, but does not automatically
|
||||||
@@ -141,6 +142,11 @@ type IdentityProviderOptions struct {
|
|||||||
// - mixed: A user entity can be mapped with multiple identifyProvider.
|
// - mixed: A user entity can be mapped with multiple identifyProvider.
|
||||||
MappingMethod MappingMethod `json:"mappingMethod" yaml:"mappingMethod"`
|
MappingMethod MappingMethod `json:"mappingMethod" yaml:"mappingMethod"`
|
||||||
|
|
||||||
|
// DisableLoginConfirmation means that when the user login successfully,
|
||||||
|
// reconfirm the account information is not required.
|
||||||
|
// Username from IDP must math [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
||||||
|
DisableLoginConfirmation bool `json:"disableLoginConfirmation" yaml:"disableLoginConfirmation"`
|
||||||
|
|
||||||
// The type of identify provider
|
// The type of identify provider
|
||||||
// OpenIDIdentityProvider LDAPIdentityProvider GitHubIdentityProvider
|
// OpenIDIdentityProvider LDAPIdentityProvider GitHubIdentityProvider
|
||||||
Type string `json:"type" yaml:"type"`
|
Type string `json:"type" yaml:"type"`
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ identityProviders:
|
|||||||
if err := yaml.Unmarshal([]byte(config), &options); err != nil {
|
if err := yaml.Unmarshal([]byte(config), &options); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
expected := `{"identityProviders":[{"name":"ldap","mappingMethod":"auto","type":"LDAPIdentityProvider","provider":{"host":"xxxx.sn.mynetname.net:389","loginAttribute":"uid","mailAttribute":"mail","managerDN":"uid=root,cn=users,dc=xxxx,dc=sn,dc=mynetname,dc=net","userSearchBase":"dc=xxxx,dc=sn,dc=mynetname,dc=net"}},{"name":"github","mappingMethod":"mixed","type":"GitHubIdentityProvider","provider":{"clientID":"xxxxxx","endpoint":{"authURL":"https://github.com/login/oauth/authorize","tokenURL":"https://github.com/login/oauth/access_token"},"redirectURL":"https://ks-console/oauth/redirect","scopes":["user"]}}],"accessTokenMaxAge":3600000000000,"accessTokenInactivityTimeout":1800000000000}`
|
expected := `{"identityProviders":[{"name":"ldap","mappingMethod":"auto","disableLoginConfirmation":false,"type":"LDAPIdentityProvider","provider":{"host":"xxxx.sn.mynetname.net:389","loginAttribute":"uid","mailAttribute":"mail","managerDN":"uid=root,cn=users,dc=xxxx,dc=sn,dc=mynetname,dc=net","userSearchBase":"dc=xxxx,dc=sn,dc=mynetname,dc=net"}},{"name":"github","mappingMethod":"mixed","disableLoginConfirmation":false,"type":"GitHubIdentityProvider","provider":{"clientID":"xxxxxx","endpoint":{"authURL":"https://github.com/login/oauth/authorize","tokenURL":"https://github.com/login/oauth/access_token"},"redirectURL":"https://ks-console/oauth/redirect","scopes":["user"]}}],"accessTokenMaxAge":3600000000000,"accessTokenInactivityTimeout":1800000000000}`
|
||||||
output, _ := json.Marshal(options)
|
output, _ := json.Marshal(options)
|
||||||
if expected != string(output) {
|
if expected != string(output) {
|
||||||
t.Errorf("expected: %s, but got: %s", expected, output)
|
t.Errorf("expected: %s, but got: %s", expected, output)
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ func (a *EmailValidator) Handle(ctx context.Context, req admission.Request) admi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func emailAlreadyExist(users v1alpha2.UserList, user *v1alpha2.User) bool {
|
func emailAlreadyExist(users v1alpha2.UserList, user *v1alpha2.User) bool {
|
||||||
|
// empty email is allowed
|
||||||
|
if user.Spec.Email == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
for _, exist := range users.Items {
|
for _, exist := range users.Items {
|
||||||
if exist.Spec.Email == user.Spec.Email && exist.Name != user.Name {
|
if exist.Spec.Email == user.Spec.Email && exist.Name != user.Name {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -19,8 +19,12 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
@@ -113,17 +117,27 @@ func (p *passwordAuthenticator) Authenticate(username, password string) (authuse
|
|||||||
return nil, providerOptions.Name, err
|
return nil, providerOptions.Name, err
|
||||||
}
|
}
|
||||||
linkedAccount, err := p.userGetter.findLinkedAccount(providerOptions.Name, authenticated.GetUserID())
|
linkedAccount, err := p.userGetter.findLinkedAccount(providerOptions.Name, authenticated.GetUserID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, providerOptions.Name, err
|
||||||
|
}
|
||||||
// using this method requires you to manually provision users.
|
// using this method requires you to manually provision users.
|
||||||
if providerOptions.MappingMethod == oauth.MappingMethodLookup && linkedAccount == nil {
|
if providerOptions.MappingMethod == oauth.MappingMethodLookup && linkedAccount == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// the user will automatically create and mapping when login successful.
|
||||||
|
if linkedAccount == nil && providerOptions.MappingMethod == oauth.MappingMethodAuto {
|
||||||
|
if !providerOptions.DisableLoginConfirmation {
|
||||||
|
return preRegistrationUser(providerOptions.Name, authenticated), providerOptions.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
linkedAccount, err = p.ksClient.IamV1alpha2().Users().Create(context.Background(), mappedUser(providerOptions.Name, authenticated), metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, providerOptions.Name, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if linkedAccount != nil {
|
if linkedAccount != nil {
|
||||||
return &authuser.DefaultInfo{Name: linkedAccount.GetName()}, providerOptions.Name, nil
|
return &authuser.DefaultInfo{Name: linkedAccount.GetName()}, providerOptions.Name, nil
|
||||||
}
|
}
|
||||||
// the user will automatically create and mapping when login successful.
|
|
||||||
if providerOptions.MappingMethod == oauth.MappingMethodAuto {
|
|
||||||
return preRegistrationUser(providerOptions.Name, authenticated), providerOptions.Name, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +197,22 @@ func preRegistrationUser(idp string, identity identityprovider.Identity) authuse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o oauth2Authenticator) Authenticate(provider, code string) (authuser.Info, string, error) {
|
func mappedUser(idp string, identity identityprovider.Identity) *iamv1alpha2.User {
|
||||||
|
// username convert
|
||||||
|
username := strings.ToLower(identity.GetUsername())
|
||||||
|
return &iamv1alpha2.User{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: username,
|
||||||
|
Labels: map[string]string{
|
||||||
|
iamv1alpha2.IdentifyProviderLabel: idp,
|
||||||
|
iamv1alpha2.OriginUIDLabel: identity.GetUserID(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.UserSpec{Email: identity.GetEmail()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *oauth2Authenticator) Authenticate(provider, code string) (authuser.Info, string, error) {
|
||||||
providerOptions, err := o.authOptions.OAuthOptions.IdentityProviderOptions(provider)
|
providerOptions, err := o.authOptions.OAuthOptions.IdentityProviderOptions(provider)
|
||||||
// identity provider not registered
|
// identity provider not registered
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -206,10 +235,18 @@ func (o oauth2Authenticator) Authenticate(provider, code string) (authuser.Info,
|
|||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// the user will automatically create and mapping when login successful.
|
// the user will automatically create and mapping when login successful.
|
||||||
if user == nil && providerOptions.MappingMethod == oauth.MappingMethodAuto {
|
if user == nil && providerOptions.MappingMethod == oauth.MappingMethodAuto {
|
||||||
|
if !providerOptions.DisableLoginConfirmation {
|
||||||
return preRegistrationUser(providerOptions.Name, authenticated), providerOptions.Name, nil
|
return preRegistrationUser(providerOptions.Name, authenticated), providerOptions.Name, nil
|
||||||
}
|
}
|
||||||
|
user, err = o.ksClient.IamV1alpha2().Users().Create(context.Background(), mappedUser(providerOptions.Name, authenticated), metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, providerOptions.Name, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if user != nil {
|
if user != nil {
|
||||||
return &authuser.DefaultInfo{Name: user.GetName()}, providerOptions.Name, nil
|
return &authuser.DefaultInfo{Name: user.GetName()}, providerOptions.Name, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user