From 84c1547c853081bf101528ecc60529e023ebc2ff Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Wed, 29 Jun 2016 18:50:02 +0200 Subject: [PATCH] Add application specific passwords Fixes https://github.com/nextcloud/user_saml/issues/1 --- admin.php | 2 +- appinfo/app.php | 16 +- appinfo/database.xml | 50 +++++ appinfo/info.xml | 2 +- appinfo/routes.php | 52 +++-- css/{settings.css => admin.css} | 0 css/personal.css | 30 +++ js/{settings.js => admin.js} | 1 - js/personal.js | 9 + js/personal/authtoken-collection.js | 52 +++++ js/personal/authtoken.js | 33 ++++ js/personal/authtoken_view.js | 230 ++++++++++++++++++++++ lib/appinfo/application.php | 14 ++ lib/controller/authsettingscontroller.php | 167 ++++++++++++++++ lib/controller/settingscontroller.php | 14 +- lib/userbackend.php | 40 +++- personal.php | 25 +++ templates/{settings.php => admin.php} | 4 +- templates/personal.php | 40 ++++ 19 files changed, 746 insertions(+), 35 deletions(-) create mode 100644 appinfo/database.xml rename css/{settings.css => admin.css} (100%) create mode 100644 css/personal.css rename js/{settings.js => admin.js} (99%) create mode 100644 js/personal.js create mode 100644 js/personal/authtoken-collection.js create mode 100644 js/personal/authtoken.js create mode 100644 js/personal/authtoken_view.js create mode 100644 lib/controller/authsettingscontroller.php create mode 100644 personal.php rename templates/{settings.php => admin.php} (98%) create mode 100644 templates/personal.php diff --git a/admin.php b/admin.php index 18dbca8..5e380b0 100644 --- a/admin.php +++ b/admin.php @@ -22,4 +22,4 @@ $app = new \OCA\User_SAML\AppInfo\Application(); /** @var \OCA\User_SAML\Controller\SettingsController $controller */ $controller = $app->getContainer()->query('SettingsController'); -return $controller->displayPanel()->render(); +return $controller->displayAdminPanel()->render(); diff --git a/appinfo/app.php b/appinfo/app.php index 54fd093..a940ca0 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -22,6 +22,7 @@ require_once __DIR__ . '/../3rdparty/vendor/autoload.php'; \OCP\App::registerAdmin('user_saml', 'admin'); +\OCP\App::registerPersonal('user_saml', 'personal'); $urlGenerator = \OC::$server->getURLGenerator(); $config = \OC::$server->getConfig(); @@ -30,21 +31,24 @@ $samlSettings = new \OCA\User_SAML\SAMLSettings( $urlGenerator, $config ); -try { - $oneLoginSettings = new \OneLogin_Saml2_Settings($samlSettings->getOneLoginSettingsArray()); -} catch(OneLogin_Saml2_Error $e) { - return; -} $userBackend = new \OCA\User_SAML\UserBackend( \OC::$server->getConfig(), \OC::$server->getLogger(), \OC::$server->getURLGenerator(), - \OC::$server->getSession() + \OC::$server->getSession(), + \OC::$server->getDb() ); OC_User::useBackend($userBackend); OC_User::handleApacheAuth(); +// Setting up the one login config may fail, if so, do not catch the requests later. +try { + $oneLoginSettings = new \OneLogin_Saml2_Settings($samlSettings->getOneLoginSettingsArray()); +} catch(OneLogin_Saml2_Error $e) { + return; +} + // Redirect all requests to the login page to the SAML login $currentUrl = substr(explode('?',$request->getRequestUri(), 2)[0], strlen(\OC::$WEBROOT)); if($currentUrl === '/index.php/login' && !OC_User::isLoggedIn()) { diff --git a/appinfo/database.xml b/appinfo/database.xml new file mode 100644 index 0000000..35f47ae --- /dev/null +++ b/appinfo/database.xml @@ -0,0 +1,50 @@ + + + + *dbname* + true + false + utf8 + + + + *dbprefix*user_saml_auth_token + + + + id + integer + 0 + true + 1 + true + 4 + + + + + uid + text + + true + 64 + + + + name + clob + + true + + + + token + text + + true + 200 + + +
+
\ No newline at end of file diff --git a/appinfo/info.xml b/appinfo/info.xml index c654d1e..dbb9158 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -7,6 +7,6 @@ Nextcloud 1.0.0 - + diff --git a/appinfo/routes.php b/appinfo/routes.php index f7d6cf8..3ce6eda 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -21,26 +21,36 @@ namespace OCA\User_SAML\AppInfo; -(new \OCP\AppFramework\App('user_saml'))->registerRoutes($this, array('routes' => array( +(new Application())->registerRoutes( + $this, [ - 'name' => 'SAML#login', - 'url' => '/saml/login', - 'verb' => 'GET', - ], - [ - 'name' => 'SAML#getMetadata', - 'url' => '/saml/metadata', - 'verb' => 'GET', - ], - [ - 'name' => 'SAML#assertionConsumerService', - 'url' => '/saml/acs', - 'verb' => 'POST', - ], - [ - 'name' => 'SAML#singleLogoutService', - 'url' => '/saml/sls', - 'verb' => 'GET', - ], -))); + 'resources' => [ + 'AuthSettings' => [ + 'url' => '/authtokens' + ], + ], + 'routes' => [ + [ + 'name' => 'SAML#login', + 'url' => '/saml/login', + 'verb' => 'GET', + ], + [ + 'name' => 'SAML#getMetadata', + 'url' => '/saml/metadata', + 'verb' => 'GET', + ], + [ + 'name' => 'SAML#assertionConsumerService', + 'url' => '/saml/acs', + 'verb' => 'POST', + ], + [ + 'name' => 'SAML#singleLogoutService', + 'url' => '/saml/sls', + 'verb' => 'GET', + ], + ] + ] +); diff --git a/css/settings.css b/css/admin.css similarity index 100% rename from css/settings.css rename to css/admin.css diff --git a/css/personal.css b/css/personal.css new file mode 100644 index 0000000..7495c58 --- /dev/null +++ b/css/personal.css @@ -0,0 +1,30 @@ +#user-saml-apppasswords table { + width: 100%; + min-height: 150px; + padding-top: 25px; +} +#user-saml-appasswords table th { + font-weight: 800; +} +#user-saml-apppasswords table th, +#user-saml-apppasswords table td { + padding: 10px; +} + +#user-saml-apppasswords .token-list td { + border-top: 1px solid #DDD; + text-overflow: ellipsis; + max-width: 200px; + white-space: nowrap; + overflow: hidden; +} +#user-saml-apppasswords .token-list td a.icon-delete { + display: block; + opacity: 0.6; +} + +#user-saml-new-app-password { + width: 186px; + font-family: monospace; + background-color: lightyellow; +} diff --git a/js/settings.js b/js/admin.js similarity index 99% rename from js/settings.js rename to js/admin.js index 8628616..9ee0ee4 100644 --- a/js/settings.js +++ b/js/admin.js @@ -1,4 +1,3 @@ - function setSAMLConfigValue(category, setting, value) { OC.msg.startSaving('#user-saml-save-indicator'); OC.AppConfig.setValue('user_saml', category+'-'+setting, value); diff --git a/js/personal.js b/js/personal.js new file mode 100644 index 0000000..4897e78 --- /dev/null +++ b/js/personal.js @@ -0,0 +1,9 @@ +$(function() { + + // Show token views + var collection = new OCA.User_SAML.AuthTokenCollection(); + var view = new OCA.User_SAML.AuthTokenView({ + collection: collection + }); + view.reload(); +}); \ No newline at end of file diff --git a/js/personal/authtoken-collection.js b/js/personal/authtoken-collection.js new file mode 100644 index 0000000..4c0cf38 --- /dev/null +++ b/js/personal/authtoken-collection.js @@ -0,0 +1,52 @@ +/* global Backbone */ + +/** + * @author Christoph Wurst + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +(function(OCA, Backbone) { + 'use strict'; + + OCA.User_SAML = OCA.User_SAML || {}; + + var AuthTokenCollection = Backbone.Collection.extend({ + + model: OCA.User_SAML.AuthToken, + + /** + * Show recently used sessions/devices first + * + * @param {OCA.User_SAML.AuthToken} t1 + * @param {OCA.User_SAML.AuthToken} t2 + * @returns {Boolean} + */ + comparator: function (t1, t2) { + var ts1 = parseInt(t1.get('lastActivity'), 10); + var ts2 = parseInt(t2.get('lastActivity'), 10); + return ts1 < ts2; + }, + + tokenType: null, + + url: OC.generateUrl('/apps/user_saml/authtokens') + }); + + OCA.User_SAML.AuthTokenCollection = AuthTokenCollection; + +})(OCA, Backbone); diff --git a/js/personal/authtoken.js b/js/personal/authtoken.js new file mode 100644 index 0000000..3271713 --- /dev/null +++ b/js/personal/authtoken.js @@ -0,0 +1,33 @@ +/* global Backbone */ + +/** + * @author Christoph Wurst + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +(function(OCA, Backbone) { + 'use strict'; + + OCA.User_SAML = OCA.User_SAML || {}; + + var AuthToken = Backbone.Model.extend({ + }); + + OCA.User_SAML.AuthToken = AuthToken; + +})(OCA, Backbone); diff --git a/js/personal/authtoken_view.js b/js/personal/authtoken_view.js new file mode 100644 index 0000000..dfe60bf --- /dev/null +++ b/js/personal/authtoken_view.js @@ -0,0 +1,230 @@ +/* global Backbone, Handlebars, moment */ + +/** + * @author Christoph Wurst + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +(function(OCA, _, Backbone, $, Handlebars, moment) { + 'use strict'; + + OCA.User_SAML = OCA.User_SAML|| {}; + + var TEMPLATE_TOKEN = + '' + + '{{name}}' + + '' + + ''; + + var SubView = Backbone.View.extend({ + collection: null, + type: 0, + _template: undefined, + + template: function(data) { + if (_.isUndefined(this._template)) { + this._template = Handlebars.compile(TEMPLATE_TOKEN); + } + + return this._template(data); + }, + + initialize: function(options) { + this.type = options.type; + this.collection = options.collection; + + this.on(this.collection, 'change', this.render); + }, + + render: function() { + var _this = this; + + var list = this.$('.token-list'); + var tokens = this.collection.filter(function(token) { + return parseInt(token.get('type'), 10) === _this.type; + }); + list.html(''); + + // Show header only if there are tokens to show + this._toggleHeader(tokens.length > 0); + + tokens.forEach(function(token) { + var viewData = token.toJSON(); + var ts = viewData.lastActivity * 1000; + viewData.lastActivity = OC.Util.relativeModifiedDate(ts); + viewData.lastActivityTime = OC.Util.formatDate(ts, 'LLL'); + var html = _this.template(viewData); + var $html = $(html); + $html.find('.has-tooltip').tooltip({container: 'body'}); + list.append($html); + }); + }, + + toggleLoading: function(state) { + this.$('.token-list').toggleClass('icon-loading', state); + }, + + _toggleHeader: function(show) { + this.$('.hidden-when-empty').toggleClass('hidden', !show); + } + }); + + var AuthTokenView = Backbone.View.extend({ + collection: null, + + _views: [], + + _form: undefined, + + _tokenName: undefined, + + _addAppPasswordBtn: undefined, + + _result: undefined, + + _newAppPassword: undefined, + + _hideAppPasswordBtn: undefined, + + _addingToken: false, + + initialize: function(options) { + this.collection = options.collection; + + var tokenTypes = [0, 1]; + var _this = this; + _.each(tokenTypes, function(type) { + var el = '#user-saml-apppasswords'; + _this._views.push(new SubView({ + el: el, + type: type, + collection: _this.collection + })); + + var $el = $(el); + $el.on('click', 'a.icon-delete', _.bind(_this._onDeleteToken, _this)); + }); + + this._form = $('#user-saml-app-password-form'); + this._tokenName = $('#user-saml-app-password-name'); + this._addAppPasswordBtn = $('#user-saml-add-app-password'); + this._addAppPasswordBtn.click(_.bind(this._addAppPassword, this)); + + this._result = $('#user-saml-app-password-result'); + this._newAppPassword = $('#user-saml-new-app-password'); + this._newAppPassword.on('focus', _.bind(this._onNewTokenFocus, this)); + this._hideAppPasswordBtn = $('#user-saml-app-password-hide'); + this._hideAppPasswordBtn.click(_.bind(this._hideToken, this)); + }, + + render: function() { + _.each(this._views, function(view) { + view.render(); + view.toggleLoading(false); + }); + }, + + reload: function() { + var _this = this; + + _.each(this._views, function(view) { + view.toggleLoading(true); + }); + + var loadingTokens = this.collection.fetch(); + + $.when(loadingTokens).done(function() { + _this.render(); + }); + $.when(loadingTokens).fail(function() { + OC.Notification.showTemporary(t('core', 'Error while loading browser sessions and device tokens')); + }); + }, + + _addAppPassword: function() { + var _this = this; + this._toggleAddingToken(true); + + var deviceName = this._tokenName.val(); + var creatingToken = $.ajax(OC.generateUrl('/apps/user_saml/authtokens'), { + method: 'POST', + data: { + name: deviceName + } + }); + + $.when(creatingToken).done(function(resp) { + _this.collection.add(resp.deviceToken); + _this.render(); + _this._newAppPassword.val(resp.token); + _this._toggleFormResult(false); + _this._newAppPassword.select(); + _this._tokenName.val(''); + }); + $.when(creatingToken).fail(function() { + OC.Notification.showTemporary(t('core', 'Error while creating device token')); + }); + $.when(creatingToken).always(function() { + _this._toggleAddingToken(false); + }); + }, + + _onNewTokenFocus: function() { + this._newAppPassword.select(); + }, + + _hideToken: function() { + this._toggleFormResult(true); + }, + + _toggleAddingToken: function(state) { + this._addingToken = state; + this._addAppPasswordBtn.toggleClass('icon-loading-small', state); + }, + + _onDeleteToken: function(event) { + var $target = $(event.target); + var $row = $target.closest('tr'); + var id = $row.data('id'); + + var token = this.collection.get(id); + if (_.isUndefined(token)) { + // Ignore event + return; + } + + var destroyingToken = token.destroy(); + + var _this = this; + $.when(destroyingToken).fail(function() { + OC.Notification.showTemporary(t('core', 'Error while deleting the token')); + }); + $.when(destroyingToken).always(function() { + _this.render(); + }); + }, + + _toggleFormResult: function(showForm) { + this._form.toggleClass('hidden', !showForm); + this._result.toggleClass('hidden', showForm); + } + }); + + OCA.User_SAML.AuthTokenView = AuthTokenView; + +})(OCA, _, Backbone, $, Handlebars, moment); diff --git a/lib/appinfo/application.php b/lib/appinfo/application.php index 9a3d630..4bc03c2 100644 --- a/lib/appinfo/application.php +++ b/lib/appinfo/application.php @@ -21,6 +21,7 @@ namespace OCA\User_SAML\AppInfo; +use OCA\User_SAML\Controller\AuthSettingsController; use OCA\User_SAML\Controller\SAMLController; use OCA\User_SAML\Controller\SettingsController; use OCA\User_SAML\SAMLSettings; @@ -35,6 +36,19 @@ class Application extends App { /** * Controller */ + $container->registerService('AuthSettingsController', function(IAppContainer $c) { + /** @var \OC\Server $server */ + $server = $c->query('ServerContainer'); + return new AuthSettingsController( + $c->getAppName(), + $server->getRequest(), + $server->getUserManager(), + $server->getSession(), + $server->getSecureRandom(), + $server->getDb(), + $server->getUserSession()->getUser()->getUID() + ); + }); $container->registerService('SettingsController', function(IAppContainer $c) { /** @var \OC\Server $server */ $server = $c->query('ServerContainer'); diff --git a/lib/controller/authsettingscontroller.php b/lib/controller/authsettingscontroller.php new file mode 100644 index 0000000..ddc0fad --- /dev/null +++ b/lib/controller/authsettingscontroller.php @@ -0,0 +1,167 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ +namespace OCA\User_SAML\Controller; + +use OC\AppFramework\Http; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDb; +use OCP\IRequest; +use OCP\ISession; +use OCP\IUserManager; +use OCP\Security\ISecureRandom; + +class AuthSettingsController extends Controller { + /** @var IUserManager */ + private $userManager; + /** @var ISession */ + private $session; + /** @var string */ + private $uid; + /** @var ISecureRandom */ + private $random; + /** @var IDb */ + private $db; + + /** + * @param string $appName + * @param IRequest $request + * @param IUserManager $userManager + * @param ISession $session + * @param ISecureRandom $random + * @param IDb $db + * @param string $uid + */ + public function __construct($appName, + IRequest $request, + IUserManager $userManager, + ISession $session, + ISecureRandom $random, + IDb $db, + $uid) { + parent::__construct($appName, $request); + $this->userManager = $userManager; + $this->uid = $uid; + $this->session = $session; + $this->random = $random; + $this->db = $db; + } + /** + * @NoAdminRequired + * + * @return JSONResponse + */ + public function index() { + $user = $this->userManager->get($this->uid); + if (is_null($user)) { + return []; + } + + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $qb->select('id', 'uid', 'name', 'token') + ->from('user_saml_auth_token') + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) + ->setMaxResults(1000); + $result = $qb->execute(); + $data = $result->fetchAll(); + $result->closeCursor(); + + foreach($data as $key => $entry) { + unset($data[$key]['token']); + unset($data[$key]['uid']); + $data[$key]['id'] = (int)$data[$key]['id']; + $data[$key]['type'] = 1; + } + + return $data; + } + + /** + * @NoAdminRequired + * + * @param string $name + * @return JSONResponse + */ + public function create($name) { + $token = $this->generateRandomDeviceToken(); + + $values = [ + 'uid' => $this->uid, + 'name' => $name, + 'token' => password_hash($token, PASSWORD_DEFAULT), + ]; + + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $qb->insert('user_saml_auth_token'); + foreach($values as $column => $value) { + $qb->setValue($column, $qb->createNamedParameter($value)); + } + $qb->execute(); + + return [ + 'token' => $token, + 'loginName' => $name, + 'deviceToken' => [ + 'id' => $qb->getLastInsertId(), + 'name' => $name, + 'type' => 1, + ], + ]; + } + /** + * Return a 20 digit device password + * + * Example: ABCDE-FGHIJ-KLMNO-PQRST + * + * @return string + */ + private function generateRandomDeviceToken() { + $groups = []; + for ($i = 0; $i < 4; $i++) { + $groups[] = $this->random->generate(5, implode('', range('A', 'Z'))); + } + return implode('-', $groups); + } + /** + * @NoAdminRequired + * + * @param string $id + * @return JSONResponse + */ + public function destroy($id) { + $user = $this->userManager->get($this->uid); + if (is_null($user)) { + return []; + } + + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $qb->delete('user_saml_auth_token') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))); + $qb->execute(); + + return []; + } +} \ No newline at end of file diff --git a/lib/controller/settingscontroller.php b/lib/controller/settingscontroller.php index 5ccbeb8..333f6f7 100644 --- a/lib/controller/settingscontroller.php +++ b/lib/controller/settingscontroller.php @@ -45,7 +45,17 @@ class SettingsController extends Controller { $this->l10n = $l10n; } - public function displayPanel() { + /** + * @return Http\TemplateResponse + */ + public function displayPersonalPanel() { + return new Http\TemplateResponse($this->appName, 'personal', [], 'blank'); + } + + /** + * @return Http\TemplateResponse + */ + public function displayAdminPanel() { $serviceProviderFields = [ 'x509cert' => $this->l10n->t('X.509 certificate of the Service Provider'), 'privateKey' => $this->l10n->t('Private key of the Service Provider'), @@ -80,7 +90,7 @@ class SettingsController extends Controller { 'general' => $generalSettings, ]; - return new Http\TemplateResponse($this->appName, 'settings', $params, 'blank'); + return new Http\TemplateResponse($this->appName, 'admin', $params, 'blank'); } } diff --git a/lib/userbackend.php b/lib/userbackend.php index 4b286b3..7ef3b90 100644 --- a/lib/userbackend.php +++ b/lib/userbackend.php @@ -22,6 +22,8 @@ namespace OCA\User_SAML; use OCP\Authentication\IApacheBackend; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDb; use OCP\UserInterface; use OCP\IUserBackend; use OCP\IConfig; @@ -38,21 +40,26 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { private $urlGenerator; /** @var ISession */ private $session; + /** @var IDb */ + private $db; /** * @param IConfig $config * @param ILogger $logger * @param IURLGenerator $urlGenerator * @param ISession $session + * @param IDb $db */ public function __construct(IConfig $config, ILogger $logger, IURLGenerator $urlGenerator, - ISession $session) { + ISession $session, + IDb $db) { $this->config = $config; $this->logger = $logger; $this->urlGenerator = $urlGenerator; $this->session = $session; + $this->db = $db; } /** @@ -65,9 +72,40 @@ class UserBackend implements IApacheBackend, UserInterface, IUserBackend { * @since 4.5.0 */ public function implementsActions($actions) { + return (bool)((\OC_User_Backend::CHECK_PASSWORD | \OC_User_Backend::GET_DISPLAYNAME) + & $actions); + } + + /** + * Check if the provided token is correct + * @param string $uid The username + * @param string $password The password + * @return string + * + * Check if the password is correct without logging in the user + * returns the user id or false + */ + public function checkPassword($uid, $password) { + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $qb->select('token') + ->from('user_saml_auth_token') + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) + ->setMaxResults(1000); + $result = $qb->execute(); + $data = $result->fetchAll(); + $result->closeCursor(); + + foreach($data as $passwords) { + if(password_verify($password, $passwords['token'])) { + return $uid; + } + } + return false; } + /** * delete a user * @param string $uid The username of the user to delete diff --git a/personal.php b/personal.php new file mode 100644 index 0000000..6690e93 --- /dev/null +++ b/personal.php @@ -0,0 +1,25 @@ + + * + * @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 . + * + */ + +$app = new \OCA\User_SAML\AppInfo\Application(); +/** @var \OCA\User_SAML\Controller\SettingsController $controller */ +$controller = $app->getContainer()->query('SettingsController'); +return $controller->displayPersonalPanel()->render(); diff --git a/templates/settings.php b/templates/admin.php similarity index 98% rename from templates/settings.php rename to templates/admin.php index 7644a7e..2f751ae 100644 --- a/templates/settings.php +++ b/templates/admin.php @@ -1,6 +1,6 @@ diff --git a/templates/personal.php b/templates/personal.php new file mode 100644 index 0000000..6471658 --- /dev/null +++ b/templates/personal.php @@ -0,0 +1,40 @@ + + +
+

t('App passwords'));?>

+ t("You've linked these apps."));?> + + + + + + + + + +
t('Name'));?>
+

t('An app password is a passcode that gives an app or device permissions to access your %s account.', [$theme->getName()]));?>

+
+ + +
+ +