From 6adb1179c707e43406cc5dcbf5d72e40a5d1b7b7 Mon Sep 17 00:00:00 2001
From: Jamie0
Date: Fri, 13 Sep 2019 20:41:23 +0100
Subject: [PATCH 1/2] Add basic OpenID Connect support
---
app/Factories/LinkFactory.php | 2 +-
app/Http/Controllers/SetupController.php | 11 ++
app/Http/Controllers/UserController.php | 68 ++++++++++++
app/Http/routes.php | 3 +
bootstrap/app.php | 1 +
composer.json | 3 +-
composer.lock | 130 ++++++++++++++++++++++-
resources/views/env.blade.php | 5 +
resources/views/setup.blade.php | 28 +++++
9 files changed, 247 insertions(+), 4 deletions(-)
diff --git a/app/Factories/LinkFactory.php b/app/Factories/LinkFactory.php
index 7ff2bda5d..9f3e504e3 100644
--- a/app/Factories/LinkFactory.php
+++ b/app/Factories/LinkFactory.php
@@ -9,7 +9,7 @@
class LinkFactory {
const MAXIMUM_LINK_LENGTH = 65535;
- private static function formatLink($link_ending, $secret_ending=false) {
+ public static function formatLink($link_ending, $secret_ending=false) {
/**
* Given a link ending and a boolean indicating whether a secret ending is needed,
* return a link formatted with app protocol, app address, and link ending.
diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php
index d29006548..3ba51d504 100644
--- a/app/Http/Controllers/SetupController.php
+++ b/app/Http/Controllers/SetupController.php
@@ -133,6 +133,11 @@ public static function performSetup(Request $request) {
$mail_from = $request->input('app:smtp_from');
$mail_from_name = $request->input('app:smtp_from_name');
+ $openid_connect_url = $request->input('settings:openid_connect_url');
+ $openid_connect_configuration = $request->input('settings:openid_connect_configuration');
+ $openid_connect_client_id = $request->input('settings:openid_connect_client_id');
+ $openid_connect_client_secret = $request->input('settings:openid_connect_client_secret');
+
if ($mail_host) {
$mail_enabled = true;
}
@@ -168,6 +173,11 @@ public static function performSetup(Request $request) {
'POLR_RECAPTCHA_SITE_KEY' => $polr_recaptcha_site_key,
'POLR_RECAPTCHA_SECRET' => $polr_recaptcha_secret_key,
+ 'OPENID_CONNECT_URL' => $openid_connect_url,
+ 'OPENID_CONNECT_CONFIGURATION' => $openid_connect_configuration,
+ 'OPENID_CONNECT_CLIENT_ID' => $openid_connect_client_id,
+ 'OPENID_CONNECT_CLIENT_SECRET' => $openid_connect_client_secret,
+
'MAIL_ENABLED' => $mail_enabled,
'MAIL_HOST' => $mail_host,
'MAIL_PORT' => $mail_port,
@@ -226,6 +236,7 @@ public static function finishSetup(Request $request) {
// unset cookie
setcookie('setup_arguments', '', time()-3600);
+
$transaction_authorised = env('TMP_SETUP_AUTH_KEY') == $setup_finish_args->setup_auth_key;
if ($transaction_authorised != true) {
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index 62e8c27d5..dc08936ff 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -34,6 +34,74 @@ public function performLogoutUser(Request $request) {
return redirect()->route('index');
}
+ public function performOpenIDConnect(Request $request) {
+ $client = app()->make('openidconnect');
+
+ $client->authenticate();
+
+ $info = $client->requestUserInfo();
+
+ $username = $info->preferred_username ?: $info->email ?: $info->sub;
+ $email = $info->email;
+
+ if (!$username || !$email) {
+ abort(503, 'OpenID Connect provider did not return the requested mandatory fields username and email.');
+ }
+
+ if (UserHelper::emailExists($email)) {
+ $user = UserHelper::getUserByEmail($email);
+ } else if (UserHelper::userExists($username)) {
+ // user account already exists
+ $user = UserHelper::getUserByUsername($username);
+ } else {
+ // create the new user!
+
+ if (env('SETTING_RESTRICT_EMAIL_DOMAIN')) {
+ $email_domain = explode('@', $email)[1];
+ $permitted_email_domains = explode(',', env('SETTING_ALLOWED_EMAIL_DOMAINS'));
+
+ if (!in_array($email_domain, $permitted_email_domains)) {
+ return redirect(route('signup'))->with('error', 'Sorry, your email\'s domain is not permitted to create new accounts.');
+ }
+ }
+
+ $ip = $request->ip();
+
+ $api_active = false;
+ $api_key = null;
+
+ if (env('SETTING_AUTO_API')) {
+ // if automatic API key assignment is on
+ $api_active = 1;
+ $api_key = CryptoHelper::generateRandomHex(env('_API_KEY_LENGTH'));
+ }
+
+ $user = UserFactory::createUser($username, $email, CryptoHelper::generateRandomHex(48), 1, $ip, $api_key, $api_active);
+ }
+
+
+ // great, we have a user!
+
+ if (isset($info->group) && env('OPENID_CONNECT_ADMIN_GROUP')) {
+ $admin_groups = explode(',', env('OPENID_CONNECT_ADMIN_GROUP'));
+
+ if (in_array($info->group, $admin_groups)) {
+ // make user admin!
+ $user->role = UserHelper::$USER_ROLES['admin'];
+ $user->save();
+ } else {
+ $user->role = UserHelper::$USER_ROLES['default'];
+ $user->save();
+ }
+ }
+
+ // we're logged in!
+ $request->session()->put('username', $user->username);
+ $request->session()->put('role', $user->role);
+
+ return redirect()->route('index');
+ }
+
public function performLogin(Request $request) {
$username = $request->input('username');
$password = $request->input('password');
diff --git a/app/Http/routes.php b/app/Http/routes.php
index e936cb10f..9eea8b1ac 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -17,6 +17,9 @@
$app->get('/', ['as' => 'index', 'uses' => 'IndexController@showIndexPage']);
$app->get('/logout', ['as' => 'logout', 'uses' => 'UserController@performLogoutUser']);
$app->get('/login', ['as' => 'login', 'uses' => 'UserController@displayLoginPage']);
+
+$app->get('/login/openid', ['as' => 'login', 'uses' => 'UserController@performOpenIDConnect']);
+
$app->get('/about-polr', ['as' => 'about', 'uses' => 'StaticPageController@displayAbout']);
$app->get('/lost_password', ['as' => 'lost_password', 'uses' => 'UserController@displayLostPasswordPage']);
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 65581199b..ad8794b28 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -82,6 +82,7 @@
$app->register(App\Providers\AppServiceProvider::class);
$app->register(\Yajra\Datatables\DatatablesServiceProvider::class);
$app->register(\Torann\GeoIP\GeoIPServiceProvider::class);
+$app->register(App\Providers\OpenIDConnectProvider::class);
// $app->register(App\Providers\EventServiceProvider::class);
/*
diff --git a/composer.json b/composer.json
index 4f883ee9a..f62745900 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,8 @@
"geoip2/geoip2": "^2.4",
"nesbot/carbon": "^1.22",
"doctrine/dbal": "^2.5",
- "google/recaptcha": "~1.1"
+ "google/recaptcha": "~1.1",
+ "jumbojett/openid-connect-php": "^0.8.0"
},
"require-dev": {
"fzaninotto/faker": "~1.0",
diff --git a/composer.lock b/composer.lock
index 14d90c676..3750cc029 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,10 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1b7ae24ee886aba13a99bf0207be0cdd",
+ "content-hash": "28a6aaebd5593bc9e06b660c8387b836",
"packages": [
{
"name": "composer/ca-bundle",
@@ -1938,6 +1938,39 @@
"homepage": "http://laravel.com",
"time": "2015-11-29T16:58:05+00:00"
},
+ {
+ "name": "jumbojett/openid-connect-php",
+ "version": "v0.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jumbojett/OpenID-Connect-PHP.git",
+ "reference": "08c29b063803538f345183b66ac05cff7ed6eb9b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jumbojett/OpenID-Connect-PHP/zipball/08c29b063803538f345183b66ac05cff7ed6eb9b",
+ "reference": "08c29b063803538f345183b66ac05cff7ed6eb9b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "php": ">=5.4",
+ "phpseclib/phpseclib": "~2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Bare-bones OpenID Connect client",
+ "time": "2019-01-02T19:05:13+00:00"
+ },
{
"name": "laravel/lumen-framework",
"version": "v5.1.6",
@@ -2657,8 +2690,101 @@
"xls",
"xlsx"
],
+ "abandoned": "phpoffice/phpspreadsheet",
"time": "2015-05-01T07:00:55+00:00"
},
+ {
+ "name": "phpseclib/phpseclib",
+ "version": "2.0.21",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpseclib/phpseclib.git",
+ "reference": "9f1287e68b3f283339a9f98f67515dd619e5bf9d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9f1287e68b3f283339a9f98f67515dd619e5bf9d",
+ "reference": "9f1287e68b3f283339a9f98f67515dd619e5bf9d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phing/phing": "~2.7",
+ "phpunit/phpunit": "^4.8.35|^5.7|^6.0",
+ "sami/sami": "~2.0",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "suggest": {
+ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
+ "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
+ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "phpseclib/bootstrap.php"
+ ],
+ "psr-4": {
+ "phpseclib\\": "phpseclib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
+ },
+ {
+ "name": "Patrick Monnerat",
+ "email": "pm@datasphere.ch",
+ "role": "Developer"
+ },
+ {
+ "name": "Andreas Fischer",
+ "email": "bantu@phpbb.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Hans-Jürgen Petrich",
+ "email": "petrich@tronic-media.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "graham@alt-three.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
+ "homepage": "http://phpseclib.sourceforge.net",
+ "keywords": [
+ "BigInteger",
+ "aes",
+ "asn.1",
+ "asn1",
+ "blowfish",
+ "crypto",
+ "cryptography",
+ "encryption",
+ "rsa",
+ "security",
+ "sftp",
+ "signature",
+ "signing",
+ "ssh",
+ "twofish",
+ "x.509",
+ "x509"
+ ],
+ "time": "2019-07-12T12:53:49+00:00"
+ },
{
"name": "psr/log",
"version": "1.0.0",
diff --git a/resources/views/env.blade.php b/resources/views/env.blade.php
index f36b23dbc..94e92a996 100644
--- a/resources/views/env.blade.php
+++ b/resources/views/env.blade.php
@@ -97,6 +97,11 @@
# reCAPTCHA secret key
POLR_RECAPTCHA_SECRET_KEY="{{$POLR_RECAPTCHA_SECRET}}"
+OPENID_CONNECT_URL="{{$OPENID_CONNECT_URL}}"
+OPENID_CONNECT_CONFIGURATION="{{$OPENID_CONNECT_CONFIGURATION}}"
+OPENID_CONNECT_CLIENT_ID="{{$OPENID_CONNECT_CLIENT_ID}}"
+OPENID_CONNECT_CLIENT_SECRET="{{$OPENID_CONNECT_CLIENT_SECRET}}"
+
# Set each to blank to disable mail
@if($MAIL_ENABLED)
MAIL_DRIVER=smtp
diff --git a/resources/views/setup.blade.php b/resources/views/setup.blade.php
index 39fbd6c4c..08609c592 100644
--- a/resources/views/setup.blade.php
+++ b/resources/views/setup.blade.php
@@ -207,6 +207,34 @@
Please ensure SMTP is properly set up before enabling password recovery.
+
+ OpenID Connect URL:
+
+
+
+
+
+ OpenID Connect Client ID:
+
+
+
+
+
+ OpenID Connect Client Secret:
+
+
+
+
+
+ OpenID Connect Configuration:
+
+
+
+
Require reCAPTCHA for Registrations
From 6d49b278683ef0b6e9f180c3352bf4188faad0ba Mon Sep 17 00:00:00 2001
From: Jamie0
Date: Sun, 15 Sep 2019 12:13:30 +0100
Subject: [PATCH 2/2] Add UI template entries to reflect OpenID options. Add
OPENID_CONNECT_LOGIN_CAPTION env to allow customisation of login button text
---
app/Http/Controllers/UserController.php | 8 ++++++
app/Providers/OpenIDConnectProvider.php | 34 +++++++++++++++++++++++
resources/views/login.blade.php | 5 ++++
resources/views/snippets/navbar.blade.php | 26 ++++++++++++-----
4 files changed, 66 insertions(+), 7 deletions(-)
create mode 100644 app/Providers/OpenIDConnectProvider.php
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index dc08936ff..f4f90cee6 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -17,6 +17,9 @@ class UserController extends Controller {
* @return Response
*/
public function displayLoginPage(Request $request) {
+ if (env('OPENID_CONNECT_CONFIGURATION') == 'always') {
+ return $this->performOpenIDConnect($request);
+ }
return view('login');
}
@@ -35,6 +38,11 @@ public function performLogoutUser(Request $request) {
}
public function performOpenIDConnect(Request $request) {
+ if (!env('OPENID_CONNECT_CONFIGURATION') || env('OPENID_CONNECT_CONFIGURATION') == 'none') {
+ abort(403, 'OpenID Connect is not enabled on this Polr installation.');
+ return;
+ }
+
$client = app()->make('openidconnect');
$client->authenticate();
diff --git a/app/Providers/OpenIDConnectProvider.php b/app/Providers/OpenIDConnectProvider.php
new file mode 100644
index 000000000..d99e06ea2
--- /dev/null
+++ b/app/Providers/OpenIDConnectProvider.php
@@ -0,0 +1,34 @@
+enabled = false;
+ return;
+ }
+
+ $this->app->singleton('openidconnect', function ($app) {
+ $client = new OpenIDConnectClient(
+ env('OPENID_CONNECT_URL'),
+ env('OPENID_CONNECT_CLIENT_ID'),
+ env('OPENID_CONNECT_CLIENT_SECRET')
+ );
+
+ $client->redirectURL = LinkFactory::formatLink('login/openid');
+
+ return $client;
+ });
+ }
+
+}
diff --git a/resources/views/login.blade.php b/resources/views/login.blade.php
index 511aaacc6..f62c05f94 100644
--- a/resources/views/login.blade.php
+++ b/resources/views/login.blade.php
@@ -10,6 +10,11 @@