diff --git a/src/main/java/us/kbase/auth2/lib/token/MFAStatus.java b/src/main/java/us/kbase/auth2/lib/token/MFAStatus.java new file mode 100644 index 00000000..d7f1dab4 --- /dev/null +++ b/src/main/java/us/kbase/auth2/lib/token/MFAStatus.java @@ -0,0 +1,59 @@ +package us.kbase.auth2.lib.token; + +import java.util.HashMap; +import java.util.Map; + +/** An enumeration representing the multi-factor authentication status of a user's login. */ +public enum MFAStatus { + + /* first arg is ID, second arg is description. ID CANNOT change + * since that field is stored in the DB. + */ + /** User authenticated with MFA during token creation. */ + USED ("Used", "MFA used"), + /** User explicitly chose not to use MFA when available. */ + NOT_USED ("NotUsed", "MFA not used"), + /** MFA status unknown or not applicable to authentication method. */ + UNKNOWN ("Unknown", "MFA status unknown"); + + private static final Map STATUS_MAP = new HashMap<>(); + static { + for (final MFAStatus status: MFAStatus.values()) { + STATUS_MAP.put(status.getID(), status); + } + } + + private final String id; + private final String description; + + private MFAStatus(final String id, final String description) { + this.id = id; + this.description = description; + } + + /** Get the ID of this MFA status. + * @return the ID. + */ + public String getID() { + return id; + } + + /** Get the description of this MFA status. + * @return the description. + */ + public String getDescription() { + return description; + } + + /** Get an MFA status from its ID. + * @param id the ID of the MFA status. + * @return the MFA status. + * @throws IllegalArgumentException if there is no MFA status matching the ID. + */ + public static MFAStatus fromID(final String id) { + if (!STATUS_MAP.containsKey(id)) { + throw new IllegalArgumentException("Invalid MFA status: " + id); + } + return STATUS_MAP.get(id); + } +} diff --git a/src/test/java/us/kbase/test/auth2/lib/token/MFAStatusTest.java b/src/test/java/us/kbase/test/auth2/lib/token/MFAStatusTest.java new file mode 100644 index 00000000..e1eb73c9 --- /dev/null +++ b/src/test/java/us/kbase/test/auth2/lib/token/MFAStatusTest.java @@ -0,0 +1,64 @@ +package us.kbase.test.auth2.lib.token; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import us.kbase.auth2.lib.token.MFAStatus; + +public class MFAStatusTest { + + @Test + public void testValues() { + final MFAStatus[] expected = {MFAStatus.USED, MFAStatus.NOT_USED, MFAStatus.UNKNOWN}; + assertThat("incorrect values", MFAStatus.values(), is(expected)); + } + + @Test + public void testMFAStatusGetDescription() throws Exception { + assertThat("incorrect Used description", MFAStatus.USED.getDescription(), + is("MFA used")); + assertThat("incorrect NotUsed description", MFAStatus.NOT_USED.getDescription(), + is("MFA not used")); + assertThat("incorrect Unknown description", MFAStatus.UNKNOWN.getDescription(), + is("MFA status unknown")); + } + + @Test + public void testMFAStatusIDsAreStableForSerialization() throws Exception { + // These IDs are persisted to database via JSON serialization and must never change. + // Changing these values would break backwards compatibility with existing tokens + // and user data stored in MongoDB. + assertThat("USED ID must be stable", MFAStatus.USED.getID(), is("Used")); + assertThat("NOT_USED ID must be stable", MFAStatus.NOT_USED.getID(), is("NotUsed")); + assertThat("UNKNOWN ID must be stable", MFAStatus.UNKNOWN.getID(), is("Unknown")); + } + + @Test + public void testMFAStatusFromIDValidValues() throws Exception { + assertThat("incorrect fromID for Used", MFAStatus.fromID("Used"), is(MFAStatus.USED)); + assertThat("incorrect fromID for NotUsed", MFAStatus.fromID("NotUsed"), + is(MFAStatus.NOT_USED)); + assertThat("incorrect fromID for Unknown", MFAStatus.fromID("Unknown"), + is(MFAStatus.UNKNOWN)); + } + + @Test + public void testFromIDFail() throws Exception { + failFromId(null, "Invalid MFA status: null"); + failFromId(" \t ", "Invalid MFA status: \t "); + failFromId("INVALID", "Invalid MFA status: INVALID"); + failFromId("used", "Invalid MFA status: used"); + } + + private void failFromId(final String id, final String exception) { + try { + MFAStatus.fromID(id); + fail("expected exception"); + } catch (IllegalArgumentException e) { + assertThat("correct exception message", e.getMessage(), is(exception)); + } + } +}