Files
kubesphere/pkg/models/auth/password.go
2025-02-28 16:48:36 +08:00

118 lines
3.8 KiB
Go

/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package auth
import (
"context"
"fmt"
"golang.org/x/crypto/bcrypt"
"k8s.io/apimachinery/pkg/api/errors"
authuser "k8s.io/apiserver/pkg/authentication/user"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/apiserver/authentication"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
)
type passwordAuthenticator struct {
userMapper UserMapper
client runtimeclient.Client
authOptions *authentication.Options
identityProviderConfigurationGetter identityprovider.ConfigurationGetter
}
func NewPasswordAuthenticator(cacheClient runtimeclient.Client, options *authentication.Options) PasswordAuthenticator {
passwordAuthenticator := &passwordAuthenticator{
client: cacheClient,
userMapper: &userMapper{cache: cacheClient},
identityProviderConfigurationGetter: identityprovider.NewConfigurationGetter(cacheClient),
authOptions: options,
}
return passwordAuthenticator
}
func (p *passwordAuthenticator) Authenticate(ctx context.Context, provider, username, password string) (authuser.Info, error) {
// empty username or password are not allowed
if username == "" || password == "" {
return nil, IncorrectPasswordError
}
if provider != "" {
return p.authByProvider(ctx, provider, username, password)
}
return p.authByKubeSphere(ctx, username, password)
}
// authByKubeSphere authenticate by the kubesphere user
func (p *passwordAuthenticator) authByKubeSphere(ctx context.Context, username, password string) (authuser.Info, error) {
user, err := p.userMapper.Find(ctx, username)
if err != nil {
return nil, fmt.Errorf("failed to find user: %s", err)
}
if user.Name == "" {
return nil, IncorrectPasswordError
}
switch user.Status.State {
case iamv1beta1.UserAuthLimitExceeded:
return nil, RateLimitExceededError
case iamv1beta1.UserActive:
break
default:
return nil, AccountIsNotActiveError
}
if user.Spec.EncryptedPassword == "" || PasswordVerify(user.Spec.EncryptedPassword, password) != nil {
return nil, IncorrectPasswordError
}
info := &authuser.DefaultInfo{
Name: user.Name,
Groups: user.Spec.Groups,
}
// check if the password is initialized
if uninitialized := user.Annotations[iamv1beta1.UninitializedAnnotation]; uninitialized != "" {
info.Extra = map[string][]string{
iamv1beta1.ExtraUninitialized: {uninitialized},
}
}
return info, nil
}
// authByProvider authenticate by the third-party identity provider user
func (p *passwordAuthenticator) authByProvider(ctx context.Context, provider, username, password string) (authuser.Info, error) {
genericProvider, exist := identityprovider.SharedIdentityProviderController.GetGenericProvider(provider)
if !exist {
return nil, fmt.Errorf("generic identity provider %s not found", provider)
}
providerConfig, err := p.identityProviderConfigurationGetter.GetConfiguration(ctx, provider)
if err != nil {
return nil, fmt.Errorf("failed to get identity provider configuration: %s", err)
}
identity, err := genericProvider.Authenticate(username, password)
if err != nil {
if errors.IsUnauthorized(err) {
return nil, IncorrectPasswordError
}
return nil, fmt.Errorf("failed to authenticate by identity provider %s: %s", provider, err)
}
return authByIdentityProvider(ctx, p.client, p.userMapper, providerConfig, identity)
}
func PasswordVerify(encryptedPassword, password string) error {
if err := bcrypt.CompareHashAndPassword([]byte(encryptedPassword), []byte(password)); err != nil {
return IncorrectPasswordError
}
return nil
}