From e49ae096e6e5197c222a3751844a2227444ed0d5 Mon Sep 17 00:00:00 2001 From: Yuriy Savchenko Date: Thu, 7 Dec 2023 22:48:22 +0300 Subject: [PATCH] Add AuthorizationManager factory methods Factory methods to create AuthorizationManager with a configurable default AuthorizationDecision. Closes gh-13085 --- .../authorization/AuthorizationManagers.java | 40 +++++- .../AuthorizationManagersTests.java | 136 +++++++++++++++++- 2 files changed, 172 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java b/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java index 6fa0ba8049..94a072b5a4 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,23 @@ public final class AuthorizationManagers { */ @SafeVarargs public static AuthorizationManager anyOf(AuthorizationManager... managers) { + return anyOf(new AuthorizationDecision(false), managers); + } + + /** + * Creates an {@link AuthorizationManager} that grants access if at least one + * {@link AuthorizationManager} granted, if managers are empty or + * abstained, a default {@link AuthorizationDecision} is returned. + * @param the type of object that is being authorized + * @param allAbstainDefaultDecision the default decision if all + * {@link AuthorizationManager}s abstained + * @param managers the {@link AuthorizationManager}s to use + * @return the {@link AuthorizationManager} to use + * @since 6.3 + */ + @SafeVarargs + public static AuthorizationManager anyOf(AuthorizationDecision allAbstainDefaultDecision, + AuthorizationManager... managers) { return (authentication, object) -> { List decisions = new ArrayList<>(); for (AuthorizationManager manager : managers) { @@ -50,7 +67,7 @@ public final class AuthorizationManagers { decisions.add(decision); } if (decisions.isEmpty()) { - return new AuthorizationDecision(false); + return allAbstainDefaultDecision; } return new CompositeAuthorizationDecision(false, decisions); }; @@ -66,6 +83,23 @@ public final class AuthorizationManagers { */ @SafeVarargs public static AuthorizationManager allOf(AuthorizationManager... managers) { + return allOf(new AuthorizationDecision(true), managers); + } + + /** + * Creates an {@link AuthorizationManager} that grants access if all + * {@link AuthorizationManager}s granted, if managers are empty or + * abstained, a default {@link AuthorizationDecision} is returned. + * @param the type of object that is being authorized + * @param allAbstainDefaultDecision the default decision if all + * {@link AuthorizationManager}s abstained + * @param managers the {@link AuthorizationManager}s to use + * @return the {@link AuthorizationManager} to use + * @since 6.3 + */ + @SafeVarargs + public static AuthorizationManager allOf(AuthorizationDecision allAbstainDefaultDecision, + AuthorizationManager... managers) { return (authentication, object) -> { List decisions = new ArrayList<>(); for (AuthorizationManager manager : managers) { @@ -79,7 +113,7 @@ public final class AuthorizationManagers { decisions.add(decision); } if (decisions.isEmpty()) { - return new AuthorizationDecision(true); + return allAbstainDefaultDecision; } return new CompositeAuthorizationDecision(true, decisions); }; diff --git a/core/src/test/java/org/springframework/security/authorization/AuthorizationManagersTests.java b/core/src/test/java/org/springframework/security/authorization/AuthorizationManagersTests.java index b05b2f4106..9debc7c247 100644 --- a/core/src/test/java/org/springframework/security/authorization/AuthorizationManagersTests.java +++ b/core/src/test/java/org/springframework/security/authorization/AuthorizationManagersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,16 @@ class AuthorizationManagersTests { assertThat(decision.isGranted()).isTrue(); } + @Test + void checkAnyOfWithAllAbstainDefaultDecisionWhenOneGrantedThenGrantedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false); + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, + (a, o) -> new AuthorizationDecision(false), (a, o) -> new AuthorizationDecision(true)); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + // gh-13069 @Test void checkAnyOfWhenAllNonAbstainingDeniesThenDeniedDecision() { @@ -54,6 +64,58 @@ class AuthorizationManagersTests { assertThat(decision.isGranted()).isFalse(); } + @Test + void checkAnyOfWithAllAbstainDefaultDecisionIsDeniedWhenEmptyThenDeniedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false); + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + @Test + void checkAnyOfWithAllAbstainDefaultDecisionIsGrantedWhenEmptyThenGrantedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true); + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + void checkAnyOfWithAllAbstainDefaultDecisionIsAbstainWhenEmptyThenAbstainDecision() { + AuthorizationDecision allAbstainDefaultDecision = null; + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNull(); + } + + @Test + void checkAnyOfWhenAllAbstainDefaultDecisionIsGrantedAndAllManagersAbstainThenGrantedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true); + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, (a, o) -> null); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + void checkAnyOfWhenAllAbstainDefaultDecisionIsDeniedAndAllManagersAbstainThenDeniedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false); + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, (a, o) -> null); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + @Test + void checkAnyOfWhenAllAbstainDefaultDecisionIsAbstainAndAllManagersAbstainThenAbstainDecision() { + AuthorizationDecision allAbstainDefaultDecision = null; + AuthorizationManager composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, (a, o) -> null); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNull(); + } + @Test void checkAllOfWhenAllGrantedThenGrantedDecision() { AuthorizationManager composed = AuthorizationManagers.allOf((a, o) -> new AuthorizationDecision(true), @@ -63,6 +125,16 @@ class AuthorizationManagersTests { assertThat(decision.isGranted()).isTrue(); } + @Test + void checkAllOfWithAllAbstainDefaultDecisionWhenAllGrantedThenGrantedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false); + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, + (a, o) -> new AuthorizationDecision(true), (a, o) -> new AuthorizationDecision(true)); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + // gh-13069 @Test void checkAllOfWhenAllNonAbstainingGrantsThenGrantedDecision() { @@ -82,6 +154,16 @@ class AuthorizationManagersTests { assertThat(decision.isGranted()).isFalse(); } + @Test + void checkAllOfWithAllAbstainDefaultDecisionWhenOneDeniedThenDeniedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true); + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, + (a, o) -> new AuthorizationDecision(true), (a, o) -> new AuthorizationDecision(false)); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + @Test void checkAllOfWhenEmptyThenGrantedDecision() { AuthorizationManager composed = AuthorizationManagers.allOf(); @@ -90,4 +172,56 @@ class AuthorizationManagersTests { assertThat(decision.isGranted()).isTrue(); } + @Test + void checkAllOfWithAllAbstainDefaultDecisionIsDeniedWhenEmptyThenDeniedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false); + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + @Test + void checkAllOfWithAllAbstainDefaultDecisionIsGrantedWhenEmptyThenGrantedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true); + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + void checkAllOfWithAllAbstainDefaultDecisionIsAbstainWhenEmptyThenAbstainDecision() { + AuthorizationDecision allAbstainDefaultDecision = null; + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNull(); + } + + @Test + void checkAllOfWhenAllAbstainDefaultDecisionIsDeniedAndAllManagersAbstainThenDeniedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false); + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, (a, o) -> null); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + @Test + void checkAllOfWhenAllAbstainDefaultDecisionIsGrantedAndAllManagersAbstainThenGrantedDecision() { + AuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true); + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, (a, o) -> null); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + void checkAllOfWhenAllAbstainDefaultDecisionIsAbstainAndAllManagersAbstainThenAbstainDecision() { + AuthorizationDecision allAbstainDefaultDecision = null; + AuthorizationManager composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, (a, o) -> null); + AuthorizationDecision decision = composed.check(null, null); + assertThat(decision).isNull(); + } + }