113 lines
4.2 KiB
Go
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
|
|
}
|