Merge pull request #762 from openziti/fix.posture.timeouts

fixes posture check timeouts, tests, service updates
This commit is contained in:
Andrew
2021-09-02 11:04:12 -04:00
committed by GitHub
42 changed files with 1720 additions and 375 deletions
+3 -4
View File
@@ -251,12 +251,11 @@ func (ae *AppEnv) FillRequestContext(rc *response.RequestContext) error {
}
}
//updates for session timeouts
if rc.ApiSession != nil {
ae.GetHandlers().ApiSession.MarkActivityById(rc.ApiSession.Id)
}
if rc.ApiSession != nil {
//updates for api session timeouts
ae.GetHandlers().ApiSession.MarkActivityById(rc.ApiSession.Id)
var err error
rc.Identity, err = ae.GetHandlers().Identity.Read(rc.ApiSession.IdentityId)
if err != nil {
@@ -180,6 +180,7 @@ func (self *baseTunnelRequestContext) ensureSessionForService(sessionId, session
Token: uuid.NewString(),
ApiSessionId: self.apiSession.Id,
ServiceId: self.service.Id,
IdentityId: self.identity.Id,
Type: sessionType,
}
@@ -57,10 +57,19 @@ func (s *ApiSessionEnforcer) Run() error {
oldest := time.Now().Add(s.sessionTimeout * -1)
query := fmt.Sprintf("lastActivityAt < datetime(%s) limit %d", oldest.UTC().Format(time.RFC3339), maxDeletePerIteration)
log := pfxlog.Logger()
for i := 0; i < maxIterations; i++ {
ids := make([]string, 0, maxDeletePerIteration)
//iterate over API Sessions that do not have a recent enough lastAccessedAt
err := s.appEnv.GetHandlers().ApiSession.StreamIds(query, func(id string, err error) error {
if lastActivityAt, hasUnflushedValue := s.appEnv.GetHandlers().ApiSession.HeartbeatCollector.LastAccessedAt(id); !hasUnflushedValue || lastActivityAt.Before(oldest) {
if lastActivityAt, ok := s.appEnv.GetHandlers().ApiSession.HeartbeatCollector.LastAccessedAt(id); ok && lastActivityAt != nil {
log.Tracef("during API session enforcement lastAccessedAt check, API Session [%s] was found in the cache with time [%s]", id, lastActivityAt.String())
if lastActivityAt.Before(oldest) {
ids = append(ids, id)
}
} else {
log.Tracef("during API session enforcement lastAccessedAt check, API Session [%s] was not in the cache, using lastAccessedAt from db", id)
ids = append(ids, id)
}
@@ -102,7 +102,7 @@ func MapApiSessionToRestModel(ae *env.AppEnv, apiSession *model.ApiSession) (*re
}
if val, ok := ae.GetHandlers().ApiSession.HeartbeatCollector.LastAccessedAt(apiSession.Id); ok {
cachedActivityAt := strfmt.DateTime(val)
cachedActivityAt := strfmt.DateTime(*val)
ret.CachedLastActivityAt = cachedActivityAt
} else {
ret.CachedLastActivityAt = lastActivityAt
@@ -53,19 +53,23 @@ func NewAuthRouter() *AuthRouter {
func (ro *AuthRouter) Register(ae *env.AppEnv) {
ro.createTimer = ae.GetHostController().GetNetwork().GetMetricsRegistry().Timer("api-session.create")
ae.ClientApi.AuthenticationAuthenticateHandler = clientApiAuthentication.AuthenticateHandlerFunc(func(params clientApiAuthentication.AuthenticateParams) middleware.Responder {
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { ro.authHandler(ae, rc, params.HTTPRequest, params.Method, params.Auth) }, params.HTTPRequest, "", "", permissions.Always())
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) {
ro.authHandler(ae, rc, params.HTTPRequest, params.Method, params.Auth)
}, params.HTTPRequest, "", "", permissions.Always())
})
ae.ClientApi.AuthenticationAuthenticateMfaHandler = clientApiAuthentication.AuthenticateMfaHandlerFunc(func(params clientApiAuthentication.AuthenticateMfaParams, i interface{}) middleware.Responder {
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { ro.authMfa(ae, rc, params.MfaAuth) }, params.HTTPRequest, "", "", permissions.IsPartiallyAuthenticated())
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { ro.authMfa(ae, rc, params.MfaAuth) }, params.HTTPRequest, "", "", permissions.HasOneOf(permissions.IsAuthenticated(), permissions.IsPartiallyAuthenticated()))
})
ae.ManagementApi.AuthenticationAuthenticateHandler = managementApiAuthentication.AuthenticateHandlerFunc(func(params managementApiAuthentication.AuthenticateParams) middleware.Responder {
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { ro.authHandler(ae, rc, params.HTTPRequest, params.Method, params.Auth) }, params.HTTPRequest, "", "", permissions.Always())
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) {
ro.authHandler(ae, rc, params.HTTPRequest, params.Method, params.Auth)
}, params.HTTPRequest, "", "", permissions.Always())
})
ae.ManagementApi.AuthenticationAuthenticateMfaHandler = managementApiAuthentication.AuthenticateMfaHandlerFunc(func(params managementApiAuthentication.AuthenticateMfaParams, i interface{}) middleware.Responder {
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { ro.authMfa(ae, rc, params.MfaAuth) }, params.HTTPRequest, "", "", permissions.IsPartiallyAuthenticated())
return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { ro.authMfa(ae, rc, params.MfaAuth) }, params.HTTPRequest, "", "", permissions.HasOneOf(permissions.IsAuthenticated(), permissions.IsPartiallyAuthenticated()))
})
}
@@ -0,0 +1,42 @@
/*
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 routes
import (
"github.com/openziti/edge/controller/env"
"github.com/openziti/edge/controller/response"
"github.com/openziti/edge/rest_model"
"net/http"
)
type PostureResponseResponder struct {
response.Responder
ae *env.AppEnv
services []*rest_model.PostureResponseService
}
func (responder *PostureResponseResponder) RespondWithCreatedId(id string, _ rest_model.Link) {
data := &rest_model.PostureResponse{
Services: responder.services,
}
newSessionEnvelope := &rest_model.PostureResponseEnvelope{
Data: data,
Meta: &rest_model.Meta{},
}
responder.Respond(newSessionEnvelope, http.StatusCreated)
}
@@ -53,19 +53,66 @@ func (r *PostureResponseRouter) Register(ae *env.AppEnv) {
}
func (r *PostureResponseRouter) Create(ae *env.AppEnv, rc *response.RequestContext, params posture_checks.CreatePostureResponseParams) {
Create(rc, rc, PostureResponseLinkFactory, func() (string, error) {
apiPostureResponse := params.PostureResponse
r.handlePostureResponse(ae, rc, apiPostureResponse)
return "", nil
})
bulkParams := posture_checks.CreatePostureResponseBulkParams{
HTTPRequest: params.HTTPRequest,
PostureResponse: []rest_model.PostureResponseCreate{params.PostureResponse},
}
r.CreateBulk(ae, rc, bulkParams)
}
func (r *PostureResponseRouter) CreateBulk(ae *env.AppEnv, rc *response.RequestContext, params posture_checks.CreatePostureResponseBulkParams) {
responder := &PostureResponseResponder{
Responder: rc,
ae: ae,
services: []*rest_model.PostureResponseService{},
}
onWake := false
onUnlock := false
for _, apiPostureResponse := range params.PostureResponse {
if state, ok := apiPostureResponse.(*rest_model.PostureResponseEndpointStateCreate); ok {
if state.Woken {
onWake = true
}
if state.Unlocked {
onUnlock = true
}
}
r.handlePostureResponse(ae, rc, apiPostureResponse)
}
Create(rc, rc, PostureResponseLinkFactory, func() (string, error) {
if onWake || onUnlock {
mfaType := string(rest_model.PostureCheckTypeMFA)
gracePeriod := model.MfaPromptGracePeriod * -1
gracePeriodSeconds := int64(gracePeriod.Seconds())
postureData := ae.Handlers.PostureResponse.PostureData(rc.Identity.Id)
if postureData != nil && postureData.ApiSessions != nil {
if apiPostureData, ok := postureData.ApiSessions[rc.ApiSession.Id]; ok && apiPostureData.Mfa.PassedMfaAt != nil {
//if the last time Mfa was passed at is outside of the grace period, send timeout update
durationSinceLastMfa := time.Now().Sub(*apiPostureData.Mfa.PassedMfaAt)
modelServicesWithTimeouts := ae.Handlers.PostureResponse.GetEndpointStateChangeAffectedServices(durationSinceLastMfa, gracePeriod, onWake, onUnlock)
for _, modelServiceWithTimeout := range modelServicesWithTimeouts {
responder.services = append(responder.services, &rest_model.PostureResponseService{
ID: &modelServiceWithTimeout.Service.Id,
Name: &modelServiceWithTimeout.Service.Name,
PostureQueryType: &mfaType,
TimeoutRemaining: &gracePeriodSeconds,
Timeout: &modelServiceWithTimeout.Timeout,
})
}
}
}
}
CreateWithResponder(rc, responder, PostureResponseLinkFactory, func() (string, error) {
return "", nil
})
}
@@ -48,7 +48,7 @@ func (factory *SessionLinkFactoryImpl) Links(entity models.Entity) rest_model.Li
return links
}
func MapCreateSessionToModel(apiSessionId string, session *rest_model.SessionCreate) *model.Session {
func MapCreateSessionToModel(identityId, apiSessionId string, session *rest_model.SessionCreate) *model.Session {
ret := &model.Session{
BaseEntity: models.BaseEntity{
Tags: TagsOrDefault(session.Tags),
@@ -56,6 +56,7 @@ func MapCreateSessionToModel(apiSessionId string, session *rest_model.SessionCre
Token: uuid.New().String(),
ApiSessionId: apiSessionId,
ServiceId: session.ServiceID,
IdentityId: identityId,
Type: string(session.Type),
SessionCerts: nil,
ServicePolicies: nil,
@@ -126,6 +127,7 @@ func MapSessionToRestModel(ae *env.AppEnv, sessionModel *model.Session) (*rest_m
APISessionID: &apiSession.Id,
Service: ToEntityRef(service.Name, service, ServiceLinkFactory),
ServiceID: &service.Id,
IdentityID: &sessionModel.IdentityId,
EdgeRouters: edgeRouters,
Type: &dialBindType,
Token: &sessionModel.Token,
+1 -1
View File
@@ -126,7 +126,7 @@ func (r *SessionRouter) Create(ae *env.AppEnv, rc *response.RequestContext, para
start := time.Now()
responder := &SessionRequestResponder{ae: ae, Responder: rc}
CreateWithResponder(rc, responder, SessionLinkFactory, func() (string, error) {
return ae.Handlers.Session.Create(MapCreateSessionToModel(rc.ApiSession.Id, params.Session))
return ae.Handlers.Session.Create(MapCreateSessionToModel(rc.Identity.Id, rc.ApiSession.Id, params.Session))
})
r.createTimer.UpdateSince(start)
}
+7 -1
View File
@@ -52,7 +52,13 @@ func (handler *ApiSessionHandler) newModelEntity() boltEntitySink {
func (handler *ApiSessionHandler) Create(entity *ApiSession) (string, error) {
entity.Id = cuid.New() //use cuids which are longer than shortids but are monotonic
return handler.createEntity(entity)
id, err := handler.createEntity(entity)
if err != nil {
handler.MarkActivityById(id)
}
return id, err
}
func (handler *ApiSessionHandler) Read(id string) (*ApiSession, error) {
+27 -24
View File
@@ -26,12 +26,12 @@ import (
)
type HeartbeatCollector struct {
apiSessionLastUpdate cmap.ConcurrentMap //map[apiSessionId string] => *HeartbeatStatus
updateInterval time.Duration
closeNotify chan struct{}
isFlushing concurrenz.AtomicBoolean
flushAction func(beats []*Heartbeat)
batchSize int
apiSessionLastAccessedAtMap cmap.ConcurrentMap //map[apiSessionId string] => *HeartbeatStatus
updateInterval time.Duration
closeNotify chan struct{}
isFlushing concurrenz.AtomicBoolean
flushAction func(beats []*Heartbeat)
batchSize int
}
type Heartbeat struct {
@@ -39,17 +39,17 @@ type Heartbeat struct {
LastActivityAt time.Time
}
// Creates a new HeartbeatCollector which is used to manage situations where an SDK is connecting to multiiple
// Edge Routers and making API calls that all update their last updated at and trigger a write. The heartbeat
// collector aggregates all of those calls into a single write and acts as an in memory buffer for last update
// times.
// NewHeartbeatCollector creates a HeartbeatCollector which is used to manage situations where an SDK is
// connecting to multiple Edge Routers and making API calls that all update their last updated at and trigger
// writes. The heartbeat collector aggregates all of those calls into a single write and acts as an in memory
// buffer for last update times.
func NewHeartbeatCollector(env Env, batchSize int, updateInterval time.Duration, action func([]*Heartbeat)) *HeartbeatCollector {
collector := &HeartbeatCollector{
apiSessionLastUpdate: cmap.New(),
updateInterval: updateInterval,
batchSize: batchSize,
flushAction: action,
closeNotify: make(chan struct{}, 0),
apiSessionLastAccessedAtMap: cmap.New(),
updateInterval: updateInterval,
batchSize: batchSize,
flushAction: action,
closeNotify: make(chan struct{}, 0),
}
env.GetStores().ApiSession.AddListener(boltz.EventDelete, collector.onApiSessionDelete)
@@ -69,17 +69,21 @@ func (self *HeartbeatCollector) Mark(apiSessionId string) {
lastAccessedAt: time.Now().UTC(),
flushed: false,
}
self.apiSessionLastUpdate.Set(apiSessionId, newStatus)
self.apiSessionLastAccessedAtMap.Set(apiSessionId, newStatus)
}
func (self *HeartbeatCollector) LastAccessedAt(apiSessionId string) (time.Time, bool) {
if val, ok := self.apiSessionLastUpdate.Get(apiSessionId); ok {
// LastAccessedAt will return the last time an API Sessions was either connected to an Edge Router
// or made a REST API call and true. If no such action has happened or the API Session no longer exists
// nil and false will be returned.
func (self *HeartbeatCollector) LastAccessedAt(apiSessionId string) (*time.Time, bool) {
if val, ok := self.apiSessionLastAccessedAtMap.Get(apiSessionId); ok {
if status, ok := val.(*HeartbeatStatus); ok {
return status.lastAccessedAt, true
lastAccessedAt := status.lastAccessedAt
return &lastAccessedAt, true
}
}
return time.Time{}, false
return nil, false
}
func (self *HeartbeatCollector) Start() {
@@ -105,11 +109,12 @@ func (self *HeartbeatCollector) Stop() {
func (self *HeartbeatCollector) flush() {
if self.isFlushing.CompareAndSwap(false, true) {
defer self.isFlushing.CompareAndSwap(true, false)
pfxlog.Logger().Trace("flushing heartbeat collector")
var beats []*Heartbeat
self.apiSessionLastUpdate.IterCb(func(key string, v interface{}) {
self.apiSessionLastAccessedAtMap.IterCb(func(key string, v interface{}) {
if len(beats) >= self.batchSize {
self.flushAction(beats)
beats = nil
@@ -130,8 +135,6 @@ func (self *HeartbeatCollector) flush() {
self.flushAction(beats)
}
self.isFlushing.CompareAndSwap(true, false)
} else {
pfxlog.Logger().Warn("attempting to flush heartbeats from collector, a flush is already in progress, skipping")
}
@@ -147,5 +150,5 @@ func (self *HeartbeatCollector) onApiSessionDelete(i ...interface{}) {
}
func (self *HeartbeatCollector) Remove(id string) {
self.apiSessionLastUpdate.Remove(id)
self.apiSessionLastAccessedAtMap.Remove(id)
}
+21 -15
View File
@@ -66,6 +66,10 @@ func (p *PostureCheckMfa) GetTimeoutSeconds() int64 {
}
func (p *PostureCheckMfa) GetTimeoutRemainingSeconds(apiSessionId string, pd *PostureData) int64 {
return p.getTimeoutRemainingAtSeconds(apiSessionId, pd, time.Now())
}
func (p *PostureCheckMfa) getTimeoutRemainingAtSeconds(apiSessionId string, pd *PostureData, now time.Time) int64 {
if p.TimeoutSeconds == PostureCheckNoTimeout {
return PostureCheckNoTimeout
}
@@ -82,7 +86,7 @@ func (p *PostureCheckMfa) GetTimeoutRemainingSeconds(apiSessionId string, pd *Po
return PostureCheckNoTimeout
}
timeSinceLastMfa := time.Now().Sub(*apiSessionData.Mfa.PassedMfaAt)
timeSinceLastMfa := now.Sub(*apiSessionData.Mfa.PassedMfaAt)
timeoutRemaining := p.TimeoutSeconds - int64(timeSinceLastMfa.Seconds())
if timeoutRemaining <= 0 {
@@ -90,7 +94,7 @@ func (p *PostureCheckMfa) GetTimeoutRemainingSeconds(apiSessionId string, pd *Po
}
if p.PromptOnWake && apiSessionData.EndpointState != nil && apiSessionData.EndpointState.WokenAt != nil {
onWakeTimeOut := calculateTimeout(time.Now(), *apiSessionData.EndpointState.WokenAt, -1 * MfaPromptGracePeriod)
onWakeTimeOut := calculateTimeout(now, *apiSessionData.EndpointState.WokenAt, -1*MfaPromptGracePeriod)
if onWakeTimeOut < timeoutRemaining {
timeoutRemaining = onWakeTimeOut
@@ -98,7 +102,7 @@ func (p *PostureCheckMfa) GetTimeoutRemainingSeconds(apiSessionId string, pd *Po
}
if p.PromptOnUnlock && apiSessionData.EndpointState != nil && apiSessionData.EndpointState.UnlockedAt != nil {
onUnlockTimeout := calculateTimeout(time.Now(), *apiSessionData.EndpointState.UnlockedAt, -1 * MfaPromptGracePeriod)
onUnlockTimeout := calculateTimeout(now, *apiSessionData.EndpointState.UnlockedAt, -1*MfaPromptGracePeriod)
if onUnlockTimeout < timeoutRemaining {
timeoutRemaining = onUnlockTimeout
@@ -143,14 +147,19 @@ func (p *PostureCheckMfa) FailureValues(apiSessionId string, pd *PostureData) Po
ret.ActualValue.TimedOutSeconds = apiSessionData.Mfa.TimedOut
ret.ActualValue.PassedMfa = apiSessionData.Mfa.PassedMfaAt != nil
}
ret.ActualValue.PassedOnUnlock = p.PassedOnUnlock(apiSessionData)
ret.ActualValue.PassedOnWake = p.PassedOnWake(apiSessionData)
now := time.Now().UTC()
ret.ActualValue.PassedOnUnlock = p.PassedOnUnlock(apiSessionData, now)
ret.ActualValue.PassedOnWake = p.PassedOnWake(apiSessionData, now)
}
return ret
}
func (p *PostureCheckMfa) Evaluate(apiSessionId string, pd *PostureData) bool {
return p.evaluateAt(apiSessionId, pd, time.Now())
}
func (p *PostureCheckMfa) evaluateAt(apiSessionId string, pd *PostureData, now time.Time) bool {
apiSessionData := pd.ApiSessions[apiSessionId]
if apiSessionData == nil {
@@ -164,11 +173,9 @@ func (p *PostureCheckMfa) Evaluate(apiSessionId string, pd *PostureData) bool {
if apiSessionData.Mfa.PassedMfaAt == nil {
return false
}
now := time.Now().UTC()
timeoutRemainingSeconds := p.GetTimeoutRemainingSeconds(apiSessionId, pd)
if timeoutRemainingSeconds != PostureCheckNoTimeout {
expiresAt := apiSessionData.Mfa.PassedMfaAt.Add(time.Duration(timeoutRemainingSeconds) * time.Second)
if p.TimeoutSeconds != PostureCheckNoTimeout {
expiresAt := apiSessionData.Mfa.PassedMfaAt.Add(time.Duration(p.TimeoutSeconds) * time.Second)
if expiresAt.Before(now) {
apiSessionData.Mfa.TimedOut = true
}
@@ -178,18 +185,18 @@ func (p *PostureCheckMfa) Evaluate(apiSessionId string, pd *PostureData) bool {
return false
}
if !p.PassedOnWake(apiSessionData) {
if !p.PassedOnWake(apiSessionData, now) {
return false
}
if !p.PassedOnUnlock(apiSessionData) {
if !p.PassedOnUnlock(apiSessionData, now) {
return false
}
return true
}
func (p *PostureCheckMfa) PassedOnWake(apiSessionData *ApiSessionPostureData) bool {
func (p *PostureCheckMfa) PassedOnWake(apiSessionData *ApiSessionPostureData, now time.Time) bool {
if !p.PromptOnWake {
return true
}
@@ -201,7 +208,7 @@ func (p *PostureCheckMfa) PassedOnWake(apiSessionData *ApiSessionPostureData) bo
if apiSessionData.Mfa == nil || apiSessionData.Mfa.PassedMfaAt == nil {
return false
}
now := time.Now().UTC()
if apiSessionData.EndpointState == nil || apiSessionData.EndpointState.WokenAt == nil || apiSessionData.EndpointState.WokenAt.Before(*apiSessionData.Mfa.PassedMfaAt) || now.Add(MfaPromptGracePeriod).Before(*apiSessionData.EndpointState.WokenAt) {
return true
}
@@ -209,7 +216,7 @@ func (p *PostureCheckMfa) PassedOnWake(apiSessionData *ApiSessionPostureData) bo
return false
}
func (p *PostureCheckMfa) PassedOnUnlock(apiSessionData *ApiSessionPostureData) bool {
func (p *PostureCheckMfa) PassedOnUnlock(apiSessionData *ApiSessionPostureData, now time.Time) bool {
if !p.PromptOnUnlock {
return true
}
@@ -222,7 +229,6 @@ func (p *PostureCheckMfa) PassedOnUnlock(apiSessionData *ApiSessionPostureData)
return false
}
now := time.Now().UTC()
if apiSessionData.EndpointState == nil || apiSessionData.EndpointState.UnlockedAt == nil || apiSessionData.EndpointState.UnlockedAt.Before(*apiSessionData.Mfa.PassedMfaAt) || now.Add(MfaPromptGracePeriod).Before(*apiSessionData.EndpointState.UnlockedAt) {
return true
}
@@ -28,6 +28,45 @@ const (
)
func TestPostureCheckModelMfa(t *testing.T) {
t.Run("evaluateAt", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
mfaCheck.TimeoutSeconds = 40
t.Run("passes at 3/4 timeout", func(t *testing.T) {
threeFourthsTimeout := 10
now := postureData.ApiSessions[mfaTestApiSessionId].Mfa.PassedMfaAt.Add(time.Duration(threeFourthsTimeout) * time.Second)
result := mfaCheck.evaluateAt(mfaTestApiSessionId, postureData, now)
req := require.New(t)
req.True(result)
})
t.Run("passes at 1/2 timeout", func(t *testing.T) {
halfTimeout := 20
now := postureData.ApiSessions[mfaTestApiSessionId].Mfa.PassedMfaAt.Add(time.Duration(halfTimeout) * time.Second)
result := mfaCheck.evaluateAt(mfaTestApiSessionId, postureData, now)
req := require.New(t)
req.True(result)
})
t.Run("passes at 1/4 timeout", func(t *testing.T) {
quarterTimeout := 30
now := postureData.ApiSessions[mfaTestApiSessionId].Mfa.PassedMfaAt.Add(time.Duration(quarterTimeout) * time.Second)
result := mfaCheck.evaluateAt(mfaTestApiSessionId, postureData, now)
req := require.New(t)
req.True(result)
})
})
t.Run("Evaluate", func(t *testing.T) {
t.Run("returns true for valid MFA check", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
@@ -212,7 +251,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
t.Run("returns true with default case", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
result := mfaCheck.PassedOnWake(postureData.ApiSessions[mfaTestApiSessionId])
result := mfaCheck.PassedOnWake(postureData.ApiSessions[mfaTestApiSessionId], time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -223,7 +262,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
mfaCheck.PromptOnWake = false
result := mfaCheck.PassedOnWake(postureData.ApiSessions[mfaTestApiSessionId])
result := mfaCheck.PassedOnWake(postureData.ApiSessions[mfaTestApiSessionId], time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -232,7 +271,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
t.Run("returns false when PromptOnWake is true and ApiSessionData is nil", func(t *testing.T) {
mfaCheck, _ := newMfaCheckAndPostureData()
result := mfaCheck.PassedOnWake(nil)
result := mfaCheck.PassedOnWake(nil, time.Now().UTC())
req := require.New(t)
req.False(result)
@@ -244,7 +283,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.Mfa = nil
result := mfaCheck.PassedOnWake(apiSessionData)
result := mfaCheck.PassedOnWake(apiSessionData, time.Now().UTC())
req := require.New(t)
req.False(result)
@@ -256,7 +295,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.Mfa.PassedMfaAt = nil
result := mfaCheck.PassedOnWake(apiSessionData)
result := mfaCheck.PassedOnWake(apiSessionData, time.Now().UTC())
req := require.New(t)
req.False(result)
@@ -268,7 +307,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.EndpointState = nil
result := mfaCheck.PassedOnWake(apiSessionData)
result := mfaCheck.PassedOnWake(apiSessionData, time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -280,7 +319,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.EndpointState.WokenAt = nil
result := mfaCheck.PassedOnWake(apiSessionData)
result := mfaCheck.PassedOnWake(apiSessionData, time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -292,7 +331,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
t.Run("returns true with default case", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
result := mfaCheck.PassedOnUnlock(postureData.ApiSessions[mfaTestApiSessionId])
result := mfaCheck.PassedOnUnlock(postureData.ApiSessions[mfaTestApiSessionId], time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -303,7 +342,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
mfaCheck.PromptOnUnlock = false
result := mfaCheck.PassedOnUnlock(postureData.ApiSessions[mfaTestApiSessionId])
result := mfaCheck.PassedOnUnlock(postureData.ApiSessions[mfaTestApiSessionId], time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -312,7 +351,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
t.Run("returns false when PromptOnUnlock is true and ApiSessionData is nil", func(t *testing.T) {
mfaCheck, _ := newMfaCheckAndPostureData()
result := mfaCheck.PassedOnUnlock(nil)
result := mfaCheck.PassedOnUnlock(nil, time.Now().UTC())
req := require.New(t)
req.False(result)
@@ -324,7 +363,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.Mfa = nil
result := mfaCheck.PassedOnUnlock(apiSessionData)
result := mfaCheck.PassedOnUnlock(apiSessionData, time.Now().UTC())
req := require.New(t)
req.False(result)
@@ -336,7 +375,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.Mfa.PassedMfaAt = nil
result := mfaCheck.PassedOnUnlock(apiSessionData)
result := mfaCheck.PassedOnUnlock(apiSessionData, time.Now().UTC())
req := require.New(t)
req.False(result)
@@ -348,7 +387,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.EndpointState = nil
result := mfaCheck.PassedOnUnlock(apiSessionData)
result := mfaCheck.PassedOnUnlock(apiSessionData, time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -360,7 +399,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
apiSessionData.EndpointState.UnlockedAt = nil
result := mfaCheck.PassedOnUnlock(apiSessionData)
result := mfaCheck.PassedOnUnlock(apiSessionData, time.Now().UTC())
req := require.New(t)
req.True(result)
@@ -552,9 +591,9 @@ func TestPostureCheckModelMfa(t *testing.T) {
req.GreaterOrEqual(mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData), mfaCheck.TimeoutSeconds-5)
})
})
t.Run("calculateTimeout", func(t *testing.T) {
t.Run("eventAt == now, results in 5m", func(t *testing.T) {
req := require.New(t)
@@ -564,7 +603,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
eventAt, err := time.Parse(time.RFC3339, "2020-01-01T11:30:00Z")
req.NoError(err)
result := calculateTimeout(now, eventAt, -1 * MfaPromptGracePeriod)
result := calculateTimeout(now, eventAt, -1*MfaPromptGracePeriod)
req.Equal(int64(300), result)
})
@@ -578,7 +617,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
eventAt, err := time.Parse(time.RFC3339, "2020-01-01T11:27:00Z")
req.NoError(err)
result := calculateTimeout(now, eventAt, -1 * MfaPromptGracePeriod)
result := calculateTimeout(now, eventAt, -1*MfaPromptGracePeriod)
req.Equal(int64(120), result)
})
@@ -592,7 +631,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
eventAt, err := time.Parse(time.RFC3339, "2020-01-01T11:25:00Z")
req.NoError(err)
result := calculateTimeout(now, eventAt, -1 * MfaPromptGracePeriod)
result := calculateTimeout(now, eventAt, -1*MfaPromptGracePeriod)
req.Equal(int64(0), result)
})
@@ -606,7 +645,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
eventAt, err := time.Parse(time.RFC3339, "2020-01-01T11:20:00Z")
req.NoError(err)
result := calculateTimeout(now, eventAt, -1 * MfaPromptGracePeriod)
result := calculateTimeout(now, eventAt, -1*MfaPromptGracePeriod)
req.Equal(int64(0), result)
})
+3 -3
View File
@@ -41,7 +41,7 @@ func (entity *PostureCheckType) toBoltEntity() (boltz.Entity, error) {
})
}
return &persistence.PostureCheckType{
return &persistence.PostureCheckOs{
Name: entity.Name,
OperatingSystems: operatingSystems,
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
@@ -61,9 +61,9 @@ func (entity *PostureCheckType) toBoltEntityForPatch(*bbolt.Tx, Handler, boltz.F
}
func (entity *PostureCheckType) fillFrom(_ Handler, _ *bbolt.Tx, boltEntity boltz.Entity) error {
boltPostureCheckType, ok := boltEntity.(*persistence.PostureCheckType)
boltPostureCheckType, ok := boltEntity.(*persistence.PostureCheckOs)
if !ok {
return errors.Errorf("unexpected type %v when filling model PostureCheckType", reflect.TypeOf(boltEntity))
return errors.Errorf("unexpected type %v when filling model PostureCheckOs", reflect.TypeOf(boltEntity))
}
var operatingSystems []OperatingSystem
@@ -19,6 +19,9 @@ package model
import (
"fmt"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/edge/controller/persistence"
"github.com/openziti/fabric/controller/db"
"github.com/openziti/foundation/storage/ast"
"go.etcd.io/bbolt"
"runtime/debug"
"time"
@@ -247,3 +250,100 @@ func (handler *PostureResponseHandler) SetSdkInfo(identityId, apiSessionId strin
return postureData
})
}
type ServiceWithTimeout struct {
Service *Service
Timeout int64
}
func shouldPostureCheckTimeoutBeAltered(mfaCheck *persistence.PostureCheckMfa, timeSinceLastMfa, gracePeriod time.Duration, onWake, onUnlock bool) bool {
if mfaCheck == nil {
return false
}
if (mfaCheck.PromptOnUnlock && onUnlock) || (mfaCheck.PromptOnWake && onWake) {
//no time out the remaining time was bigger than the grace period
timeSinceLastMfaSeconds := int64(timeSinceLastMfa.Seconds())
gracePeriodSeconds := int64(gracePeriod.Seconds())
timeoutShouldBeAltered := mfaCheck.TimeoutSeconds == -1 || (mfaCheck.TimeoutSeconds-timeSinceLastMfaSeconds > gracePeriodSeconds)
return timeoutShouldBeAltered
}
return false
}
func (handler *PostureResponseHandler) GetEndpointStateChangeAffectedServices(timeSinceLastMfa, gracePeriod time.Duration, onWake bool, onUnlock bool) []*ServiceWithTimeout {
affectedChecks := map[string]int64{} //check id -> timeout
if onWake || onUnlock {
queryStr := fmt.Sprintf("%s=true or %s=true", persistence.FieldPostureCheckMfaPromptOnUnlock, persistence.FieldPostureCheckMfaPromptOnWake)
query, err := ast.Parse(handler.env.GetStores().PostureCheck, queryStr)
if err != nil {
pfxlog.Logger().Errorf("error querying for onWake/onUnlock posture checks: %v", err)
} else {
handler.env.GetDbProvider().GetDb().View(func(tx *bbolt.Tx) error {
cursor := handler.env.GetStores().PostureCheck.IterateIds(tx, query)
for cursor.IsValid() {
if check, err := handler.env.GetStores().PostureCheck.LoadOneById(tx, string(cursor.Current())); err == nil {
if mfaCheck, ok := check.SubType.(*persistence.PostureCheckMfa); ok {
if shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, onWake, onUnlock) {
affectedChecks[check.Id] = mfaCheck.TimeoutSeconds
}
}
} else {
pfxlog.Logger().Errorf("error querying for onWake/onUnlock posture checks by id: %v", err)
}
cursor.Next()
}
return nil
})
}
}
services := map[string]*ServiceWithTimeout{}
if len(affectedChecks) > 0 {
_ = handler.env.GetDbProvider().GetDb().View(func(tx *bbolt.Tx) error {
for checkId, timeout := range affectedChecks {
policyCursor := handler.env.GetStores().PostureCheck.GetRelatedEntitiesCursor(tx, checkId, persistence.EntityTypeServicePolicies, true)
for policyCursor.IsValid() {
serviceCursor := handler.env.GetStores().ServicePolicy.GetRelatedEntitiesCursor(tx, string(policyCursor.Current()), db.EntityTypeServices, true)
for serviceCursor.IsValid() {
if _, ok := services[string(serviceCursor.Current())]; !ok {
service, err := handler.env.GetStores().EdgeService.LoadOneById(tx, string(serviceCursor.Current()))
if err == nil {
modelService := &Service{}
if err := modelService.fillFrom(handler.env.GetHandlers().EdgeService, tx, service); err == nil {
//use the lowest configured timeout (which is some timeout or no timeout)
if existingService, ok := services[service.Id]; !ok || timeout < existingService.Timeout {
services[service.Id] = &ServiceWithTimeout{
Service: modelService,
Timeout: timeout,
}
}
}
}
}
serviceCursor.Next()
}
policyCursor.Next()
}
}
return nil
})
}
var serviceSlice []*ServiceWithTimeout
for _, service := range services {
serviceSlice = append(serviceSlice, service)
}
return serviceSlice
}
@@ -0,0 +1,255 @@
/*
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 (
"github.com/openziti/edge/controller/persistence"
"github.com/stretchr/testify/require"
"testing"
"time"
)
func mustParseDuration(s string) time.Duration {
result, err := time.ParseDuration(s)
if err != nil {
panic(err)
}
return result
}
func TestPostureCheckResponseHandlers_shouldPostureCheckTimeoutBeAltered(t *testing.T) {
t.Run("returns false if the check is null", func(t *testing.T) {
result := shouldPostureCheckTimeoutBeAltered(nil, mustParseDuration("10m"), mustParseDuration("5m"), true, true)
require.New(t).False(result)
})
t.Run("returns false if no prompts (all false), timeout remaining is greater than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: false,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("3m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, true, true)
require.New(t).False(result)
})
// promptOnWake = true, wake = true
t.Run("returns true if promptOnWake=true, wake=true, timeout remaining is greater than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: true,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("3m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, true, false)
require.New(t).True(result)
})
t.Run("returns false if promptOnWake=true, wake=true, timeout remaining is less than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: true,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("6m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, true, false)
require.New(t).False(result)
})
t.Run("returns false if promptOnWake=true, wake=true, timeout remaining equals grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: true,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("5m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, true, false)
require.New(t).False(result)
})
// promptOnWake = true, wake = false
t.Run("returns false if promptOnWake=true, wake=false, timeout remaining is greater than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: true,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("3m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, false)
require.New(t).False(result)
})
t.Run("returns false if promptOnWake=true, wake=false, timeout remaining is less than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: true,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("6m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, false)
require.New(t).False(result)
})
t.Run("returns false if promptOnWake=true, wake=false, timeout remaining equals grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnWake: true,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("5m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, false)
require.New(t).False(result)
})
// promptOnUnlock = true, unlock = true
t.Run("returns true if promptOnUnlock=true, unlock=true, timeout remaining is greater than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnUnlock: true,
PromptOnWake: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("3m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, true)
require.New(t).True(result)
})
t.Run("returns false if promptOnUnlock=true, unlock=true, timeout remaining is less than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnUnlock: true,
PromptOnWake: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("6m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, true)
require.New(t).False(result)
})
t.Run("returns false if promptOnUnlock=true, unlock=true, timeout remaining equals grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnUnlock: true,
PromptOnWake: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("5m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, true)
require.New(t).False(result)
})
// promptOnUnlock = true, unlock = false
t.Run("returns false if promptOnUnlock=true, unlock=false, timeout remaining is greater than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnUnlock: true,
PromptOnWake: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("3m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, false)
require.New(t).False(result)
})
t.Run("returns false if promptOnUnlock=true, unlock=false, timeout remaining is less than grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnUnlock: true,
PromptOnWake: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("6m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, false)
require.New(t).False(result)
})
t.Run("returns false if promptOnUnlock=true, unlock=false, timeout remaining equals grace", func(t *testing.T) {
mfaCheck := &persistence.PostureCheckMfa{
TimeoutSeconds: int64(mustParseDuration("10m").Seconds()),
PromptOnUnlock: true,
PromptOnWake: false,
IgnoreLegacyEndpoints: false,
}
timeSinceLastMfa := mustParseDuration("5m")
gracePeriod := mustParseDuration("5m")
result := shouldPostureCheckTimeoutBeAltered(mfaCheck, timeSinceLastMfa, gracePeriod, false, false)
require.New(t).False(result)
})
}
+160 -145
View File
@@ -17,11 +17,14 @@
package model
import (
"bytes"
"github.com/jinzhu/copier"
"github.com/kataras/go-events"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/edge/controller/persistence"
"github.com/openziti/foundation/storage/ast"
"github.com/openziti/foundation/storage/boltz"
"github.com/openziti/foundation/util/concurrenz"
cmap "github.com/orcaman/concurrent-map"
"go.etcd.io/bbolt"
"regexp"
@@ -30,14 +33,14 @@ import (
)
const (
EventIdentityPostureDataAltered = "EventIdentityPostureDataAltered"
EventApiSessionPostureDataAltered = "EventApiSessionPostureDataAltered"
EventIdentityPostureDataAltered = "EventIdentityPostureDataAltered"
)
type PostureCache struct {
identityToPostureData cmap.ConcurrentMap //identityId -> PostureData
apiSessionIdToIdentityId cmap.ConcurrentMap //apiSessionId -> identityId
ticker *time.Ticker
isRunning concurrenz.AtomicBoolean
events.EventEmmiter
env Env
}
@@ -49,16 +52,18 @@ func newPostureCache(env Env) *PostureCache {
ticker: time.NewTicker(5 * time.Second),
EventEmmiter: events.New(),
env: env,
isRunning: concurrenz.AtomicBoolean(0),
}
pc.run(env.GetHostController().GetCloseNotifyChannel())
env.GetStores().Session.AddListener(boltz.EventCreate, pc.SessionCreated)
env.GetStores().Session.AddListener(boltz.EventDelete, pc.SessionDeleted)
env.GetStores().ApiSession.AddListener(boltz.EventCreate, pc.ApiSessionCreated)
env.GetStores().ApiSession.AddListener(boltz.EventDelete, pc.ApiSessionDeleted)
env.GetStores().Identity.AddListener(boltz.EventDelete, pc.IdentityDeleted)
env.GetStores().PostureCheckType.AddListener(boltz.EventCreate, pc.PostureCheckChanged)
env.GetStores().PostureCheckType.AddListener(boltz.EventUpdate, pc.PostureCheckChanged)
return pc
}
@@ -67,27 +72,7 @@ func (pc *PostureCache) run(closeNotify <-chan struct{}) {
for {
select {
case <-pc.ticker.C:
changedIdentityIds := map[string]struct{}{}
for _, identityId := range pc.identityToPostureData.Keys() {
pc.identityToPostureData.Upsert(identityId, newPostureData(), func(exist bool, valueInMap interface{}, newValue interface{}) interface{} {
var postureData *PostureData
if exist {
postureData = valueInMap.(*PostureData)
} else {
postureData = newPostureData()
}
if changed := postureData.CheckTimeouts(); changed {
changedIdentityIds[identityId] = struct{}{}
}
return postureData
})
}
for identityId := range changedIdentityIds {
pc.Emit(EventIdentityPostureDataAltered, identityId)
}
go pc.evaluate()
case <-closeNotify:
pc.ticker.Stop()
return
@@ -96,6 +81,94 @@ func (pc *PostureCache) run(closeNotify <-chan struct{}) {
}()
}
func (pc *PostureCache) evaluate() {
if !pc.isRunning.CompareAndSwap(false, true) {
return
}
log := pfxlog.Logger()
defer func() {
if r := recover(); r != nil {
log.Errorf("error during posture timeout enforcement: %v", r)
}
pc.isRunning.Set(false)
}()
var lastId []byte
const maxToDeletePerTx = 1000
var toDeleteSessionIds []string
newIdentityServiceUpdates := map[string]struct{}{} //tracks the current loops identityIds that have had updates
completedIdentityServiceUpdates := map[string]struct{}{} //tracks overall identityIds that have been notified of an update
done := false
// Chunk data in maxToDelete bunches to limit how many sessions we are deleting in a transaction.
// Requires tracking of which session was last evaluated, kept in lastId.
for !done {
_ = pc.env.GetDbProvider().GetDb().View(func(tx *bbolt.Tx) error {
cursor := pc.env.GetStores().Session.IterateIds(tx, ast.BoolNodeTrue)
if len(lastId) != 0 {
cursor.Seek(lastId)
if cursor.IsValid() {
if bytes.Compare(cursor.Current(), lastId) == 0 {
cursor.Next()
}
}
}
done = !cursor.IsValid()
for cursor.IsValid() && len(toDeleteSessionIds) < maxToDeletePerTx {
lastId = cursor.Current()
session, err := pc.env.GetStores().Session.LoadOneById(tx, string(cursor.Current()))
if err == nil && session != nil {
result := pc.env.GetHandlers().Session.EvaluatePostureForService(session.IdentityId, session.ApiSessionId, session.Type, session.ServiceId, "")
if !result.Passed {
log.WithFields(map[string]interface{}{
"apiSessionId": session.ApiSessionId,
"identityId": session.IdentityId,
"sessionId": session.Id,
}).Tracef("session [%s] failed posture checks, removing", session.Id)
toDeleteSessionIds = append(toDeleteSessionIds, session.Id)
newIdentityServiceUpdates[session.IdentityId] = struct{}{}
}
}
cursor.Next()
}
return nil
})
//delete sessions that failed pc checks, clear list
for _, sessionId := range toDeleteSessionIds {
err := pc.env.GetHandlers().Session.Delete(sessionId)
if err != nil {
log.WithError(err).Errorf("error removing session [%s] due to posture check failure, delete error: %v", sessionId, err)
}
}
toDeleteSessionIds = []string{}
//notify endpoints that they may have new service posture queries
for identityId, _ := range newIdentityServiceUpdates {
if _, ok := completedIdentityServiceUpdates[identityId]; !ok {
completedIdentityServiceUpdates[identityId] = struct{}{}
pc.env.HandleServiceUpdatedEventForIdentityId(identityId)
}
}
newIdentityServiceUpdates = map[string]struct{}{}
}
}
func (pc *PostureCache) Add(identityId string, postureResponses []*PostureResponse) {
pc.Upsert(identityId, true, func(exist bool, valueInMap interface{}, newValue interface{}) interface{} {
var postureData *PostureData
@@ -187,114 +260,13 @@ func (pc *PostureCache) PostureData(identityId string) *PostureData {
return result
}
func (pc *PostureCache) SessionCreated(args ...interface{}) {
var session *persistence.Session
if len(args) == 1 {
session, _ = args[0].(*persistence.Session)
}
if session == nil {
pfxlog.Logger().Error("session created event trigger with args[0] not convertible to *persistence.Session")
return
}
mfaTimeout := int64(0)
postureCheckLinks := pc.env.GetStores().ServicePolicy.GetLinkCollection(persistence.EntityTypePostureChecks)
for _, policyId := range session.ServicePolicies {
pc.env.GetDbProvider().GetDb().View(func(tx *bbolt.Tx) error {
cursor := postureCheckLinks.IterateLinks(tx, []byte(policyId))
for cursor.IsValid() {
checkId := string(cursor.Current())
if check, err := pc.env.GetStores().PostureCheck.LoadOneById(tx, checkId); err == nil {
if check.TypeId == PostureCheckTypeMFA {
if mfaCheck, ok := check.SubType.(*persistence.PostureCheckMfa); ok {
if mfaCheck.TimeoutSeconds == PostureCheckNoTimeout {
mfaTimeout = PostureCheckNoTimeout
} else if mfaTimeout != PostureCheckNoTimeout {
if mfaCheck.TimeoutSeconds > mfaTimeout {
mfaTimeout = mfaCheck.TimeoutSeconds
}
}
}
}
}
cursor.Next()
}
return nil
})
}
if identityIdVal, ok := pc.apiSessionIdToIdentityId.Get(session.ApiSessionId); ok {
identityId := identityIdVal.(string)
pc.identityToPostureData.Upsert(identityId, newPostureData(), func(exist bool, valueInMap interface{}, newValue interface{}) interface{} {
var pd *PostureData
if exist {
pd = valueInMap.(*PostureData)
} else {
pd = newValue.(*PostureData)
}
if pd.ApiSessions == nil {
pd.ApiSessions = map[string]*ApiSessionPostureData{}
}
if _, ok := pd.ApiSessions[session.ApiSessionId]; !ok {
pd.ApiSessions[session.ApiSessionId] = &ApiSessionPostureData{}
}
if pd.ApiSessions[session.ApiSessionId].Sessions == nil {
pd.ApiSessions[session.ApiSessionId].Sessions = map[string]*PostureSessionData{}
}
pd.ApiSessions[session.ApiSessionId].Sessions[session.Id] = &PostureSessionData{
MfaTimeout: mfaTimeout,
}
return pd
})
}
}
func (pc *PostureCache) SessionDeleted(args ...interface{}) {
var session *persistence.Session
if len(args) == 1 {
session, _ = args[0].(*persistence.Session)
}
if session == nil {
pfxlog.Logger().Error("session deleted event trigger with args[0] not convertible to *persistence.Session")
return
}
if identityIdVal, ok := pc.apiSessionIdToIdentityId.Get(session.ApiSessionId); ok {
identityId := identityIdVal.(string)
pc.identityToPostureData.Upsert(identityId, newPostureData(), func(exist bool, valueInMap interface{}, newValue interface{}) interface{} {
var postureData *PostureData
if exist {
postureData = valueInMap.(*PostureData)
} else {
postureData = newValue.(*PostureData)
}
if apiSessionData, ok := postureData.ApiSessions[session.ApiSessionId]; ok {
if apiSessionData.Sessions != nil {
delete(apiSessionData.Sessions, session.Id)
}
}
return postureData
})
}
}
func (pc *PostureCache) ApiSessionCreated(args ...interface{}) {
var apiSession *persistence.ApiSession
if len(args) == 1 {
apiSession, _ = args[0].(*persistence.ApiSession)
} else {
pfxlog.Logger().Errorf("unexpected number of args [%d]", len(args))
return
}
if apiSession == nil {
@@ -309,6 +281,9 @@ func (pc *PostureCache) ApiSessionDeleted(args ...interface{}) {
var apiSession *persistence.ApiSession
if len(args) == 1 {
apiSession, _ = args[0].(*persistence.ApiSession)
} else {
pfxlog.Logger().Errorf("unexpected number of args [%d]", len(args))
return
}
if apiSession == nil {
@@ -337,6 +312,9 @@ func (pc *PostureCache) IdentityDeleted(args ...interface{}) {
var identity *persistence.Identity
if len(args) == 1 {
identity, _ = args[0].(*persistence.Identity)
} else {
pfxlog.Logger().Errorf("unexpected number of args [%d]", len(args))
return
}
if identity == nil {
@@ -347,6 +325,59 @@ func (pc *PostureCache) IdentityDeleted(args ...interface{}) {
pc.identityToPostureData.Remove(identity.Id)
}
//PostureCheckChanged notifies all associated identities that posture configuration has changed
//and that endpoints may need to reevaluate posture queries.
func (pc *PostureCache) PostureCheckChanged(args ...interface{}) {
var entity boltz.Entity
if len(args) == 1 {
var ok bool
entity, ok = args[0].(boltz.Entity)
if !ok {
pfxlog.Logger().Errorf("unexpected type [%T] expected boltz.Entity: %v", args[0], args[0])
}
} else {
pfxlog.Logger().Errorf("unexpected number of args [%d]", len(args))
return
}
servicePolicyLinks := pc.env.GetStores().PostureCheck.GetLinkCollection(persistence.EntityTypeServicePolicies)
if servicePolicyLinks == nil {
pfxlog.Logger().Error("posture checks had no links to service policies")
return
}
identitiesToNotify := map[string]struct{}{}
_ = pc.env.GetDbProvider().GetDb().View(func(tx *bbolt.Tx) error {
servicePolicyCursor := servicePolicyLinks.IterateLinks(tx, []byte(entity.GetId()))
for servicePolicyCursor.IsValid() {
identityLink := pc.env.GetStores().ServicePolicy.GetLinkCollection(persistence.EntityTypeIdentities)
if identityLink == nil {
pfxlog.Logger().Error("service policies had no link to identities")
return nil
}
identityCursor := identityLink.IterateLinks(tx, servicePolicyCursor.Current())
for identityCursor.IsValid() {
identitiesToNotify[string(identityCursor.Current())] = struct{}{}
}
servicePolicyCursor.Next()
}
return nil
})
for identityId, _ := range identitiesToNotify {
pc.env.HandleServiceUpdatedEventForIdentityId(identityId)
}
}
type PostureSessionData struct {
MfaTimeout int64
}
@@ -354,7 +385,6 @@ type PostureSessionData struct {
type ApiSessionPostureData struct {
Mfa *PostureResponseMfa `json:"mfa"`
EndpointState *PostureResponseEndpointState `json:"endpointState"`
Sessions map[string]*PostureSessionData
SdkInfo *SdkInfo
}
@@ -415,21 +445,6 @@ func (pd *PostureData) Evaluate(apiSessionId string, checks []*PostureCheck) (bo
return len(failures) == 0, failures
}
func (pd *PostureData) CheckTimeouts() bool {
for _, apiSessionData := range pd.ApiSessions {
for _, sessionData := range apiSessionData.Sessions {
if sessionData.MfaTimeout != PostureCheckNoTimeout && apiSessionData.Mfa != nil && apiSessionData.Mfa.PassedMfaAt != nil {
expiresAt := apiSessionData.Mfa.PassedMfaAt.Add(time.Duration(sessionData.MfaTimeout) * time.Second)
if expiresAt.Before(time.Now()) {
return true
}
}
}
}
return false
}
func (pd *PostureData) Copy() *PostureData {
dest := &PostureData{}
copier.Copy(dest, pd)
+2
View File
@@ -261,6 +261,7 @@ func (handler *EdgeServiceHandler) mergeConfigs(tx *bbolt.Tx, configTypes map[st
type PolicyPostureChecks struct {
PostureChecks []*PostureCheck
PolicyType persistence.PolicyType
PolicyName string
}
func (handler *EdgeServiceHandler) GetPolicyPostureChecks(identityId, serviceId string) map[string]*PolicyPostureChecks {
@@ -293,6 +294,7 @@ func (handler *EdgeServiceHandler) GetPolicyPostureChecks(identityId, serviceId
policyIdToChecks[policyIdStr] = &PolicyPostureChecks{
PostureChecks: []*PostureCheck{},
PolicyType: policy.PolicyType,
PolicyName: policy.Name,
}
cursor := postureCheckLinks.IterateLinks(tx, policyIdBytes)
+108 -85
View File
@@ -46,6 +46,109 @@ func (handler *SessionHandler) newModelEntity() boltEntitySink {
return &Session{}
}
type SessionPostureResult struct {
Passed bool
Failure *PostureSessionRequestFailure
PassingPolicyIds []string
Cause *apierror.GenericCauseError
}
func (handler *SessionHandler) EvaluatePostureForService(identityId, apiSessionId, sessionType, serviceId, serviceName string) *SessionPostureResult {
failureByPostureCheckId := map[string]*PostureCheckFailure{} //cache individual check status
validPosture := false
hasMatchingPolicies := false
policyPostureCheckMap := handler.GetEnv().GetHandlers().EdgeService.GetPolicyPostureChecks(identityId, serviceId)
failedPolicies := map[string][]*PostureCheckFailure{}
failedPoliciesIdToName := map[string]string{}
var failedPolicyIds []string
var successPolicyIds []string
for policyId, policyPostureCheck := range policyPostureCheckMap {
if policyPostureCheck.PolicyType.String() != sessionType {
continue
}
hasMatchingPolicies = true
var failedChecks []*PostureCheckFailure
for _, postureCheck := range policyPostureCheck.PostureChecks {
found := false
if _, found = failureByPostureCheckId[postureCheck.Id]; !found {
_, failureByPostureCheckId[postureCheck.Id] = handler.GetEnv().GetHandlers().PostureResponse.Evaluate(identityId, apiSessionId, postureCheck)
}
if failureByPostureCheckId[postureCheck.Id] != nil {
failedChecks = append(failedChecks, failureByPostureCheckId[postureCheck.Id])
}
}
if len(failedChecks) == 0 {
validPosture = true
successPolicyIds = append(successPolicyIds, policyId)
} else {
//save for error output
failedPolicies[policyId] = failedChecks
failedPoliciesIdToName[policyId] = policyPostureCheck.PolicyName
failedPolicyIds = append(failedPolicyIds, policyId)
}
}
if hasMatchingPolicies && !validPosture {
failureMap := map[string]interface{}{}
sessionFailure := &PostureSessionRequestFailure{
When: time.Now(),
ServiceId: serviceId,
ServiceName: serviceName,
ApiSessionId: apiSessionId,
SessionType: sessionType,
PolicyFailures: []*PosturePolicyFailure{},
}
for policyId, failures := range failedPolicies {
policyFailure := &PosturePolicyFailure{
PolicyId: policyId,
PolicyName: failedPoliciesIdToName[policyId],
Checks: failures,
}
var outFailures []interface{}
for _, failure := range failures {
outFailures = append(outFailures, failure.ToClientErrorData())
}
failureMap[policyId] = outFailures
sessionFailure.PolicyFailures = append(sessionFailure.PolicyFailures, policyFailure)
}
cause := &apierror.GenericCauseError{
Message: fmt.Sprintf("Failed to pass posture checks for service policies: %v", failedPolicyIds),
DataMap: failureMap,
}
return &SessionPostureResult{
Passed: false,
Cause: cause,
PassingPolicyIds: nil,
Failure: sessionFailure,
}
}
return &SessionPostureResult{
Passed: true,
Cause: nil,
PassingPolicyIds: successPolicyIds,
Failure: nil,
}
}
func (handler *SessionHandler) Create(entity *Session) (string, error) {
entity.Id = cuid.New() //use cuids which are longer than shortids but are monotonic
@@ -74,91 +177,11 @@ func (handler *SessionHandler) Create(entity *Session) (string, error) {
return "", errorz.NewFieldError("service not found", "ServiceId", entity.ServiceId)
}
failureByPostureCheckId := map[string]*PostureCheckFailure{} //cache individual check status
validPosture := false
hasMatchingPolicies := false
policyResult := handler.EvaluatePostureForService(apiSession.IdentityId, apiSession.Id, entity.Type, service.Id, service.Name)
policyPostureCheckMap := handler.GetEnv().GetHandlers().EdgeService.GetPolicyPostureChecks(apiSession.IdentityId, entity.ServiceId)
failedPolicies := map[string][]*PostureCheckFailure{}
failedPoliciesIdToName := map[string]string{}
var failedPolicyIds []string
var successPolicyIds []string
for policyId, policyPostureCheck := range policyPostureCheckMap {
policy, err := handler.GetEnv().GetHandlers().ServicePolicy.Read(policyId)
if err != nil {
continue
}
if policy.PolicyType != entity.Type {
continue
}
hasMatchingPolicies = true
var failedChecks []*PostureCheckFailure
for _, postureCheck := range policyPostureCheck.PostureChecks {
found := false
if _, found = failureByPostureCheckId[postureCheck.Id]; !found {
_, failureByPostureCheckId[postureCheck.Id] = handler.GetEnv().GetHandlers().PostureResponse.Evaluate(apiSession.IdentityId, apiSession.Id, postureCheck)
}
if failureByPostureCheckId[postureCheck.Id] != nil {
failedChecks = append(failedChecks, failureByPostureCheckId[postureCheck.Id])
}
}
if len(failedChecks) == 0 {
validPosture = true
successPolicyIds = append(successPolicyIds, policyId)
} else {
//save for error output
failedPolicies[policy.Id] = failedChecks
failedPoliciesIdToName[policy.Id] = policy.Name
failedPolicyIds = append(failedPolicyIds, policy.Id)
}
}
if hasMatchingPolicies && !validPosture {
failureMap := map[string]interface{}{}
sessionFailure := &PostureSessionRequestFailure{
When: time.Now(),
ServiceId: service.Id,
ServiceName: service.Name,
ApiSessionId: apiSession.Id,
SessionType: entity.Type,
PolicyFailures: []*PosturePolicyFailure{},
}
for policyId, failures := range failedPolicies {
policyFailure := &PosturePolicyFailure{
PolicyId: policyId,
PolicyName: failedPoliciesIdToName[policyId],
Checks: failures,
}
var outFailures []interface{}
for _, failure := range failures {
outFailures = append(outFailures, failure.ToClientErrorData())
}
failureMap[policyId] = outFailures
sessionFailure.PolicyFailures = append(sessionFailure.PolicyFailures, policyFailure)
}
handler.env.GetHandlers().PostureResponse.postureCache.AddSessionRequestFailure(apiSession.IdentityId, sessionFailure)
cause := apierror.GenericCauseError{
Message: fmt.Sprintf("Failed to pass posture checks for service policies: %v", failedPolicyIds),
DataMap: failureMap,
}
return "", apierror.NewInvalidPosture(cause)
if !policyResult.Passed {
handler.env.GetHandlers().PostureResponse.postureCache.AddSessionRequestFailure(apiSession.IdentityId, policyResult.Failure)
return "", apierror.NewInvalidPosture(policyResult.Cause)
}
maxRows := 1
@@ -170,7 +193,7 @@ func (handler *SessionHandler) Create(entity *Session) (string, error) {
return "", apierror.NewNoEdgeRoutersAvailable()
}
entity.ServicePolicies = successPolicyIds
entity.ServicePolicies = policyResult.PassingPolicyIds
return handler.createEntity(entity)
}
+4
View File
@@ -31,6 +31,7 @@ import (
type Session struct {
models.BaseEntity
Token string
IdentityId string
ApiSessionId string
ServiceId string
Type string
@@ -58,6 +59,7 @@ func (entity *Session) toBoltEntityForCreate(tx *bbolt.Tx, handler Handler) (bol
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Token: entity.Token,
ApiSessionId: entity.ApiSessionId,
IdentityId: entity.IdentityId,
ServiceId: entity.ServiceId,
Type: entity.Type,
ApiSession: apiSession,
@@ -103,6 +105,7 @@ func (entity *Session) toBoltEntityForUpdate(*bbolt.Tx, Handler) (boltz.Entity,
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Token: entity.Token,
ApiSessionId: entity.ApiSessionId,
IdentityId: entity.IdentityId,
ServiceId: entity.ServiceId,
Type: entity.Type,
ServicePolicies: entity.ServicePolicies,
@@ -121,6 +124,7 @@ func (entity *Session) fillFrom(_ Handler, _ *bbolt.Tx, boltEntity boltz.Entity)
entity.FillCommon(boltSession)
entity.Token = boltSession.Token
entity.ApiSessionId = boltSession.ApiSessionId
entity.IdentityId = boltSession.IdentityId
entity.ServiceId = boltSession.ServiceId
entity.Type = boltSession.Type
entity.ServicePolicies = boltSession.ServicePolicies
@@ -628,7 +628,7 @@ func (m *Migrations) addPostureCheckTypes(step *boltz.MigrationStep) {
iOS,
}
types := []*PostureCheckType{
types := []*PostureCheckOs{
{
BaseExtEntity: boltz.BaseExtEntity{
Id: "OS",
+1 -1
View File
@@ -21,7 +21,7 @@ func (m *Migrations) addProcessMultiPostureCheck(step *boltz.MigrationStep) {
OsVersions: []string{},
}
processCheckType := &PostureCheckType{
processCheckType := &PostureCheckOs{
BaseExtEntity: boltz.BaseExtEntity{
Id: "PROCESS_MULTI",
CreatedAt: time.Now(),
+16 -7
View File
@@ -21,9 +21,9 @@ import (
)
const (
FieldPostureCheckMfaTimeoutSeconds = "timeoutSeconds"
FieldPostureCheckMfaPromptOnWake = "promptOnWake"
FieldPostureCheckMfaPromptOnUnlock = "promptOnUnlock"
FieldPostureCheckMfaTimeoutSeconds = "timeoutSeconds"
FieldPostureCheckMfaPromptOnWake = "promptOnWake"
FieldPostureCheckMfaPromptOnUnlock = "promptOnUnlock"
FieldPostureCheckMfaIgnoreLegacyEndpoints = "ignoreLegacyEndpoints"
)
@@ -36,21 +36,30 @@ type PostureCheckMfa struct {
func newPostureCheckMfa() PostureCheckSubType {
return &PostureCheckMfa{
TimeoutSeconds: 0,
PromptOnWake: false,
PromptOnUnlock: false,
TimeoutSeconds: 0,
PromptOnWake: false,
PromptOnUnlock: false,
IgnoreLegacyEndpoints: false,
}
}
func (entity *PostureCheckMfa) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
entity.TimeoutSeconds = bucket.GetInt64WithDefault(FieldPostureCheckMfaTimeoutSeconds, 0)
entity.TimeoutSeconds = bucket.GetInt64WithDefault(FieldPostureCheckMfaTimeoutSeconds, -1)
if entity.TimeoutSeconds <= 0 {
entity.TimeoutSeconds = -1
}
entity.PromptOnWake = bucket.GetBoolWithDefault(FieldPostureCheckMfaPromptOnWake, false)
entity.PromptOnUnlock = bucket.GetBoolWithDefault(FieldPostureCheckMfaPromptOnUnlock, false)
entity.IgnoreLegacyEndpoints = bucket.GetBoolWithDefault(FieldPostureCheckMfaIgnoreLegacyEndpoints, false)
}
func (entity *PostureCheckMfa) SetValues(ctx *boltz.PersistContext, bucket *boltz.TypedBucket) {
if entity.TimeoutSeconds <= 0 {
entity.TimeoutSeconds = -1
}
bucket.SetInt64(FieldPostureCheckMfaTimeoutSeconds, entity.TimeoutSeconds, ctx.FieldChecker)
bucket.SetBool(FieldPostureCheckMfaPromptOnWake, entity.PromptOnWake, ctx.FieldChecker)
bucket.SetBool(FieldPostureCheckMfaPromptOnUnlock, entity.PromptOnUnlock, ctx.FieldChecker)
+17 -6
View File
@@ -32,13 +32,22 @@ const (
FieldPostureCheckDialServices = "dialServices"
)
const (
PostureCheckTypeOs = "OS"
PostureCheckTypeDomain = "DOMAIN"
PostureCheckTypeProcess = "PROCESS"
PostureCheckTypeProcessMulti = "PROCESS_MULTI"
PostureCheckTypeMAC = "MAC"
PostureCheckTypeMFA = "MFA"
)
var postureCheckSubTypeMap = map[string]newPostureCheckSubType{
"OS": newPostureCheckOperatingSystem,
"DOMAIN": newPostureCheckWindowsDomain,
"PROCESS": newPostureCheckProcess,
"PROCESS_MULTI": newPostureCheckProcessMulti,
"MAC": newPostureCheckMacAddresses,
"MFA": newPostureCheckMfa,
PostureCheckTypeOs: newPostureCheckOperatingSystem,
PostureCheckTypeDomain: newPostureCheckWindowsDomain,
PostureCheckTypeProcess: newPostureCheckProcess,
PostureCheckTypeProcessMulti: newPostureCheckProcessMulti,
PostureCheckTypeMAC: newPostureCheckMacAddresses,
PostureCheckTypeMFA: newPostureCheckMfa,
}
type newPostureCheckSubType func() PostureCheckSubType
@@ -149,6 +158,8 @@ func (store *postureCheckStoreImpl) GetRoleAttributesIndex() boltz.SetReadIndex
func (store *postureCheckStoreImpl) initializeLocal() {
store.AddExtEntitySymbols()
store.indexName = store.addUniqueNameField()
store.AddSymbol(FieldPostureCheckMfaPromptOnUnlock, ast.NodeTypeBool, PostureCheckTypeMFA)
store.AddSymbol(FieldPostureCheckMfaPromptOnWake, ast.NodeTypeBool, PostureCheckTypeMFA)
store.symbolRoleAttributes = store.AddSetSymbol(FieldRoleAttributes, ast.NodeTypeString)
store.indexRoleAttributes = store.AddSetIndex(store.symbolRoleAttributes)
@@ -25,17 +25,17 @@ const (
FieldPostureCheckTypeOperatingSystems = "operatingSystems"
)
type PostureCheckType struct {
type PostureCheckOs struct {
boltz.BaseExtEntity
Name string
OperatingSystems []OperatingSystem
}
func (entity *PostureCheckType) GetName() string {
func (entity *PostureCheckOs) GetName() string {
return entity.Name
}
func (entity *PostureCheckType) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
func (entity *PostureCheckOs) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
entity.LoadBaseValues(bucket)
entity.Name = bucket.GetStringOrError(FieldName)
@@ -60,7 +60,7 @@ func (entity *PostureCheckType) LoadValues(_ boltz.CrudStore, bucket *boltz.Type
}
}
func (entity *PostureCheckType) SetValues(ctx *boltz.PersistContext) {
func (entity *PostureCheckOs) SetValues(ctx *boltz.PersistContext) {
entity.SetBaseValues(ctx)
ctx.SetString(FieldName, entity.Name)
@@ -86,14 +86,14 @@ func (entity *PostureCheckType) SetValues(ctx *boltz.PersistContext) {
}
}
func (entity *PostureCheckType) GetEntityType() string {
func (entity *PostureCheckOs) GetEntityType() string {
return EntityTypePostureCheckTypes
}
type PostureCheckTypeStore interface {
NameIndexedStore
LoadOneById(tx *bbolt.Tx, id string) (*PostureCheckType, error)
LoadOneByName(tx *bbolt.Tx, id string) (*PostureCheckType, error)
LoadOneById(tx *bbolt.Tx, id string) (*PostureCheckOs, error)
LoadOneByName(tx *bbolt.Tx, id string) (*PostureCheckOs, error)
}
func newPostureCheckTypeStore(stores *stores) *postureCheckTypeStoreImpl {
@@ -110,7 +110,7 @@ type postureCheckTypeStoreImpl struct {
}
func (store *postureCheckTypeStoreImpl) NewStoreEntity() boltz.Entity {
return &PostureCheckType{}
return &PostureCheckOs{}
}
func (store *postureCheckTypeStoreImpl) initializeLocal() {
@@ -126,15 +126,15 @@ func (store *postureCheckTypeStoreImpl) GetNameIndex() boltz.ReadIndex {
return store.indexName
}
func (store *postureCheckTypeStoreImpl) LoadOneById(tx *bbolt.Tx, id string) (*PostureCheckType, error) {
entity := &PostureCheckType{}
func (store *postureCheckTypeStoreImpl) LoadOneById(tx *bbolt.Tx, id string) (*PostureCheckOs, error) {
entity := &PostureCheckOs{}
if err := store.baseLoadOneById(tx, id, entity); err != nil {
return nil, err
}
return entity, nil
}
func (store *postureCheckTypeStoreImpl) LoadOneByName(tx *bbolt.Tx, name string) (*PostureCheckType, error) {
func (store *postureCheckTypeStoreImpl) LoadOneByName(tx *bbolt.Tx, name string) (*PostureCheckOs, error) {
id := store.indexName.Read(tx, []byte(name))
if id != nil {
return store.LoadOneById(tx, string(id))
@@ -142,8 +142,8 @@ func (store *postureCheckTypeStoreImpl) LoadOneByName(tx *bbolt.Tx, name string)
return nil, nil
}
func (store *postureCheckTypeStoreImpl) LoadOneByQuery(tx *bbolt.Tx, query string) (*PostureCheckType, error) {
entity := &PostureCheckType{}
func (store *postureCheckTypeStoreImpl) LoadOneByQuery(tx *bbolt.Tx, query string) (*PostureCheckOs, error) {
entity := &PostureCheckOs{}
if found, err := store.BaseLoadOneByQuery(tx, query, entity); !found || err != nil {
return nil, err
}
+4
View File
@@ -30,6 +30,7 @@ const (
FieldSessionToken = "token"
FieldSessionApiSession = "apiSession"
FieldSessionService = "service"
FieldSessionIdentity = "identity"
FieldSessionType = "type"
FieldSessionServicePolicies = "servicePolicies"
@@ -47,6 +48,7 @@ var validSessionTypes = []string{SessionTypeDial, SessionTypeBind}
type Session struct {
boltz.BaseExtEntity
Token string
IdentityId string
ApiSessionId string
ServiceId string
Type string
@@ -60,6 +62,7 @@ func (entity *Session) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket)
entity.Token = bucket.GetStringOrError(FieldSessionToken)
entity.ApiSessionId = bucket.GetStringOrError(FieldSessionApiSession)
entity.ServiceId = bucket.GetStringOrError(FieldSessionService)
entity.IdentityId = bucket.GetStringOrError(FieldSessionIdentity)
entity.Type = bucket.GetStringWithDefault(FieldSessionType, "Dial")
entity.ServicePolicies = bucket.GetStringList(FieldSessionServicePolicies)
}
@@ -78,6 +81,7 @@ func (entity *Session) SetValues(ctx *boltz.PersistContext) {
ctx.SetString(FieldSessionToken, entity.Token)
ctx.SetString(FieldSessionApiSession, entity.ApiSessionId)
ctx.SetString(FieldSessionService, entity.ServiceId)
ctx.SetString(FieldSessionIdentity, entity.IdentityId)
ctx.SetString(FieldSessionType, entity.Type)
ctx.SetStringList(FieldSessionServicePolicies, entity.ServicePolicies)
@@ -77,22 +77,22 @@ func NewCreatePostureResponseBulkOK() *CreatePostureResponseBulkOK {
/* CreatePostureResponseBulkOK describes a response with status code 200, with default header values.
Base empty response
Contains a list of services that have had their timers altered
*/
type CreatePostureResponseBulkOK struct {
Payload *rest_model.Empty
Payload *rest_model.PostureResponseEnvelope
}
func (o *CreatePostureResponseBulkOK) Error() string {
return fmt.Sprintf("[POST /posture-response-bulk][%d] createPostureResponseBulkOK %+v", 200, o.Payload)
}
func (o *CreatePostureResponseBulkOK) GetPayload() *rest_model.Empty {
func (o *CreatePostureResponseBulkOK) GetPayload() *rest_model.PostureResponseEnvelope {
return o.Payload
}
func (o *CreatePostureResponseBulkOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
o.Payload = new(rest_model.Empty)
o.Payload = new(rest_model.PostureResponseEnvelope)
// response payload
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
@@ -77,22 +77,22 @@ func NewCreatePostureResponseCreated() *CreatePostureResponseCreated {
/* CreatePostureResponseCreated describes a response with status code 201, with default header values.
The create request was successful and the resource has been added at the following location
Contains a list of services that have had their timers altered
*/
type CreatePostureResponseCreated struct {
Payload *rest_model.CreateEnvelope
Payload *rest_model.PostureResponseEnvelope
}
func (o *CreatePostureResponseCreated) Error() string {
return fmt.Sprintf("[POST /posture-response][%d] createPostureResponseCreated %+v", 201, o.Payload)
}
func (o *CreatePostureResponseCreated) GetPayload() *rest_model.CreateEnvelope {
func (o *CreatePostureResponseCreated) GetPayload() *rest_model.PostureResponseEnvelope {
return o.Payload
}
func (o *CreatePostureResponseCreated) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
o.Payload = new(rest_model.CreateEnvelope)
o.Payload = new(rest_model.PostureResponseEnvelope)
// response payload
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
+130 -10
View File
@@ -2238,9 +2238,9 @@ func init() {
],
"responses": {
"201": {
"description": "The create request was successful and the resource has been added at the following location",
"description": "Contains a list of services that have had their timers altered",
"schema": {
"$ref": "#/definitions/createEnvelope"
"$ref": "#/definitions/postureResponseEnvelope"
}
},
"400": {
@@ -2336,9 +2336,9 @@ func init() {
],
"responses": {
"200": {
"description": "Base empty response",
"description": "Contains a list of services that have had their timers altered",
"schema": {
"$ref": "#/definitions/empty"
"$ref": "#/definitions/postureResponseEnvelope"
}
},
"400": {
@@ -4952,6 +4952,20 @@ func init() {
}
}
},
"postureResponse": {
"type": "object",
"required": [
"services"
],
"properties": {
"services": {
"type": "array",
"items": {
"$ref": "#/definitions/postureResponseService"
}
}
}
},
"postureResponseCreate": {
"type": "object",
"required": [
@@ -5006,6 +5020,21 @@ func init() {
],
"x-class": "ENDPOINT_STATE"
},
"postureResponseEnvelope": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"data": {
"$ref": "#/definitions/postureResponse"
},
"meta": {
"$ref": "#/definitions/meta"
}
}
},
"postureResponseMacAddressCreate": {
"allOf": [
{
@@ -5085,6 +5114,33 @@ func init() {
],
"x-class": "PROCESS"
},
"postureResponseService": {
"type": "object",
"required": [
"id",
"name",
"postureQueryType",
"timeout",
"timeoutRemaining"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"postureQueryType": {
"type": "string"
},
"timeout": {
"type": "integer"
},
"timeoutRemaining": {
"type": "integer"
}
}
},
"protocol": {
"type": "object",
"required": [
@@ -5302,7 +5358,8 @@ func init() {
"serviceId",
"service",
"token",
"edgeRouters"
"edgeRouters",
"identityId"
],
"properties": {
"apiSession": {
@@ -5317,6 +5374,9 @@ func init() {
"$ref": "#/definitions/sessionEdgeRouter"
}
},
"identityId": {
"type": "string"
},
"service": {
"$ref": "#/definitions/entityRef"
},
@@ -7712,9 +7772,9 @@ func init() {
],
"responses": {
"201": {
"description": "The create request was successful and the resource has been added at the following location",
"description": "Contains a list of services that have had their timers altered",
"schema": {
"$ref": "#/definitions/createEnvelope"
"$ref": "#/definitions/postureResponseEnvelope"
}
},
"400": {
@@ -7810,9 +7870,9 @@ func init() {
],
"responses": {
"200": {
"description": "Base empty response",
"description": "Contains a list of services that have had their timers altered",
"schema": {
"$ref": "#/definitions/empty"
"$ref": "#/definitions/postureResponseEnvelope"
}
},
"400": {
@@ -10502,6 +10562,20 @@ func init() {
}
}
},
"postureResponse": {
"type": "object",
"required": [
"services"
],
"properties": {
"services": {
"type": "array",
"items": {
"$ref": "#/definitions/postureResponseService"
}
}
}
},
"postureResponseCreate": {
"type": "object",
"required": [
@@ -10556,6 +10630,21 @@ func init() {
],
"x-class": "ENDPOINT_STATE"
},
"postureResponseEnvelope": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"data": {
"$ref": "#/definitions/postureResponse"
},
"meta": {
"$ref": "#/definitions/meta"
}
}
},
"postureResponseMacAddressCreate": {
"allOf": [
{
@@ -10635,6 +10724,33 @@ func init() {
],
"x-class": "PROCESS"
},
"postureResponseService": {
"type": "object",
"required": [
"id",
"name",
"postureQueryType",
"timeout",
"timeoutRemaining"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"postureQueryType": {
"type": "string"
},
"timeout": {
"type": "integer"
},
"timeoutRemaining": {
"type": "integer"
}
}
},
"protocol": {
"type": "object",
"required": [
@@ -10852,7 +10968,8 @@ func init() {
"serviceId",
"service",
"token",
"edgeRouters"
"edgeRouters",
"identityId"
],
"properties": {
"apiSession": {
@@ -10867,6 +10984,9 @@ func init() {
"$ref": "#/definitions/sessionEdgeRouter"
}
},
"identityId": {
"type": "string"
},
"service": {
"$ref": "#/definitions/entityRef"
},
@@ -40,7 +40,7 @@ import (
// CreatePostureResponseBulkOKCode is the HTTP code returned for type CreatePostureResponseBulkOK
const CreatePostureResponseBulkOKCode int = 200
/*CreatePostureResponseBulkOK Base empty response
/*CreatePostureResponseBulkOK Contains a list of services that have had their timers altered
swagger:response createPostureResponseBulkOK
*/
@@ -49,7 +49,7 @@ type CreatePostureResponseBulkOK struct {
/*
In: Body
*/
Payload *rest_model.Empty `json:"body,omitempty"`
Payload *rest_model.PostureResponseEnvelope `json:"body,omitempty"`
}
// NewCreatePostureResponseBulkOK creates CreatePostureResponseBulkOK with default headers values
@@ -59,13 +59,13 @@ func NewCreatePostureResponseBulkOK() *CreatePostureResponseBulkOK {
}
// WithPayload adds the payload to the create posture response bulk o k response
func (o *CreatePostureResponseBulkOK) WithPayload(payload *rest_model.Empty) *CreatePostureResponseBulkOK {
func (o *CreatePostureResponseBulkOK) WithPayload(payload *rest_model.PostureResponseEnvelope) *CreatePostureResponseBulkOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the create posture response bulk o k response
func (o *CreatePostureResponseBulkOK) SetPayload(payload *rest_model.Empty) {
func (o *CreatePostureResponseBulkOK) SetPayload(payload *rest_model.PostureResponseEnvelope) {
o.Payload = payload
}
@@ -40,7 +40,7 @@ import (
// CreatePostureResponseCreatedCode is the HTTP code returned for type CreatePostureResponseCreated
const CreatePostureResponseCreatedCode int = 201
/*CreatePostureResponseCreated The create request was successful and the resource has been added at the following location
/*CreatePostureResponseCreated Contains a list of services that have had their timers altered
swagger:response createPostureResponseCreated
*/
@@ -49,7 +49,7 @@ type CreatePostureResponseCreated struct {
/*
In: Body
*/
Payload *rest_model.CreateEnvelope `json:"body,omitempty"`
Payload *rest_model.PostureResponseEnvelope `json:"body,omitempty"`
}
// NewCreatePostureResponseCreated creates CreatePostureResponseCreated with default headers values
@@ -59,13 +59,13 @@ func NewCreatePostureResponseCreated() *CreatePostureResponseCreated {
}
// WithPayload adds the payload to the create posture response created response
func (o *CreatePostureResponseCreated) WithPayload(payload *rest_model.CreateEnvelope) *CreatePostureResponseCreated {
func (o *CreatePostureResponseCreated) WithPayload(payload *rest_model.PostureResponseEnvelope) *CreatePostureResponseCreated {
o.Payload = payload
return o
}
// SetPayload sets the payload to the create posture response created response
func (o *CreatePostureResponseCreated) SetPayload(payload *rest_model.CreateEnvelope) {
func (o *CreatePostureResponseCreated) SetPayload(payload *rest_model.PostureResponseEnvelope) {
o.Payload = payload
}
+10 -2
View File
@@ -19745,7 +19745,8 @@ func init() {
"serviceId",
"service",
"token",
"edgeRouters"
"edgeRouters",
"identityId"
],
"properties": {
"apiSession": {
@@ -19760,6 +19761,9 @@ func init() {
"$ref": "#/definitions/sessionEdgeRouter"
}
},
"identityId": {
"type": "string"
},
"service": {
"$ref": "#/definitions/entityRef"
},
@@ -39979,7 +39983,8 @@ func init() {
"serviceId",
"service",
"token",
"edgeRouters"
"edgeRouters",
"identityId"
],
"properties": {
"apiSession": {
@@ -39994,6 +39999,9 @@ func init() {
"$ref": "#/definitions/sessionEdgeRouter"
}
},
"identityId": {
"type": "string"
},
"service": {
"$ref": "#/definitions/entityRef"
},
+139
View File
@@ -0,0 +1,139 @@
// Code generated by go-swagger; DO NOT EDIT.
//
// 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.
//
// __ __ _
// \ \ / / (_)
// \ \ /\ / /_ _ _ __ _ __ _ _ __ __ _
// \ \/ \/ / _` | '__| '_ \| | '_ \ / _` |
// \ /\ / (_| | | | | | | | | | | (_| | : This file is generated, do not edit it.
// \/ \/ \__,_|_| |_| |_|_|_| |_|\__, |
// __/ |
// |___/
package rest_model
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// PostureResponse posture response
//
// swagger:model postureResponse
type PostureResponse struct {
// services
// Required: true
Services []*PostureResponseService `json:"services"`
}
// Validate validates this posture response
func (m *PostureResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateServices(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PostureResponse) validateServices(formats strfmt.Registry) error {
if err := validate.Required("services", "body", m.Services); err != nil {
return err
}
for i := 0; i < len(m.Services); i++ {
if swag.IsZero(m.Services[i]) { // not required
continue
}
if m.Services[i] != nil {
if err := m.Services[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("services" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this posture response based on the context it is used
func (m *PostureResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateServices(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PostureResponse) contextValidateServices(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.Services); i++ {
if m.Services[i] != nil {
if err := m.Services[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("services" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *PostureResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PostureResponse) UnmarshalBinary(b []byte) error {
var res PostureResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
+171
View File
@@ -0,0 +1,171 @@
// Code generated by go-swagger; DO NOT EDIT.
//
// 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.
//
// __ __ _
// \ \ / / (_)
// \ \ /\ / /_ _ _ __ _ __ _ _ __ __ _
// \ \/ \/ / _` | '__| '_ \| | '_ \ / _` |
// \ /\ / (_| | | | | | | | | | | (_| | : This file is generated, do not edit it.
// \/ \/ \__,_|_| |_| |_|_|_| |_|\__, |
// __/ |
// |___/
package rest_model
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// PostureResponseEnvelope posture response envelope
//
// swagger:model postureResponseEnvelope
type PostureResponseEnvelope struct {
// data
// Required: true
Data *PostureResponse `json:"data"`
// meta
// Required: true
Meta *Meta `json:"meta"`
}
// Validate validates this posture response envelope
func (m *PostureResponseEnvelope) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateData(formats); err != nil {
res = append(res, err)
}
if err := m.validateMeta(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PostureResponseEnvelope) validateData(formats strfmt.Registry) error {
if err := validate.Required("data", "body", m.Data); err != nil {
return err
}
if m.Data != nil {
if err := m.Data.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
}
return err
}
}
return nil
}
func (m *PostureResponseEnvelope) validateMeta(formats strfmt.Registry) error {
if err := validate.Required("meta", "body", m.Meta); err != nil {
return err
}
if m.Meta != nil {
if err := m.Meta.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("meta")
}
return err
}
}
return nil
}
// ContextValidate validate this posture response envelope based on the context it is used
func (m *PostureResponseEnvelope) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateData(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateMeta(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PostureResponseEnvelope) contextValidateData(ctx context.Context, formats strfmt.Registry) error {
if m.Data != nil {
if err := m.Data.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
}
return err
}
}
return nil
}
func (m *PostureResponseEnvelope) contextValidateMeta(ctx context.Context, formats strfmt.Registry) error {
if m.Meta != nil {
if err := m.Meta.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("meta")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *PostureResponseEnvelope) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PostureResponseEnvelope) UnmarshalBinary(b []byte) error {
var res PostureResponseEnvelope
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
+163
View File
@@ -0,0 +1,163 @@
// Code generated by go-swagger; DO NOT EDIT.
//
// 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.
//
// __ __ _
// \ \ / / (_)
// \ \ /\ / /_ _ _ __ _ __ _ _ __ __ _
// \ \/ \/ / _` | '__| '_ \| | '_ \ / _` |
// \ /\ / (_| | | | | | | | | | | (_| | : This file is generated, do not edit it.
// \/ \/ \__,_|_| |_| |_|_|_| |_|\__, |
// __/ |
// |___/
package rest_model
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// PostureResponseService posture response service
//
// swagger:model postureResponseService
type PostureResponseService struct {
// id
// Required: true
ID *string `json:"id"`
// name
// Required: true
Name *string `json:"name"`
// posture query type
// Required: true
PostureQueryType *string `json:"postureQueryType"`
// timeout
// Required: true
Timeout *int64 `json:"timeout"`
// timeout remaining
// Required: true
TimeoutRemaining *int64 `json:"timeoutRemaining"`
}
// Validate validates this posture response service
func (m *PostureResponseService) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateID(formats); err != nil {
res = append(res, err)
}
if err := m.validateName(formats); err != nil {
res = append(res, err)
}
if err := m.validatePostureQueryType(formats); err != nil {
res = append(res, err)
}
if err := m.validateTimeout(formats); err != nil {
res = append(res, err)
}
if err := m.validateTimeoutRemaining(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *PostureResponseService) validateID(formats strfmt.Registry) error {
if err := validate.Required("id", "body", m.ID); err != nil {
return err
}
return nil
}
func (m *PostureResponseService) validateName(formats strfmt.Registry) error {
if err := validate.Required("name", "body", m.Name); err != nil {
return err
}
return nil
}
func (m *PostureResponseService) validatePostureQueryType(formats strfmt.Registry) error {
if err := validate.Required("postureQueryType", "body", m.PostureQueryType); err != nil {
return err
}
return nil
}
func (m *PostureResponseService) validateTimeout(formats strfmt.Registry) error {
if err := validate.Required("timeout", "body", m.Timeout); err != nil {
return err
}
return nil
}
func (m *PostureResponseService) validateTimeoutRemaining(formats strfmt.Registry) error {
if err := validate.Required("timeoutRemaining", "body", m.TimeoutRemaining); err != nil {
return err
}
return nil
}
// ContextValidate validates this posture response service based on context it is used
func (m *PostureResponseService) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *PostureResponseService) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PostureResponseService) UnmarshalBinary(b []byte) error {
var res PostureResponseService
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
+25
View File
@@ -57,6 +57,10 @@ type SessionDetail struct {
// Required: true
EdgeRouters []*SessionEdgeRouter `json:"edgeRouters"`
// identity Id
// Required: true
IdentityID *string `json:"identityId"`
// service
// Required: true
Service *EntityRef `json:"service"`
@@ -91,6 +95,8 @@ func (m *SessionDetail) UnmarshalJSON(raw []byte) error {
EdgeRouters []*SessionEdgeRouter `json:"edgeRouters"`
IdentityID *string `json:"identityId"`
Service *EntityRef `json:"service"`
ServiceID *string `json:"serviceId"`
@@ -109,6 +115,8 @@ func (m *SessionDetail) UnmarshalJSON(raw []byte) error {
m.EdgeRouters = dataAO1.EdgeRouters
m.IdentityID = dataAO1.IdentityID
m.Service = dataAO1.Service
m.ServiceID = dataAO1.ServiceID
@@ -136,6 +144,8 @@ func (m SessionDetail) MarshalJSON() ([]byte, error) {
EdgeRouters []*SessionEdgeRouter `json:"edgeRouters"`
IdentityID *string `json:"identityId"`
Service *EntityRef `json:"service"`
ServiceID *string `json:"serviceId"`
@@ -151,6 +161,8 @@ func (m SessionDetail) MarshalJSON() ([]byte, error) {
dataAO1.EdgeRouters = m.EdgeRouters
dataAO1.IdentityID = m.IdentityID
dataAO1.Service = m.Service
dataAO1.ServiceID = m.ServiceID
@@ -188,6 +200,10 @@ func (m *SessionDetail) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateIdentityID(formats); err != nil {
res = append(res, err)
}
if err := m.validateService(formats); err != nil {
res = append(res, err)
}
@@ -262,6 +278,15 @@ func (m *SessionDetail) validateEdgeRouters(formats strfmt.Registry) error {
return nil
}
func (m *SessionDetail) validateIdentityID(formats strfmt.Registry) error {
if err := validate.Required("identityId", "body", m.IdentityID); err != nil {
return err
}
return nil
}
func (m *SessionDetail) validateService(formats strfmt.Registry) error {
if err := validate.Required("service", "body", m.Service); err != nil {
+45 -5
View File
@@ -1685,10 +1685,9 @@ paths:
$ref: '#/definitions/postureResponseCreate'
responses:
"201":
description: The create request was successful and the resource has been
added at the following location
description: Contains a list of services that have had their timers altered
schema:
$ref: '#/definitions/createEnvelope'
$ref: '#/definitions/postureResponseEnvelope'
"400":
description: The supplied request contains invalid fields or could not be
parsed (json and non-json bodies). The error's code, message, and cause
@@ -1757,9 +1756,9 @@ paths:
$ref: '#/definitions/postureResponseCreate'
responses:
"200":
description: Base empty response
description: Contains a list of services that have had their timers altered
schema:
$ref: '#/definitions/empty'
$ref: '#/definitions/postureResponseEnvelope'
"400":
description: The supplied request contains invalid fields or could not be
parsed (json and non-json bodies). The error's code, message, and cause
@@ -3566,6 +3565,15 @@ definitions:
$ref: '#/definitions/osType'
path:
type: string
postureResponse:
type: object
required:
- services
properties:
services:
type: array
items:
$ref: '#/definitions/postureResponseService'
postureResponseCreate:
type: object
required:
@@ -3597,6 +3605,16 @@ definitions:
woken:
type: boolean
x-class: ENDPOINT_STATE
postureResponseEnvelope:
type: object
required:
- meta
- data
properties:
data:
$ref: '#/definitions/postureResponse'
meta:
$ref: '#/definitions/meta'
postureResponseMacAddressCreate:
allOf:
- $ref: '#/definitions/postureResponseCreate'
@@ -3642,6 +3660,25 @@ definitions:
items:
type: string
x-class: PROCESS
postureResponseService:
type: object
required:
- id
- name
- postureQueryType
- timeout
- timeoutRemaining
properties:
id:
type: string
name:
type: string
postureQueryType:
type: string
timeout:
type: integer
timeoutRemaining:
type: integer
protocol:
type: object
required:
@@ -3789,6 +3826,7 @@ definitions:
- service
- token
- edgeRouters
- identityId
properties:
apiSession:
$ref: '#/definitions/entityRef'
@@ -3798,6 +3836,8 @@ definitions:
type: array
items:
$ref: '#/definitions/sessionEdgeRouter'
identityId:
type: string
service:
$ref: '#/definitions/entityRef'
serviceId:
+3
View File
@@ -13995,6 +13995,7 @@ definitions:
- service
- token
- edgeRouters
- identityId
properties:
apiSession:
$ref: '#/definitions/entityRef'
@@ -14004,6 +14005,8 @@ definitions:
type: array
items:
$ref: '#/definitions/sessionEdgeRouter'
identityId:
type: string
service:
$ref: '#/definitions/entityRef'
serviceId:
+44 -2
View File
@@ -18,7 +18,7 @@ paths:
$ref: '#/definitions/postureResponseCreate'
responses:
'201':
$ref: '../shared/standard-responses.yml#/responses/createResponse'
$ref: '#/responses/postureResponse'
'400':
$ref: '../shared/standard-responses.yml#/responses/badRequestResponse'
'401':
@@ -44,14 +44,56 @@ paths:
$ref: "#/definitions/postureResponseCreate"
responses:
'200':
$ref: '../shared/standard-responses.yml#/responses/emptyResponse'
$ref: '#/responses/postureResponse'
'400':
$ref: '../shared/standard-responses.yml#/responses/badRequestResponse'
'401':
$ref: '../shared/standard-responses.yml#/responses/unauthorizedResponse'
responses:
postureResponse:
description: Contains a list of services that have had their timers altered
schema:
$ref: '#/definitions/postureResponseEnvelope'
definitions:
postureResponseEnvelope:
type: object
required:
- meta
- data
properties:
meta:
$ref: '../shared/standard-responses.yml#/definitions/meta'
data:
$ref: '#/definitions/postureResponse'
postureResponseService:
type: object
required:
- id
- name
- postureQueryType
- timeout
- timeoutRemaining
properties:
id:
type: string
name:
type: string
postureQueryType:
type: string
timeout:
type: integer
timeoutRemaining:
type: integer
postureResponse:
type: object
required:
- services
properties:
services:
type: array
items:
$ref: '#/definitions/postureResponseService'
postureResponseCreate:
type: object
discriminator: typeId
+3
View File
@@ -50,6 +50,7 @@ definitions:
- service
- token
- edgeRouters
- identityId
properties:
type:
$ref: 'base-entity.yml#/definitions/dialBind'
@@ -67,6 +68,8 @@ definitions:
type: array
items:
$ref: '#/definitions/sessionEdgeRouter'
identityId:
type: string
sessionEdgeRouter:
allOf:
- $ref: "edge-routers.yml#/definitions/commonEdgeRouterProperties"
+2 -1
View File
@@ -578,7 +578,8 @@ func (request *authenticatedRequests) requireNewPostureCheckProcessMulti(semanti
func (request *authenticatedRequests) requireNewService(roleAttributes, configs []string) *service {
service := request.testContext.newService(roleAttributes, configs)
request.requireCreateEntity(service)
id := request.requireCreateEntity(service)
service.Id = id
return service
}
+49
View File
@@ -449,6 +449,23 @@ func Test_PostureChecks_MFA(t *testing.T) {
ctx.Req.NoError(err)
standardJsonResponseTests(resp, http.StatusCreated, t)
t.Run("woke posture response should include service timeout change event", func(t *testing.T) {
ctx.testContextChanged(t)
responseEnvelope := &rest_model.PostureResponseEnvelope{}
err := responseEnvelope.UnmarshalBinary(resp.Body())
ctx.Req.NoError(err)
ctx.Req.Len(responseEnvelope.Data.Services, 1)
ctx.Req.Equal(origTimeout, *responseEnvelope.Data.Services[0].Timeout)
ctx.Req.Equal(service.Name, *responseEnvelope.Data.Services[0].Name)
ctx.Req.Equal(service.Id, *responseEnvelope.Data.Services[0].ID)
ctx.Req.Equal("MFA", *responseEnvelope.Data.Services[0].PostureQueryType)
ctx.Req.Greater(*responseEnvelope.Data.Services[0].TimeoutRemaining, int64(1))
ctx.Req.LessOrEqual(*responseEnvelope.Data.Services[0].TimeoutRemaining, int64(300))
})
t.Run("service has the posture check in its queries", func(t *testing.T) {
ctx.testContextChanged(t)
code, body := enrolledIdentitySession.query("/services/" + service.Id)
@@ -728,6 +745,19 @@ func Test_PostureChecks_MFA(t *testing.T) {
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
resp, err = newSession.NewRequest().Get("/current-api-session/service-updates")
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
serviceUpdateContainer, err := gabs.ParseJSON(resp.Body())
ctx.Req.NoError(err)
ctx.Req.True(serviceUpdateContainer.ExistsP("data.lastChangeAt"))
lastChangeAt, ok := serviceUpdateContainer.Path("data.lastChangeAt").Data().(string)
ctx.Req.True(ok)
ctx.Req.NotEmpty(lastChangeAt)
t.Run("after the MFA posture check timeout", func(t *testing.T) {
ctx.testContextChanged(t)
@@ -736,6 +766,25 @@ func Test_PostureChecks_MFA(t *testing.T) {
time.Sleep(durationTillTimeout)
}
t.Run("the last updated at serviced endpoint has changed", func(t *testing.T) {
ctx.testContextChanged(t)
resp, err = newSession.NewRequest().Get("/current-api-session/service-updates")
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
serviceUpdateContainer, err := gabs.ParseJSON(resp.Body())
ctx.Req.NoError(err)
ctx.Req.True(serviceUpdateContainer.ExistsP("data.lastChangeAt"))
newLastChangeAt, ok := serviceUpdateContainer.Path("data.lastChangeAt").Data().(string)
ctx.Req.True(ok)
ctx.Req.NotEmpty(newLastChangeAt)
ctx.Req.NotEqual(newLastChangeAt, lastChangeAt)
})
t.Run("the existing session was removed", func(t *testing.T) {
ctx.testContextChanged(t)