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

113 lines
4.2 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"
"net/http"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
authuser "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog/v2"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
)
var (
RateLimitExceededError = fmt.Errorf("auth rate limit exceeded")
IncorrectPasswordError = fmt.Errorf("incorrect password")
AccountIsNotActiveError = fmt.Errorf("account is not active")
)
// PasswordAuthenticator is an interface implemented by authenticator which take a
// username ,password and provider. provider refers to the identity provider`s name,
// if the provider is empty, authenticate from kubesphere account. Note that implement this
// interface you should also obey the error specification errors.Error defined at package
// "k8s.io/apimachinery/pkg/api", and restful.ServerError defined at package
// "github.com/emicklei/go-restful/v3", or the server cannot handle error correctly.
type PasswordAuthenticator interface {
Authenticate(ctx context.Context, provider, username, password string) (authuser.Info, error)
}
// OAuthAuthenticator authenticate users by OAuth 2.0 Authorization Framework. Note that implement this
// interface you should also obey the error specification errors.Error defined at package
// "k8s.io/apimachinery/pkg/api", and restful.ServerError defined at package
// "github.com/emicklei/go-restful/v3", or the server cannot handle error correctly.
type OAuthAuthenticator interface {
Authenticate(ctx context.Context, provider string, req *http.Request) (authuser.Info, error)
}
func newRreRegistrationUser(idp string, identity identityprovider.Identity) authuser.Info {
return &authuser.DefaultInfo{
Name: iamv1beta1.PreRegistrationUser,
Extra: map[string][]string{
iamv1beta1.ExtraIdentityProvider: {idp},
iamv1beta1.ExtraUID: {identity.GetUserID()},
iamv1beta1.ExtraUsername: {identity.GetUsername()},
iamv1beta1.ExtraEmail: {identity.GetEmail()},
},
}
}
func authByIdentityProvider(ctx context.Context, client client.Client, mapper UserMapper, providerConfig *identityprovider.Configuration, identity identityprovider.Identity) (authuser.Info, error) {
mappedUser, err := mapper.FindMappedUser(ctx, providerConfig.Name, identity.GetUserID())
if err != nil {
return nil, fmt.Errorf("failed to find mapped user: %s", err)
}
if mappedUser.Name == "" {
if providerConfig.MappingMethod == identityprovider.MappingMethodLookup {
return nil, fmt.Errorf("failed to find mapped user: %s", identity.GetUserID())
}
if providerConfig.MappingMethod == identityprovider.MappingMethodManual {
return newRreRegistrationUser(providerConfig.Name, identity), nil
}
if providerConfig.MappingMethod == identityprovider.MappingMethodAuto {
mappedUser := iamv1beta1.User{ObjectMeta: metav1.ObjectMeta{Name: strings.ToLower(identity.GetUsername())}}
op, err := ctrl.CreateOrUpdate(ctx, client, &mappedUser, func() error {
if mappedUser.Status.State == iamv1beta1.UserDisabled {
return AccountIsNotActiveError
}
if mappedUser.Annotations == nil {
mappedUser.Annotations = make(map[string]string)
}
mappedUser.Annotations[fmt.Sprintf("%s.%s", iamv1beta1.IdentityProviderAnnotation, providerConfig.Name)] = identity.GetUserID()
mappedUser.Status.State = iamv1beta1.UserActive
if identity.GetEmail() != "" {
mappedUser.Spec.Email = identity.GetEmail()
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to create or update user %s, error: %v", mappedUser.Name, err)
}
klog.V(4).Infof("user %s has been updated successfully, operation: %s", mappedUser.Name, op)
return &authuser.DefaultInfo{Name: mappedUser.GetName()}, nil
}
return nil, fmt.Errorf("invalid mapping method found %s", providerConfig.MappingMethod)
}
if mappedUser.Status.State == iamv1beta1.UserDisabled {
return nil, AccountIsNotActiveError
}
return &authuser.DefaultInfo{Name: mappedUser.GetName()}, nil
}