mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-13 08:13:09 +02:00
* handle device states in deny block, fix default device type * fix tests Co-authored-by: Caleb Doxsey <cdoxsey@pomerium.com>
This commit is contained in:
parent
2850df0e6a
commit
3cb65e013a
4 changed files with 41 additions and 46 deletions
|
@ -39,15 +39,28 @@ func (a *Authorize) handleResultDenied(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
in *envoy_service_auth_v3.CheckRequest,
|
in *envoy_service_auth_v3.CheckRequest,
|
||||||
result *evaluator.Result,
|
result *evaluator.Result,
|
||||||
|
isForwardAuthVerify bool,
|
||||||
|
reasons criteria.Reasons,
|
||||||
) (*envoy_service_auth_v3.CheckResponse, error) {
|
) (*envoy_service_auth_v3.CheckResponse, error) {
|
||||||
denyStatusCode := int32(http.StatusForbidden)
|
denyStatusCode := int32(http.StatusForbidden)
|
||||||
denyStatusText := http.StatusText(http.StatusForbidden)
|
denyStatusText := http.StatusText(http.StatusForbidden)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case result.Deny.Reasons.Has(criteria.ReasonRouteNotFound):
|
case reasons.Has(criteria.ReasonUserUnauthenticated):
|
||||||
|
// when the user is unauthenticated it means they haven't
|
||||||
|
// logged in yet, so redirect to authenticate
|
||||||
|
return a.requireLoginResponse(ctx, in, isForwardAuthVerify)
|
||||||
|
case reasons.Has(criteria.ReasonDeviceUnauthenticated):
|
||||||
|
// when the user's device is unauthenticated it means they haven't
|
||||||
|
// registered a webauthn device yet, so redirect to the webauthn flow
|
||||||
|
return a.requireWebAuthnResponse(ctx, in, result, isForwardAuthVerify)
|
||||||
|
case reasons.Has(criteria.ReasonDeviceUnauthorized):
|
||||||
|
denyStatusCode = httputil.StatusDeviceUnauthorized
|
||||||
|
denyStatusText = httputil.DetailsText(httputil.StatusDeviceUnauthorized)
|
||||||
|
case reasons.Has(criteria.ReasonRouteNotFound):
|
||||||
denyStatusCode = http.StatusNotFound
|
denyStatusCode = http.StatusNotFound
|
||||||
denyStatusText = httputil.DetailsText(http.StatusNotFound)
|
denyStatusText = httputil.DetailsText(http.StatusNotFound)
|
||||||
case result.Deny.Reasons.Has(criteria.ReasonInvalidClientCertificate):
|
case reasons.Has(criteria.ReasonInvalidClientCertificate):
|
||||||
denyStatusCode = httputil.StatusInvalidClientCertificate
|
denyStatusCode = httputil.StatusInvalidClientCertificate
|
||||||
denyStatusText = httputil.DetailsText(httputil.StatusInvalidClientCertificate)
|
denyStatusText = httputil.DetailsText(httputil.StatusInvalidClientCertificate)
|
||||||
}
|
}
|
||||||
|
@ -55,31 +68,6 @@ func (a *Authorize) handleResultDenied(
|
||||||
return a.deniedResponse(ctx, in, denyStatusCode, denyStatusText, nil)
|
return a.deniedResponse(ctx, in, denyStatusCode, denyStatusText, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authorize) handleResultNotAllowed(
|
|
||||||
ctx context.Context,
|
|
||||||
in *envoy_service_auth_v3.CheckRequest,
|
|
||||||
result *evaluator.Result,
|
|
||||||
isForwardAuthVerify bool,
|
|
||||||
) (*envoy_service_auth_v3.CheckResponse, error) {
|
|
||||||
switch {
|
|
||||||
case result.Allow.Reasons.Has(criteria.ReasonUserUnauthenticated):
|
|
||||||
// when the user is unauthenticated it means they haven't
|
|
||||||
// logged in yet, so redirect to authenticate
|
|
||||||
return a.requireLoginResponse(ctx, in, isForwardAuthVerify)
|
|
||||||
case result.Allow.Reasons.Has(criteria.ReasonDeviceUnauthenticated):
|
|
||||||
// when the user's device is unauthenticated it means they haven't
|
|
||||||
// registered a webauthn device yet, so redirect to the webauthn flow
|
|
||||||
return a.requireWebAuthnResponse(ctx, in, result, isForwardAuthVerify)
|
|
||||||
case result.Allow.Reasons.Has(criteria.ReasonDeviceUnauthorized):
|
|
||||||
return a.deniedResponse(ctx, in,
|
|
||||||
httputil.StatusDeviceUnauthorized,
|
|
||||||
httputil.DetailsText(httputil.StatusDeviceUnauthorized),
|
|
||||||
nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.deniedResponse(ctx, in, http.StatusForbidden, httputil.DetailsText(http.StatusForbidden), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Authorize) okResponse(headers http.Header) *envoy_service_auth_v3.CheckResponse {
|
func (a *Authorize) okResponse(headers http.Header) *envoy_service_auth_v3.CheckResponse {
|
||||||
var requestHeaders []*envoy_config_core_v3.HeaderValueOption
|
var requestHeaders []*envoy_config_core_v3.HeaderValueOption
|
||||||
for k, vs := range headers {
|
for k, vs := range headers {
|
||||||
|
@ -212,6 +200,8 @@ func (a *Authorize) requireWebAuthnResponse(
|
||||||
|
|
||||||
if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok {
|
if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok {
|
||||||
q.Set(urlutil.QueryDeviceType, deviceType)
|
q.Set(urlutil.QueryDeviceType, deviceType)
|
||||||
|
} else if deviceType, ok := result.Deny.AdditionalData["device_type"].(string); ok {
|
||||||
|
q.Set(urlutil.QueryDeviceType, deviceType)
|
||||||
} else {
|
} else {
|
||||||
q.Set(urlutil.QueryDeviceType, webauthnutil.DefaultDeviceType)
|
q.Set(urlutil.QueryDeviceType, webauthnutil.DefaultDeviceType)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,16 +72,20 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
|
||||||
a.logAuthorizeCheck(ctx, in, out, res, s, u)
|
a.logAuthorizeCheck(ctx, in, out, res, s, u)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
isForwardAuthVerify := isForwardAuth && hreq.URL.Path == "/verify"
|
||||||
|
|
||||||
|
// if there's a deny, the result is denied using the deny reasons.
|
||||||
if res.Deny.Value {
|
if res.Deny.Value {
|
||||||
return a.handleResultDenied(ctx, in, res)
|
return a.handleResultDenied(ctx, in, res, isForwardAuthVerify, res.Deny.Reasons)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there's an allow, the result is allowed.
|
||||||
if res.Allow.Value {
|
if res.Allow.Value {
|
||||||
return a.handleResultAllowed(ctx, in, res)
|
return a.handleResultAllowed(ctx, in, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
isForwardAuthVerify := isForwardAuth && hreq.URL.Path == "/verify"
|
// otherwise, the result is denied using the allow reasons.
|
||||||
return a.handleResultNotAllowed(ctx, in, res, isForwardAuthVerify)
|
return a.handleResultDenied(ctx, in, res, isForwardAuthVerify, res.Allow.Reasons)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getForwardAuthURL(r *http.Request) *url.URL {
|
func getForwardAuthURL(r *http.Request) *url.URL {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/pkg/policy/generator"
|
"github.com/pomerium/pomerium/pkg/policy/generator"
|
||||||
"github.com/pomerium/pomerium/pkg/policy/parser"
|
"github.com/pomerium/pomerium/pkg/policy/parser"
|
||||||
"github.com/pomerium/pomerium/pkg/policy/rules"
|
"github.com/pomerium/pomerium/pkg/policy/rules"
|
||||||
|
"github.com/pomerium/pomerium/pkg/webauthnutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -73,7 +74,7 @@ func (c deviceCriterion) GenerateRule(_ string, data parser.Value) (*ast.Rule, [
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceType := "default"
|
deviceType := webauthnutil.DefaultDeviceType
|
||||||
if v, ok := obj[deviceOperatorType]; ok {
|
if v, ok := obj[deviceOperatorType]; ok {
|
||||||
s, ok := v.(parser.String)
|
s, ok := v.(parser.String)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -27,7 +27,7 @@ allow:
|
||||||
is: dc1
|
is: dc1
|
||||||
`, []dataBrokerRecord{}, Input{Session: InputSession{ID: "s1"}})
|
`, []dataBrokerRecord{}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{false, A{ReasonUserUnauthenticated}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{false, A{ReasonUserUnauthenticated}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("no device credential", func(t *testing.T) {
|
t.Run("no device credential", func(t *testing.T) {
|
||||||
|
@ -37,10 +37,10 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
is: dc1
|
is: dc1
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{false, A{ReasonDeviceUnauthenticated}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{false, A{ReasonDeviceUnauthenticated}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("allowed by is", func(t *testing.T) {
|
t.Run("allowed by is", func(t *testing.T) {
|
||||||
|
@ -50,12 +50,12 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
is: dc1
|
is: dc1
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
||||||
&device.Enrollment{Id: "de1"},
|
&device.Enrollment{Id: "de1"},
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{true, A{ReasonDeviceOK}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{true, A{ReasonDeviceOK}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("not allowed by is", func(t *testing.T) {
|
t.Run("not allowed by is", func(t *testing.T) {
|
||||||
|
@ -65,14 +65,14 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
is: dc2
|
is: dc2
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
||||||
&device.Enrollment{Id: "de1"},
|
&device.Enrollment{Id: "de1"},
|
||||||
&device.Credential{Id: "dc2", EnrollmentId: "de2"},
|
&device.Credential{Id: "dc2", EnrollmentId: "de2"},
|
||||||
&device.Enrollment{Id: "de2"},
|
&device.Enrollment{Id: "de2"},
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{false, A{ReasonDeviceUnauthorized}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{false, A{ReasonDeviceUnauthorized}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("allowed by approved", func(t *testing.T) {
|
t.Run("allowed by approved", func(t *testing.T) {
|
||||||
|
@ -82,12 +82,12 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
approved: true
|
approved: true
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
||||||
&device.Enrollment{Id: "de1", ApprovedBy: "u1"},
|
&device.Enrollment{Id: "de1", ApprovedBy: "u1"},
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{true, A{ReasonDeviceOK}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{true, A{ReasonDeviceOK}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("not allowed by approved", func(t *testing.T) {
|
t.Run("not allowed by approved", func(t *testing.T) {
|
||||||
|
@ -97,12 +97,12 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
approved: true
|
approved: true
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
||||||
&device.Enrollment{Id: "de1"},
|
&device.Enrollment{Id: "de1"},
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{false, A{ReasonDeviceUnauthorized}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{false, A{ReasonDeviceUnauthorized}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("allowed by not approved", func(t *testing.T) {
|
t.Run("allowed by not approved", func(t *testing.T) {
|
||||||
|
@ -112,12 +112,12 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
approved: false
|
approved: false
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
||||||
&device.Enrollment{Id: "de1"},
|
&device.Enrollment{Id: "de1"},
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{true, A{ReasonDeviceOK}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{true, A{ReasonDeviceOK}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("not allowed by not approved", func(t *testing.T) {
|
t.Run("not allowed by not approved", func(t *testing.T) {
|
||||||
|
@ -127,12 +127,12 @@ allow:
|
||||||
- device:
|
- device:
|
||||||
approved: false
|
approved: false
|
||||||
`, []dataBrokerRecord{
|
`, []dataBrokerRecord{
|
||||||
mkDeviceSession("s1", "default", "dc1"),
|
mkDeviceSession("s1", "any", "dc1"),
|
||||||
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
&device.Credential{Id: "dc1", EnrollmentId: "de1"},
|
||||||
&device.Enrollment{Id: "de1", ApprovedBy: "u1"},
|
&device.Enrollment{Id: "de1", ApprovedBy: "u1"},
|
||||||
}, Input{Session: InputSession{ID: "s1"}})
|
}, Input{Session: InputSession{ID: "s1"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, A{false, A{ReasonDeviceUnauthorized}, M{"device_type": "default"}}, res["allow"])
|
require.Equal(t, A{false, A{ReasonDeviceUnauthorized}, M{"device_type": "any"}}, res["allow"])
|
||||||
require.Equal(t, A{false, A{}}, res["deny"])
|
require.Equal(t, A{false, A{}}, res["deny"])
|
||||||
})
|
})
|
||||||
t.Run("allowed by type", func(t *testing.T) {
|
t.Run("allowed by type", func(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue