mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 10:43:56 +00:00
modify the union authorizer to return on the first Approve or Deny and to continue on Unknown
This commit is contained in:
parent
12125455d8
commit
cfe580c99f
@ -14,6 +14,14 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Package union implements an authorizer that combines multiple subauthorizer.
|
||||||
|
// The union authorizer iterates over each subauthorizer and returns the first
|
||||||
|
// decision that is either an Allow decision or a Deny decision. If a
|
||||||
|
// subauthorizer returns a NoOpinion, then the union authorizer moves onto the
|
||||||
|
// next authorizer or, if the subauthorizer was the last authorizer, returns
|
||||||
|
// NoOpinion as the aggregate decision. I.e. union authorizer creates an
|
||||||
|
// aggregate decision and supports short-circut allows and denies from
|
||||||
|
// subauthorizers.
|
||||||
package union
|
package union
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -33,14 +41,14 @@ func New(authorizationHandlers ...authorizer.Authorizer) authorizer.Authorizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Authorizes against a chain of authorizer.Authorizer objects and returns nil if successful and returns error if unsuccessful
|
// Authorizes against a chain of authorizer.Authorizer objects and returns nil if successful and returns error if unsuccessful
|
||||||
func (authzHandler unionAuthzHandler) Authorize(a authorizer.Attributes) (bool, string, error) {
|
func (authzHandler unionAuthzHandler) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||||
var (
|
var (
|
||||||
errlist []error
|
errlist []error
|
||||||
reasonlist []string
|
reasonlist []string
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, currAuthzHandler := range authzHandler {
|
for _, currAuthzHandler := range authzHandler {
|
||||||
authorized, reason, err := currAuthzHandler.Authorize(a)
|
decision, reason, err := currAuthzHandler.Authorize(a)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errlist = append(errlist, err)
|
errlist = append(errlist, err)
|
||||||
@ -48,13 +56,15 @@ func (authzHandler unionAuthzHandler) Authorize(a authorizer.Attributes) (bool,
|
|||||||
if len(reason) != 0 {
|
if len(reason) != 0 {
|
||||||
reasonlist = append(reasonlist, reason)
|
reasonlist = append(reasonlist, reason)
|
||||||
}
|
}
|
||||||
if !authorized {
|
switch decision {
|
||||||
continue
|
case authorizer.DecisionAllow, authorizer.DecisionDeny:
|
||||||
|
return decision, reason, err
|
||||||
|
case authorizer.DecisionNoOpinion:
|
||||||
|
// continue to the next authorizer
|
||||||
}
|
}
|
||||||
return true, reason, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, strings.Join(reasonlist, "\n"), utilerrors.NewAggregate(errlist)
|
return authorizer.DecisionNoOpinion, strings.Join(reasonlist, "\n"), utilerrors.NewAggregate(errlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unionAuthzRulesHandler authorizer against a chain of authorizer.RuleResolver
|
// unionAuthzRulesHandler authorizer against a chain of authorizer.RuleResolver
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package union
|
package union
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
@ -26,49 +27,43 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type mockAuthzHandler struct {
|
type mockAuthzHandler struct {
|
||||||
isAuthorized bool
|
decision authorizer.Decision
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *mockAuthzHandler) Authorize(a authorizer.Attributes) (bool, string, error) {
|
func (mock *mockAuthzHandler) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||||
if mock.err != nil {
|
return mock.decision, "", mock.err
|
||||||
return false, "", mock.err
|
|
||||||
}
|
|
||||||
if !mock.isAuthorized {
|
|
||||||
return false, "", nil
|
|
||||||
}
|
|
||||||
return true, "", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorizationSecondPasses(t *testing.T) {
|
func TestAuthorizationSecondPasses(t *testing.T) {
|
||||||
handler1 := &mockAuthzHandler{isAuthorized: false}
|
handler1 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}
|
||||||
handler2 := &mockAuthzHandler{isAuthorized: true}
|
handler2 := &mockAuthzHandler{decision: authorizer.DecisionAllow}
|
||||||
authzHandler := New(handler1, handler2)
|
authzHandler := New(handler1, handler2)
|
||||||
|
|
||||||
authorized, _, _ := authzHandler.Authorize(nil)
|
authorized, _, _ := authzHandler.Authorize(nil)
|
||||||
if !authorized {
|
if authorized != authorizer.DecisionAllow {
|
||||||
t.Errorf("Unexpected authorization failure")
|
t.Errorf("Unexpected authorization failure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorizationFirstPasses(t *testing.T) {
|
func TestAuthorizationFirstPasses(t *testing.T) {
|
||||||
handler1 := &mockAuthzHandler{isAuthorized: true}
|
handler1 := &mockAuthzHandler{decision: authorizer.DecisionAllow}
|
||||||
handler2 := &mockAuthzHandler{isAuthorized: false}
|
handler2 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}
|
||||||
authzHandler := New(handler1, handler2)
|
authzHandler := New(handler1, handler2)
|
||||||
|
|
||||||
authorized, _, _ := authzHandler.Authorize(nil)
|
authorized, _, _ := authzHandler.Authorize(nil)
|
||||||
if !authorized {
|
if authorized != authorizer.DecisionAllow {
|
||||||
t.Errorf("Unexpected authorization failure")
|
t.Errorf("Unexpected authorization failure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorizationNonePasses(t *testing.T) {
|
func TestAuthorizationNonePasses(t *testing.T) {
|
||||||
handler1 := &mockAuthzHandler{isAuthorized: false}
|
handler1 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}
|
||||||
handler2 := &mockAuthzHandler{isAuthorized: false}
|
handler2 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}
|
||||||
authzHandler := New(handler1, handler2)
|
authzHandler := New(handler1, handler2)
|
||||||
|
|
||||||
authorized, _, _ := authzHandler.Authorize(nil)
|
authorized, _, _ := authzHandler.Authorize(nil)
|
||||||
if authorized {
|
if authorized == authorizer.DecisionAllow {
|
||||||
t.Errorf("Expected failed authorization")
|
t.Errorf("Expected failed authorization")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,3 +218,49 @@ func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizer.De
|
|||||||
}
|
}
|
||||||
return rules
|
return rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthorizationUnequivocalDeny(t *testing.T) {
|
||||||
|
cs := []struct {
|
||||||
|
authorizers []authorizer.Authorizer
|
||||||
|
decision authorizer.Decision
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
authorizers: []authorizer.Authorizer{},
|
||||||
|
decision: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
authorizers: []authorizer.Authorizer{
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionNoOpinion},
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionAllow},
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionDeny},
|
||||||
|
},
|
||||||
|
decision: authorizer.DecisionAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
authorizers: []authorizer.Authorizer{
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionNoOpinion},
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionDeny},
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionAllow},
|
||||||
|
},
|
||||||
|
decision: authorizer.DecisionDeny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
authorizers: []authorizer.Authorizer{
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionNoOpinion},
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionDeny, err: errors.New("webhook failed closed")},
|
||||||
|
&mockAuthzHandler{decision: authorizer.DecisionAllow},
|
||||||
|
},
|
||||||
|
decision: authorizer.DecisionDeny,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, c := range cs {
|
||||||
|
t.Run(fmt.Sprintf("case %v", i), func(t *testing.T) {
|
||||||
|
authzHandler := New(c.authorizers...)
|
||||||
|
|
||||||
|
decision, _, _ := authzHandler.Authorize(nil)
|
||||||
|
if decision != c.decision {
|
||||||
|
t.Errorf("Unexpected authorization failure: %v, expected: %v", decision, c.decision)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user