mirror of
https://github.com/openziti/ziti.git
synced 2026-10-14 08:10:17 +00:00
447 lines
12 KiB
Go
447 lines
12 KiB
Go
/*
|
|
Copyright NetFoundry, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package model
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/michaelquigley/pfxlog"
|
|
"github.com/openziti/edge/controller/apierror"
|
|
"github.com/openziti/edge/controller/persistence"
|
|
"github.com/openziti/edge/eid"
|
|
"github.com/openziti/fabric/controller/models"
|
|
"github.com/openziti/foundation/metrics"
|
|
"github.com/openziti/foundation/storage/boltz"
|
|
"github.com/openziti/foundation/validation"
|
|
"go.etcd.io/bbolt"
|
|
"time"
|
|
)
|
|
|
|
type IdentityHandler struct {
|
|
baseHandler
|
|
allowedFieldsChecker boltz.FieldChecker
|
|
updateSdkInfoTimer metrics.Timer
|
|
}
|
|
|
|
func NewIdentityHandler(env Env) *IdentityHandler {
|
|
handler := &IdentityHandler{
|
|
baseHandler: newBaseHandler(env, env.GetStores().Identity),
|
|
allowedFieldsChecker: boltz.MapFieldChecker{
|
|
persistence.FieldName: struct{}{},
|
|
persistence.FieldIdentityIsDefaultAdmin: struct{}{},
|
|
persistence.FieldIdentityIsAdmin: struct{}{},
|
|
persistence.FieldIdentityType: struct{}{},
|
|
boltz.FieldTags: struct{}{},
|
|
},
|
|
updateSdkInfoTimer: env.GetMetricsRegistry().Timer("identity.update-sdk-info"),
|
|
}
|
|
handler.impl = handler
|
|
return handler
|
|
}
|
|
|
|
func (handler IdentityHandler) newModelEntity() boltEntitySink {
|
|
return &Identity{}
|
|
}
|
|
|
|
func (handler *IdentityHandler) Create(identityModel *Identity) (string, error) {
|
|
identityType, err := handler.env.GetHandlers().IdentityType.ReadByIdOrName(identityModel.IdentityTypeId)
|
|
|
|
if err != nil && !boltz.IsErrNotFoundErr(err) {
|
|
return "", err
|
|
}
|
|
|
|
if identityType == nil {
|
|
apiErr := apierror.NewNotFound()
|
|
apiErr.Cause = validation.NewFieldError("typeId not found", "typeId", identityModel.IdentityTypeId)
|
|
apiErr.AppendCause = true
|
|
return "", apiErr
|
|
}
|
|
|
|
identityModel.IdentityTypeId = identityType.Id
|
|
|
|
return handler.createEntity(identityModel)
|
|
}
|
|
|
|
func (handler *IdentityHandler) CreateWithEnrollments(identityModel *Identity, enrollmentsModels []*Enrollment) (string, []string, error) {
|
|
identityType, err := handler.env.GetHandlers().IdentityType.ReadByIdOrName(identityModel.IdentityTypeId)
|
|
|
|
if err != nil && !boltz.IsErrNotFoundErr(err) {
|
|
return "", nil, err
|
|
}
|
|
|
|
if identityType == nil {
|
|
apiErr := apierror.NewNotFound()
|
|
apiErr.Cause = validation.NewFieldError("identityTypeId not found", "identityTypeId", identityModel.IdentityTypeId)
|
|
apiErr.AppendCause = true
|
|
return "", nil, apiErr
|
|
}
|
|
|
|
identityModel.IdentityTypeId = identityType.Id
|
|
|
|
if identityModel.Id == "" {
|
|
identityModel.Id = eid.New()
|
|
}
|
|
var enrollmentIds []string
|
|
|
|
err = handler.GetDb().Update(func(tx *bbolt.Tx) error {
|
|
ctx := boltz.NewMutateContext(tx)
|
|
boltEntity, err := identityModel.toBoltEntityForCreate(tx, handler.impl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := handler.GetStore().Create(ctx, boltEntity); err != nil {
|
|
pfxlog.Logger().WithError(err).Errorf("could not create %v in bolt storage", handler.GetStore().GetSingularEntityType())
|
|
return err
|
|
}
|
|
|
|
for _, enrollmentModel := range enrollmentsModels {
|
|
enrollmentModel.IdentityId = &identityModel.Id
|
|
|
|
err := enrollmentModel.FillJwtInfo(handler.env, identityModel.Id)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
enrollmentId, err := handler.env.GetHandlers().Enrollment.createEntityInTx(ctx, enrollmentModel)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
enrollmentIds = append(enrollmentIds, enrollmentId)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
return identityModel.Id, enrollmentIds, nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) Update(identity *Identity) error {
|
|
identityType, err := handler.env.GetHandlers().IdentityType.ReadByIdOrName(identity.IdentityTypeId)
|
|
|
|
if err != nil && !boltz.IsErrNotFoundErr(err) {
|
|
return err
|
|
}
|
|
|
|
if identityType == nil {
|
|
apiErr := apierror.NewNotFound()
|
|
apiErr.Cause = validation.NewFieldError("identityTypeId not found", "identityTypeId", identity.IdentityTypeId)
|
|
apiErr.AppendCause = true
|
|
return apiErr
|
|
}
|
|
|
|
identity.IdentityTypeId = identityType.Id
|
|
|
|
return handler.updateEntity(identity, handler)
|
|
}
|
|
|
|
func (handler *IdentityHandler) Patch(identity *Identity, checker boltz.FieldChecker) error {
|
|
combinedChecker := &AndFieldChecker{first: handler, second: checker}
|
|
if checker.IsUpdated("type") {
|
|
identityType, err := handler.env.GetHandlers().IdentityType.ReadByIdOrName(identity.IdentityTypeId)
|
|
if err != nil && !boltz.IsErrNotFoundErr(err) {
|
|
return err
|
|
}
|
|
|
|
if identityType == nil {
|
|
apiErr := apierror.NewNotFound()
|
|
apiErr.Cause = validation.NewFieldError("identityTypeId not found", "identityTypeId", identity.IdentityTypeId)
|
|
apiErr.AppendCause = true
|
|
return apiErr
|
|
}
|
|
|
|
identity.IdentityTypeId = identityType.Id
|
|
}
|
|
|
|
return handler.patchEntity(identity, combinedChecker)
|
|
}
|
|
|
|
func (handler *IdentityHandler) Delete(id string) error {
|
|
identity, err := handler.Read(id)
|
|
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
if identity.IsDefaultAdmin {
|
|
return apierror.NewEntityCanNotBeDeleted()
|
|
}
|
|
|
|
return handler.deleteEntity(id)
|
|
}
|
|
|
|
func (handler IdentityHandler) IsUpdated(field string) bool {
|
|
return field != persistence.FieldIdentityAuthenticators && field != persistence.FieldIdentityEnrollments && field != persistence.FieldIdentityIsDefaultAdmin
|
|
}
|
|
|
|
func (handler *IdentityHandler) Read(id string) (*Identity, error) {
|
|
entity := &Identity{}
|
|
if err := handler.readEntity(id, entity); err != nil {
|
|
return nil, err
|
|
}
|
|
return entity, nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) readInTx(tx *bbolt.Tx, id string) (*Identity, error) {
|
|
identity := &Identity{}
|
|
if err := handler.readEntityInTx(tx, id, identity); err != nil {
|
|
return nil, err
|
|
}
|
|
return identity, nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) ReadDefaultAdmin() (*Identity, error) {
|
|
return handler.ReadOneByQuery("isDefaultAdmin = true")
|
|
}
|
|
|
|
func (handler *IdentityHandler) ReadOneByQuery(query string) (*Identity, error) {
|
|
result, err := handler.readEntityByQuery(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result == nil {
|
|
return nil, nil
|
|
}
|
|
return result.(*Identity), nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) InitializeDefaultAdmin(username, password, name string) error {
|
|
identity, err := handler.ReadDefaultAdmin()
|
|
|
|
if err != nil && !boltz.IsErrNotFoundErr(err) {
|
|
pfxlog.Logger().Panic(err)
|
|
}
|
|
|
|
if identity != nil {
|
|
return errors.New("already initialized: Ziti Edge default admin already defined")
|
|
}
|
|
|
|
identityType, err := handler.env.GetHandlers().IdentityType.ReadByName(IdentityTypeUser)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
identityId := eid.New()
|
|
authenticatorId := eid.New()
|
|
|
|
defaultAdmin := &Identity{
|
|
BaseEntity: models.BaseEntity{
|
|
Id: identityId,
|
|
},
|
|
Name: name,
|
|
IdentityTypeId: identityType.Id,
|
|
IsDefaultAdmin: true,
|
|
IsAdmin: true,
|
|
}
|
|
|
|
authenticator := &Authenticator{
|
|
BaseEntity: models.BaseEntity{
|
|
Id: authenticatorId,
|
|
},
|
|
Method: persistence.MethodAuthenticatorUpdb,
|
|
IdentityId: identityId,
|
|
SubType: &AuthenticatorUpdb{
|
|
Username: username,
|
|
Password: password,
|
|
},
|
|
}
|
|
|
|
if _, err := handler.Create(defaultAdmin); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := handler.env.GetHandlers().Authenticator.Create(authenticator); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) CollectAuthenticators(id string, collector func(entity *Authenticator) error) error {
|
|
return handler.GetDb().View(func(tx *bbolt.Tx) error {
|
|
_, err := handler.readInTx(tx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
authenticatorIds := handler.GetStore().GetRelatedEntitiesIdList(tx, id, persistence.FieldIdentityAuthenticators)
|
|
for _, authenticatorId := range authenticatorIds {
|
|
authenticator := &Authenticator{}
|
|
err := handler.env.GetHandlers().Authenticator.readEntityInTx(tx, authenticatorId, authenticator)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = collector(authenticator); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (handler *IdentityHandler) CollectEnrollments(id string, collector func(entity *Enrollment) error) error {
|
|
return handler.GetDb().View(func(tx *bbolt.Tx) error {
|
|
return handler.collectEnrollmentsInTx(tx, id, collector)
|
|
})
|
|
}
|
|
|
|
func (handler *IdentityHandler) collectEnrollmentsInTx(tx *bbolt.Tx, id string, collector func(entity *Enrollment) error) error {
|
|
_, err := handler.readInTx(tx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
associationIds := handler.GetStore().GetRelatedEntitiesIdList(tx, id, persistence.FieldIdentityEnrollments)
|
|
for _, enrollmentId := range associationIds {
|
|
enrollment, err := handler.env.GetHandlers().Enrollment.readInTx(tx, enrollmentId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = collector(enrollment)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) CreateWithAuthenticator(identity *Identity, authenticator *Authenticator) (string, string, error) {
|
|
if identity.Id == "" {
|
|
identity.Id = eid.New()
|
|
}
|
|
|
|
if authenticator.Id == "" {
|
|
authenticator.Id = eid.New()
|
|
}
|
|
|
|
if authenticator.IdentityId == "" {
|
|
authenticator.IdentityId = identity.Id
|
|
}
|
|
|
|
identityType, err := handler.env.GetHandlers().IdentityType.ReadByIdOrName(identity.IdentityTypeId)
|
|
|
|
if err != nil && !boltz.IsErrNotFoundErr(err) {
|
|
return "", "", err
|
|
}
|
|
|
|
if identityType == nil {
|
|
apiErr := apierror.NewNotFound()
|
|
apiErr.Cause = validation.NewFieldError("typeId not found", "typeId", identity.IdentityTypeId)
|
|
apiErr.AppendCause = true
|
|
return "", "", apiErr
|
|
}
|
|
|
|
err = handler.env.GetDbProvider().GetDb().Update(func(tx *bbolt.Tx) error {
|
|
ctx := boltz.NewMutateContext(tx)
|
|
boltIdentity, err := identity.toBoltEntityForCreate(tx, handler)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = handler.env.GetStores().Identity.Create(ctx, boltIdentity); err != nil {
|
|
return err
|
|
}
|
|
|
|
boltAuthenticator, err := authenticator.toBoltEntityForCreate(tx, handler.env.GetHandlers().Authenticator)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = handler.env.GetStores().Authenticator.Create(ctx, boltAuthenticator); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
return identity.Id, authenticator.Id, nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) GetServiceConfigs(id string) ([]ServiceConfig, error) {
|
|
var result []ServiceConfig
|
|
err := handler.GetDb().Update(func(tx *bbolt.Tx) error {
|
|
configs, err := handler.env.GetStores().Identity.GetServiceConfigs(tx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, config := range configs {
|
|
result = append(result, ServiceConfig{Service: config.ServiceId, Config: config.ConfigId})
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (handler *IdentityHandler) AssignServiceConfigs(id string, serviceConfigs []ServiceConfig) error {
|
|
return handler.GetDb().Update(func(tx *bbolt.Tx) error {
|
|
boltServiceConfigs, err := toBoltServiceConfigs(tx, handler, serviceConfigs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return handler.env.GetStores().Identity.AssignServiceConfigs(tx, id, boltServiceConfigs...)
|
|
})
|
|
}
|
|
|
|
func (handler *IdentityHandler) RemoveServiceConfigs(id string, serviceConfigs []ServiceConfig) error {
|
|
return handler.GetDb().Update(func(tx *bbolt.Tx) error {
|
|
boltServiceConfigs, err := toBoltServiceConfigs(tx, handler, serviceConfigs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return handler.env.GetStores().Identity.RemoveServiceConfigs(tx, id, boltServiceConfigs...)
|
|
})
|
|
}
|
|
|
|
func (handler *IdentityHandler) QueryRoleAttributes(queryString string) ([]string, *models.QueryMetaData, error) {
|
|
index := handler.env.GetStores().Identity.GetRoleAttributesIndex()
|
|
return handler.queryRoleAttributes(index, queryString)
|
|
}
|
|
|
|
func (handler IdentityHandler) PatchInfo(identity *Identity) error {
|
|
start := time.Now()
|
|
checker := boltz.MapFieldChecker{
|
|
persistence.FieldIdentityEnvInfoArch: struct{}{},
|
|
persistence.FieldIdentityEnvInfoOs: struct{}{},
|
|
persistence.FieldIdentityEnvInfoOsRelease: struct{}{},
|
|
persistence.FieldIdentityEnvInfoOsVersion: struct{}{},
|
|
persistence.FieldIdentitySdkInfoBranch: struct{}{},
|
|
persistence.FieldIdentitySdkInfoRevision: struct{}{},
|
|
persistence.FieldIdentitySdkInfoType: struct{}{},
|
|
persistence.FieldIdentitySdkInfoVersion: struct{}{},
|
|
}
|
|
|
|
err := handler.patchEntityBatch(identity, checker)
|
|
|
|
handler.updateSdkInfoTimer.UpdateSince(start)
|
|
|
|
return err
|
|
}
|