Commit 5b9aed6c authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Fix tons of bugs and actually get auth workin for saml and everything

else
parent 968371e5
......@@ -27,7 +27,7 @@ $config = [
* external url, no matter where you come from (direct access or via the
* reverse proxy).
*/
'baseurlpath' => 'http://localhost:8080/simplesaml-idp/',
'baseurlpath' => 'https://localhost:8080/simplesaml-idp/',
/*
* The 'application' configuration array groups a set configuration options
......
......@@ -6,13 +6,13 @@
* See: https://simplesamlphp.org/docs/stable/simplesamlphp-reference-sp-remote
*/
$metadata['http://localhost:8080/simplesaml/module.php/saml/sp/metadata.php/default-sp'] = array (
$metadata['https://localhost:8080/simplesaml/module.php/saml/sp/metadata.php/default-sp'] = array (
'SingleLogoutService' =>
array (
0 =>
array (
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://localhost:8080/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp',
'Location' => 'https://localhost:8080/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp',
),
),
'AssertionConsumerService' =>
......@@ -21,25 +21,25 @@ $metadata['http://localhost:8080/simplesaml/module.php/saml/sp/metadata.php/defa
array (
'index' => 0,
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
'Location' => 'http://localhost:8080/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
'Location' => 'https://localhost:8080/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
),
1 =>
array (
'index' => 1,
'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post',
'Location' => 'http://localhost:8080/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp',
'Location' => 'https://localhost:8080/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp',
),
2 =>
array (
'index' => 2,
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
'Location' => 'http://localhost:8080/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
'Location' => 'https://localhost:8080/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
),
3 =>
array (
'index' => 3,
'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
'Location' => 'http://localhost:8080/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp/artifact',
'Location' => 'https://localhost:8080/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp/artifact',
),
),
'certData' => 'MIIEwTCCAymgAwIBAgIUOBK2NqzhcX63Vj6AIoGNzzRp8WgwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xFjAUBgNVBAcMDUNvbGxlZ2UgUGxhY2UxHzAdBgNVBAoMFldhbGxhIFdhbGxhIFVuaXZlcnNpdHkxEzARBgNVBAsMCktyZXRzY2htYXIwHhcNMjAwMTIxMTAyODE1WhcNMzAwMTIwMTAyODE1WjBwMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEWMBQGA1UEBwwNQ29sbGVnZSBQbGFjZTEfMB0GA1UECgwWV2FsbGEgV2FsbGEgVW5pdmVyc2l0eTETMBEGA1UECwwKS3JldHNjaG1hcjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKSU6Xayh+chaCvKHwMuvKZcxt6QrtSfjxwHOd+xsGIrQXW/8LB9yAIalb/aKavfw6sQ+Ftt1rRXJM5QFFZiADrZuds/IHIaV+YhFH2JsBY6kmiGWzYsI0Cyq+wRW9vcuCSyYZb4r7xQw++kILD1Wg2rW7jNcq4E7Nm16tgVcQwztN4I3G7fTso9AHTv1yZRzpDzGAjiVRXWScoMx7k49QOcXrfJQzoR+jQxa4M+p4ofAIAqECJ1VlYNOIcIyT2TJXMWpwocpKG+ukxHsN/az7DmbRErn8Aw7va9FzMGaiJ2xM52j+1qNzqUOn939W3UM05fnOz/rYyJh0jTiLJv6zLj+Y5L0CCASCMOF90+jrKD0EBP0NzTy85cNHqxtisEyxio54UO3LFjKR7EecrIrmDnn1xnQ8cLIMoeU6jqw8hKtnt0aAd6Xhplnmr59Mkoc/sRRAqgM5OHbE2V5H0KwJq78a2nHTR7TWL9iVDqos07FjOzJv7zc2510DT93fCKrQIDAQABo1MwUTAdBgNVHQ4EFgQUiVs1Pj0+Hf67NHYkh5QAQ8pk7vcwHwYDVR0jBBgwFoAUiVs1Pj0+Hf67NHYkh5QAQ8pk7vcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAYEASF3ITaDiuuSLmDBzFQpVZcOQfiKu1UC4cLSdz8E5pWHdm+ZPsjQFlrGt4fz+QAG5IcaCmBmpm+uuJkY8kPCrHlX5GYIDCr17RYAfTBiergdTLrH0UN+r7s0S9bvw33a4WAeq8sviE4oE742hCgHfxhDHcux2mAULeYQr7YWubfWLvVrJ7/ibIT/cLscRBmBEDQwxSIFpnQlcmmvEquVfec15twKyk6GB4/gIVylBlWixdYP1qIbMsUClXNNJY9kSERKcUxicX609n0L5eJ5AcScPuuNewKxy5vO0FsK9YrrITwJ1HPyv3JfIm71Dw/zQwl+pk97IF5Yz9oTQpsXD62PtxU+0HUZQhQZRUOyqoGlp9fRe95/mOV+ljdUa+n7PfiVL4eIocmGXAGgxeaeARGCB3lYUO9dfxoK4l2AtWgnPq5jMexm+m5DimLkC+kLViYkrN/dzLMgYREBoyY6cB9HjYI1qd3KKk6wUDwKtQp729pKLY7Mnw26U0AHCPYBn',
......
......@@ -25,7 +25,7 @@ $config = [
// The entity ID of the IdP this SP should contact.
// Can be NULL/unset, in which case the user will be shown a list of available IdPs.
'idp' => 'http://localhost:8080/simplesaml-idp/saml2/idp/metadata.php',
'idp' => 'https://localhost:8080/simplesaml-idp/saml2/idp/metadata.php',
// The URL to the discovery service.
// Can be NULL/unset, in which case a builtin discovery service will be used.
......
......@@ -8,15 +8,15 @@
* See: https://simplesamlphp.org/docs/stable/simplesamlphp-reference-idp-remote
*/
$metadata['http://localhost:8080/simplesaml-idp/saml2/idp/metadata.php'] = array (
$metadata['https://localhost:8080/simplesaml-idp/saml2/idp/metadata.php'] = array (
'metadata-set' => 'saml20-idp-remote',
'entityid' => 'http://localhost:8080/simplesaml-idp/saml2/idp/metadata.php',
'entityid' => 'https://localhost:8080/simplesaml-idp/saml2/idp/metadata.php',
'SingleSignOnService' =>
array (
0 =>
array (
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://localhost:8080/simplesaml-idp/saml2/idp/SSOService.php',
'Location' => 'https://localhost:8080/simplesaml-idp/saml2/idp/SSOService.php',
),
),
'SingleLogoutService' =>
......@@ -24,7 +24,7 @@ $metadata['http://localhost:8080/simplesaml-idp/saml2/idp/metadata.php'] = array
0 =>
array (
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://localhost:8080/simplesaml-idp/saml2/idp/SingleLogoutService.php',
'Location' => 'https://localhost:8080/simplesaml-idp/saml2/idp/SingleLogoutService.php',
),
),
'certData' => 'MIIEwTCCAymgAwIBAgIUWlixu/uHDLux2Txl3HGBngrYZxQwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xFjAUBgNVBAcMDUNvbGxlZ2UgUGxhY2UxHzAdBgNVBAoMFldhbGxhIFdhbGxhIFVuaXZlcnNpdHkxEzARBgNVBAsMCktyZXRzY2htYXIwHhcNMjAwMTIxMTAyNjQxWhcNMzAwMTIwMTAyNjQxWjBwMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEWMBQGA1UEBwwNQ29sbGVnZSBQbGFjZTEfMB0GA1UECgwWV2FsbGEgV2FsbGEgVW5pdmVyc2l0eTETMBEGA1UECwwKS3JldHNjaG1hcjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALhJUyYFwJRwiYzXnuR4dGkEUF6hjnbXxKnkwfuRO0Apy7G3RGtkqq+vGOTn2MymUpYOxXNCsz6cAwrrADjnW9cRUERkqR0KSnrTsATzF6rvnodM27hREysQEKVW+dcKIklrXxOaSRJuCoYXV+QuSK/Qph6qDYimxdLl4CWTuWtu2Pytr5ABcewYoawf816ErcNVw2pP2gxAB+OxyoERlo2E+6b4yk6e1V/StFdRgeABuMAPAgip49PPd7u0hUkay0fplB5fqg7xnuwMsJRffBJRRd5bZjMRz7M3OHL8kvhjCAWn54ERf8zYJJLaG+D96TPT7fNbyfylDzWS64wwwdo/iX3R9cwpPatAmh6ke2MGgypE1Xv2EUmqTPnlTIgxRxX4Y+N3sQibDtat1KhDIWZFk1YAkJpOarWaXolzUt/7wsYnocA51/REEDoIlVwa0xLDpGAMVHpzCmOMk62C047ptUVAKwPRKwVTRo44wyK2OOOvTWef3oeGLjQU0ICX2wIDAQABo1MwUTAdBgNVHQ4EFgQUGwOm6LVLEeLxGMOiqVHjcehg1YQwHwYDVR0jBBgwFoAUGwOm6LVLEeLxGMOiqVHjcehg1YQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAYEAfrz2N8cMRnDpD8H6o63Sa6aVJc0soSy5dzlSc/MLh9TvOuztWSovaDmfsshMTBH6ChZQ+XifZWwKJaf+fFaYGgcFDFwQVslzGjoSY2VXYSKmcfSIOpb4jkKvc4mjuxLxhi/WcKga7IVb7/xNw5uqWfJI+ndtz45AwJ/zpzQjvAMipZwjtwAgryXhcAdBlzhSRNdysPPCDCxjQWqaI+SSWMa0Ud/frXgYeOP9ID73qOf9rKrSjftJKXCYpXsjGykkv9GrCjJxe+usRSHXw6ddrO7aYfl7mXjsXQh+OlhuKog8MGUOQMa2I14qn8qTPKNmMl62Qu06pYFgDez9oLPM40mEilRpNQHO/lDqwS8J6x8Ir/Ub8a38s+VkIWGHsnvLDR/tHW0VI+RGsL10hsUbV2geIQ0CkDOJMo9kohlLqBf0WKr+sSRH7n/M/Kd0bccERTnr9NYl76Wuddo3v4JcIf48vYE8pUFDmCTGRwnTN6vCnNZLvWa/+WwYLpN8KhYW',
......
......@@ -3,37 +3,34 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Cookie\CookieJar;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Auth\AuthenticationException;
use Source\Exceptions\AuthorizationException;
use Illuminate\Validation\ValidationException;
use Source\Exceptions\EntityNotFoundException;
use Source\UseCases\Users\Authenticate\APIPresenter;
use Source\UseCases\Users\Authenticate\AuthenticateUseCase;
use Source\UseCases\Users\Authenticate\UserCreationException;
class AuthController extends ApiController {
protected Request $request;
public function __construct(Request $request) {
protected CookieJar $cookieJar;
public function __construct(Request $request, CookieJar $cookieJar) {
$this->request = $request;
$this->cookieJar = $cookieJar;
}
/**
* @param AuthenticateUseCase $authenticateUseCase
* @return JsonResponse
* @throws ValidationException
* @throws AuthenticationException
* @throws EntityNotFoundException
*/
public function login(AuthenticateUseCase $authenticateUseCase): JsonResponse {
$this->validate(
$this->request,
[
'email' => 'required',
'password' => 'required',
]
);
$presenter = new APIPresenter();
try {
......@@ -42,10 +39,53 @@ class AuthController extends ApiController {
throw new AuthenticationException();
}
return $this->respondWithData($presenter->getViewModel())->cookie(
return $this->respondWithData($presenter->getViewModel())->withCookie(
cookie(
'api_token',
$presenter->getViewModel()['token']['value'],
$presenter->getViewModel()['token']['minutes']
)
);
}
public function samlLogin(AuthenticateUseCase $authenticateUseCase): RedirectResponse {
return redirect()->to($authenticateUseCase->handToSaml());
}
/**
* @param AuthenticateUseCase $authenticateUseCase
* @return mixed
* @throws EntityNotFoundException
*/
public function handle(AuthenticateUseCase $authenticateUseCase) {
$presenter = new APIPresenter();
try {
$authenticateUseCase->handleSamlLogin($presenter);
} catch (UserCreationException $e) {
return $this->respondWithError(
'There was an error authenticating the user. Please contact an administrator.'
);
}
return redirect()->intended(url(config('saml.home_page')))->cookie(
'api_token',
$presenter->getViewModel()['token']['value'],
$presenter->getViewModel()['token']['minutes']
);
}
/**
* @param AuthenticateUseCase $authenticateUseCase
* @return RedirectResponse
*/
public function samlLogout(AuthenticateUseCase $authenticateUseCase): RedirectResponse {
Cookie::queue($this->cookieJar->forget('api_token'));
return redirect()->to(
$authenticateUseCase->samlLogout(
$this->request->cookie('api_token')
)
);
}
}
......@@ -59,6 +59,7 @@ class Kernel extends HttpKernel
],
'api' => [
EncryptCookies::class,
'throttle:60,1',
'bindings',
],
......
......@@ -12,6 +12,5 @@ class EncryptCookies extends Middleware
* @var array
*/
protected $except = [
'api_token',
];
}
......@@ -3,6 +3,7 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Saml\SamlRepositoryServiceProvider;
use Source\Gateways\Users\UsersRepositoryServiceProvider;
use Source\Gateways\Doors\DoorsRepositoryServiceProvider;
use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
......@@ -22,6 +23,7 @@ class AppServiceProvider extends ServiceProvider
* @var string[]
*/
protected array $gatewayProviders = [
SamlRepositoryServiceProvider::class,
UsersRepositoryServiceProvider::class,
DoorsRepositoryServiceProvider::class,
TokensRepositoryServiceProvider::class,
......
<?php
return [
/*
|--------------------------------------------------------------------------
| Home Page
|--------------------------------------------------------------------------
|
| Here you may set the route name the will be used to redirect the user
| after login.
|
*/
'home_page' => env('FRONTEND_URL', '/'),
/*
|--------------------------------------------------------------------------
| Login Route
|--------------------------------------------------------------------------
|
| Here you may set the route callback to go to
| after login.
|
*/
'login_route' => '/api/handle-login',
/*
|--------------------------------------------------------------------------
| Logout Route
|--------------------------------------------------------------------------
|
| Here you may set the route name the will be used to redirect the user
| after logout.
|
*/
'logout_route' => '/',
/*
|--------------------------------------------------------------------------
| SimpleSAMLphp
|--------------------------------------------------------------------------
|
| Here you may set the SimpleSAMLphp configuration.
|
| Note that the autoload file is relative to the
| base directory of this project.
|
*/
'simplesamlphp' => [
'autoload' => env('SAML_SIMPLESAMLPHP_AUTOLOAD'),
'auth_source' => env('SAML_SIMPLESAMLPHP_AUTH_SOURCE', 'default-sp'),
]
];
......@@ -24,7 +24,7 @@ class CreateUsersTable extends Migration
// hashed
$table->string('password')->nullable()->default(null);
// hashed
$table->string('doorcode');
$table->string('doorcode')->nullable()->default(null);
$table->timestamp('expires_at')->nullable();
$table->timestamps();
$table->softDeletes();
......
......@@ -17,6 +17,8 @@ use App\Http\Controllers\UsersController;
*/
Route::post('login', [AuthController::class, 'login']);
Route::post('logout', [AuthController::class, 'logout']);
Route::group(['middleware' => 'auth:api'], static function () {
Route::group(
[
......
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
/*
|--------------------------------------------------------------------------
......@@ -19,3 +20,9 @@ Route::get(
return ['api endpoint'];
}
);
Route::get('login', [AuthController::class, 'samlLogin']);
Route::get('handle-login', [AuthController::class, 'handle']);
Route::get('logout', [AuthController::class, 'samlLogout']);
<?php
namespace Source\Entities;
class SamlUser {
/** @var string */
protected $firstName;
/** @var string */
protected $lastName;
/** @var string */
protected $emplid;
/** @var string */
protected $email;
/**
* @param string $firstName
* @param string $lastName
* @param string $emplid
* @param string $email
*/
public function __construct(string $firstName,
string $lastName,
string $emplid,
string $email) {
$this->firstName = ucfirst($firstName);
$this->lastName = ucfirst($lastName);
$this->email = strtolower($email);
$this->emplid = $emplid;
}
/**
* @return string
*/
public function getFirstName(): string {
return $this->firstName;
}
/**
* @return string
*/
public function getLastName(): string {
return $this->lastName;
}
/**
* @return string
*/
public function getEmplid(): string {
return $this->emplid;
}
/**
* @return string
*/
public function getEmail(): string {
return $this->email;
}
/**
* @return string
*/
public function getDisplayName(): string {
return $this->getFirstName() . ' ' . $this->getLastName();
}
}
......@@ -43,9 +43,9 @@ class User {
protected ?string $password;
/**
* @var string
* @var string|null
*/
protected string $doorcode;
protected ?string $doorcode;
/**
* @var Carbon|null
......@@ -69,8 +69,8 @@ class User {
* @param string $displayName
* @param string|null $emplid
* @param string $email
* @param string $password
* @param string $doorcode
* @param string|null $password
* @param string|null $doorcode
* @param Carbon|null $expiresAt
* @param Carbon|null $createdAt
* @param Carbon|null $updatedAt
......@@ -82,10 +82,10 @@ class User {
?string $emplid,
string $email,
?string $password,
string $doorcode,
?Carbon $expiresAt,
?Carbon $createdAt,
?Carbon $updatedAt) {
?string $doorcode,
?Carbon $expiresAt = null,
?Carbon $createdAt = null,
?Carbon $updatedAt = null) {
$this->id = $id;
$this->firstName = $firstName;
$this->lastName = $lastName;
......@@ -149,9 +149,9 @@ class User {
}
/**
* @return string
* @return string|null
*/
public function getDoorcode(): string {
public function getDoorcode(): ?string {
return $this->doorcode;
}
......@@ -208,6 +208,14 @@ class User {
* @return bool
*/
public function hasDoorcodeOf(?string $doorcode): bool {
if (!$doorcode) {
return false;
}
return $this->getDoorcode() === $doorcode;
}
public function hasEmailOf(?string $email): bool {
return $this->getEmail() === strtolower($email);
}
}
<?php
namespace Source\Gateways\Saml;
use Source\Entities\SamlUser;
class InMemorySamlRepository implements SamlRepository {
protected ?SamlUser $userToLogInAs;
protected ?SamlUser $loggedInUser;
protected string $loginUrl;
protected string $logoutUrl;
public function __construct(
string $loginUrl,
string $logoutUrl
) {
$this->loginUrl = $loginUrl;
$this->logoutUrl = $logoutUrl;
}
public function setLoginUser(SamlUser $user): void {
$this->userToLogInAs = $user;
}
/**
* @inheritDoc
*/
public function login(array $options = []): string {
$this->loggedInUser = $this->userToLogInAs;
return $this->loginUrl;
}
/**
* @inheritDoc
*/
public function handleLogin(): ?SamlUser {
return $this->loggedInUser;
}
/**
* @inheritDoc
*/
public function logout(): string {
$this->loggedInUser = null;
return $this->logoutUrl;
}
/**
* @inheritDoc
*/
public function isAuthenticated(): bool {
return $this->loggedInUser !== null;
}
}
<?php
namespace Source\Gateways\Saml;
use Source\Entities\SamlUser;
interface SamlRepository {
/**
* This function returns the url to redirect to to log a user in.
*
* @param array $options
* @return string
*/
public function login(array $options = []): string;
/**
* Processes a user after a login and returns the logged in user.
* If null is returned, the user was not able to be created, or
* they could not be logged in.
*
* @return SamlUser|null
*/
public function handleLogin(): ?SamlUser;
/**
* This returns the logout url to redirect to.
*
* @return string
*/
public function logout(): string;
/**
* Checks if the current user is authenticated or not
*
* @return bool
*/
public function isAuthenticated(): bool;
}
<?php
namespace Source\Gateways\Saml;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class SamlRepositoryServiceProvider extends ServiceProvider implements DeferrableProvider {
/**
* Register any application services.
*
* @return void
*/
public function register() {
$this->app->singleton(SamlRepository::class, static function (Application $app) {
if(env('APP_ENV') === 'testing') {
return new InMemorySamlRepository(
config('saml.login_route'),
config('saml.logout_route')
);
}
return new SimpleSamlPhpSamlRepository(
config('saml.login_route'),
config('saml.logout_route'),
config('saml.simplesamlphp.autoload'),
config('saml.simplesamlphp.auth_source')
);
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void {
}