Create a Nextcloud app to send a user to an external URL to change their password

This commit is contained in:
Raoul Snyman 2022-05-16 23:04:44 -07:00
parent 66d1df4bc5
commit d82e7c8197
13 changed files with 373 additions and 47 deletions

3
CHANGELOG.md Normal file
View file

@ -0,0 +1,3 @@
## [0.1.0] - 2022-05-16
### Added
- Create a Nextcloud app to send users to an external URL to change their password

View file

@ -5,7 +5,7 @@
<name>External Password</name> <name>External Password</name>
<summary>An app for Nextcloud to allow an administrator to direct a user to an external site for changing their password.</summary> <summary>An app for Nextcloud to allow an administrator to direct a user to an external site for changing their password.</summary>
<description><![CDATA[An app for Nextcloud to allow an administrator to direct a user to an external site for changing their password. This is useful in conjunction with an app like external_users.]]></description> <description><![CDATA[An app for Nextcloud to allow an administrator to direct a user to an external site for changing their password. This is useful in conjunction with an app like external_users.]]></description>
<version>0.1.0</version> <version>0.1.1</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="raoul@snyman.info" >Raoul Snyman</author> <author mail="raoul@snyman.info" >Raoul Snyman</author>
<namespace>ExternalPassword</namespace> <namespace>ExternalPassword</namespace>
@ -15,12 +15,10 @@
<repository>https://git.snyman.info/raoul/externalpassword</repository> <repository>https://git.snyman.info/raoul/externalpassword</repository>
<bugs>https://git.snyman.info/raoul/externalpassword/issues</bugs> <bugs>https://git.snyman.info/raoul/externalpassword/issues</bugs>
<dependencies> <dependencies>
<nextcloud min-version="22"/> <nextcloud min-version="22" max-version="25" />
</dependencies> </dependencies>
<settings> <settings>
<admin>OCA\ExternalPassword\Settings\Admin</admin> <admin>OCA\ExternalPassword\Settings\Admin</admin>
<admin-section>OCA\ExternalPassword\Settings\AdminSection</admin-section>
<personal>OCA\ExternalPassword\Settings\Personal</personal> <personal>OCA\ExternalPassword\Settings\Personal</personal>
<personal-section>OCA\ExternalPassword\Settings\PersonalSection</personal-section>
</settings> </settings>
</info> </info>

View file

@ -9,6 +9,6 @@
*/ */
return [ return [
'routes' => [ 'routes' => [
['name' => 'Settings#save', 'url' => '/settings/admin/security/externalpassword', 'verb' => 'POST'], ['name' => 'settings#save', 'url' => '/settings', 'verb' => 'POST'],
] ]
]; ];

12
css/admin.css Normal file
View file

@ -0,0 +1,12 @@
#security-externalpassword label {
display: inline-block;
width: 12rem;
}
#security-externalpassword input[type=text] {
width: 30rem;
}
#security-externalpassword input[type=text].small {
width: 12rem;
}

View file

@ -1,3 +0,0 @@
#hello {
color: red;
}

44
js/admin.js Normal file
View file

@ -0,0 +1,44 @@
/* global OC */
/**
* @copyright Copyright (c) 2022 Raoul Snyman <raoul@snyman.info>
*
* @author Raoul Snyman <raoul@snyman.info>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
window.addEventListener('DOMContentLoaded', function () {
$('#externalpassword-save').click(function () {
var formData = $('#externalpassword-form').serialize();
$('#externalpassword-error-msg').hide();
$('#externalpassword-save').attr('disabled', 'disabled');
$.post(OC.generateUrl('apps/externalpassword/settings'), formData, function (response) {
if (typeof(response.data) !== "undefined") {
OC.msg.finishedSaving('#externalpassword-error-msg', response);
} else {
OC.msg.finishedSaving('#externalpassword-error-msg', {
'status' : 'error',
'data' : {
'message' : t('externalpassword', 'Unable to save settings')
}
});
}
$("#externalpassword-save").removeAttr('disabled');
});
return false;
});
});

View file

@ -1,34 +1,62 @@
<?php <?php
/**
* @copyright Copyright (c) 2022 Raoul Snyman <raoul@snyman.info>
*
* @author Raoul Snyman <raoul@snyman.info>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\ExternalPassword\Controller; namespace OCA\ExternalPassword\Controller;
use OCP\IRequest;
use OCP\IConfig;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;
use OCP\IRequest;
class SettingsController extends Controller { class SettingsController extends Controller {
/** @var IConfig */ /** @var IConfig */
private $config; private $config;
/**
* @param string $AppName
* @param IRequest $request
* @param IConfig $config
*/
public function __construct($AppName, IRequest $request, IConfig $config) { public function __construct($AppName, IRequest $request, IConfig $config) {
parent::__construct($AppName, $request); parent::__construct($AppName, $request);
$this->config = $config; $this->config = $config;
} }
/** /**
* @param string * @param string $changePasswordUrl
* @param string $descriptionText
* @param string $buttonText
*/ */
public function save(string $changePasswordUrl, string $descriptionText, string $buttonText): JSONResponse { public function save(string $changePasswordUrl, string $descriptionText, string $buttonText): JSONResponse {
$this->config->setAppValue('externalpassword', 'changePasswordUrl', $changePasswordUrl); $this->config->setAppValue('externalpassword', 'changePasswordUrl', $changePasswordUrl);
$this->config->setAppValue('externalpassword', 'descriptionText', $descriptionText); $this->config->setAppValue('externalpassword', 'descriptionText', $descriptionText);
$this->config->setAppValue('externalpassword', 'buttonText', $buttonText); $this->config->setAppValue('externalpassword', 'buttonText', $buttonText);
$parameters = [ $parameters = [
'changePasswordUrl' => $changePasswordUrl, 'status' => 'success',
'descriptionText' => $descriptionText, 'data' => [
'buttonText' => $buttonText 'message' => 'Saved'
]
]; ];
return new JSONResponse($parameters); return new JSONResponse($parameters);
} }
} }

81
lib/Settings/Admin.php Normal file
View file

@ -0,0 +1,81 @@
<?php
/**
* @copyright Copyright (c) 2022 Raoul Snyman <raoul@snyman.info>
*
* @author Raoul Snyman <raoul@snyman.info>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\ExternalPassword\Settings;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\Settings\ISettings;
class Admin implements ISettings {
/** @var IConfig */
private $config;
/** @var IL10N */
private $l;
/**
* Admin constructor.
*
* @param IConfig $config
* @param IL10N $l
*/
public function __construct(IConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
/**
* @return TemplateResponse
*/
public function getForm() {
$changePasswordUrl = $this->config->getAppValue('externalpassword', 'changePasswordUrl', '');
$descriptionText = $this->config->getAppValue('externalpassword', 'descriptionText',
'Your password is managed externally, please click the button below to change your password.');
$buttonText = $this->config->getAppValue('externalpassword', 'buttonText', 'Change password');
$parameters = [
'changePasswordUrl' => $changePasswordUrl,
'descriptionText' => $descriptionText,
'buttonText' => $buttonText
];
return new TemplateResponse('externalpassword', 'settings/admin', $parameters);
}
/**
* @return string the section ID, e.g. 'sharing'
*/
public function getSection() {
return 'security';
}
/**
* @return int whether the form should be rather on the top or bottom of
* the admin section. The forms are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
*/
public function getPriority() {
return 10;
}
}

81
lib/Settings/Personal.php Normal file
View file

@ -0,0 +1,81 @@
<?php
/**
* @copyright Copyright (c) 2022 Raoul Snyman <raoul@snyman.info>
*
* @author Raoul Snyman <raoul@snyman.info>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\ExternalPassword\Settings;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\Settings\ISettings;
class Personal implements ISettings {
/** @var IConfig */
private $config;
/** @var IL10N */
private $l;
/**
* Admin constructor.
*
* @param IConfig $config
* @param IL10N $l
*/
public function __construct(IConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
/**
* @return TemplateResponse
*/
public function getForm() {
$changePasswordUrl = $this->config->getAppValue('externalpassword', 'changePasswordUrl', '');
$descriptionText = $this->config->getAppValue('externalpassword', 'descriptionText',
'Your password is managed externally, please click the button below to change your password.');
$buttonText = $this->config->getAppValue('externalpassword', 'buttonText', 'Change password');
$parameters = [
'changePasswordUrl' => $changePasswordUrl,
'descriptionText' => $descriptionText,
'buttonText' => $buttonText
];
return new TemplateResponse('externalpassword', 'settings/personal', $parameters);
}
/**
* @return string the section ID, e.g. 'sharing'
*/
public function getSection() {
return 'security';
}
/**
* @return int whether the form should be rather on the top or bottom of
* the admin section. The forms are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
*/
public function getPriority() {
return 10;
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* @copyright Copyright (c) 2022 Raoul Snyman <raoul@snyman.info>
*
* @author Raoul Snyman <raoul@snyman.info>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
script('externalpassword', 'admin');
style('externalpassword', 'admin');
?>
<div id="security-externalpassword" class="section">
<h2 class="inlineblock"><?php p($l->t('External Password'));?></h2>
<span id="externalpassword-error-msg" class="msg success hidden">Saved</span>
<p class="settings-hint"><?php p($l->t('To direct users to an external website in order to change their password, set the URL below.')); ?></p>
<div class="admin-settings-externalpassword">
<form id="externalpassword-form" method="POST">
<div class="form-section">
<label for="change-password-url"><?php p($l->t('External password URL')); ?></label>
<input type="text" id="change-password-url" name="changePasswordUrl" value="<?php p($_['changePasswordUrl']); ?>" />
</div>
<div class="form-section">
<label for="description-text"><?php p($l->t('Description')); ?></label>
<input type="text" id="description-text" name="descriptionText" value="<?php p($_['descriptionText']); ?>" />
</div>
<div class="form-section">
<label for="button-text"><?php p($l->t('Button text')); ?></label>
<input type="text" id="button-text" name="buttonText" value="<?php p($_['buttonText']); ?>" class="small" />
</div>
<input type="submit" id="externalpassword-save" value="<?php p($l->t('Save')); ?>" />
</form>
</div>
</div>

View file

@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2022 Raoul Snyman <raoul@snyman.info>
*
* @author Raoul Snyman <raoul@snyman.info>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
?>
<?php if ($_['changePasswordUrl']) { ?>
<div id="security-external-password" class="section">
<h2><?php p($l->t('Password'));?></h2>
<div>
<p class="settings-hint"><?php p($_['descriptionText']); ?></p>
<p><a href="<?php p($_['changePasswordUrl']); ?>" class="button" target="_blank"><?php p($_['buttonText']); ?></a></p>
</div>
</div>
<?php } ?>

View file

@ -1,31 +0,0 @@
<?php
namespace OCA\ExternalPassword\Tests\Unit\Controller;
use PHPUnit_Framework_TestCase;
use OCP\AppFramework\Http\TemplateResponse;
use OCA\ExternalPassword\Controller\PageController;
class PageControllerTest extends PHPUnit_Framework_TestCase {
private $controller;
private $userId = 'john';
public function setUp() {
$request = $this->getMockBuilder('OCP\IRequest')->getMock();
$this->controller = new PageController(
'externalpassword', $request, $this->userId
);
}
public function testIndex() {
$result = $this->controller->index();
$this->assertEquals('index', $result->getTemplateName());
$this->assertTrue($result instanceof TemplateResponse);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace OCA\ExternalPassword\Tests\Unit\Controller;
use PHPUnit_Framework_TestCase;
use OCP\AppFramework\Http\JSONResponse;
use OCA\ExternalPassword\Controller\SettingsController;
class SettingsControllerTest extends PHPUnit_Framework_TestCase {
private $controller;
public function setUp() {
$request = $this->getMockBuilder('OCP\IRequest')->getMock();
$config = $this->getMockBuilder('OCP\IConfig')->getMock();
$this->controller = new SettingsController(
'externalpassword', $request, $config
);
}
public function testSave() {
$changePasswordUrl = 'https://example.com/change-password';
$descriptionText = 'Use the link below to change your password';
$buttonText = 'Change password';
$result = $this->controller->saveSettings($changePasswordUrl, $descriptionText, $buttonText);
$this->assertTrue($result instanceof JSONResponse);
}
}