Merge pull request #719 from openziti/add.timeout.remaining.for.posture.checks

adds timeoutRemainingSeconds for posture queries
This commit is contained in:
Andrew
2021-07-22 13:47:38 -04:00
committed by GitHub
17 changed files with 194 additions and 36 deletions
@@ -191,9 +191,12 @@ func MapServiceToRestModel(ae *env.AppEnv, rc *response.RequestContext, service
isCheckPassing, _ = ae.Handlers.PostureResponse.Evaluate(rc.Identity.Id, rc.ApiSession.Id, postureCheck)
validChecks[postureCheck.Id] = isCheckPassing
}
timeoutSeconds := postureCheck.TimeoutSeconds(rc.ApiSession.Id, ae.Handlers.PostureResponse.PostureData(rc.Identity.Id))
timeout := postureCheck.TimeoutSeconds()
timeoutRemaining := postureCheck.TimeoutRemainingSeconds(rc.ApiSession.Id, ae.Handlers.PostureResponse.PostureData(rc.Identity.Id))
query.IsPassing = &isCheckPassing
query.Timeout = &timeoutSeconds
query.TimeoutRemaining = &timeoutRemaining
query.Timeout = &timeout
querySet.PostureQueries = append(querySet.PostureQueries, query)
if !isCheckPassing {
+8 -3
View File
@@ -42,7 +42,8 @@ type PostureCheckSubType interface {
fillFrom(handler Handler, tx *bbolt.Tx, check *persistence.PostureCheck, subType persistence.PostureCheckSubType) error
Evaluate(apiSessionId string, pd *PostureData) bool
FailureValues(_ string, pd *PostureData) PostureCheckFailureValues
GetTimeoutSeconds(apiSessionId string, pd *PostureData) int64
GetTimeoutSeconds() int64
GetTimeoutRemainingSeconds(apiSessionId string, pd *PostureData) int64
}
type PostureCheckFailureValues interface {
@@ -141,6 +142,10 @@ func (entity *PostureCheck) Evaluate(apiSessionId string, pd *PostureData) (bool
return true, nil
}
func (entity *PostureCheck) TimeoutSeconds(apiSessionId string, pd *PostureData) int64 {
return entity.SubType.GetTimeoutSeconds(apiSessionId, pd)
func (entity *PostureCheck) TimeoutSeconds() int64 {
return entity.SubType.GetTimeoutSeconds()
}
func (entity *PostureCheck) TimeoutRemainingSeconds(apiSessionId string, pd *PostureData) int64 {
return entity.SubType.GetTimeoutRemainingSeconds(apiSessionId, pd)
}
+4 -1
View File
@@ -27,8 +27,11 @@ var _ PostureCheckSubType = &PostureCheckMacAddresses{}
type PostureCheckMacAddresses struct {
MacAddresses []string
}
func (p *PostureCheckMacAddresses) GetTimeoutRemainingSeconds(_ string, _ *PostureData) int64 {
return PostureCheckNoTimeout
}
func (p *PostureCheckMacAddresses) GetTimeoutSeconds(_ string, _ *PostureData) int64 {
func (p *PostureCheckMacAddresses) GetTimeoutSeconds() int64 {
return PostureCheckNoTimeout
}
+22 -13
View File
@@ -51,13 +51,22 @@ func (p *PostureCheckMfa) IsLegacyClient(apiSessionData *ApiSessionPostureData)
return false
}
func (p *PostureCheckMfa) GetTimeoutSeconds(apiSessionId string, pd *PostureData) int64 {
func (p *PostureCheckMfa) GetTimeoutSeconds() int64 {
if p.TimeoutSeconds > 0 {
return p.TimeoutSeconds
}
return PostureCheckNoTimeout
}
func (p *PostureCheckMfa) GetTimeoutRemainingSeconds(apiSessionId string, pd *PostureData) int64 {
if p.TimeoutSeconds == PostureCheckNoTimeout {
return PostureCheckNoTimeout
}
apiSessionData := pd.ApiSessions[apiSessionId]
// no MFA data, return 0 as we have a timeout of some value
if apiSessionData == nil || apiSessionData.Mfa == nil || apiSessionData.Mfa.PassedMfaAt == nil {
return 0
}
@@ -69,36 +78,36 @@ func (p *PostureCheckMfa) GetTimeoutSeconds(apiSessionId string, pd *PostureData
timeSinceLastMfa := time.Now().Sub(*apiSessionData.Mfa.PassedMfaAt)
timeout := p.TimeoutSeconds - int64(timeSinceLastMfa.Seconds())
if timeout < 0 {
timeoutRemaining := p.TimeoutSeconds - int64(timeSinceLastMfa.Seconds())
if timeoutRemaining <= 0 {
return 0
}
if p.PromptOnWake && apiSessionData.EndpointState != nil && apiSessionData.EndpointState.WokenAt != nil {
onWakeTimeOut := int64(time.Now().Sub(apiSessionData.EndpointState.WokenAt.Add(MfaPromptGracePeriod)).Seconds())
if onWakeTimeOut < 0 {
if onWakeTimeOut <= 0 {
onWakeTimeOut = 0
}
if onWakeTimeOut < timeout {
timeout = onWakeTimeOut
if onWakeTimeOut < timeoutRemaining {
timeoutRemaining = onWakeTimeOut
}
}
if p.PromptOnUnlock && apiSessionData.EndpointState != nil && apiSessionData.EndpointState.UnlockedAt != nil {
onUnlockTimeOut := int64(time.Now().Sub(apiSessionData.EndpointState.UnlockedAt.Add(MfaPromptGracePeriod)).Seconds())
if onUnlockTimeOut < 0 {
if onUnlockTimeOut <= 0 {
onUnlockTimeOut = 0
}
if onUnlockTimeOut < timeout {
timeout = onUnlockTimeOut
if onUnlockTimeOut < timeoutRemaining {
timeoutRemaining = onUnlockTimeOut
}
}
return timeout
return timeoutRemaining
}
func (p *PostureCheckMfa) FailureValues(apiSessionId string, pd *PostureData) PostureCheckFailureValues {
@@ -146,10 +155,10 @@ func (p *PostureCheckMfa) Evaluate(apiSessionId string, pd *PostureData) bool {
return false
}
now := time.Now().UTC()
timeoutSeconds := p.GetTimeoutSeconds(apiSessionId, pd)
timeoutRemainingSeconds := p.GetTimeoutRemainingSeconds(apiSessionId, pd)
if timeoutSeconds != PostureCheckNoTimeout {
expiresAt := apiSessionData.Mfa.PassedMfaAt.Add(time.Duration(timeoutSeconds) * time.Second)
if timeoutRemainingSeconds != PostureCheckNoTimeout {
expiresAt := apiSessionData.Mfa.PassedMfaAt.Add(time.Duration(timeoutRemainingSeconds) * time.Second)
if expiresAt.Before(now) {
apiSessionData.Mfa.TimedOut = true
}
@@ -407,7 +407,7 @@ func TestPostureCheckModelMfa(t *testing.T) {
req.True(failureValues.ActualValue.TimedOutSeconds)
})
})
t.Run("IsLegacyClient", func(t *testing.T) {
t.Run("returns false for ziti-sdk-c and 0.25.0", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
@@ -478,6 +478,80 @@ func TestPostureCheckModelMfa(t *testing.T) {
req.False(result)
})
})
t.Run("GetTimeoutSeconds", func(t *testing.T) {
t.Run("returns the static timeout value", func(t *testing.T) {
mfaCheck, _ := newMfaCheckAndPostureData()
req := require.New(t)
req.Equal(mfaCheck.TimeoutSeconds, mfaCheck.GetTimeoutSeconds())
})
t.Run("returns no timeout for negative numbers", func(t *testing.T) {
mfaCheck, _ := newMfaCheckAndPostureData()
mfaCheck.TimeoutSeconds = -100
req := require.New(t)
req.Equal(PostureCheckNoTimeout, mfaCheck.GetTimeoutSeconds())
})
t.Run("returns no timeout for 0", func(t *testing.T) {
mfaCheck, _ := newMfaCheckAndPostureData()
mfaCheck.TimeoutSeconds = 0
req := require.New(t)
req.Equal(PostureCheckNoTimeout, mfaCheck.GetTimeoutSeconds())
})
})
t.Run("GetTimeoutRemainingSeconds", func(t *testing.T) {
t.Run("returns 0 if no ApiSessionData is present", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
postureData.ApiSessions = map[string]*ApiSessionPostureData{}
req := require.New(t)
req.Equal(int64(0), mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData))
})
t.Run("returns 0 if no MFA data is present", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
postureData.ApiSessions[mfaTestApiSessionId].Mfa = nil
req := require.New(t)
req.Equal(int64(0), mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData))
})
t.Run("returns 0 if no last MFA passedAt is nil", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
postureData.ApiSessions[mfaTestApiSessionId].Mfa.PassedMfaAt = nil
req := require.New(t)
req.Equal(int64(0), mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData))
})
t.Run("returns near timeout if much time hasn't passed", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
req := require.New(t)
//time based, if on a slow machine this might slide
req.LessOrEqual(mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData), mfaCheck.TimeoutSeconds)
req.GreaterOrEqual(mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData), mfaCheck.TimeoutSeconds-5)
})
t.Run("returns not timeout for legacy clients if ignore legacy is true", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
mfaCheck.IgnoreLegacyEndpoints = true
postureData.ApiSessions[mfaTestApiSessionId].SdkInfo.Version = "0.24.4"
req := require.New(t)
req.Equal(PostureCheckNoTimeout, mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData))
})
t.Run("returns near timeout for legacy client if ignore legacy is false", func(t *testing.T) {
mfaCheck, postureData := newMfaCheckAndPostureData()
postureData.ApiSessions[mfaTestApiSessionId].SdkInfo.Version = "0.24.4"
req := require.New(t)
//time based, if on a slow machine this might slide
req.LessOrEqual(mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData), mfaCheck.TimeoutSeconds)
req.GreaterOrEqual(mfaCheck.GetTimeoutRemainingSeconds(mfaTestApiSessionId, postureData), mfaCheck.TimeoutSeconds-5)
})
})
}
// newMfaCheckAndPostureData returns a MFA posture check and posture data that will
@@ -516,8 +590,8 @@ func newMfaCheckAndPostureData() (*PostureCheckMfa, *PostureData) {
UnlockedAt: &unlockedAt,
},
SdkInfo: &SdkInfo{
Type: "ziti-sdk-c",
Version: "0.25.0",
Type: "ziti-sdk-c",
Version: "0.25.0",
},
},
},
+5 -1
View File
@@ -32,7 +32,11 @@ type PostureCheckOperatingSystem struct {
OperatingSystems []OperatingSystem
}
func (p *PostureCheckOperatingSystem) GetTimeoutSeconds(_ string, _ *PostureData) int64 {
func (p *PostureCheckOperatingSystem) GetTimeoutRemainingSeconds(_ string, _ *PostureData) int64 {
return PostureCheckNoTimeout
}
func (p *PostureCheckOperatingSystem) GetTimeoutSeconds() int64 {
return PostureCheckNoTimeout
}
@@ -33,7 +33,11 @@ type PostureCheckProcess struct {
Fingerprint string
}
func (p *PostureCheckProcess) GetTimeoutSeconds(_ string, _ *PostureData) int64 {
func (p *PostureCheckProcess) GetTimeoutSeconds() int64 {
return PostureCheckNoTimeout
}
func (p *PostureCheckProcess) GetTimeoutRemainingSeconds(_ string, _ *PostureData) int64 {
return PostureCheckNoTimeout
}
@@ -32,7 +32,11 @@ type PostureCheckProcessMulti struct {
Processes []*ProcessMulti
}
func (p *PostureCheckProcessMulti) GetTimeoutSeconds(_ string, _ *PostureData) int64 {
func (p *PostureCheckProcessMulti) GetTimeoutSeconds() int64 {
return PostureCheckNoTimeout
}
func (p *PostureCheckProcessMulti) GetTimeoutRemainingSeconds(_ string, _ *PostureData) int64 {
return PostureCheckNoTimeout
}
@@ -29,7 +29,11 @@ type PostureCheckDomains struct {
Domains []string
}
func (p *PostureCheckDomains) GetTimeoutSeconds(_ string, _ *PostureData) int64 {
func (p *PostureCheckDomains) GetTimeoutSeconds() int64 {
return PostureCheckNoTimeout
}
func (p *PostureCheckDomains) GetTimeoutRemainingSeconds(_ string, _ *PostureData) int64 {
return PostureCheckNoTimeout
}
+1 -1
View File
@@ -219,7 +219,7 @@ func (pc *PostureCache) SessionCreated(args ...interface{}) {
if exist {
pd = valueInMap.(*PostureData)
} else {
pd = newPostureData()
pd = newValue.(*PostureData)
}
if pd.ApiSessions == nil {
+10 -2
View File
@@ -4703,7 +4703,8 @@ func init() {
"required": [
"queryType",
"isPassing",
"timeout"
"timeout",
"timeoutRemaining"
],
"properties": {
"isPassing": {
@@ -4723,6 +4724,9 @@ func init() {
},
"timeout": {
"type": "integer"
},
"timeoutRemaining": {
"type": "integer"
}
}
}
@@ -10040,7 +10044,8 @@ func init() {
"required": [
"queryType",
"isPassing",
"timeout"
"timeout",
"timeoutRemaining"
],
"properties": {
"isPassing": {
@@ -10060,6 +10065,9 @@ func init() {
},
"timeout": {
"type": "integer"
},
"timeoutRemaining": {
"type": "integer"
}
}
}
+10 -2
View File
@@ -17635,7 +17635,8 @@ func init() {
"required": [
"queryType",
"isPassing",
"timeout"
"timeout",
"timeoutRemaining"
],
"properties": {
"isPassing": {
@@ -17655,6 +17656,9 @@ func init() {
},
"timeout": {
"type": "integer"
},
"timeoutRemaining": {
"type": "integer"
}
}
}
@@ -36455,7 +36459,8 @@ func init() {
"required": [
"queryType",
"isPassing",
"timeout"
"timeout",
"timeoutRemaining"
],
"properties": {
"isPassing": {
@@ -36475,6 +36480,9 @@ func init() {
},
"timeout": {
"type": "integer"
},
"timeoutRemaining": {
"type": "integer"
}
}
}
+25
View File
@@ -62,6 +62,10 @@ type PostureQuery struct {
// timeout
// Required: true
Timeout *int64 `json:"timeout"`
// timeout remaining
// Required: true
TimeoutRemaining *int64 `json:"timeoutRemaining"`
}
// UnmarshalJSON unmarshals this object from a JSON structure
@@ -84,6 +88,8 @@ func (m *PostureQuery) UnmarshalJSON(raw []byte) error {
QueryType *PostureCheckType `json:"queryType"`
Timeout *int64 `json:"timeout"`
TimeoutRemaining *int64 `json:"timeoutRemaining"`
}
if err := swag.ReadJSON(raw, &dataAO1); err != nil {
return err
@@ -99,6 +105,8 @@ func (m *PostureQuery) UnmarshalJSON(raw []byte) error {
m.Timeout = dataAO1.Timeout
m.TimeoutRemaining = dataAO1.TimeoutRemaining
return nil
}
@@ -121,6 +129,8 @@ func (m PostureQuery) MarshalJSON() ([]byte, error) {
QueryType *PostureCheckType `json:"queryType"`
Timeout *int64 `json:"timeout"`
TimeoutRemaining *int64 `json:"timeoutRemaining"`
}
dataAO1.IsPassing = m.IsPassing
@@ -133,6 +143,8 @@ func (m PostureQuery) MarshalJSON() ([]byte, error) {
dataAO1.Timeout = m.Timeout
dataAO1.TimeoutRemaining = m.TimeoutRemaining
jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1)
if errAO1 != nil {
return nil, errAO1
@@ -170,6 +182,10 @@ func (m *PostureQuery) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateTimeoutRemaining(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -259,6 +275,15 @@ func (m *PostureQuery) validateTimeout(formats strfmt.Registry) error {
return nil
}
func (m *PostureQuery) validateTimeoutRemaining(formats strfmt.Registry) error {
if err := validate.Required("timeoutRemaining", "body", m.TimeoutRemaining); err != nil {
return err
}
return nil
}
// ContextValidate validate this posture query based on the context it is used
func (m *PostureQuery) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
+3
View File
@@ -3379,6 +3379,7 @@ definitions:
- queryType
- isPassing
- timeout
- timeoutRemaining
properties:
isPassing:
type: boolean
@@ -3392,6 +3393,8 @@ definitions:
$ref: '#/definitions/postureCheckType'
timeout:
type: integer
timeoutRemaining:
type: integer
postureQueryProcess:
type: object
properties:
+3
View File
@@ -12437,6 +12437,7 @@ definitions:
- queryType
- isPassing
- timeout
- timeoutRemaining
properties:
isPassing:
type: boolean
@@ -12450,6 +12451,8 @@ definitions:
$ref: '#/definitions/postureCheckType'
timeout:
type: integer
timeoutRemaining:
type: integer
postureQueryProcess:
type: object
properties:
+3
View File
@@ -27,11 +27,14 @@ definitions:
- queryType
- isPassing
- timeout
- timeoutRemaining
properties:
isPassing:
type: boolean
timeout:
type: integer
timeoutRemaining:
type: integer
queryType:
$ref: 'posture-checks.yml#/definitions/postureCheckType'
process:
+3 -5
View File
@@ -333,7 +333,7 @@ func Test_PostureChecks_MFA(t *testing.T) {
ctx.testContextChanged(t)
ctx.Req.False(querySet[0].Path("isPassing").Data().(bool))
ctx.Req.False(postureQueries[0].Path("isPassing").Data().(bool))
ctx.Req.Equal(0, int(postureQueries[0].Path("timeout").Data().(float64)))
ctx.Req.Equal(0, int(postureQueries[0].Path("timeoutRemaining").Data().(float64)))
})
})
@@ -470,13 +470,11 @@ func Test_PostureChecks_MFA(t *testing.T) {
ctx.testContextChanged(t)
ctx.Req.True(querySet[0].Path("isPassing").Data().(bool))
ctx.Req.True(postureQueries[0].Path("isPassing").Data().(bool))
ctx.Req.Greater(int(postureQueries[0].Path("timeout").Data().(float64)), 0)
ctx.Req.LessOrEqual(int(postureQueries[0].Path("timeout").Data().(float64)), 300)
ctx.Req.Greater(int(postureQueries[0].Path("timeoutRemaining").Data().(float64)), 0)
ctx.Req.LessOrEqual(int(postureQueries[0].Path("timeoutRemaining").Data().(float64)), 300)
})
})
})
})
t.Run("a new api session can pass MFA auth to create service session if there are no endpoint state changes", func(t *testing.T) {