login record CRD (#2565)
* Signed-off-by: hongming <talonwan@yunify.com> support ldap identity provider Signed-off-by: hongming <talonwan@yunify.com> * add login record Signed-off-by: Jeff <zw0948@gmail.com> Co-authored-by: hongming <talonwan@yunify.com>
This commit is contained in:
@@ -16,7 +16,6 @@ limitations under the License.
|
||||
package im
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
@@ -24,11 +23,9 @@ import (
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
|
||||
"net/mail"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IdentityManagementInterface interface {
|
||||
@@ -37,34 +34,24 @@ type IdentityManagementInterface interface {
|
||||
DeleteUser(username string) error
|
||||
UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error)
|
||||
DescribeUser(username string) (*iamv1alpha2.User, error)
|
||||
Authenticate(username, password string) (*iamv1alpha2.User, error)
|
||||
ModifyPassword(username string, password string) error
|
||||
ListLoginRecords(query *query.Query) (*api.ListResult, error)
|
||||
PasswordVerify(username string, password string) error
|
||||
}
|
||||
|
||||
var (
|
||||
AuthRateLimitExceeded = errors.New("user auth rate limit exceeded")
|
||||
AuthFailedIncorrectPassword = errors.New("incorrect password")
|
||||
UserAlreadyExists = errors.New("user already exists")
|
||||
UserNotExists = errors.New("user not exists")
|
||||
)
|
||||
|
||||
func NewOperator(ksClient kubesphereclient.Interface, factory informers.InformerFactory, options *authoptions.AuthenticationOptions) IdentityManagementInterface {
|
||||
func NewOperator(ksClient kubesphere.Interface, factory informers.InformerFactory, options *authoptions.AuthenticationOptions) IdentityManagementInterface {
|
||||
im := &defaultIMOperator{
|
||||
ksClient: ksClient,
|
||||
resourceGetter: resourcev1alpha3.NewResourceGetter(factory),
|
||||
}
|
||||
if options != nil {
|
||||
im.authenticateRateLimiterDuration = options.AuthenticateRateLimiterDuration
|
||||
im.authenticateRateLimiterMaxTries = options.AuthenticateRateLimiterMaxTries
|
||||
options: options,
|
||||
}
|
||||
return im
|
||||
}
|
||||
|
||||
type defaultIMOperator struct {
|
||||
ksClient kubesphereclient.Interface
|
||||
resourceGetter *resourcev1alpha3.ResourceGetter
|
||||
authenticateRateLimiterMaxTries int
|
||||
authenticateRateLimiterDuration time.Duration
|
||||
ksClient kubesphere.Interface
|
||||
resourceGetter *resourcev1alpha3.ResourceGetter
|
||||
options *authoptions.AuthenticationOptions
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
||||
@@ -110,50 +97,6 @@ func (im *defaultIMOperator) ModifyPassword(username string, password string) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) {
|
||||
|
||||
var user *iamv1alpha2.User
|
||||
|
||||
if _, err := mail.ParseAddress(username); err != nil {
|
||||
obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
user = obj.(*iamv1alpha2.User)
|
||||
} else {
|
||||
objs, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", &query.Query{
|
||||
Pagination: query.NoPagination,
|
||||
Filters: map[query.Field]query.Value{iamv1alpha2.FieldEmail: query.Value(username)},
|
||||
})
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
if len(objs.Items) != 1 {
|
||||
if len(objs.Items) == 0 {
|
||||
klog.Warningf("username or email: %s not exist", username)
|
||||
} else {
|
||||
klog.Errorf("duplicate user entries: %+v", objs)
|
||||
}
|
||||
return nil, AuthFailedIncorrectPassword
|
||||
}
|
||||
user = objs.Items[0].(*iamv1alpha2.User)
|
||||
}
|
||||
|
||||
if im.authRateLimitExceeded(user) {
|
||||
im.authFailRecord(user, AuthRateLimitExceeded)
|
||||
return nil, AuthRateLimitExceeded
|
||||
}
|
||||
|
||||
if checkPasswordHash(password, user.Spec.EncryptedPassword) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
im.authFailRecord(user, AuthFailedIncorrectPassword)
|
||||
return nil, AuthFailedIncorrectPassword
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) ListUsers(query *query.Query) (result *api.ListResult, err error) {
|
||||
result, err = im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query)
|
||||
if err != nil {
|
||||
@@ -171,6 +114,19 @@ func (im *defaultIMOperator) ListUsers(query *query.Query) (result *api.ListResu
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) PasswordVerify(username string, password string) error {
|
||||
obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
user := obj.(*iamv1alpha2.User)
|
||||
if checkPasswordHash(password, user.Spec.EncryptedPassword) {
|
||||
return nil
|
||||
}
|
||||
return AuthFailedIncorrectPassword
|
||||
}
|
||||
|
||||
func checkPasswordHash(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
@@ -200,27 +156,13 @@ func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.Us
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) authRateLimitEnabled() bool {
|
||||
if im.authenticateRateLimiterMaxTries <= 0 || im.authenticateRateLimiterDuration == 0 {
|
||||
return false
|
||||
func (im *defaultIMOperator) ListLoginRecords(query *query.Query) (*api.ListResult, error) {
|
||||
result, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralLoginRecord, "", query)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) authRateLimitExceeded(user *iamv1alpha2.User) bool {
|
||||
if !im.authRateLimitEnabled() {
|
||||
return false
|
||||
}
|
||||
// TODO record login history using CRD
|
||||
return false
|
||||
}
|
||||
|
||||
func (im *defaultIMOperator) authFailRecord(user *iamv1alpha2.User, err error) {
|
||||
if !im.authRateLimitEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO record login history using CRD
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ensurePasswordNotOutput(user *iamv1alpha2.User) *iamv1alpha2.User {
|
||||
|
||||
Reference in New Issue
Block a user