Skip to content

Commit

Permalink
GOVSI-1161 - Enforce the ordering of the CredentialTrustLevel
Browse files Browse the repository at this point in the history
- Only accept the medium Credential trust level when it is ordered ['Cl.Cm'] and return an error when it is received ['Cm.Cl'] to make it completely explicit.
- Add some extra tests and remove code that we no longer need.
  • Loading branch information
JHjava committed Dec 3, 2021
1 parent d568a21 commit 3f49596
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ public static CredentialTrustLevel retrieveCredentialTrustLevel(List<String> vtr
.orElseThrow(() -> new IllegalArgumentException("Invalid CredentialTrustLevel"));
}

public static CredentialTrustLevel retrieveCredentialTrustLevel(String vtrSets) {

return Arrays.stream(values())
.filter(tl -> vtrSets.equals(tl.getValue()))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Invalid CredentialTrustLevel"));
}

public static CredentialTrustLevel getDefault() {
return MEDIUM_LEVEL;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import java.util.Objects;
import java.util.stream.Collectors;

import static java.lang.String.format;
import static net.minidev.json.parser.JSONParser.DEFAULT_PERMISSIVE_MODE;

public class VectorOfTrust {
Expand Down Expand Up @@ -63,11 +62,7 @@ public static VectorOfTrust parseFromAuthRequestAttribute(List<String> vtr) {
LOGGER.error("Error when parsing vtr attribute", e);
throw new IllegalArgumentException("Invalid VTR attribute", e);
}
List<String> vtrSets = new ArrayList<>();
for (int i = 0; i < vtrJsonArray.size(); i++) {
vtrSets.add((String) vtrJsonArray.get(i));
}
VectorOfTrust vectorOfTrust = parseVtrSet(vtrSets);
VectorOfTrust vectorOfTrust = parseVtrSet(vtrJsonArray);
LOGGER.info("VTR has been processed at vectorOfTrust: {}", vectorOfTrust.toString());

return vectorOfTrust;
Expand All @@ -77,34 +72,27 @@ public static VectorOfTrust getDefaults() {
return new VectorOfTrust(CredentialTrustLevel.getDefault());
}

private static VectorOfTrust parseVtrSet(List<String> vtrSet) {
private static VectorOfTrust parseVtrSet(JSONArray vtrJsonArray) {
List<VectorOfTrust> vectorOfTrusts = new ArrayList<>();
for (String vtr : vtrSet) {
LevelOfConfidence loc = null;
CredentialTrustLevel ctl;
String[] splitVtr = vtr.split("\\.");
for (Object obj : vtrJsonArray) {
String vtr = (String) obj;
var splitVtr = vtr.split("\\.");

List<String> levelOfConfidence =
List<LevelOfConfidence> levelOfConfidence =
Arrays.stream(splitVtr)
.filter((a) -> a.startsWith("P"))
.filter(a -> a.startsWith("P"))
.map(LevelOfConfidence::retrieveLevelOfConfidence)
.collect(Collectors.toList());
if (splitVtr.length > 3 || (splitVtr.length == 3 && levelOfConfidence.isEmpty())) {
throw new IllegalArgumentException(
format("Invalid number of attributes in VTR, %s", vtr));
}
if (!levelOfConfidence.isEmpty()) {
if (levelOfConfidence.size() > 1 || !vtr.startsWith("P")) {
throw new IllegalArgumentException(
format("Invalid Identity vector value exists in VTS: %s", vtr));
}
loc = LevelOfConfidence.retrieveLevelOfConfidence(levelOfConfidence.get(0));
ctl =
CredentialTrustLevel.retrieveCredentialTrustLevel(
List.of(vtr.substring(vtr.indexOf(".") + 1)));
if (levelOfConfidence.isEmpty()) {
var ctl = CredentialTrustLevel.retrieveCredentialTrustLevel(vtr);
vectorOfTrusts.add(new VectorOfTrust(ctl));
} else {
ctl = CredentialTrustLevel.retrieveCredentialTrustLevel(List.of(vtr));
var loc = levelOfConfidence.get(0);
var ctl =
CredentialTrustLevel.retrieveCredentialTrustLevel(
vtr.substring(vtr.indexOf(".") + 1));
vectorOfTrusts.add(new VectorOfTrust(ctl, loc));
}
vectorOfTrusts.add(new VectorOfTrust(ctl, loc));
}

return vectorOfTrusts.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.List;
import java.util.stream.Stream;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static uk.gov.di.authentication.shared.entity.CredentialTrustLevel.LOW_LEVEL;
import static uk.gov.di.authentication.shared.entity.CredentialTrustLevel.MEDIUM_LEVEL;

Expand All @@ -23,17 +23,25 @@ void valuesShouldBeComparable() {

@ParameterizedTest
@MethodSource("validCredentialTrustLevelValues")
void valuesShouldBeParsable(List<String> vtrSet, CredentialTrustLevel expectedValue) {
void valuesShouldBeParsable(String vtrSet, CredentialTrustLevel expectedValue) {
assertThat(
CredentialTrustLevel.retrieveCredentialTrustLevel(vtrSet), equalTo(expectedValue));
}

@ParameterizedTest
@MethodSource("invalidCredentialTrustLevelValues")
void shouldThrowWhenInvalidValueIsPassed(String vtrSet) {
assertThrows(
IllegalArgumentException.class,
() -> CredentialTrustLevel.retrieveCredentialTrustLevel(vtrSet),
"Expected to throw exception");
}

private static Stream<Arguments> validCredentialTrustLevelValues() {
return Stream.of(
Arguments.of(List.of("Cl"), LOW_LEVEL),
Arguments.of(List.of("Cl.Cm"), MEDIUM_LEVEL),
Arguments.of(List.of("Cl", "Cl.Cm"), LOW_LEVEL),
Arguments.of(List.of("Cl.Cm", "Cl"), LOW_LEVEL),
Arguments.of(List.of("Cm.Cl"), MEDIUM_LEVEL));
return Stream.of(Arguments.of("Cl", LOW_LEVEL), Arguments.of("Cl.Cm", MEDIUM_LEVEL));
}

private static Stream<String> invalidCredentialTrustLevelValues() {
return Stream.of("Cm", "Cm.Cl", "Cl.Cm.Cl.Cm", "Pm.Cl.Cm");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.di.authentication.shared.entity;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
Expand All @@ -8,14 +9,18 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static uk.gov.di.authentication.shared.entity.LevelOfConfidence.HIGH_LEVEL;
import static uk.gov.di.authentication.shared.entity.LevelOfConfidence.LOW_LEVEL;
import static uk.gov.di.authentication.shared.entity.LevelOfConfidence.MEDIUM_LEVEL;
import static uk.gov.di.authentication.shared.entity.LevelOfConfidence.VERY_HIGH_LEVEL;

class LevelOfConfidenceTest {

@Test
void valuesShouldBeComparable() {
assertThat(LevelOfConfidence.LOW_LEVEL, lessThan(LevelOfConfidence.MEDIUM_LEVEL));
assertThat(LevelOfConfidence.MEDIUM_LEVEL, lessThan(LevelOfConfidence.HIGH_LEVEL));
assertThat(LevelOfConfidence.HIGH_LEVEL, lessThan(LevelOfConfidence.VERY_HIGH_LEVEL));
}

@ParameterizedTest
@MethodSource("validLevelOfConfidence")
void shouldReturnLevelOfConfidenceForValidValue(
Expand All @@ -25,28 +30,21 @@ void shouldReturnLevelOfConfidenceForValidValue(

@ParameterizedTest
@MethodSource("invalidLevelOfConfidence")
void shouldThrowWhenInvalidValueIsPassed(String vtrSet, String message) {
void shouldThrowWhenInvalidValueIsPassed(String vtrSet) {
assertThrows(
IllegalArgumentException.class,
() -> LevelOfConfidence.retrieveLevelOfConfidence(vtrSet),
message);
() -> LevelOfConfidence.retrieveLevelOfConfidence(vtrSet));
}

private static Stream<Arguments> validLevelOfConfidence() {
return Stream.of(
Arguments.of("Pl", LOW_LEVEL),
Arguments.of("Pm", MEDIUM_LEVEL),
Arguments.of("Ph", HIGH_LEVEL),
Arguments.of("Pv", VERY_HIGH_LEVEL));
Arguments.of("Pl", LevelOfConfidence.LOW_LEVEL),
Arguments.of("Pm", LevelOfConfidence.MEDIUM_LEVEL),
Arguments.of("Ph", LevelOfConfidence.HIGH_LEVEL),
Arguments.of("Pv", LevelOfConfidence.VERY_HIGH_LEVEL));
}

private static Stream<Arguments> invalidLevelOfConfidence() {
return Stream.of(
Arguments.of("Cl.Pl", "Should throw when LevelOfConfidence is not first in String"),
Arguments.of(
"Cl.Cm", "Should throw when no LevelOfConfidence is included in String"),
Arguments.of(
"Pm.Ph.Cl",
"Should throw when multiple LevelOfConfidence is included in single String"));
return Stream.of(Arguments.of("Pm.Pl"), Arguments.of("Cl.Cm"), Arguments.of("Pm.Ph.Cl"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ void shouldThrowWhenIdentityValueIsNotFirstValueInVector() {
Collections.singletonList(jsonArray.toJSONString())));
}

@Test
void shouldThrowWhenTooManyValuesInVector() {
JSONArray jsonArray = new JSONArray();
jsonArray.add("Cl.Cm.Cl");
assertThrows(
IllegalArgumentException.class,
() ->
VectorOfTrust.parseFromAuthRequestAttribute(
Collections.singletonList(jsonArray.toJSONString())));
}

@Test
void shouldThrowWhenOnlyIdentityLevelIsSentInRequest() {
JSONArray jsonArray = new JSONArray();
Expand All @@ -130,18 +141,18 @@ void shouldThrowWhenOnlyIdentityLevelIsSentInRequest() {
}

@Test
void shouldParseVectorWhenCredentialTrustLevelsAreOrderedDifferently() {
void shouldThrowWhenCredentialTrustLevelsAreOrderedIncorrectly() {
JSONArray jsonArray = new JSONArray();
jsonArray.add("Pm.Cm.Cl");
VectorOfTrust vectorOfTrust =
VectorOfTrust.parseFromAuthRequestAttribute(
Collections.singletonList(jsonArray.toJSONString()));
assertThat(vectorOfTrust.getCredentialTrustLevel(), equalTo(MEDIUM_LEVEL));
assertThat(vectorOfTrust.getLevelOfConfidence(), equalTo(LevelOfConfidence.MEDIUM_LEVEL));
assertThrows(
IllegalArgumentException.class,
() ->
VectorOfTrust.parseFromAuthRequestAttribute(
Collections.singletonList(jsonArray.toJSONString())));
}

@Test
void shouldParseValidStringAndReThrowIfInvalidValueIsPresent() {
void shouldThrowWhenMultipleIdentityValuesArePresentInVector() {
JSONArray jsonArray = new JSONArray();
jsonArray.add("Pl.Pb");
assertThrows(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ void shouldSuccessfullyValidateAuthRequestWhenIdentityValuesAreIncludedInVtrAttr
Scope scope = new Scope();
scope.add(OIDCScopeValue.OPENID);
JSONArray jsonArray = new JSONArray();
jsonArray.add("Pl.Cm.Cl");
jsonArray.add("Pl.Cl.Cm");
jsonArray.add("Pl.Cl");
when(dynamoClientService.getClient(clientID.toString()))
.thenReturn(
Expand Down Expand Up @@ -461,7 +461,7 @@ private ClientRegistry generateClientRegistry(
private AuthenticationRequest generateAuthRequest(
ClientID clientID, String redirectUri, ResponseType responseType, Scope scope) {
JSONArray jsonArray = new JSONArray();
jsonArray.add("Cm.Cl");
jsonArray.add("Cl.Cm");
jsonArray.add("Cl");
State state = new State();
Nonce nonce = new Nonce();
Expand Down

0 comments on commit 3f49596

Please sign in to comment.