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:
zryfish
2020-07-23 22:10:39 +08:00
committed by GitHub
parent 50a6c7b2b5
commit 3d74bb0589
51 changed files with 2163 additions and 548 deletions

View File

@@ -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 {