mirror of
https://github.com/hanzozt/zt.git
synced 2026-10-30 15:04:55 +00:00
325 lines
7.6 KiB
Go
325 lines
7.6 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 common
|
|
|
|
import (
|
|
"github.com/michaelquigley/pfxlog"
|
|
"github.com/openziti/foundation/v2/concurrenz"
|
|
"sync"
|
|
)
|
|
|
|
type IdentityConfig struct {
|
|
Config *Config
|
|
ConfigType *ConfigType
|
|
}
|
|
|
|
type IdentityService struct {
|
|
Service *Service
|
|
Checks map[string]struct{}
|
|
Configs map[string]*IdentityConfig
|
|
DialAllowed bool
|
|
BindAllowed bool
|
|
}
|
|
|
|
func (self *IdentityService) Equals(other *IdentityService) bool {
|
|
if self.Service.Index != other.Service.Index {
|
|
return false
|
|
}
|
|
|
|
if len(self.Checks) != len(other.Checks) {
|
|
return false
|
|
}
|
|
|
|
if len(self.Configs) != len(other.Configs) {
|
|
return false
|
|
}
|
|
|
|
if self.DialAllowed != other.DialAllowed {
|
|
return false
|
|
}
|
|
|
|
if self.BindAllowed != other.BindAllowed {
|
|
return false
|
|
}
|
|
|
|
for id := range self.Checks {
|
|
if _, ok := other.Checks[id]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for id, config := range self.Configs {
|
|
otherConfig, ok := other.Configs[id]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if config.Config.Index != otherConfig.Config.Index {
|
|
return false
|
|
}
|
|
if config.ConfigType.Index != otherConfig.ConfigType.Index {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type IdentitySubscription struct {
|
|
IdentityId string
|
|
Identity *Identity
|
|
Services map[string]*IdentityService
|
|
Listeners concurrenz.CopyOnWriteSlice[IdentityEventSubscriber]
|
|
Checks map[string]*PostureCheck
|
|
|
|
sync.Mutex
|
|
}
|
|
|
|
func (self *IdentitySubscription) getState() *IdentityState {
|
|
return &IdentityState{
|
|
Identity: self.Identity,
|
|
PostureChecks: self.Checks,
|
|
Services: self.Services,
|
|
}
|
|
}
|
|
|
|
func (self *IdentitySubscription) identityUpdated(rdm *RouterDataModel, identity *Identity) {
|
|
notify := false
|
|
present := false
|
|
var state *IdentityState
|
|
self.Lock()
|
|
if self.Identity != nil {
|
|
if identity.IdentityIndex > self.Identity.IdentityIndex {
|
|
self.Identity = identity
|
|
notify = true
|
|
}
|
|
present = true
|
|
state = self.getState()
|
|
}
|
|
self.Unlock()
|
|
|
|
if !present {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventFullState)
|
|
}
|
|
} else if notify {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventIdentityUpdated)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (self *IdentitySubscription) identityRemoved() {
|
|
notify := false
|
|
self.Lock()
|
|
var state *IdentityState
|
|
if self.Identity != nil {
|
|
state = self.getState()
|
|
self.Identity = nil
|
|
self.Checks = nil
|
|
self.Services = nil
|
|
notify = true
|
|
}
|
|
self.Unlock()
|
|
|
|
if notify {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventIdentityDeleted)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (self *IdentitySubscription) initialize(rdm *RouterDataModel, identity *Identity) *IdentityState {
|
|
self.Lock()
|
|
defer self.Unlock()
|
|
if self.Identity == nil {
|
|
self.Identity = identity
|
|
if self.Services == nil {
|
|
self.Services, self.Checks = rdm.buildServiceList(self)
|
|
}
|
|
}
|
|
return self.getState()
|
|
}
|
|
|
|
func (self *IdentitySubscription) checkForChanges(rdm *RouterDataModel) {
|
|
self.Lock()
|
|
newIdentity, ok := rdm.Identities.Get(self.IdentityId)
|
|
notifyRemoved := !ok && self.Identity != nil
|
|
oldIdentity := self.Identity
|
|
oldServices := self.Services
|
|
oldChecks := self.Checks
|
|
self.Identity = newIdentity
|
|
if ok {
|
|
self.Services, self.Checks = rdm.buildServiceList(self)
|
|
}
|
|
newServices := self.Services
|
|
newChecks := self.Checks
|
|
self.Unlock()
|
|
|
|
if notifyRemoved {
|
|
state := &IdentityState{
|
|
Identity: oldIdentity,
|
|
PostureChecks: oldChecks,
|
|
Services: oldServices,
|
|
}
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventIdentityDeleted)
|
|
}
|
|
return
|
|
}
|
|
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
state := &IdentityState{
|
|
Identity: newIdentity,
|
|
PostureChecks: newChecks,
|
|
Services: newServices,
|
|
}
|
|
|
|
if oldIdentity == nil {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventFullState)
|
|
}
|
|
return
|
|
}
|
|
|
|
if oldIdentity.IdentityIndex < newIdentity.IdentityIndex {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventIdentityUpdated)
|
|
}
|
|
}
|
|
|
|
for svcId, service := range oldServices {
|
|
newService, ok := newServices[svcId]
|
|
if !ok {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyServiceChange(state, service, EventAccessRemoved)
|
|
}
|
|
} else if !service.Equals(newService) {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyServiceChange(state, newService, EventUpdated)
|
|
}
|
|
}
|
|
}
|
|
|
|
for svcId, service := range newServices {
|
|
if _, ok := oldServices[svcId]; !ok {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyServiceChange(state, service, EventAccessGained)
|
|
}
|
|
}
|
|
}
|
|
|
|
checksChanged := false
|
|
if len(oldChecks) != len(newChecks) {
|
|
checksChanged = true
|
|
} else {
|
|
for checkId, check := range oldChecks {
|
|
newCheck, ok := newChecks[checkId]
|
|
if !ok {
|
|
checksChanged = true
|
|
break
|
|
}
|
|
if check.Index != newCheck.Index {
|
|
checksChanged = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if checksChanged {
|
|
for _, subscriber := range self.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventPostureChecksUpdated)
|
|
}
|
|
}
|
|
}
|
|
|
|
type IdentityEventType byte
|
|
|
|
type ServiceEventType byte
|
|
|
|
const (
|
|
EventAccessGained ServiceEventType = 1
|
|
EventUpdated ServiceEventType = 2
|
|
EventAccessRemoved ServiceEventType = 3
|
|
|
|
EventFullState IdentityEventType = 4
|
|
EventIdentityUpdated IdentityEventType = 5
|
|
EventPostureChecksUpdated IdentityEventType = 6
|
|
EventIdentityDeleted IdentityEventType = 7
|
|
)
|
|
|
|
type IdentityState struct {
|
|
Identity *Identity
|
|
PostureChecks map[string]*PostureCheck
|
|
Services map[string]*IdentityService
|
|
}
|
|
|
|
type IdentityEventSubscriber interface {
|
|
NotifyIdentityEvent(state *IdentityState, eventType IdentityEventType)
|
|
NotifyServiceChange(state *IdentityState, service *IdentityService, eventType ServiceEventType)
|
|
}
|
|
|
|
type subscriberEvent interface {
|
|
process(rdm *RouterDataModel)
|
|
}
|
|
|
|
type identityRemoveEvent struct {
|
|
identityId string
|
|
}
|
|
|
|
func (self identityRemoveEvent) process(rdm *RouterDataModel) {
|
|
if sub, found := rdm.subscriptions.Get(self.identityId); found {
|
|
sub.identityRemoved()
|
|
}
|
|
}
|
|
|
|
type identityCreatedEvent struct {
|
|
identity *Identity
|
|
}
|
|
|
|
func (self identityCreatedEvent) process(rdm *RouterDataModel) {
|
|
pfxlog.Logger().WithField("subs", rdm.subscriptions.Count()).WithField("identityId", self.identity.Id).Info("handling identity created event")
|
|
|
|
if sub, found := rdm.subscriptions.Get(self.identity.Id); found {
|
|
state := sub.initialize(rdm, self.identity)
|
|
for _, subscriber := range sub.Listeners.Value() {
|
|
subscriber.NotifyIdentityEvent(state, EventFullState)
|
|
}
|
|
}
|
|
}
|
|
|
|
type identityUpdatedEvent struct {
|
|
identity *Identity
|
|
}
|
|
|
|
func (self identityUpdatedEvent) process(rdm *RouterDataModel) {
|
|
if sub, found := rdm.subscriptions.Get(self.identity.Id); found {
|
|
sub.identityUpdated(rdm, self.identity)
|
|
}
|
|
}
|
|
|
|
type syncAllSubscribersEvent struct{}
|
|
|
|
func (self syncAllSubscribersEvent) process(rdm *RouterDataModel) {
|
|
pfxlog.Logger().WithField("subs", rdm.subscriptions.Count()).Info("sync all subscribers")
|
|
rdm.subscriptions.IterCb(func(key string, v *IdentitySubscription) {
|
|
v.checkForChanges(rdm)
|
|
})
|
|
}
|