From a2e2e92f5a52482246a7cb30b789fc4945fc7b2c Mon Sep 17 00:00:00 2001
From: Nicholas Blair <nicholas.blair@wisc.edu>
Date: Wed, 24 Aug 2016 09:39:19 -0500
Subject: [PATCH] fix: provide factory method for jackson to deserialize
 authorities

@JsonProperty won't work with the abstract '? extends GrantedAuthority' type, as Jackson doesn't have a constructor or factory to convert a string into a concrete implementation.

This change adds a factory method which takes a Collection<String> (which Jackson can understand) and uses Spring Security's utility method to convert the Collection of Strings to a Collection of GrantedAuthority instances.

Note: the 'Amy Administrator' user provided by the 'local-users.json|yaml' file is no longer strictly equivalent to those provided in 'local-users.properties'. See https://git.doit.wisc.edu/adi-ia/uw-spring-security/issues/5; correcting that seems to be outside of the scope of a bugfix, as it will result in something that likely requires a new feature release.
---
 .../java/edu/wisc/uwss/UWUserDetailsImpl.java | 38 ++++++++++++++-----
 .../edu/wisc/uwss/local/local-users.json      |  2 +-
 .../edu/wisc/uwss/local/local-users.yaml      |  3 +-
 .../LocalUserDetailsManagerImplTest.java      | 10 +++++
 4 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/uw-spring-security-core/src/main/java/edu/wisc/uwss/UWUserDetailsImpl.java b/uw-spring-security-core/src/main/java/edu/wisc/uwss/UWUserDetailsImpl.java
index dd253b6..2e73b58 100644
--- a/uw-spring-security-core/src/main/java/edu/wisc/uwss/UWUserDetailsImpl.java
+++ b/uw-spring-security-core/src/main/java/edu/wisc/uwss/UWUserDetailsImpl.java
@@ -9,6 +9,7 @@ import java.util.Collections;
 
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.util.DigestUtils;
 
@@ -54,6 +55,7 @@ public class UWUserDetailsImpl extends User implements UWUserDetails, HasModifia
   
   /**
    * The primary constructor for {@link UWUserDetails}.
+   *
    * @param pvi
    * @param username
    * @param password
@@ -61,22 +63,40 @@ public class UWUserDetailsImpl extends User implements UWUserDetails, HasModifia
    * @param emailAddress
    * @param uddsMembership
    * @param grantedAuthorities
-   * 
-   * No other constructors should be annotated with JsonCreator.  
-   * @see http://stackoverflow.com/questions/15931082/how-to-deserialize-a-class-with-overloaded-constructors-using-jsoncreator
    */
-  @JsonCreator
-  public UWUserDetailsImpl(@JsonProperty("pvi") String pvi, @JsonProperty("username") String username, @JsonProperty("password") String password,
-      @JsonProperty("fullName") String fullName, @JsonProperty("emailAddress") String emailAddress,
-      @JsonProperty("uddsMembership") Collection<String> uddsMembership, @JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
+  public UWUserDetailsImpl(String pvi, String username, String password,
+                           String fullName, String emailAddress,
+                           Collection<String> uddsMembership, Collection<? extends GrantedAuthority> authorities) {
     super(username, password, authorities);
     this.pvi = pvi;
     this.fullName = fullName;
     this.uddsMembership = uddsMembership;
     this.emailAddress = StringUtils.isNotBlank(emailAddress) ?
-        emailAddress.trim().toLowerCase() : emailAddress;
+            emailAddress.trim().toLowerCase() : emailAddress;
     this.emailAddressHash = StringUtils.isNotBlank(emailAddress) ? DigestUtils
-        .md5DigestAsHex(this.emailAddress.getBytes()) : null;
+            .md5DigestAsHex(this.emailAddress.getBytes()) : null;
+  }
+
+  /**
+   * Utility factory method to help Jackson deserialize JSON objects to instances of this class.
+   * There can be only one constructor or factory method on the class with {@link JsonCreator}
+   * annotation.
+   *
+   * @param pvi
+   * @param username
+   * @param password
+   * @param fullName
+   * @param emailAddress
+   * @param uddsMembership
+   * @param authorities
+   */
+  @JsonCreator
+  public static UWUserDetailsImpl newInstance(@JsonProperty("pvi") String pvi, @JsonProperty("username") String username, @JsonProperty("password") String password,
+                           @JsonProperty("fullName") String fullName, @JsonProperty("emailAddress") String emailAddress,
+                           @JsonProperty("uddsMembership") Collection<String> uddsMembership, @JsonProperty("authorities") Collection<String> authorities) {
+    return new UWUserDetailsImpl(pvi, username, password, fullName, emailAddress,
+            uddsMembership,
+            AuthorityUtils.createAuthorityList(authorities.toArray(new String[]{})));
   }
 
   /**
diff --git a/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.json b/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.json
index 2b90305..7ad27b0 100644
--- a/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.json
+++ b/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.json
@@ -8,7 +8,7 @@
     "emailAddress": "amy.administrator@demo.wisc.edu",
     "pvi": "UW000A000",
     "uddsMembership": [ "A535900" ],
-    "authorities": []
+    "authorities": [ "edu.wisc.uwss.local.administrator" ]
   },
   {
     "username": "jane",
diff --git a/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.yaml b/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.yaml
index 677bf6f..0ee9082 100644
--- a/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.yaml
+++ b/uw-spring-security-core/src/main/resources/edu/wisc/uwss/local/local-users.yaml
@@ -6,7 +6,8 @@
   emailAddress: "amy.administrator@demo.wisc.edu"
   uddsMembership:
   - "A535900"
-  authorities: []
+  authorities:
+  - "edu.wisc.uwss.local.administrator"
   firstName: "Amy"
   lastName: "Administrator"
 - pvi: "UW000A001"
diff --git a/uw-spring-security-core/src/test/java/edu/wisc/uwss/local/LocalUserDetailsManagerImplTest.java b/uw-spring-security-core/src/test/java/edu/wisc/uwss/local/LocalUserDetailsManagerImplTest.java
index e24c411..4a4c972 100644
--- a/uw-spring-security-core/src/test/java/edu/wisc/uwss/local/LocalUserDetailsManagerImplTest.java
+++ b/uw-spring-security-core/src/test/java/edu/wisc/uwss/local/LocalUserDetailsManagerImplTest.java
@@ -91,6 +91,11 @@ public class LocalUserDetailsManagerImplTest {
    * Confirm that {@link LocalUserDetailsManagerImpl} with {@link LocalUserDetailsManagerImpl#setLoaderEnabled(boolean)}
    * of true results in equivalent {@link UWUserDetails} instances being loaded from JSON.
    *
+   * Note: As of 1.5.x, this test passes, but potentially should not, as the {@link UWUserDetails}
+   * provided by the default {@link LocalUserDetailsAttributesMapper} do not have any
+   * {@link GrantedAuthority}, where the {@link UWUserDetails} provided by the
+   * {@link LocalUserDetailsLoader} do. See https://git.doit.wisc.edu/adi-ia/uw-spring-security/issues/5.
+   *
    * @throws IOException
    */
   @Test
@@ -118,6 +123,11 @@ public class LocalUserDetailsManagerImplTest {
    * Confirm that {@link LocalUserDetailsManagerImpl} with {@link LocalUserDetailsManagerImpl#setLoaderEnabled(boolean)}
    * of true results in equivalent {@link UWUserDetails} instances being loaded from YAML.
    *
+   * Note: As of 1.5.x, this test passes, but potentially should not, as the {@link UWUserDetails}
+   * provided by the default {@link LocalUserDetailsAttributesMapper} do not have any
+   * {@link GrantedAuthority}, where the {@link UWUserDetails} provided by the
+   * {@link LocalUserDetailsLoader} do. See https://git.doit.wisc.edu/adi-ia/uw-spring-security/issues/5.
+   *
    * @throws IOException
    */
   @Test
-- 
GitLab