From 06e1cdbb1c1986d3dbdb44ed19bf9da79d9a889c Mon Sep 17 00:00:00 2001
From: Andy Summers <andrew.summers@wisc.edu>
Date: Tue, 12 Jul 2016 14:53:37 -0500
Subject: [PATCH] Refactor uw-php-security to use 'Provider' classes

After a discussion about the project's design and requirements moving
forward, it was decided to refactor the project to remove
`AttributeMapper` classes and have `Provider` classes handle attribute
mapping as well as user loading. This also adds a third provider,
`PreauthHTTPUserDetailsProvider`, which can be used in environments
where Shibboleth attributes are sent as HTTP headers.
---
 .../doit/LocalUserDetailsAttributeMapper.php  | 25 -------
 .../wisc/doit/LocalUserDetailsProvider.php    | 36 ++++++++++
 .../edu/wisc/doit/LocalUserDetailsService.php | 52 --------------
 .../doit/PreauthHTTPUserDetailsProvider.php   | 48 +++++++++++++
 .../PreauthUserDetailsAttributeMapper.php     | 36 ----------
 .../wisc/doit/PreauthUserDetailsProvider.php  | 36 ++++++++++
 .../wisc/doit/PreauthUserDetailsService.php   | 52 --------------
 ...buteMapper.php => UserDetailsProvider.php} |  9 ++-
 src/main/edu/wisc/doit/UserDetailsService.php | 20 ------
 .../LocalUserDetailsAttributeMapperTest.php   | 28 --------
 ...t.php => LocalUserDetailsProviderTest.php} |  6 +-
 .../PreauthHTTPUserDetailsProviderTest.php    | 70 +++++++++++++++++++
 src/test/edu/wisc/doit/PreauthTestCase.php    | 34 ---------
 .../PreauthUserDetailsAttributeMapperTest.php | 25 -------
 .../doit/PreauthUserDetailsProviderTest.php   | 59 ++++++++++++++++
 .../doit/PreauthUserDetailsServiceTest.php    | 40 -----------
 16 files changed, 256 insertions(+), 320 deletions(-)
 delete mode 100644 src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php
 create mode 100644 src/main/edu/wisc/doit/LocalUserDetailsProvider.php
 delete mode 100644 src/main/edu/wisc/doit/LocalUserDetailsService.php
 create mode 100644 src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php
 delete mode 100644 src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php
 create mode 100644 src/main/edu/wisc/doit/PreauthUserDetailsProvider.php
 delete mode 100644 src/main/edu/wisc/doit/PreauthUserDetailsService.php
 rename src/main/edu/wisc/doit/{UserDetailsAttributeMapper.php => UserDetailsProvider.php} (65%)
 delete mode 100644 src/main/edu/wisc/doit/UserDetailsService.php
 delete mode 100644 src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php
 rename src/test/edu/wisc/doit/{LocalUserDetailsServiceTest.php => LocalUserDetailsProviderTest.php} (78%)
 create mode 100644 src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php
 delete mode 100644 src/test/edu/wisc/doit/PreauthTestCase.php
 delete mode 100644 src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php
 create mode 100644 src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php
 delete mode 100644 src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php

diff --git a/src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php b/src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php
deleted file mode 100644
index 9f8f0f7..0000000
--- a/src/main/edu/wisc/doit/LocalUserDetailsAttributeMapper.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Implementation of {@link UserDetailsAttributeMapper} for use in local development.
- */
-class LocalUserDetailsAttributeMapper implements UserDetailsAttributeMapper
-{
-
-    /**
-     * {@inheritdoc}
-     */
-    public function mapUser()
-    {
-        $jsonString = file_get_contents(__DIR__ . "/../../../resources/localuser.json");
-        if ($jsonString === false) {
-            return null;
-        }
-
-        // Return user attributes into a standard PHP array (true specifies array)
-        return json_decode($jsonString, true);
-    }
-
-}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/LocalUserDetailsProvider.php b/src/main/edu/wisc/doit/LocalUserDetailsProvider.php
new file mode 100644
index 0000000..57fc396
--- /dev/null
+++ b/src/main/edu/wisc/doit/LocalUserDetailsProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * LocalUserDetailsProvider provides a developer with a {@link UWUserDetails} suitable for use in local development.
+ */
+class LocalUserDetailsProvider implements UserDetailsProvider
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loadUser()
+    {
+        $jsonString = file_get_contents(__DIR__ . "/../../../resources/localuser.json");
+        if ($jsonString === false) {
+            return null;
+        }
+
+        $attributes = json_decode($jsonString, true);
+
+        return new UWUserDetails(
+            $attributes[UserDetailsProvider::EPPN],
+            $attributes[UserDetailsProvider::PVI],
+            $attributes[UserDetailsProvider::FULLNAME],
+            $attributes[UserDetailsProvider::UDDS],
+            $attributes[UserDetailsProvider::EMAIL],
+            $attributes[UserDetailsProvider::SOURCE],
+            $attributes[UserDetailsProvider::ISIS_EMPLID],
+            $attributes[UserDetailsProvider::FIRST_NAME],
+            $attributes[UserDetailsProvider::LAST_NAME]
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/LocalUserDetailsService.php b/src/main/edu/wisc/doit/LocalUserDetailsService.php
deleted file mode 100644
index 42704aa..0000000
--- a/src/main/edu/wisc/doit/LocalUserDetailsService.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Implementation of {@link UserDetailsService} for use in local development.
- */
-class LocalUserDetailsService implements UserDetailsService
-{
-
-    /** @var UserDetailsAttributeMapper */
-    private $attributeMapper;
-    
-    /**
-     * LocalUserDetailsService constructor.
-     * @param UserDetailsAttributeMapper $mapper
-     */
-    public function __construct(UserDetailsAttributeMapper $mapper = null)
-    {
-        if ($mapper == null) {
-            $this->attributeMapper = new LocalUserDetailsAttributeMapper();
-        } else {
-            $this->attributeMapper = $mapper;
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function loadUser()
-    {
-        $userAttributes = $this->attributeMapper->mapUser();
-
-        // Return null if attribute reading failed
-        if ($userAttributes == null) {
-            return null;
-        }
-        
-        return new UWUserDetails(
-            $userAttributes[UserDetailsAttributeMapper::EPPN],
-            $userAttributes[UserDetailsAttributeMapper::PVI],
-            $userAttributes[UserDetailsAttributeMapper::FULLNAME],
-            $userAttributes[UserDetailsAttributeMapper::UDDS],
-            $userAttributes[UserDetailsAttributeMapper::EMAIL],
-            $userAttributes[UserDetailsAttributeMapper::SOURCE],
-            $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID],
-            $userAttributes[UserDetailsAttributeMapper::FIRST_NAME],
-            $userAttributes[UserDetailsAttributeMapper::LAST_NAME]
-        );
-    }
-    
-}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php b/src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php
new file mode 100644
index 0000000..718a0c8
--- /dev/null
+++ b/src/main/edu/wisc/doit/PreauthHTTPUserDetailsProvider.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * PreauthHTTPUserDetailsProvider provides an application with a {@link UWUserDetails} when the server is configured
+ * to send Shibboleth attributes as HTTP headers.
+ */
+class PreauthHTTPUserDetailsProvider implements UserDetailsProvider
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loadUser()
+    {
+        $userDetails = new UWUserDetails(
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::EPPN)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::PVI)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::FULLNAME)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::UDDS)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::EMAIL)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::SOURCE)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::ISIS_EMPLID)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::FIRST_NAME)],
+            $_SERVER[$this->mapAttribute(UserDetailsProvider::LAST_NAME)]
+        );
+
+        // Require EPPN, PVI and FULLNAME to be set to consider user loading successful
+        if (empty($userDetails->getEppn())) {
+            return null;
+        }
+
+        return $userDetails;
+    }
+
+    /**
+     * Map a Shibboleth attribute to its associated HTTP header name.
+     * 
+     * @param string $attribute attribute to map
+     * @return Shibboleth attribute name mapped to its equivalent HTTP header name
+     */
+    private function mapAttribute($attribute)
+    {
+        return 'HTTP_' . strtoupper($attribute);
+    }
+    
+}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php b/src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php
deleted file mode 100644
index d0b8118..0000000
--- a/src/main/edu/wisc/doit/PreauthUserDetailsAttributeMapper.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Default implementation of {@UserDetailsAttributeMapper} for use in preauthenticated (Shibboleth) environments.
- */
-class PreauthUserDetailsAttributeMapper implements UserDetailsAttributeMapper
-{
-
-    /**
-     * {@inheritdoc}
-     */
-    public function mapUser()
-    {
-        $userAttributes[UserDetailsAttributeMapper::EPPN] = $_SERVER[UserDetailsAttributeMapper::EPPN];
-        $userAttributes[UserDetailsAttributeMapper::PVI] = $_SERVER[UserDetailsAttributeMapper::PVI];
-        $userAttributes[UserDetailsAttributeMapper::FULLNAME] = $_SERVER[UserDetailsAttributeMapper::FULLNAME];
-        $userAttributes[UserDetailsAttributeMapper::FIRST_NAME] = $_SERVER[UserDetailsAttributeMapper::FIRST_NAME];
-        $userAttributes[UserDetailsAttributeMapper::LAST_NAME] = $_SERVER[UserDetailsAttributeMapper::LAST_NAME];
-        $userAttributes[UserDetailsAttributeMapper::EMAIL] = $_SERVER[UserDetailsAttributeMapper::EMAIL];
-        $userAttributes[UserDetailsAttributeMapper::UDDS] = $_SERVER[UserDetailsAttributeMapper::UDDS];
-        $userAttributes[UserDetailsAttributeMapper::SOURCE] = $_SERVER[UserDetailsAttributeMapper::SOURCE];
-        $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID] = $_SERVER[UserDetailsAttributeMapper::ISIS_EMPLID];
-
-        // Require EPPN, PVI and FULLNAME to be set to consider user loading successful
-        if (empty($userAttributes[UserDetailsAttributeMapper::EPPN]) ||
-            empty($userAttributes[UserDetailsAttributeMapper::PVI]) ||
-            empty($userAttributes[UserDetailsAttributeMapper::FULLNAME])) {
-            return null;
-        }
-        
-        return $userAttributes;
-    }
-
-}
diff --git a/src/main/edu/wisc/doit/PreauthUserDetailsProvider.php b/src/main/edu/wisc/doit/PreauthUserDetailsProvider.php
new file mode 100644
index 0000000..c86c2f7
--- /dev/null
+++ b/src/main/edu/wisc/doit/PreauthUserDetailsProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * PreauthUserDetailsProvider provides an application with a {@link UWUserDetails} from Shibboleth attributes.
+ */
+class PreauthUserDetailsProvider implements UserDetailsProvider
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loadUser()
+    {
+        $userDetails = new UWUserDetails(
+            $_SERVER[UserDetailsProvider::EPPN],
+            $_SERVER[UserDetailsProvider::PVI],
+            $_SERVER[UserDetailsProvider::FULLNAME],
+            $_SERVER[UserDetailsProvider::UDDS],
+            $_SERVER[UserDetailsProvider::EMAIL],
+            $_SERVER[UserDetailsProvider::SOURCE],
+            $_SERVER[UserDetailsProvider::ISIS_EMPLID],
+            $_SERVER[UserDetailsProvider::FIRST_NAME],
+            $_SERVER[UserDetailsProvider::LAST_NAME]
+        );
+
+        // Require EPPN to be set to consider user loading successful
+        if (empty($userDetails->getEppn())) {
+            return null;
+        }
+        
+        return $userDetails;
+    }
+
+}
diff --git a/src/main/edu/wisc/doit/PreauthUserDetailsService.php b/src/main/edu/wisc/doit/PreauthUserDetailsService.php
deleted file mode 100644
index 11a5aa0..0000000
--- a/src/main/edu/wisc/doit/PreauthUserDetailsService.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Default implementation of {@UserDetailsAttributeMapper} for use in preauthenticated (Shibboleth) environments.
- */
-class PreauthUserDetailsService implements UserDetailsService
-{
-
-    /** @var UserDetailsAttributeMapper */
-    private $attributeMapper;
-
-    /**
-     * PreauthUserDetailsService constructor.
-     * @param UserDetailsAttributeMapper|null $mapper
-     */
-    public function __construct(UserDetailsAttributeMapper $mapper = null)
-    {
-        if ($mapper == null) {
-            $this->attributeMapper = new PreauthUserDetailsAttributeMapper();
-        } else {
-            $this->attributeMapper = $mapper;
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function loadUser()
-    {
-        $userAttributes = $this->attributeMapper->mapUser();
-
-        // Return null if attribute reading failed
-        if ($userAttributes == null) {
-            return null;
-        }
-
-        return new UWUserDetails(
-            $userAttributes[UserDetailsAttributeMapper::EPPN],
-            $userAttributes[UserDetailsAttributeMapper::PVI],
-            $userAttributes[UserDetailsAttributeMapper::FULLNAME],
-            $userAttributes[UserDetailsAttributeMapper::UDDS],
-            $userAttributes[UserDetailsAttributeMapper::EMAIL],
-            $userAttributes[UserDetailsAttributeMapper::SOURCE],
-            $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID],
-            $userAttributes[UserDetailsAttributeMapper::FIRST_NAME],
-            $userAttributes[UserDetailsAttributeMapper::LAST_NAME]
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/UserDetailsAttributeMapper.php b/src/main/edu/wisc/doit/UserDetailsProvider.php
similarity index 65%
rename from src/main/edu/wisc/doit/UserDetailsAttributeMapper.php
rename to src/main/edu/wisc/doit/UserDetailsProvider.php
index 08ca6f3..2a4e37e 100644
--- a/src/main/edu/wisc/doit/UserDetailsAttributeMapper.php
+++ b/src/main/edu/wisc/doit/UserDetailsProvider.php
@@ -3,10 +3,9 @@
 namespace edu\wisc\doit;
 
 /**
- * UserDetailsAttributeMapper defines an interface for mapping common UW user attributes to an associative array. The
- * constants defined in this interface represent headers sent by UW Federated login that identify a user.
+ * TODO: Write documentation
  */
-interface UserDetailsAttributeMapper
+interface UserDetailsProvider
 {
     
     // Constants representing UW Federated login Shibboleth headers which should be mapped by concrete implementations.
@@ -23,8 +22,8 @@ interface UserDetailsAttributeMapper
     /**
      * Map Shibboleth header values to an associative array.
      *
-     * @return array
+     * @return UserDetails
      */
-    public function mapUser();
+    public function loadUser();
 
 }
\ No newline at end of file
diff --git a/src/main/edu/wisc/doit/UserDetailsService.php b/src/main/edu/wisc/doit/UserDetailsService.php
deleted file mode 100644
index 6dbb8fc..0000000
--- a/src/main/edu/wisc/doit/UserDetailsService.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * UserDetailsService is the interface that provides an instance of {@link UserDetails}, typically a {@link UWUserDetails}.
- * Two concrete implementations are provided, {@link LocalUserDetailsService} and {@link PreauthUserDetailsService}.
- */
-interface UserDetailsService
-{
-
-    /**
-     * Return a {@link UserDetails} hydrated by a {@link UserDetailsAttributeMapper}, or null if attribute
-     * mapping failed.
-     *
-     * @return UserDetails|null
-     */
-    public function loadUser();
-    
-}
\ No newline at end of file
diff --git a/src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php b/src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php
deleted file mode 100644
index d79a948..0000000
--- a/src/test/edu/wisc/doit/LocalUserDetailsAttributeMapperTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Tests for {@link LocalUserDetailsAttributeMapper}
- */
-class LocalUserDetailsAttributeMapperTest extends \PHPUnit_Framework_TestCase
-{
-
-    /**
-     * Test attribute mapping for local development.
-     */
-    public function testMapLocalUser() {
-        $attributeMapper = new LocalUserDetailsAttributeMapper();
-        $userAttributes = $attributeMapper->mapUser();
-        $this->assertEquals("bbadger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EPPN]);
-        $this->assertEquals("UW123A456", $userAttributes[UserDetailsAttributeMapper::PVI]);
-        $this->assertEquals("BUCKINGHAM BADGER", $userAttributes[UserDetailsAttributeMapper::FULLNAME]);
-        $this->assertEquals("bucky.badger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EMAIL]);
-        $this->assertEquals("a_source", $userAttributes[UserDetailsAttributeMapper::SOURCE]);
-        $this->assertEquals("123456789", $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID]);
-        $this->assertEquals("BUCKINGHAM", $userAttributes[UserDetailsAttributeMapper::FIRST_NAME]);
-        $this->assertEquals("BADGER", $userAttributes[UserDetailsAttributeMapper::LAST_NAME]);
-        $this->assertEquals(["UW123A456", "UW234A567"], $userAttributes[UserDetailsAttributeMapper::UDDS]);
-    }
-    
-}
diff --git a/src/test/edu/wisc/doit/LocalUserDetailsServiceTest.php b/src/test/edu/wisc/doit/LocalUserDetailsProviderTest.php
similarity index 78%
rename from src/test/edu/wisc/doit/LocalUserDetailsServiceTest.php
rename to src/test/edu/wisc/doit/LocalUserDetailsProviderTest.php
index 5a1afda..99b5131 100644
--- a/src/test/edu/wisc/doit/LocalUserDetailsServiceTest.php
+++ b/src/test/edu/wisc/doit/LocalUserDetailsProviderTest.php
@@ -3,14 +3,14 @@
 namespace edu\wisc\doit;
 
 /**
- * Tests for {@link LocalUserDetailsService}.
+ * Tests for {@link LocalUserDetailsProvider}.
  */
-class LocalUserDetailsServiceTest extends \PHPUnit_Framework_TestCase
+class LocalUserDetailsProviderTest extends \PHPUnit_Framework_TestCase
 {
 
     public function testLoadUser()
     {
-        $userDetailsService = new LocalUserDetailsService();
+        $userDetailsService = new LocalUserDetailsProvider();
         $user = $userDetailsService->loadUser();
         $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
         $this->assertEquals("UW123A456", $user->getPvi());
diff --git a/src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php b/src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php
new file mode 100644
index 0000000..3f8d1d5
--- /dev/null
+++ b/src/test/edu/wisc/doit/PreauthHTTPUserDetailsProviderTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * Tests for {@link PreauthHTTPUserDetailsProvider}.
+ */
+class PreauthHTTPUserDetailsProviderTest extends \PHPUnit_Framework_TestCase
+{
+
+    /** @var UserDetailsProvider */
+    private $userService;
+
+    /**
+     * Populate $_SERVER with Shib attributes to simulate a logged in user
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        $jsonString = file_get_contents(__DIR__ . "/../../../resources/testuser.json");
+        if ($jsonString === false) {
+            return null;
+        }
+
+        $attributes = json_decode($jsonString, true);
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::EPPN)] = $attributes[UserDetailsProvider::EPPN];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::PVI)] = $attributes[UserDetailsProvider::PVI];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::FULLNAME)] = $attributes[UserDetailsProvider::FULLNAME];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::FIRST_NAME)] = $attributes[UserDetailsProvider::FIRST_NAME];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::LAST_NAME)] = $attributes[UserDetailsProvider::LAST_NAME];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::UDDS)] = $attributes[UserDetailsProvider::UDDS];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::EMAIL)] = $attributes[UserDetailsProvider::EMAIL];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::SOURCE)] = $attributes[UserDetailsProvider::SOURCE];
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::ISIS_EMPLID)] = $attributes[UserDetailsProvider::ISIS_EMPLID];
+
+        $this->userService = new PreauthHTTPUserDetailsProvider();
+    }
+
+    public function testLoadUser() {
+        $user = $this->userService->loadUser();
+        $this->assertNotNull($user);
+        $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
+        $this->assertEquals("UW123A456", $user->getPvi());
+        $this->assertEquals("BUCKINGHAM BADGER", $user->getFullName());
+        $this->assertEquals("bucky.badger@wisc.edu", $user->getEmailAddress());
+        $this->assertEquals("a_source", $user->getSource());
+        $this->assertEquals("123456789", $user->getIsisEmplid());
+        $this->assertEquals("BUCKINGHAM", $user->getFirstName());
+        $this->assertEquals("BADGER", $user->getLastName());
+    }
+
+    public function testLoadUserWithNoEPPN() {
+        // Clear EPPN to simulate no EPPN
+        $_SERVER[$this->mapAttribute(UserDetailsProvider::EPPN)] = null;
+        $user = $this->userService->loadUser();
+        $this->assertNull($user);
+    }
+
+    /**
+     * Map Shibboleth attribute to equivalent HTTP header value.
+     *
+     * @param $attribute
+     * @return string
+     */
+    private function mapAttribute($attribute)
+    {
+        return 'HTTP_' . strtoupper($attribute);
+    }
+
+}
diff --git a/src/test/edu/wisc/doit/PreauthTestCase.php b/src/test/edu/wisc/doit/PreauthTestCase.php
deleted file mode 100644
index b79a69b..0000000
--- a/src/test/edu/wisc/doit/PreauthTestCase.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Class to do basic setup needed to simulate a logged in Shibboleth user.
- */
-abstract class PreauthTestCase extends \PHPUnit_Framework_TestCase
-{
-
-    /**
-     * Populate $_SERVER with Shib attributes to simulate a logged in user
-     */
-    protected function setUp()
-    {
-        parent::setUp();
-        $jsonString = file_get_contents(__DIR__ . "/../../../resources/testuser.json");
-        if ($jsonString === false) {
-            return null;
-        }
-
-        $attributes = json_decode($jsonString, true);
-        $_SERVER[UserDetailsAttributeMapper::EPPN] = $attributes[UserDetailsAttributeMapper::EPPN];
-        $_SERVER[UserDetailsAttributeMapper::PVI] = $attributes[UserDetailsAttributeMapper::PVI];
-        $_SERVER[UserDetailsAttributeMapper::FULLNAME] = $attributes[UserDetailsAttributeMapper::FULLNAME];
-        $_SERVER[UserDetailsAttributeMapper::FIRST_NAME] = $attributes[UserDetailsAttributeMapper::FIRST_NAME];
-        $_SERVER[UserDetailsAttributeMapper::LAST_NAME] = $attributes[UserDetailsAttributeMapper::LAST_NAME];
-        $_SERVER[UserDetailsAttributeMapper::UDDS] = $attributes[UserDetailsAttributeMapper::UDDS];
-        $_SERVER[UserDetailsAttributeMapper::EMAIL] = $attributes[UserDetailsAttributeMapper::EMAIL];
-        $_SERVER[UserDetailsAttributeMapper::SOURCE] = $attributes[UserDetailsAttributeMapper::SOURCE];
-        $_SERVER[UserDetailsAttributeMapper::ISIS_EMPLID] = $attributes[UserDetailsAttributeMapper::ISIS_EMPLID];
-    }
-
-}
\ No newline at end of file
diff --git a/src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php b/src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php
deleted file mode 100644
index 63258c4..0000000
--- a/src/test/edu/wisc/doit/PreauthUserDetailsAttributeMapperTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Tests for {@link PreauthUserDetailsAttributeMapper}.
- */
-class PreauthUserDetailsAttributeMapperTest extends PreauthTestCase
-{
-
-    public function testMapUser() {
-        $attributeMapper = new PreauthUserDetailsAttributeMapper();
-        $userAttributes = $attributeMapper->mapUser();
-        $this->assertEquals("bbadger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EPPN]);
-        $this->assertEquals("UW123A456", $userAttributes[UserDetailsAttributeMapper::PVI]);
-        $this->assertEquals("BUCKINGHAM BADGER", $userAttributes[UserDetailsAttributeMapper::FULLNAME]);
-        $this->assertEquals("bucky.badger@wisc.edu", $userAttributes[UserDetailsAttributeMapper::EMAIL]);
-        $this->assertEquals("a_source", $userAttributes[UserDetailsAttributeMapper::SOURCE]);
-        $this->assertEquals("123456789", $userAttributes[UserDetailsAttributeMapper::ISIS_EMPLID]);
-        $this->assertEquals("BUCKINGHAM", $userAttributes[UserDetailsAttributeMapper::FIRST_NAME]);
-        $this->assertEquals("BADGER", $userAttributes[UserDetailsAttributeMapper::LAST_NAME]);
-        $this->assertEquals(["UW123A456", "UW234A567"], $userAttributes[UserDetailsAttributeMapper::UDDS]);
-    }
-
-}
diff --git a/src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php b/src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php
new file mode 100644
index 0000000..a3c4d27
--- /dev/null
+++ b/src/test/edu/wisc/doit/PreauthUserDetailsProviderTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace edu\wisc\doit;
+
+/**
+ * Tests for {@link PreauthUserDetailsProvider}.
+ */
+class PreauthUserDetailsProviderTest extends \PHPUnit_Framework_TestCase
+{
+
+    /** @var UserDetailsProvider */
+    private $userService;
+
+    /**
+     * Populate $_SERVER with Shib attributes to simulate a logged in user
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        $jsonString = file_get_contents(__DIR__ . "/../../../resources/testuser.json");
+        if ($jsonString === false) {
+            return null;
+        }
+
+        $attributes = json_decode($jsonString, true);
+        $_SERVER[UserDetailsProvider::EPPN] = $attributes[UserDetailsProvider::EPPN];
+        $_SERVER[UserDetailsProvider::PVI] = $attributes[UserDetailsProvider::PVI];
+        $_SERVER[UserDetailsProvider::FULLNAME] = $attributes[UserDetailsProvider::FULLNAME];
+        $_SERVER[UserDetailsProvider::FIRST_NAME] = $attributes[UserDetailsProvider::FIRST_NAME];
+        $_SERVER[UserDetailsProvider::LAST_NAME] = $attributes[UserDetailsProvider::LAST_NAME];
+        $_SERVER[UserDetailsProvider::UDDS] = $attributes[UserDetailsProvider::UDDS];
+        $_SERVER[UserDetailsProvider::EMAIL] = $attributes[UserDetailsProvider::EMAIL];
+        $_SERVER[UserDetailsProvider::SOURCE] = $attributes[UserDetailsProvider::SOURCE];
+        $_SERVER[UserDetailsProvider::ISIS_EMPLID] = $attributes[UserDetailsProvider::ISIS_EMPLID];
+
+        $this->userService = new PreauthUserDetailsProvider();
+    }
+
+    public function testLoadUser() {
+        $user = $this->userService->loadUser();
+        $this->assertNotNull($user);
+        $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
+        $this->assertEquals("UW123A456", $user->getPvi());
+        $this->assertEquals("BUCKINGHAM BADGER", $user->getFullName());
+        $this->assertEquals("bucky.badger@wisc.edu", $user->getEmailAddress());
+        $this->assertEquals("a_source", $user->getSource());
+        $this->assertEquals("123456789", $user->getIsisEmplid());
+        $this->assertEquals("BUCKINGHAM", $user->getFirstName());
+        $this->assertEquals("BADGER", $user->getLastName());
+    }
+
+    public function testLoadUserWithNoEPPN() {
+        // Clear EPPN to simulate no EPPN
+        $_SERVER[UserDetailsProvider::EPPN] = null;
+        $user = $this->userService->loadUser();
+        $this->assertNull($user);
+    }
+
+}
diff --git a/src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php b/src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php
deleted file mode 100644
index ffa97c1..0000000
--- a/src/test/edu/wisc/doit/PreauthUserDetailsServiceTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace edu\wisc\doit;
-
-/**
- * Tests for {@link PreauthUserDetailsService}.
- */
-class PreauthUserDetailsServiceTest extends PreauthTestCase
-{
-
-    /** @var UserDetailsService */
-    private $userService;
-
-    protected function setUp()
-    {
-        parent::setUp();
-        $this->userService = new PreauthUserDetailsService();
-    }
-
-    public function testLoadUser() {
-        $user = $this->userService->loadUser();
-        $this->assertNotNull($user);
-        $this->assertEquals("bbadger@wisc.edu", $user->getEppn());
-        $this->assertEquals("UW123A456", $user->getPvi());
-        $this->assertEquals("BUCKINGHAM BADGER", $user->getFullName());
-        $this->assertEquals("bucky.badger@wisc.edu", $user->getEmailAddress());
-        $this->assertEquals("a_source", $user->getSource());
-        $this->assertEquals("123456789", $user->getIsisEmplid());
-        $this->assertEquals("BUCKINGHAM", $user->getFirstName());
-        $this->assertEquals("BADGER", $user->getLastName());
-    }
-
-    public function testLoadUserWithNoEPPN() {
-        // Clear EPPN to simulate no EPPN
-        $_SERVER[UserDetailsAttributeMapper::EPPN] = null;
-        $user = $this->userService->loadUser();
-        $this->assertNull($user);
-    }
-
-}
-- 
GitLab