mirror of
https://github.com/openziti/ziti.git
synced 2026-10-15 00:22:01 +00:00
Merge pull request #762 from openziti/fix.posture.timeouts
fixes posture check timeouts, tests, service updates
This commit is contained in:
Vendored
+3
-4
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
+4
-4
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user