'lib' rewritten.
This commit is contained in:
136
lib/Action/EmailSync.php
Normal file
136
lib/Action/EmailSync.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Action;
|
||||
|
||||
use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Constant\Opt;
|
||||
use OCA\UserSQL\Model\User;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCA\UserSQL\Repository\UserRepository;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* Synchronizes the user email address.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class EmailSync implements IUserAction
|
||||
{
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
/**
|
||||
* @var IConfig The config instance.
|
||||
*/
|
||||
private $config;
|
||||
/**
|
||||
* @var UserRepository The user repository.
|
||||
*/
|
||||
private $userRepository;
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $appName The application name.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param UserRepository $userRepository The user repository.
|
||||
*/
|
||||
public function __construct(
|
||||
$appName, ILogger $logger, Properties $properties, IConfig $config,
|
||||
UserRepository $userRepository
|
||||
) {
|
||||
$this->appName = $appName;
|
||||
$this->logger = $logger;
|
||||
$this->properties = $properties;
|
||||
$this->config = $config;
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function doAction(User $user)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering EmailSync#doAction($user->uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$ncMail = $this->config->getUserValue(
|
||||
$user->uid, "settings", "email", ""
|
||||
);
|
||||
|
||||
$result = false;
|
||||
|
||||
switch ($this->properties[Opt::EMAIL_SYNC]) {
|
||||
case App::EMAIL_INITIAL:
|
||||
if (empty($ncMail) && !empty($user->email)) {
|
||||
$this->config->setUserValue(
|
||||
$user->uid, "settings", "email", $user->email
|
||||
);
|
||||
}
|
||||
|
||||
$result = true;
|
||||
break;
|
||||
case App::EMAIL_FORCE_NC:
|
||||
if (!empty($ncMail) && $user->email !== $ncMail) {
|
||||
$user = $this->userRepository->findByUid($user->uid);
|
||||
if (!($user instanceof User)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$user->email = $ncMail;
|
||||
$result = $this->userRepository->save($user);
|
||||
}
|
||||
|
||||
break;
|
||||
case App::EMAIL_FORCE_SQL:
|
||||
if (!empty($user->email) && $user->email !== $ncMail) {
|
||||
$this->config->setUserValue(
|
||||
$user->uid, "settings", "email", $user->email
|
||||
);
|
||||
}
|
||||
|
||||
$result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning EmailSync#doAction($user->uid): " . ($result ? "true"
|
||||
: "false"),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
41
lib/Action/IUserAction.php
Normal file
41
lib/Action/IUserAction.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Action;
|
||||
|
||||
use OCA\UserSQL\Model\User;
|
||||
|
||||
/**
|
||||
* Action to execute every time an user account is queried.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
interface IUserAction
|
||||
{
|
||||
/**
|
||||
* Execute an action.
|
||||
*
|
||||
* @param User $user The user entity.
|
||||
*
|
||||
* @return bool The action status.
|
||||
*/
|
||||
public function doAction(User $user);
|
||||
}
|
||||
67
lib/AppInfo/Application.php
Normal file
67
lib/AppInfo/Application.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\QueryException;
|
||||
|
||||
/**
|
||||
* The application bootstrap class.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Application extends App
|
||||
{
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param array $urlParams An array with variables extracted
|
||||
* from the routes.
|
||||
*/
|
||||
public function __construct(array $urlParams = array())
|
||||
{
|
||||
parent::__construct('user_sql', $urlParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application backends
|
||||
* if all necessary configuration is provided.
|
||||
*
|
||||
* @throws QueryException If the query container's could not be resolved
|
||||
*/
|
||||
public function registerBackends()
|
||||
{
|
||||
$userBackend = $this->getContainer()->query(
|
||||
'\OCA\UserSQL\Backend\UserBackend'
|
||||
);
|
||||
$groupBackend = $this->getContainer()->query(
|
||||
'\OCA\UserSQL\Backend\GroupBackend'
|
||||
);
|
||||
|
||||
if ($userBackend->isConfigured()) {
|
||||
\OC::$server->getUserManager()->registerBackend($userBackend);
|
||||
}
|
||||
if ($groupBackend->isConfigured()) {
|
||||
\OC::$server->getGroupManager()->addBackend($groupBackend);
|
||||
}
|
||||
}
|
||||
}
|
||||
457
lib/Backend/GroupBackend.php
Normal file
457
lib/Backend/GroupBackend.php
Normal file
@@ -0,0 +1,457 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Backend;
|
||||
|
||||
use OC\Group\Backend;
|
||||
use OCA\UserSQL\Cache;
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Model\Group;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCA\UserSQL\Repository\GroupRepository;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* The SQL group backend manager.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
final class GroupBackend extends Backend
|
||||
{
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var Cache The cache instance.
|
||||
*/
|
||||
private $cache;
|
||||
/**
|
||||
* @var GroupRepository The group repository.
|
||||
*/
|
||||
private $groupRepository;
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param Cache $cache The cache instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param GroupRepository $groupRepository The group repository.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, Cache $cache, ILogger $logger, Properties $properties,
|
||||
GroupRepository $groupRepository
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
$this->cache = $cache;
|
||||
$this->logger = $logger;
|
||||
$this->properties = $properties;
|
||||
$this->groupRepository = $groupRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getGroups($search = "", $limit = null, $offset = null)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getGroups($search, $limit, $offset)",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "groups_" . $search . "_" . $limit . "_"
|
||||
. $offset;
|
||||
$groups = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($groups)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache getGroups($search, $limit, $offset): count("
|
||||
. count($groups) . ")", ["app" => $this->appName]
|
||||
);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
$groups = $this->groupRepository->findAllBySearchTerm(
|
||||
"%" . $search . "%", $limit, $offset
|
||||
);
|
||||
|
||||
if ($groups === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$this->cache->set("group_" . $group->gid, $group);
|
||||
}
|
||||
|
||||
$groups = array_map(
|
||||
function ($group) {
|
||||
return $group->gid;
|
||||
}, $groups
|
||||
);
|
||||
|
||||
$this->cache->set($cacheKey, $groups);
|
||||
$this->logger->debug(
|
||||
"Returning getGroups($search, $limit, $offset): count(" . count(
|
||||
$groups
|
||||
) . ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of users in given group matching the search term.
|
||||
*
|
||||
* @param string $gid The group ID.
|
||||
* @param string $search The search term.
|
||||
*
|
||||
* @return int The number of users in given group matching the search term.
|
||||
*/
|
||||
public function countUsersInGroup($gid, $search = "")
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering countUsersInGroup($gid, $search)",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "users#_" . $gid . "_" . $search;
|
||||
$count = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($count)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache countUsersInGroup($gid, $search): $count",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return $count;
|
||||
}
|
||||
|
||||
$count = $this->groupRepository->countAll($gid, "%" . $search . "%");
|
||||
|
||||
if ($count === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->cache->set($cacheKey, $count);
|
||||
$this->logger->debug(
|
||||
"Returning countUsersInGroup($gid, $search): $count",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function inGroup($uid, $gid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering inGroup($uid, $gid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "user_group_" . $uid . "_" . $gid;
|
||||
$inGroup = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($inGroup)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache inGroup($uid, $gid): " . ($inGroup
|
||||
? "true" : "false"), ["app" => $this->appName]
|
||||
);
|
||||
return $inGroup;
|
||||
}
|
||||
|
||||
$inGroup = in_array($gid, $this->getUserGroups($uid));
|
||||
|
||||
$this->cache->set($cacheKey, $inGroup);
|
||||
$this->logger->debug(
|
||||
"Returning inGroup($uid, $gid): " . ($inGroup ? "true" : "false"),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $inGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getUserGroups($uid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getUserGroups($uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "user_groups_" . $uid;
|
||||
$groups = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($groups)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache getUserGroups($uid): count(" . count(
|
||||
$groups
|
||||
) . ")", ["app" => $this->appName]
|
||||
);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
$groups = $this->groupRepository->findAllByUid($uid);
|
||||
|
||||
if ($groups === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$this->cache->set("group_" . $group->gid, $group);
|
||||
}
|
||||
|
||||
$groups = array_map(
|
||||
function ($group) {
|
||||
return $group->gid;
|
||||
}, $groups
|
||||
);
|
||||
|
||||
$this->cache->set($cacheKey, $groups);
|
||||
$this->logger->debug(
|
||||
"Returning getUserGroups($uid): count(" . count(
|
||||
$groups
|
||||
) . ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function groupExists($gid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering groupExists($gid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$group = $this->getGroup($gid);
|
||||
|
||||
if ($group === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$exists = !is_null($group);
|
||||
$this->logger->debug(
|
||||
"Returning groupExists($gid): " . ($exists ? "true" : "false"),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a group entity object. If it's found value from cache is used.
|
||||
*
|
||||
* @param $gid $uid The group ID.
|
||||
*
|
||||
* @return Group The group entity, NULL if it does not exists or
|
||||
* FALSE on failure.
|
||||
*/
|
||||
private function getGroup($gid)
|
||||
{
|
||||
$cacheKey = self::class . "group_" . $gid;
|
||||
$cachedGroup = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($cachedGroup)) {
|
||||
if ($cachedGroup === false) {
|
||||
$this->logger->debug(
|
||||
"Found null group in cache: $gid", ["app" => $this->appName]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
$group = new Group();
|
||||
foreach ($cachedGroup as $key => $value) {
|
||||
$group->{$key} = $value;
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Found group in cache: " . $group->gid,
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
$group = $this->groupRepository->findByGid($gid);
|
||||
|
||||
if ($group instanceof Group) {
|
||||
$this->cache->set($cacheKey, $group);
|
||||
} elseif (is_null($group)) {
|
||||
$this->cache->set($cacheKey, false);
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function usersInGroup($gid, $search = "", $limit = -1, $offset = 0)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering usersInGroup($gid, $search, $limit, $offset)",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "group_users_" . $gid . "_" . $search . "_"
|
||||
. $limit . "_" . $offset;
|
||||
$users = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($users)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache usersInGroup($gid, $search, $limit, $offset): count("
|
||||
. count($users) . ")", ["app" => $this->appName]
|
||||
);
|
||||
return $users;
|
||||
}
|
||||
|
||||
$uids = $this->groupRepository->findAllUidsBySearchTerm(
|
||||
$gid, "%" . $search . "%", $limit, $offset
|
||||
);
|
||||
|
||||
if ($uids === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->cache->set($cacheKey, $uids);
|
||||
$this->logger->debug(
|
||||
"Returning usersInGroup($gid, $search, $limit, $offset): count("
|
||||
. count($uids) . ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $uids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a user is in the admin group.
|
||||
*
|
||||
* @param string $uid User ID.
|
||||
*
|
||||
* @return bool TRUE if a user is in the admin group, FALSE otherwise.
|
||||
*/
|
||||
public function isAdmin($uid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering isAdmin($uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "admin_" . $uid;
|
||||
$admin = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($admin)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache isAdmin($uid): " . ($admin ? "true"
|
||||
: "false"), ["app" => $this->appName]
|
||||
);
|
||||
return $admin;
|
||||
}
|
||||
|
||||
$admin = $this->groupRepository->belongsToAdmin($uid);
|
||||
|
||||
if (is_null($admin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->cache->set($cacheKey, $admin);
|
||||
$this->logger->debug(
|
||||
"Returning isAdmin($uid): " . ($admin ? "true" : "false"),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associative array of the group details.
|
||||
*
|
||||
* @param string $gid The group ID.
|
||||
*
|
||||
* @return array Associative array of the group details.
|
||||
*/
|
||||
public function getGroupDetails($gid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getGroupDetails($gid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$group = $this->getGroup($gid);
|
||||
|
||||
if (!($group instanceof Group)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$details = ["displayName" => $group->name];
|
||||
$this->logger->debug(
|
||||
"Returning getGroupDetails($gid): " . implode(", ", $details),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSupportedActions()
|
||||
{
|
||||
$actions = parent::getSupportedActions();
|
||||
|
||||
$actions &= empty($this->properties[DB::GROUP_ADMIN_COLUMN])
|
||||
? ~Backend::IS_ADMIN : ~0;
|
||||
$actions &= empty($this->properties[DB::GROUP_NAME_COLUMN])
|
||||
? ~Backend::GROUP_DETAILS : ~0;
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this backend is correctly set and can be enabled.
|
||||
*
|
||||
* @return bool TRUE if all necessary options for this backend
|
||||
* are configured, FALSE otherwise.
|
||||
*/
|
||||
public function isConfigured()
|
||||
{
|
||||
return !empty($this->properties[DB::DATABASE])
|
||||
&& !empty($this->properties[DB::DRIVER])
|
||||
&& !empty($this->properties[DB::HOSTNAME])
|
||||
&& !empty($this->properties[DB::USERNAME])
|
||||
&& !empty($this->properties[DB::GROUP_TABLE])
|
||||
&& !empty($this->properties[DB::USER_GROUP_TABLE])
|
||||
&& !empty($this->properties[DB::GROUP_GID_COLUMN])
|
||||
&& !empty($this->properties[DB::USER_GROUP_GID_COLUMN])
|
||||
&& !empty($this->properties[DB::USER_GROUP_UID_COLUMN]);
|
||||
}
|
||||
}
|
||||
566
lib/Backend/UserBackend.php
Normal file
566
lib/Backend/UserBackend.php
Normal file
@@ -0,0 +1,566 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Backend;
|
||||
|
||||
use OC\User\Backend;
|
||||
use OCA\UserSQL\Action\EmailSync;
|
||||
use OCA\UserSQL\Action\IUserAction;
|
||||
use OCA\UserSQL\Cache;
|
||||
use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Constant\Opt;
|
||||
use OCA\UserSQL\Crypto\IPasswordAlgorithm;
|
||||
use OCA\UserSQL\Model\User;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCA\UserSQL\Repository\UserRepository;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* The SQL user backend manager.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
final class UserBackend extends Backend
|
||||
{
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var Cache The cache instance.
|
||||
*/
|
||||
private $cache;
|
||||
/**
|
||||
* @var UserRepository The user repository.
|
||||
*/
|
||||
private $userRepository;
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
/**
|
||||
* @var IL10N The localization service.
|
||||
*/
|
||||
private $localization;
|
||||
/**
|
||||
* @var IConfig The config instance.
|
||||
*/
|
||||
private $config;
|
||||
/**
|
||||
* @var IUserAction[] The actions to execute.
|
||||
*/
|
||||
private $actions;
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param Cache $cache The cache instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param UserRepository $userRepository The user repository.
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param IConfig $config The config instance.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, Cache $cache, ILogger $logger, Properties $properties,
|
||||
UserRepository $userRepository, IL10N $localization, IConfig $config
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
$this->cache = $cache;
|
||||
$this->logger = $logger;
|
||||
$this->properties = $properties;
|
||||
$this->userRepository = $userRepository;
|
||||
$this->localization = $localization;
|
||||
$this->config = $config;
|
||||
$this->actions = [];
|
||||
|
||||
$this->initActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the actions array.
|
||||
*/
|
||||
private function initActions()
|
||||
{
|
||||
if (!empty($this->properties[Opt::EMAIL_SYNC])
|
||||
&& !empty($this->properties[DB::USER_EMAIL_COLUMN])
|
||||
) {
|
||||
$this->actions[] = new EmailSync(
|
||||
$this->appName, $this->logger, $this->properties, $this->config,
|
||||
$this->userRepository
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function hasUserListings()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count users in the database.
|
||||
*
|
||||
* @return int The number of users.
|
||||
*/
|
||||
public function countUsers()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering countUsers()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "users#";
|
||||
$count = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($count)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache countUsers(): $count",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return $count;
|
||||
}
|
||||
|
||||
$count = $this->userRepository->countAll("%");
|
||||
|
||||
if ($count === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->cache->set($cacheKey, $count);
|
||||
$this->logger->debug(
|
||||
"Returning countUsers(): $count", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function userExists($uid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering userExists($uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$user = $this->getUser($uid);
|
||||
|
||||
if ($user === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$exists = !is_null($user);
|
||||
$this->logger->debug(
|
||||
"Returning userExists($uid): " . ($exists ? "true" : "false"),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user entity object. If it's found value from cache is used.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
*
|
||||
* @return User The user entity, NULL if it does not exists or
|
||||
* FALSE on failure.
|
||||
*/
|
||||
private function getUser($uid)
|
||||
{
|
||||
$cacheKey = self::class . "user_" . $uid;
|
||||
$cachedUser = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($cachedUser)) {
|
||||
$user = new User();
|
||||
foreach ($cachedUser as $key => $value) {
|
||||
$user->{$key} = $value;
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Found user in cache: " . $user->uid, ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
$user = $this->userRepository->findByUid($uid);
|
||||
|
||||
if ($user instanceof User) {
|
||||
$this->cache->set($cacheKey, $user);
|
||||
|
||||
foreach ($this->actions as $action) {
|
||||
$action->doAction($user);
|
||||
}
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getDisplayName($uid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getDisplayName($uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$user = $this->getUser($uid);
|
||||
|
||||
if (!($user instanceof User)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = $user->name;
|
||||
$this->logger->debug(
|
||||
"Returning getDisplayName($uid): $name",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user's password is correct then return its ID or
|
||||
* FALSE on failure.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
* @param string $password The password.
|
||||
*
|
||||
* @return string|bool The user ID on success, false otherwise.
|
||||
*/
|
||||
public function checkPassword($uid, $password)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering checkPassword($uid, *)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$passwordAlgorithm = $this->getPasswordAlgorithm();
|
||||
if ($passwordAlgorithm === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->userRepository->findByUid($uid);
|
||||
if (!($user instanceof User)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isCorrect = $passwordAlgorithm->checkPassword(
|
||||
$password, $user->password
|
||||
);
|
||||
|
||||
if ($isCorrect !== true) {
|
||||
$this->logger->info(
|
||||
"Invalid password attempt for user: $uid",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->logger->info(
|
||||
"Successful password attempt for user: $uid",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a password algorithm implementation instance.
|
||||
*
|
||||
* @return IPasswordAlgorithm The password algorithm instance or FALSE
|
||||
* on failure.
|
||||
*/
|
||||
private function getPasswordAlgorithm()
|
||||
{
|
||||
$cryptoType = $this->properties[Opt::CRYPTO_CLASS];
|
||||
$passwordAlgorithm = new $cryptoType($this->localization);
|
||||
|
||||
if ($passwordAlgorithm === null) {
|
||||
$this->logger->error(
|
||||
"Cannot get password algorithm instance: " . $cryptoType,
|
||||
["app" => $this->appName]
|
||||
);
|
||||
}
|
||||
|
||||
return $passwordAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getDisplayNames($search = "", $limit = null, $offset = null)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getDisplayNames($search, $limit, $offset)",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
$users = $this->getUsers($search, $limit, $offset);
|
||||
|
||||
$names = [];
|
||||
foreach ($users as $user) {
|
||||
$names[$user->uid] = $user->name;
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning getDisplayNames($search, $limit, $offset): count("
|
||||
. count($users) . ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getUsers($search = "", $limit = null, $offset = null)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getUsers($search, $limit, $offset)",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
$cacheKey = self::class . "users_" . $search . "_" . $limit . "_"
|
||||
. $offset;
|
||||
$users = $this->cache->get($cacheKey);
|
||||
|
||||
if (!is_null($users)) {
|
||||
$this->logger->debug(
|
||||
"Returning from cache getUsers($search, $limit, $offset): count("
|
||||
. count($users) . ")", ["app" => $this->appName]
|
||||
);
|
||||
return $users;
|
||||
}
|
||||
|
||||
$users = $this->userRepository->findAllBySearchTerm(
|
||||
"%" . $search . "%", $limit, $offset
|
||||
);
|
||||
|
||||
if ($users === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->cache->set("user_" . $user->uid, $user);
|
||||
}
|
||||
|
||||
$users = array_map(
|
||||
function ($user) {
|
||||
return $user->uid;
|
||||
}, $users
|
||||
);
|
||||
|
||||
$this->cache->set($cacheKey, $users);
|
||||
$this->logger->debug(
|
||||
"Returning getUsers($search, $limit, $offset): count(" . count(
|
||||
$users
|
||||
) . ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user password.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
* @param string $password The password to set.
|
||||
*
|
||||
* @return bool TRUE if the password has been set, FALSE otherwise.
|
||||
*/
|
||||
public function setPassword($uid, $password)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering setPassword($uid, *)", ["app" => "user_sql"]
|
||||
);
|
||||
|
||||
$passwordAlgorithm = $this->getPasswordAlgorithm();
|
||||
if ($passwordAlgorithm === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$passwordHash = $passwordAlgorithm->getPasswordHash($password);
|
||||
if ($passwordHash === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->userRepository->findByUid($uid);
|
||||
if (!($user instanceof User)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user->password = $passwordHash;
|
||||
$result = $this->userRepository->save($user);
|
||||
|
||||
if ($result === true) {
|
||||
$this->logger->info(
|
||||
"Password has been set successfully for user: $uid",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getHome($uid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering getHome($uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$home = false;
|
||||
switch ($this->properties[Opt::HOME_MODE]) {
|
||||
case App::HOME_STATIC:
|
||||
$home = $this->properties[Opt::HOME_LOCATION];
|
||||
$home = str_replace("%u", $uid, $home);
|
||||
break;
|
||||
case App::HOME_QUERY:
|
||||
$user = $this->getUser($uid);
|
||||
if (!($user instanceof User)) {
|
||||
return false;
|
||||
}
|
||||
$home = $user->home;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning getHome($uid): " . $home, ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $home;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can user change its avatar.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
*
|
||||
* @return bool TRUE if the user can change its avatar, FALSE otherwise.
|
||||
*/
|
||||
public function canChangeAvatar($uid)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering canChangeAvatar($uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$user = $this->userRepository->findByUid($uid);
|
||||
if (!($user instanceof User)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$avatar = $user->avatar;
|
||||
$this->logger->debug(
|
||||
"Returning canChangeAvatar($uid): " . ($avatar ? "true"
|
||||
: "false"), ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user display name.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
* @param string $displayName The display name to set.
|
||||
*
|
||||
* @return bool TRUE if the password has been set, FALSE otherwise.
|
||||
*/
|
||||
public function setDisplayName($uid, $displayName)
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering setDisplayName($uid, $displayName)",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
$user = $this->userRepository->findByUid($uid);
|
||||
if (!($user instanceof User)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user->name = $displayName;
|
||||
$result = $this->userRepository->save($user);
|
||||
|
||||
if ($result === true) {
|
||||
$this->logger->info(
|
||||
"Display name has been set successfully for user: $uid",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSupportedActions()
|
||||
{
|
||||
$actions = parent::getSupportedActions();
|
||||
|
||||
$actions &= empty($this->properties[DB::USER_NAME_COLUMN])
|
||||
? ~Backend::GET_DISPLAYNAME : ~0;
|
||||
$actions &= empty($this->properties[Opt::HOME_MODE])
|
||||
? ~Backend::GET_HOME : ~0;
|
||||
$actions &= empty($this->properties[DB::USER_AVATAR_COLUMN])
|
||||
? ~Backend::PROVIDE_AVATAR : ~0;
|
||||
$actions &= (!empty($this->properties[DB::USER_NAME_COLUMN])
|
||||
&& $this->properties[Opt::NAME_CHANGE]) ? ~0
|
||||
: ~Backend::SET_DISPLAYNAME;
|
||||
$actions &= $this->properties[Opt::PASSWORD_CHANGE] ? ~0
|
||||
: ~Backend::SET_PASSWORD;
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this backend is correctly set and can be enabled.
|
||||
*
|
||||
* @return bool TRUE if all necessary options for this backend
|
||||
* are configured, FALSE otherwise.
|
||||
*/
|
||||
public function isConfigured()
|
||||
{
|
||||
return !empty($this->properties[DB::DATABASE])
|
||||
&& !empty($this->properties[DB::DRIVER])
|
||||
&& !empty($this->properties[DB::HOSTNAME])
|
||||
&& !empty($this->properties[DB::USERNAME])
|
||||
&& !empty($this->properties[DB::USER_TABLE])
|
||||
&& !empty($this->properties[DB::USER_PASSWORD_COLUMN])
|
||||
&& !empty($this->properties[Opt::CRYPTO_CLASS]);
|
||||
}
|
||||
}
|
||||
107
lib/Cache.php
Normal file
107
lib/Cache.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL;
|
||||
|
||||
use OC\Memcache\NullCache;
|
||||
use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Constant\Opt;
|
||||
use OCP\ICache;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* Used to store key-value pairs in the cache memory.
|
||||
* If there's no distributed cache available NULL cache is used.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
/**
|
||||
* @var ICache The cache instance.
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* The default constructor. Initiates the cache memory.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
*/
|
||||
public function __construct($AppName, IConfig $config, ILogger $logger)
|
||||
{
|
||||
$factory = \OC::$server->getMemCacheFactory();
|
||||
$useCache = $config->getAppValue(
|
||||
$AppName, Opt::USE_CACHE, App::FALSE_VALUE
|
||||
);
|
||||
|
||||
if ($useCache === App::FALSE_VALUE) {
|
||||
$this->cache = new NullCache();
|
||||
} elseif ($factory->isAvailable()) {
|
||||
$this->cache = $factory->createDistributed();
|
||||
$logger->debug("Distributed cache initiated.", ["app" => $AppName]);
|
||||
} else {
|
||||
$logger->warning(
|
||||
"There's no distributed cache available, fallback to null cache.",
|
||||
["app" => $AppName]
|
||||
);
|
||||
$this->cache = new NullCache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a value from the cache memory.
|
||||
*
|
||||
* @param string $key The cache value key.
|
||||
*
|
||||
* @return mixed|NULL Cached value or NULL if there's no value stored.
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return $this->cache->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a value in the cache memory.
|
||||
*
|
||||
* @param string $key The cache value key.
|
||||
* @param mixed $value The value to store.
|
||||
* @param int $ttl (optional) TTL in seconds. Defaults to 1 hour.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
public function set($key, $value, $ttl = 3600)
|
||||
{
|
||||
return $this->cache->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of all entries.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
return $this->cache->clear();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,33 +19,22 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm\Base;
|
||||
namespace OCA\UserSQL\Constant;
|
||||
|
||||
/**
|
||||
* Singleton pattern trait.
|
||||
* The application constants.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
trait Singleton
|
||||
final class App
|
||||
{
|
||||
private static $instance;
|
||||
const FALSE_VALUE = "0";
|
||||
const TRUE_VALUE = "1";
|
||||
|
||||
final private function __construct()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
const HOME_QUERY = "query";
|
||||
const HOME_STATIC = "static";
|
||||
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
final public static function getInstance()
|
||||
{
|
||||
return isset(static::$instance)
|
||||
? static::$instance
|
||||
: static::$instance = new static;
|
||||
}
|
||||
|
||||
final private function __clone()
|
||||
{
|
||||
}
|
||||
const EMAIL_FORCE_NC = "force_nc";
|
||||
const EMAIL_FORCE_SQL = "force_sql";
|
||||
const EMAIL_INITIAL = "initial";
|
||||
}
|
||||
54
lib/Constant/DB.php
Normal file
54
lib/Constant/DB.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Constant;
|
||||
|
||||
/**
|
||||
* The database properties.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
final class DB
|
||||
{
|
||||
const DATABASE = "db.database";
|
||||
const DRIVER = "db.driver";
|
||||
const HOSTNAME = "db.hostname";
|
||||
const PASSWORD = "db.password";
|
||||
const USERNAME = "db.username";
|
||||
|
||||
const GROUP_TABLE = "db.table.group";
|
||||
const USER_GROUP_TABLE = "db.table.user_group";
|
||||
const USER_TABLE = "db.table.user";
|
||||
|
||||
const GROUP_ADMIN_COLUMN = "db.table.group.column.admin";
|
||||
const GROUP_GID_COLUMN = "db.table.group.column.gid";
|
||||
const GROUP_NAME_COLUMN = "db.table.group.column.name";
|
||||
|
||||
const USER_GROUP_GID_COLUMN = "db.table.user_group.column.gid";
|
||||
const USER_GROUP_UID_COLUMN = "db.table.user_group.column.uid";
|
||||
|
||||
const USER_AVATAR_COLUMN = "db.table.user.column.avatar";
|
||||
const USER_EMAIL_COLUMN = "db.table.user.column.email";
|
||||
const USER_HOME_COLUMN = "db.table.user.column.home";
|
||||
const USER_NAME_COLUMN = "db.table.user.column.name";
|
||||
const USER_PASSWORD_COLUMN = "db.table.user.column.password";
|
||||
const USER_UID_COLUMN = "db.table.user.column.uid";
|
||||
}
|
||||
38
lib/Constant/Opt.php
Normal file
38
lib/Constant/Opt.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Constant;
|
||||
|
||||
/**
|
||||
* The option properties names.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
final class Opt
|
||||
{
|
||||
const CRYPTO_CLASS = "opt.crypto_class";
|
||||
const EMAIL_SYNC = "opt.email_sync";
|
||||
const HOME_LOCATION = "opt.home_location";
|
||||
const HOME_MODE = "opt.home_mode";
|
||||
const NAME_CHANGE = "opt.name_change";
|
||||
const PASSWORD_CHANGE = "opt.password_change";
|
||||
const USE_CACHE = "opt.use_cache";
|
||||
}
|
||||
47
lib/Constant/Query.php
Normal file
47
lib/Constant/Query.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Constant;
|
||||
|
||||
/**
|
||||
* The database query constants.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
final class Query
|
||||
{
|
||||
const BELONGS_TO_ADMIN = "belongs_to_admin";
|
||||
const COUNT_GROUPS = "count_groups";
|
||||
const COUNT_USERS = "count_users";
|
||||
const FIND_GROUP = "find_group";
|
||||
const FIND_GROUP_USERS = "find_group_users";
|
||||
const FIND_GROUPS = "find_groups";
|
||||
const FIND_USER = "find_user";
|
||||
const FIND_USER_GROUPS = "find_user_groups";
|
||||
const FIND_USERS = "find_users";
|
||||
const SAVE_USER = "save_user";
|
||||
|
||||
const GID_PARAM = "gid";
|
||||
const NAME_PARAM = "name";
|
||||
const PASSWORD_PARAM = "password";
|
||||
const SEARCH_PARAM = "search";
|
||||
const UID_PARAM = "uid";
|
||||
}
|
||||
350
lib/Controller/SettingsController.php
Normal file
350
lib/Controller/SettingsController.php
Normal file
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Controller;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Exception;
|
||||
use OC\DatabaseException;
|
||||
use OC\DB\Connection;
|
||||
use OC\DB\ConnectionFactory;
|
||||
use OCA\UserSQL\Cache;
|
||||
use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Platform\PlatformFactory;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
|
||||
/**
|
||||
* The settings controller.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var IL10N The localization service.
|
||||
*/
|
||||
private $localization;
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
/**
|
||||
* @var Cache The cache instance.
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $appName The application name.
|
||||
* @param IRequest $request An instance of the request.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param Cache $cache The cache instance.
|
||||
*/
|
||||
public function __construct(
|
||||
$appName, IRequest $request, ILogger $logger, IL10N $localization,
|
||||
Properties $properties, Cache $cache
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->appName = $appName;
|
||||
$this->logger = $logger;
|
||||
$this->localization = $localization;
|
||||
$this->properties = $properties;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the database connection parameters.
|
||||
*
|
||||
* @return array The request status.
|
||||
*/
|
||||
public function verifyDbConnection()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering verifyDbConnection()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
try {
|
||||
$this->getConnection();
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning verifyDbConnection(): success",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return [
|
||||
"status" => "success",
|
||||
"data" => [
|
||||
"message" => $this->localization->t(
|
||||
"Successfully connected to the database."
|
||||
)
|
||||
]
|
||||
];
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->debug(
|
||||
"Returning verifyDbConnection(): error",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return [
|
||||
"status" => "error",
|
||||
"data" => [
|
||||
"message" => $this->localization->t(
|
||||
"Error connecting to the database: "
|
||||
) . $exception->getMessage()
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database connection instance.
|
||||
*
|
||||
* @return Connection The database connection instance.
|
||||
* @throws DBALException On database connection problems.
|
||||
* @throws DatabaseException Whenever no database driver is specified.
|
||||
*/
|
||||
private function getConnection()
|
||||
{
|
||||
$dbDriver = $this->request->getParam("db-driver");
|
||||
$dbHostname = $this->request->getParam("db-hostname");
|
||||
$dbDatabase = $this->request->getParam("db-database");
|
||||
$dbUsername = $this->request->getParam("db-username");
|
||||
$dbPassword = $this->request->getParam("db-password");
|
||||
|
||||
if (empty($dbDriver)) {
|
||||
throw new DatabaseException("No database driver specified.");
|
||||
}
|
||||
|
||||
$connectionFactory = new ConnectionFactory(
|
||||
\OC::$server->getSystemConfig()
|
||||
);
|
||||
|
||||
$parameters = [
|
||||
"host" => $dbHostname,
|
||||
"password" => $dbPassword,
|
||||
"user" => $dbUsername,
|
||||
"dbname" => $dbDatabase,
|
||||
"tablePrefix" => ""
|
||||
];
|
||||
|
||||
$connection = $connectionFactory->getConnection($dbDriver, $parameters);
|
||||
$connection->executeQuery("SELECT 'user_sql'");
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save application properties.
|
||||
*
|
||||
* @return array The request status.
|
||||
*/
|
||||
public function saveProperties()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering saveProperties()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$properties = $this->properties->getArray();
|
||||
|
||||
foreach ($properties as $key => $value) {
|
||||
$reqValue = $this->request->getParam(str_replace(".", "-", $key));
|
||||
$appValue = $this->properties[$key];
|
||||
|
||||
if ((!is_bool($appValue) && isset($reqValue)
|
||||
&& $reqValue !== $appValue)
|
||||
|| (is_bool($appValue) && isset($reqValue) !== $appValue)
|
||||
) {
|
||||
$value = isset($reqValue) ? $reqValue : App::FALSE_VALUE;
|
||||
$this->properties[$key] = $value;
|
||||
|
||||
$this->logger->info(
|
||||
"Property '$key' has been set to: " . $value,
|
||||
["app" => $this->appName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning saveProperties(): success", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return [
|
||||
"status" => "success",
|
||||
"data" => [
|
||||
"message" => $this->localization->t(
|
||||
"Properties has been saved."
|
||||
)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the application cache memory.
|
||||
*
|
||||
* @return array The request status.
|
||||
*/
|
||||
public function clearCache()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering clearCache()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$this->cache->clear();
|
||||
|
||||
$this->logger->info(
|
||||
"Cache memory has been cleared.", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return [
|
||||
"status" => "success",
|
||||
"data" => [
|
||||
"message" => $this->localization->t(
|
||||
"Cache memory has been cleared."
|
||||
)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete for table select options.
|
||||
*
|
||||
* @return array The database table list.
|
||||
*/
|
||||
public function tableAutocomplete()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering tableAutocomplete()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
try {
|
||||
$connection = $this->getConnection();
|
||||
$platform = PlatformFactory::getPlatform($connection);
|
||||
$tables = $platform->getTables();
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning tableAutocomplete(): count(" . count($tables) . ")",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $tables;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete for column select options - user table.
|
||||
*
|
||||
* @return array The database table's column list.
|
||||
*/
|
||||
public function userTableAutocomplete()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering userTableAutocomplete()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$columns = $this->columnAutocomplete("db-table-user");
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning userTableAutocomplete(): count(" . count($columns) . ")",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete for column select options.
|
||||
*
|
||||
* @param string $table The table's form ID.
|
||||
*
|
||||
* @return array The table's column list.
|
||||
*/
|
||||
private function columnAutocomplete($table)
|
||||
{
|
||||
try {
|
||||
$connection = $this->getConnection();
|
||||
$platform = PlatformFactory::getPlatform($connection);
|
||||
$columns = $platform->getColumns(
|
||||
$this->request->getParam($table)
|
||||
);
|
||||
|
||||
return $columns;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete for column select options - user_group table.
|
||||
*
|
||||
* @return array The database table's column list.
|
||||
*/
|
||||
public function userGroupTableAutocomplete()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering userGroupTableAutocomplete()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$columns = $this->columnAutocomplete("db-table-user_group");
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning userGroupTableAutocomplete(): count(" . count($columns)
|
||||
. ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete for column select options - group table.
|
||||
*
|
||||
* @return array The database table's column list.
|
||||
*/
|
||||
public function groupTableAutocomplete()
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering groupTableAutocomplete()", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
$columns = $this->columnAutocomplete("db-table-group");
|
||||
|
||||
$this->logger->debug(
|
||||
"Returning groupTableAutocomplete(): count(" . count($columns)
|
||||
. ")", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
}
|
||||
77
lib/Crypto/AbstractAlgorithm.php
Normal file
77
lib/Crypto/AbstractAlgorithm.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* The abstract password algorithm class.
|
||||
* Each algorithm should extend this class, as it provides very base
|
||||
* functionality which seems to be necessary for every implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
abstract class AbstractAlgorithm implements IPasswordAlgorithm
|
||||
{
|
||||
/**
|
||||
* @var IL10N The localization service.
|
||||
*/
|
||||
private $localization;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
$this->localization = $localization;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibleName()
|
||||
{
|
||||
return $this->localization->t($this->getAlgorithmName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the algorithm name.
|
||||
*
|
||||
* @return string The algorithm name.
|
||||
*/
|
||||
protected abstract function getAlgorithmName();
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public abstract function getPasswordHash($password);
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,24 +19,21 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm\Base;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
/**
|
||||
* Implements standard Unix DES-based algorithm or
|
||||
* alternative algorithms that may be available on the system.
|
||||
* @see crypt()
|
||||
* Abstract Unix Crypt hashing implementation.
|
||||
* The hashing algorithm depends on the chosen salt.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
abstract class BaseCrypt implements HashAlgorithm
|
||||
abstract class AbstractCrypt extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
const SALT_ALPHABET = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The chars used in the salt.
|
||||
*/
|
||||
abstract public function getVisibleName();
|
||||
const SALT_ALPHABET = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
@@ -53,8 +52,12 @@ abstract class BaseCrypt implements HashAlgorithm
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate salt for hashing algorithm.
|
||||
* @return string
|
||||
* Generate a salt string for the hashing algorithm.
|
||||
*
|
||||
* @return string The salt string.
|
||||
*/
|
||||
protected abstract function getSalt();
|
||||
protected function getSalt()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,25 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Cleartext password implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Cleartext implements HashAlgorithm
|
||||
class Cleartext extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Cleartext";
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,8 +51,8 @@ class Cleartext implements HashAlgorithm
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return hash_equals($dbHash, $password);
|
||||
return "Cleartext";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,35 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Courier MD5 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CourierMD5 implements HashAlgorithm
|
||||
class CourierMD5 extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Courier base64-encoded MD5";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +45,14 @@ class CourierMD5 implements HashAlgorithm
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return '{MD5}' . self::hexToBase64(md5($password));
|
||||
return '{MD5}' . Utils::hexToBase64(md5($password));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Courier base64-encoded MD5";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,33 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Courier MD5 RAW hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CourierMD5Raw implements HashAlgorithm
|
||||
class CourierMD5Raw extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Courier hexadecimal MD5";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,4 +47,12 @@ class CourierMD5Raw implements HashAlgorithm
|
||||
{
|
||||
return '{MD5RAW}' . md5($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Courier hexadecimal MD5";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,35 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Courier SHA1 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CourierSHA1 implements HashAlgorithm
|
||||
class CourierSHA1 extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Courier base64-encoded SHA1";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +45,14 @@ class CourierSHA1 implements HashAlgorithm
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return '{SHA}' . self::hexToBase64(sha1($password));
|
||||
return '{SHA}' . Utils::hexToBase64(sha1($password));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Courier base64-encoded SHA1";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,35 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Courier SHA256 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CourierSHA256 implements HashAlgorithm
|
||||
class CourierSHA256 extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Courier base64-encoded SHA256";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +45,14 @@ class CourierSHA256 implements HashAlgorithm
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return '{SHA256}' . self::hexToBase64(hash('sha256', $password));
|
||||
return '{SHA256}' . Utils::hexToBase64(hash('sha256', $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Courier base64-encoded SHA256";
|
||||
}
|
||||
}
|
||||
59
lib/Crypto/Crypt.php
Normal file
59
lib/Crypto/Crypt.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Unix Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Crypt extends AbstractCrypt
|
||||
{
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Unix (Crypt)";
|
||||
}
|
||||
}
|
||||
97
lib/Crypto/CryptArgon2.php
Normal file
97
lib/Crypto/CryptArgon2.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Argon2 Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptArgon2 extends AbstractAlgorithm
|
||||
{
|
||||
/**
|
||||
* @var int Maximum memory (in bytes) that may be used to compute.
|
||||
*/
|
||||
private $memoryCost;
|
||||
/**
|
||||
* @var int Maximum amount of time it may take to compute.
|
||||
*/
|
||||
private $timeCost;
|
||||
/**
|
||||
* @var int Number of threads to use for computing.
|
||||
*/
|
||||
private $threads;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param int $memoryCost Maximum memory (in bytes) that may be used
|
||||
* to compute.
|
||||
* @param int $timeCost Maximum amount of time it may take to compute.
|
||||
* @param int $threads Number of threads to use for computing.
|
||||
*/
|
||||
public function __construct(
|
||||
IL10N $localization,
|
||||
$memoryCost = PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
|
||||
$timeCost = PASSWORD_ARGON2_DEFAULT_TIME_COST,
|
||||
$threads = PASSWORD_ARGON2_DEFAULT_THREADS
|
||||
) {
|
||||
parent::__construct($localization);
|
||||
$this->memoryCost = $memoryCost;
|
||||
$this->timeCost = $timeCost;
|
||||
$this->threads = $threads;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return password_verify($password, $dbHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return password_hash(
|
||||
$password, PASSWORD_ARGON2I, [
|
||||
"memory_cost" => $this->memoryCost,
|
||||
"time_cost" => $this->timeCost,
|
||||
"threads" => $this->threads
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Argon2 (Crypt)";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,25 +19,34 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Blowfish Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptBlowfish implements HashAlgorithm
|
||||
class CryptBlowfish extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
/**
|
||||
* @var int Denotes the algorithmic cost that should be used.
|
||||
*/
|
||||
private $cost;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param int $cost Denotes the algorithmic cost that should
|
||||
* be used.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization, $cost = 10)
|
||||
{
|
||||
return "Blowfish (Crypt)";
|
||||
parent::__construct($localization);
|
||||
$this->cost = $cost;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +62,18 @@ class CryptBlowfish implements HashAlgorithm
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
// TODO - add support for options: cost.
|
||||
return password_hash($password, PASSWORD_BCRYPT);
|
||||
return password_hash(
|
||||
$password, PASSWORD_BCRYPT, ["cost" => $this->cost]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the algorithm name.
|
||||
*
|
||||
* @return string The algorithm name.
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Blowfish (Crypt)";
|
||||
}
|
||||
}
|
||||
92
lib/Crypto/CryptExtendedDES.php
Normal file
92
lib/Crypto/CryptExtendedDES.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Extended DES Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptExtendedDES extends AbstractCrypt
|
||||
{
|
||||
/**
|
||||
* @var int The number of iterations.
|
||||
*/
|
||||
private $iterationCount;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param int $iterationCount The number of iterations.
|
||||
*/
|
||||
public function __construct(IL10N $localization, $iterationCount = 1000)
|
||||
{
|
||||
parent::__construct($localization);
|
||||
$this->iterationCount = $iterationCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
return self::encodeIterationCount($this->iterationCount)
|
||||
. Utils::randomString(4, self::SALT_ALPHABET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of iterations as describe below.
|
||||
* The 4 bytes of iteration count are encoded as printable characters,
|
||||
* 6 bits per character, least significant character first.
|
||||
* The values 0 to 63 are encoded as "./0-9A-Za-z".
|
||||
*
|
||||
* @param int $number The number of iterations.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function encodeIterationCount($number)
|
||||
{
|
||||
$alphabet = str_split(self::SALT_ALPHABET);
|
||||
$chars = array();
|
||||
$base = sizeof($alphabet);
|
||||
|
||||
while ($number) {
|
||||
$rem = $number % $base;
|
||||
$number = (int)($number / $base);
|
||||
$arr[] = $alphabet[$rem];
|
||||
}
|
||||
|
||||
return str_pad(implode($chars), 4, ".", STR_PAD_RIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Extended DES (Crypt)";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,25 +19,26 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\BaseCrypt;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* MD5 Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptMD5 extends BaseCrypt
|
||||
class CryptMD5 extends AbstractCrypt
|
||||
{
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "MD5 (Crypt)";
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,14 @@ class CryptMD5 extends BaseCrypt
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
return "$1$" . self::randomString(8, self::SALT_ALPHABET) . "$";
|
||||
return "$1$" . Utils::randomString(8, self::SALT_ALPHABET) . "$";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "MD5 (Crypt)";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,25 +19,34 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\BaseCrypt;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* SHA256 Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptSHA256 extends BaseCrypt
|
||||
class CryptSHA256 extends AbstractCrypt
|
||||
{
|
||||
use Utils;
|
||||
/**
|
||||
* @var int The number of rounds.
|
||||
*/
|
||||
private $rounds;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param int $rounds The number of rounds.
|
||||
* This value must be between 1000 and 999999999.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization, $rounds = 5000)
|
||||
{
|
||||
return "SHA256 (Crypt)";
|
||||
parent::__construct($localization);
|
||||
$this->rounds = $rounds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +54,16 @@ class CryptSHA256 extends BaseCrypt
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
// TODO - add support for options: rounds.
|
||||
return "$5\$rounds=5000$" . self::randomString(16, self::SALT_ALPHABET) . "$";
|
||||
return "$5\$rounds=" . $this->rounds . "$" . Utils::randomString(
|
||||
16, self::SALT_ALPHABET
|
||||
) . "$";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "SHA256 (Crypt)";
|
||||
}
|
||||
}
|
||||
69
lib/Crypto/CryptSHA512.php
Normal file
69
lib/Crypto/CryptSHA512.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* SHA512 Crypt hashing implementation.
|
||||
*
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptSHA512 extends AbstractCrypt
|
||||
{
|
||||
/**
|
||||
* @var int The number of rounds.
|
||||
*/
|
||||
private $rounds;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param int $rounds The number of rounds.
|
||||
* This value must be between 1000 and 999999999.
|
||||
*/
|
||||
public function __construct(IL10N $localization, $rounds = 5000)
|
||||
{
|
||||
parent::__construct($localization);
|
||||
$this->rounds = $rounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
return "$6\$rounds=" . $this->rounds . "$" . Utils::randomString(
|
||||
16, self::SALT_ALPHABET
|
||||
) . "$";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "SHA512 (Crypt)";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,25 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\BaseCrypt;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Standard DES Crypt hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptStandardDES extends BaseCrypt
|
||||
class CryptStandardDES extends AbstractCrypt
|
||||
{
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Standard DES (Crypt)";
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +45,14 @@ class CryptStandardDES extends BaseCrypt
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
return self::randomString(2, self::SALT_ALPHABET);
|
||||
return Utils::randomString(2, self::SALT_ALPHABET);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Standard DES (Crypt)";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,24 +19,20 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm\Base;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
/**
|
||||
* Interface which defines all function required by a hash algorithm.
|
||||
* Please note that this interface must be implemented by every hash function supported in this app.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
interface HashAlgorithm
|
||||
interface IPasswordAlgorithm
|
||||
{
|
||||
/**
|
||||
* Used by reflection to get the class instance.
|
||||
* @return HashAlgorithm
|
||||
*/
|
||||
public static function getInstance();
|
||||
|
||||
/**
|
||||
* Get the hash algorithm name.
|
||||
* This name is visible in the admin panel.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVisibleName();
|
||||
@@ -42,15 +40,19 @@ interface HashAlgorithm
|
||||
/**
|
||||
* Hash given password.
|
||||
* This value is stored in the database, when the password is changed.
|
||||
*
|
||||
* @param String $password The new password.
|
||||
*
|
||||
* @return boolean True if the password was hashed successfully, false otherwise.
|
||||
*/
|
||||
public function getPasswordHash($password);
|
||||
|
||||
/**
|
||||
* Check password given by the user against hash stored in the database.
|
||||
*
|
||||
* @param String $password Password given by the user.
|
||||
* @param String $dbHash Password hash stored in the database.
|
||||
* @param String $dbHash Password hash stored in the database.
|
||||
*
|
||||
* @return boolean True if the password is correct, false otherwise.
|
||||
*/
|
||||
public function checkPassword($password, $dbHash);
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,27 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* Joomla hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Joomla implements HashAlgorithm
|
||||
class Joomla extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "Joomla MD5 Encryption";
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,8 +45,12 @@ class Joomla implements HashAlgorithm
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return md5($password . ":" . self::randomString(32,
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
|
||||
return md5(
|
||||
$password . ":" . Utils::randomString(
|
||||
32,
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,4 +72,12 @@ class Joomla implements HashAlgorithm
|
||||
$pwHash .= ":" . $salt;
|
||||
return $pwHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "Joomla MD5 Encryption";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,33 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* MD5 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class MD5 implements HashAlgorithm
|
||||
class MD5 extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "MD5";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,4 +47,12 @@ class MD5 implements HashAlgorithm
|
||||
{
|
||||
return md5($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "MD5";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,33 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* SHA1 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class SHA1 implements HashAlgorithm
|
||||
class SHA1 extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "SHA1";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, $this->getPasswordHash($password));
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,4 +47,12 @@ class SHA1 implements HashAlgorithm
|
||||
{
|
||||
return sha1($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "SHA1";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,23 +19,35 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm\Base;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* SSHA* hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
abstract class SSHA implements HashAlgorithm
|
||||
abstract class SSHA extends AbstractAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
use Utils;
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
$saltedPassword = base64_decode(preg_replace("/" . $this->getPrefix() . "/i", "", $dbHash));
|
||||
$saltedPassword = base64_decode(
|
||||
preg_replace("/" . $this->getPrefix() . "/i", "", $dbHash)
|
||||
);
|
||||
$salt = substr($saltedPassword, -(strlen($saltedPassword) - 32));
|
||||
$hash = self::ssha($password, $salt);
|
||||
|
||||
@@ -42,23 +56,29 @@ abstract class SSHA implements HashAlgorithm
|
||||
|
||||
/**
|
||||
* Get hash prefix eg. {SSHA256}.
|
||||
* @return string
|
||||
*
|
||||
* @return string The hash prefix.
|
||||
*/
|
||||
public abstract function getPrefix();
|
||||
|
||||
/**
|
||||
* Encrypt using SSHA256 algorithm
|
||||
* Encrypt using SSHA* algorithm.
|
||||
*
|
||||
* @param string $password The password.
|
||||
* @param string $salt The salt to use.
|
||||
* @return string The hashed password, prefixed by {SSHA256}.
|
||||
* @param string $salt The salt to use.
|
||||
*
|
||||
* @return string The hashed password, prefixed by {SSHA*}.
|
||||
*/
|
||||
private function ssha($password, $salt)
|
||||
{
|
||||
return $this->getPrefix() . base64_encode(hash($this->getAlgorithm(), $password . $salt, true) . $salt);
|
||||
return $this->getPrefix() . base64_encode(
|
||||
hash($this->getAlgorithm(), $password . $salt, true) . $salt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get algorithm used by the hash() function.
|
||||
*
|
||||
* @see hash()
|
||||
* @return string
|
||||
*/
|
||||
@@ -69,7 +89,10 @@ abstract class SSHA implements HashAlgorithm
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return self::ssha($password,
|
||||
self::randomString(32, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
|
||||
return self::ssha(
|
||||
$password, Utils::randomString(
|
||||
32, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,22 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\SSHA;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* SSHA256 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class SSHA256 extends SSHA
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "SSHA256";
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,4 +55,12 @@ class SSHA256 extends SSHA
|
||||
{
|
||||
return "sha256";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "SSHA256";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,22 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\SSHA;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* SSHA512 hashing implementation.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class SSHA512 extends SSHA
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
* The class constructor.
|
||||
*
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function getVisibleName()
|
||||
public function __construct(IL10N $localization)
|
||||
{
|
||||
return "SSHA512";
|
||||
parent::__construct($localization);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,4 +55,12 @@ class SSHA512 extends SSHA
|
||||
{
|
||||
return "sha512";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAlgorithmName()
|
||||
{
|
||||
return "SSHA512";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,22 +19,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm\Base;
|
||||
namespace OCA\UserSQL\Crypto;
|
||||
|
||||
/**
|
||||
* Cryptographic utilities trait.
|
||||
* Cryptographic utilities.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
trait Utils
|
||||
final class Utils
|
||||
{
|
||||
/**
|
||||
* Convert hexadecimal message to its base64 form.
|
||||
* @param $hex string Hexadecimal encoded message.
|
||||
* @return string Same message encoded in base64.
|
||||
*
|
||||
* @param $hex string The hexadecimal encoded message.
|
||||
*
|
||||
* @return string The same message encoded in base64.
|
||||
*/
|
||||
private static function hexToBase64($hex)
|
||||
public static function hexToBase64($hex)
|
||||
{
|
||||
$hexChr = '';
|
||||
$hexChr = "";
|
||||
foreach (str_split($hex, 2) as $hexPair) {
|
||||
$hexChr .= chr(hexdec($hexPair));
|
||||
}
|
||||
@@ -41,14 +46,16 @@ trait Utils
|
||||
|
||||
/**
|
||||
* Generate random string from given alphabet.
|
||||
* @param $length int Output string length.
|
||||
* @param $alphabet string Output string alphabet.
|
||||
*
|
||||
* @param $length int The output string length.
|
||||
* @param $alphabet string The output string alphabet.
|
||||
*
|
||||
* @return string Random string from given alphabet.
|
||||
*/
|
||||
private static function randomString($length, $alphabet)
|
||||
public static function randomString($length, $alphabet)
|
||||
{
|
||||
$string = "";
|
||||
for ($i = 0; $i != $length; ++$i) {
|
||||
for ($idx = 0; $idx != $length; ++$idx) {
|
||||
$string .= $alphabet[mt_rand(0, strlen($alphabet) - 1)];
|
||||
}
|
||||
return $string;
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
|
||||
/**
|
||||
* Implements standard Unix DES-based algorithm or
|
||||
* alternative algorithms that may be available on the system.
|
||||
* This implementation does not support password changing.
|
||||
* @see crypt()
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Crypt implements HashAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibleName()
|
||||
{
|
||||
return "Crypt (Unix)";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return hash_equals($dbHash, crypt($password, $dbHash));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
return password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\HashAlgorithm;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Singleton;
|
||||
|
||||
/**
|
||||
* Argon2 Crypt hashing implementation.
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptArgon2 implements HashAlgorithm
|
||||
{
|
||||
use Singleton;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibleName()
|
||||
{
|
||||
return "Argon2 (Crypt)";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPassword($password, $dbHash)
|
||||
{
|
||||
return password_verify($password, $dbHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getPasswordHash($password)
|
||||
{
|
||||
// TODO - add support for options: memory_cost, time_cost, threads.
|
||||
return password_hash($password, PASSWORD_ARGON2I);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\BaseCrypt;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
|
||||
/**
|
||||
* Extended DES Crypt hashing implementation.
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptExtendedDES extends BaseCrypt
|
||||
{
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getVisibleName()
|
||||
{
|
||||
return "Extended DES (Crypt)";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
// TODO - add support for options: iteration_count.
|
||||
return self::base64IntEncode(1000) . self::randomString(4, self::SALT_ALPHABET);
|
||||
}
|
||||
|
||||
private static function base64IntEncode($number)
|
||||
{
|
||||
$alphabet = str_split(self::SALT_ALPHABET);
|
||||
$chars = array();
|
||||
$base = sizeof($alphabet);
|
||||
while ($number) {
|
||||
$rem = $number % $base;
|
||||
$number = (int)($number / $base);
|
||||
$arr[] = $alphabet[$rem];
|
||||
}
|
||||
$string = implode($chars);
|
||||
return str_pad($string, 4, '.', STR_PAD_RIGHT);
|
||||
}
|
||||
}
|
||||
43
lib/Model/Group.php
Normal file
43
lib/Model/Group.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Model;
|
||||
|
||||
/**
|
||||
* The group entity.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Group
|
||||
{
|
||||
/**
|
||||
* @var string The GID (group name).
|
||||
*/
|
||||
public $gid;
|
||||
/**
|
||||
* @var string The group's display name.
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @var bool Whether it is an admin group.
|
||||
*/
|
||||
public $admin;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
* Copyright (C) 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,33 +19,37 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\HashAlgorithm;
|
||||
|
||||
use OCA\UserSQL\HashAlgorithm\Base\BaseCrypt;
|
||||
use OCA\UserSQL\HashAlgorithm\Base\Utils;
|
||||
namespace OCA\UserSQL\Model;
|
||||
|
||||
/**
|
||||
* SHA512 Crypt hashing implementation.
|
||||
* The user entity.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class CryptSHA512 extends BaseCrypt
|
||||
class User
|
||||
{
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @var string The UID (username).
|
||||
*/
|
||||
public function getVisibleName()
|
||||
{
|
||||
return "SHA512 (Crypt)";
|
||||
}
|
||||
|
||||
public $uid;
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @var string The user's email address.
|
||||
*/
|
||||
protected function getSalt()
|
||||
{
|
||||
// TODO - add support for options: rounds.
|
||||
return "$5\$rounds=5000$" . self::randomString(16, self::SALT_ALPHABET) . "$";
|
||||
}
|
||||
public $email;
|
||||
/**
|
||||
* @var string The user's display name.
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @var string The user's password (hash).
|
||||
*/
|
||||
public $password;
|
||||
/**
|
||||
* @var string The user's home location.
|
||||
*/
|
||||
public $home;
|
||||
/**
|
||||
* @var bool Can user change its avatar.
|
||||
*/
|
||||
public $avatar;
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
<?php
|
||||
#
|
||||
# Portable PHP password hashing framework.
|
||||
#
|
||||
# Version 0.3 / genuine.
|
||||
#
|
||||
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
||||
# the public domain. Revised in subsequent years, still public domain.
|
||||
#
|
||||
# There's absolutely no warranty.
|
||||
#
|
||||
# The homepage URL for this framework is:
|
||||
#
|
||||
# http://www.openwall.com/phpass/
|
||||
#
|
||||
# Please be sure to update the Version line if you edit this file in any way.
|
||||
# It is suggested that you leave the main version number intact, but indicate
|
||||
# your project name (after the slash) and add your own revision information.
|
||||
#
|
||||
# Please do not change the "private" password hashing method implemented in
|
||||
# here, thereby making your hashes incompatible. However, if you must, please
|
||||
# change the hash type identifier (the "$P$") to something different.
|
||||
#
|
||||
# Obviously, since this code is in the public domain, the above are not
|
||||
# requirements (there can be none), but merely suggestions.
|
||||
#
|
||||
class PasswordHash
|
||||
{
|
||||
var $itoa64;
|
||||
var $iteration_count_log2;
|
||||
var $portable_hashes;
|
||||
var $random_state;
|
||||
|
||||
function __construct($iteration_count_log2, $portable_hashes)
|
||||
{
|
||||
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
|
||||
$iteration_count_log2 = 8;
|
||||
}
|
||||
$this->iteration_count_log2 = $iteration_count_log2;
|
||||
|
||||
$this->portable_hashes = $portable_hashes;
|
||||
|
||||
$this->random_state = microtime();
|
||||
if (function_exists('getmypid')) {
|
||||
$this->random_state .= getmypid();
|
||||
}
|
||||
}
|
||||
|
||||
function get_random_bytes($count)
|
||||
{
|
||||
$output = '';
|
||||
if (is_readable('/dev/urandom') &&
|
||||
($fh = @fopen('/dev/urandom', 'rb'))) {
|
||||
$output = fread($fh, $count);
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
if (strlen($output) < $count) {
|
||||
$output = '';
|
||||
for ($i = 0; $i < $count; $i += 16) {
|
||||
$this->random_state =
|
||||
md5(microtime() . $this->random_state);
|
||||
$output .=
|
||||
pack('H*', md5($this->random_state));
|
||||
}
|
||||
$output = substr($output, 0, $count);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function encode64($input, $count)
|
||||
{
|
||||
$output = '';
|
||||
$i = 0;
|
||||
do {
|
||||
$value = ord($input[$i++]);
|
||||
$output .= $this->itoa64[$value & 0x3f];
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 8;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 6) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 16;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 12) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 18) & 0x3f];
|
||||
} while ($i < $count);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function gensalt_private($input)
|
||||
{
|
||||
$output = '$P$';
|
||||
$output .= $this->itoa64[min($this->iteration_count_log2 +
|
||||
((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
||||
$output .= $this->encode64($input, 6);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function crypt_private($password, $setting)
|
||||
{
|
||||
$output = '*0';
|
||||
if (substr($setting, 0, 2) === $output) {
|
||||
$output = '*1';
|
||||
}
|
||||
|
||||
$id = substr($setting, 0, 3);
|
||||
# We use "$P$", phpBB3 uses "$H$" for the same thing
|
||||
if ($id !== '$P$' && $id !== '$H$') {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$count_log2 = strpos($this->itoa64, $setting[3]);
|
||||
if ($count_log2 < 7 || $count_log2 > 30) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$count = 1 << $count_log2;
|
||||
|
||||
$salt = substr($setting, 4, 8);
|
||||
if (strlen($salt) !== 8) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
# We're kind of forced to use MD5 here since it's the only
|
||||
# cryptographic primitive available in all versions of PHP
|
||||
# currently in use. To implement our own low-level crypto
|
||||
# in PHP would result in much worse performance and
|
||||
# consequently in lower iteration counts and hashes that are
|
||||
# quicker to crack (by non-PHP code).
|
||||
if (PHP_VERSION >= '5') {
|
||||
$hash = md5($salt . $password, true);
|
||||
do {
|
||||
$hash = md5($hash . $password, true);
|
||||
} while (--$count);
|
||||
} else {
|
||||
$hash = pack('H*', md5($salt . $password));
|
||||
do {
|
||||
$hash = pack('H*', md5($hash . $password));
|
||||
} while (--$count);
|
||||
}
|
||||
|
||||
$output = substr($setting, 0, 12);
|
||||
$output .= $this->encode64($hash, 16);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function gensalt_extended($input)
|
||||
{
|
||||
$count_log2 = min($this->iteration_count_log2 + 8, 24);
|
||||
# This should be odd to not reveal weak DES keys, and the
|
||||
# maximum valid value is (2**24 - 1) which is odd anyway.
|
||||
$count = (1 << $count_log2) - 1;
|
||||
|
||||
$output = '_';
|
||||
$output .= $this->itoa64[$count & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 6) & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 12) & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 18) & 0x3f];
|
||||
|
||||
$output .= $this->encode64($input, 3);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function gensalt_blowfish($input)
|
||||
{
|
||||
# This one needs to use a different order of characters and a
|
||||
# different encoding scheme from the one in encode64() above.
|
||||
# We care because the last character in our encoded string will
|
||||
# only represent 2 bits. While two known implementations of
|
||||
# bcrypt will happily accept and correct a salt string which
|
||||
# has the 4 unused bits set to non-zero, we do not want to take
|
||||
# chances and we also do not want to waste an additional byte
|
||||
# of entropy.
|
||||
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
$output = '$2a$';
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
||||
$output .= '$';
|
||||
|
||||
$i = 0;
|
||||
do {
|
||||
$c1 = ord($input[$i++]);
|
||||
$output .= $itoa64[$c1 >> 2];
|
||||
$c1 = ($c1 & 0x03) << 4;
|
||||
if ($i >= 16) {
|
||||
$output .= $itoa64[$c1];
|
||||
break;
|
||||
}
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 4;
|
||||
$output .= $itoa64[$c1];
|
||||
$c1 = ($c2 & 0x0f) << 2;
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 6;
|
||||
$output .= $itoa64[$c1];
|
||||
$output .= $itoa64[$c2 & 0x3f];
|
||||
} while (1);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function HashPassword($password)
|
||||
{
|
||||
$random = '';
|
||||
|
||||
if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) {
|
||||
$random = $this->get_random_bytes(16);
|
||||
$hash =
|
||||
crypt($password, $this->gensalt_blowfish($random));
|
||||
if (strlen($hash) === 60) {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (CRYPT_EXT_DES === 1 && !$this->portable_hashes) {
|
||||
if (strlen($random) < 3) {
|
||||
$random = $this->get_random_bytes(3);
|
||||
}
|
||||
$hash =
|
||||
crypt($password, $this->gensalt_extended($random));
|
||||
if (strlen($hash) === 20) {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($random) < 6) {
|
||||
$random = $this->get_random_bytes(6);
|
||||
}
|
||||
$hash =
|
||||
$this->crypt_private($password,
|
||||
$this->gensalt_private($random));
|
||||
if (strlen($hash) === 34) {
|
||||
return $hash;
|
||||
}
|
||||
|
||||
# Returning '*' on error is safe here, but would _not_ be safe
|
||||
# in a crypt(3)-like function used _both_ for generating new
|
||||
# hashes and for validating passwords against existing hashes.
|
||||
return '*';
|
||||
}
|
||||
|
||||
function CheckPassword($password, $stored_hash)
|
||||
{
|
||||
$hash = $this->crypt_private($password, $stored_hash);
|
||||
if ($hash[0] === '*') {
|
||||
$hash = crypt($password, $stored_hash);
|
||||
}
|
||||
|
||||
return $hash === $stored_hash;
|
||||
}
|
||||
}
|
||||
135
lib/Platform/AbstractPlatform.php
Normal file
135
lib/Platform/AbstractPlatform.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Platform;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use OC\DB\Connection;
|
||||
|
||||
/**
|
||||
* Database platform tools.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
abstract class AbstractPlatform
|
||||
{
|
||||
/**
|
||||
* @var Connection The database connection.
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param Connection $connection The database connection.
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the tables defined in the database.
|
||||
*
|
||||
* @param bool $schemaPrefix Show schema name in the results.
|
||||
*
|
||||
* @return array Array with table names.
|
||||
* @throws DBALException On a database exception.
|
||||
*/
|
||||
public function getTables($schemaPrefix = false)
|
||||
{
|
||||
$platform = $this->connection->getDatabasePlatform();
|
||||
|
||||
$queryTables = $platform->getListTablesSQL();
|
||||
$queryViews = $platform->getListViewsSQL(
|
||||
$this->connection->getDatabase()
|
||||
);
|
||||
|
||||
$tables = array();
|
||||
|
||||
$result = $this->connection->executeQuery($queryTables);
|
||||
while ($row = $result->fetch()) {
|
||||
$name = $this->getTableName($row, $schemaPrefix);
|
||||
$tables[] = $name;
|
||||
}
|
||||
|
||||
$result = $this->connection->executeQuery($queryViews);
|
||||
while ($row = $result->fetch()) {
|
||||
$name = $this->getViewName($row, $schemaPrefix);
|
||||
$tables[] = $name;
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a table name from a query result row.
|
||||
*
|
||||
* @param array $row The query result row.
|
||||
* @param string $schema Put schema name in the result.
|
||||
*
|
||||
* @return string The table name retrieved from the row.
|
||||
*/
|
||||
protected abstract function getTableName($row, $schema);
|
||||
|
||||
/**
|
||||
* Get a view name from a query result row.
|
||||
*
|
||||
* @param array $row The query result row.
|
||||
* @param string $schema Put schema name in the result.
|
||||
*
|
||||
* @return string The view name retrieved from the row.
|
||||
*/
|
||||
protected abstract function getViewName($row, $schema);
|
||||
|
||||
/**
|
||||
* Get all the columns defined in the table.
|
||||
*
|
||||
* @param string $table The table name.
|
||||
*
|
||||
* @return array Array with column names.
|
||||
* @throws DBALException On a database exception.
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$platform = $this->connection->getDatabasePlatform();
|
||||
$query = $platform->getListTableColumnsSQL($table);
|
||||
$result = $this->connection->executeQuery($query);
|
||||
|
||||
$columns = array();
|
||||
|
||||
while ($row = $result->fetch()) {
|
||||
$name = $this->getColumnName($row);
|
||||
$columns[] = $name;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a column name from a query result row.
|
||||
*
|
||||
* @param array $row The query result row.
|
||||
*
|
||||
* @return string The column name retrieved from the row.
|
||||
*/
|
||||
protected abstract function getColumnName($row);
|
||||
}
|
||||
66
lib/Platform/MySQLPlatform.php
Normal file
66
lib/Platform/MySQLPlatform.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Platform;
|
||||
|
||||
use OC\DB\Connection;
|
||||
|
||||
/**
|
||||
* MySQL database platform.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class MySQLPlatform extends AbstractPlatform
|
||||
{
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param Connection $connection The database connection.
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
parent::__construct($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getViewName($row, $schema)
|
||||
{
|
||||
return $row["TABLE_NAME"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getTableName($row, $schema)
|
||||
{
|
||||
return $row["Tables_in_" . $this->connection->getDatabase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getColumnName($row)
|
||||
{
|
||||
return $row["Field"];
|
||||
}
|
||||
}
|
||||
54
lib/Platform/PlatformFactory.php
Normal file
54
lib/Platform/PlatformFactory.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Platform;
|
||||
|
||||
use OC\DB\Connection;
|
||||
|
||||
/**
|
||||
* Factory for the database platform class instance.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class PlatformFactory
|
||||
{
|
||||
/**
|
||||
* Get the database platform.
|
||||
*
|
||||
* @param Connection $connection The database connection.
|
||||
*
|
||||
* @return AbstractPlatform The database platform.
|
||||
*/
|
||||
public static function getPlatform(Connection $connection)
|
||||
{
|
||||
switch ($connection->getDriver()->getName()) {
|
||||
case "pdo_mysql":
|
||||
return new MySQLPlatform($connection);
|
||||
case "pdo_pgsql":
|
||||
return new PostgreSQLPlatform($connection);
|
||||
default:
|
||||
throw new \InvalidArgumentException(
|
||||
"Unknown database driver: " . $connection->getDriver()->getName(
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
lib/Platform/PostgreSQLPlatform.php
Normal file
68
lib/Platform/PostgreSQLPlatform.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Platform;
|
||||
|
||||
use OC\DB\Connection;
|
||||
|
||||
/**
|
||||
* PostgreSQL database platform.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class PostgreSQLPlatform extends AbstractPlatform
|
||||
{
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param Connection $connection The database connection.
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
parent::__construct($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getViewName($row, $schema)
|
||||
{
|
||||
$schema ? ($row["schemaname"] . "." . $row["viewname"])
|
||||
: $row["viewname"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getTableName($row, $schema)
|
||||
{
|
||||
$schema ? ($row["schema_name"] . "." . $row["table_name"])
|
||||
: $row["table_name"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getColumnName($row)
|
||||
{
|
||||
return $row["field"];
|
||||
}
|
||||
}
|
||||
211
lib/Properties.php
Normal file
211
lib/Properties.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL;
|
||||
|
||||
use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Constant\Opt;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* Store and retrieve application properties.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Properties implements \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var string The cache key name.
|
||||
*/
|
||||
const CACHE_KEY = "Properties_data";
|
||||
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var IConfig The config instance.
|
||||
*/
|
||||
private $config;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var Cache The cache instance.
|
||||
*/
|
||||
private $cache;
|
||||
/**
|
||||
* @var array The properties array.
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param Cache $cache The cache instance.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, IConfig $config, ILogger $logger, Cache $cache
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
$this->cache = $cache;
|
||||
|
||||
$this->loadProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the application properties.
|
||||
*
|
||||
* First the values are fetched from the cache memory.
|
||||
* If these are not available, the database values are fetched.
|
||||
*/
|
||||
private function loadProperties()
|
||||
{
|
||||
$this->data = $this->cache->get(self::CACHE_KEY);
|
||||
|
||||
if (!is_null($this->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params = $this->getParameterArray();
|
||||
$this->data = [];
|
||||
|
||||
foreach ($params as $param) {
|
||||
$value = $this->config->getAppValue($this->appName, $param, null);
|
||||
|
||||
if ($value === App::FALSE_VALUE) {
|
||||
$value = false;
|
||||
} elseif ($value === App::TRUE_VALUE) {
|
||||
$value = true;
|
||||
}
|
||||
|
||||
$this->data[$param] = $value;
|
||||
}
|
||||
|
||||
$this->store();
|
||||
|
||||
$this->logger->debug(
|
||||
"The application properties has been loaded.",
|
||||
["app" => $this->appName]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with all supported parameters.
|
||||
*
|
||||
* @return array Array containing strings of the parameters.
|
||||
*/
|
||||
private function getParameterArray()
|
||||
{
|
||||
$params = [];
|
||||
|
||||
foreach ([DB::class, Opt::class] as $class) {
|
||||
try {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$params = array_merge(
|
||||
$params, array_values($reflection->getConstants())
|
||||
);
|
||||
} catch (\ReflectionException $exception) {
|
||||
$this->logger->logException(
|
||||
$exception, ["app" => $this->appName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store properties in the cache memory.
|
||||
*/
|
||||
private function store()
|
||||
{
|
||||
$this->cache->set(self::CACHE_KEY, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties array.
|
||||
*
|
||||
* @return array The properties array.
|
||||
*/
|
||||
public function getArray()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if (isset($this->data[$offset])) {
|
||||
return $this->data[$offset];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->config->setAppValue($this->appName, $offset, $value);
|
||||
|
||||
if ($value === App::FALSE_VALUE) {
|
||||
$value = false;
|
||||
} elseif ($value === App::TRUE_VALUE) {
|
||||
$value = true;
|
||||
}
|
||||
|
||||
$this->data[$offset] = $value;
|
||||
|
||||
if ($offset === Opt::USE_CACHE && $value === false) {
|
||||
$this->cache->clear();
|
||||
} else {
|
||||
$this->store();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
}
|
||||
267
lib/Query/DataQuery.php
Normal file
267
lib/Query/DataQuery.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Query;
|
||||
|
||||
use Doctrine\DBAL\Driver\Statement;
|
||||
use OC\DB\Connection;
|
||||
use OC\DB\ConnectionFactory;
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Constant\Query;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* Used to query a database.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class DataQuery
|
||||
{
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
/**
|
||||
* @var QueryProvider The query provider.
|
||||
*/
|
||||
private $queryProvider;
|
||||
/**
|
||||
* @var Connection The database connection.
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param QueryProvider $queryProvider The query provider.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, ILogger $logger, Properties $properties,
|
||||
QueryProvider $queryProvider
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
$this->logger = $logger;
|
||||
$this->properties = $properties;
|
||||
$this->queryProvider = $queryProvider;
|
||||
$this->connection = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an update query.
|
||||
*
|
||||
* @param string $queryName The query name.
|
||||
* @param array $params The query parameters.
|
||||
*
|
||||
* @see Query
|
||||
* @return bool TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
public function update($queryName, $params = [])
|
||||
{
|
||||
return $this->execQuery($queryName, $params) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a given query and return the result.
|
||||
*
|
||||
* @param string $queryName The query to execute.
|
||||
* @param array $params The query parameters to bind.
|
||||
* @param int $limit Results limit. Defaults to -1 (no limit).
|
||||
* @param int $offset Results offset. Defaults to 0.
|
||||
*
|
||||
* @return Statement|bool Result of query or FALSE on failure.
|
||||
*/
|
||||
private function execQuery(
|
||||
$queryName, $params = [], $limit = -1, $offset = 0
|
||||
) {
|
||||
if ($this->connection === false) {
|
||||
$this->connectToDatabase();
|
||||
}
|
||||
|
||||
$query = $this->queryProvider[$queryName];
|
||||
$result = $this->connection->prepare($query, $limit, $offset);
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
$result->bindValue(":" . $param, $value);
|
||||
}
|
||||
|
||||
$this->logger->debug(
|
||||
"Executing query:" . $query . ", " . implode(",", $params),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
|
||||
if ($result->execute() !== true) {
|
||||
$error = $result->errorInfo();
|
||||
$this->logger->error(
|
||||
"Could not execute the query: " . implode(", ", $error),
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the database using Nextcloud's DBAL.
|
||||
*/
|
||||
private function connectToDatabase()
|
||||
{
|
||||
$connectionFactory = new ConnectionFactory(
|
||||
\OC::$server->getSystemConfig()
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
"host" => $this->properties[DB::HOSTNAME],
|
||||
"password" => $this->properties[DB::PASSWORD],
|
||||
"user" => $this->properties[DB::USERNAME],
|
||||
"dbname" => $this->properties[DB::DATABASE],
|
||||
"tablePrefix" => ""
|
||||
);
|
||||
|
||||
$this->connection = $connectionFactory->getConnection(
|
||||
$this->properties[DB::DRIVER], $parameters
|
||||
);
|
||||
|
||||
$this->logger->debug(
|
||||
"Database connection established.", ["app" => $this->appName]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a value from the first row and the first column which
|
||||
* the given query returns. Empty result set is consider to be a failure.
|
||||
*
|
||||
* @param string $queryName The query to execute.
|
||||
* @param array $params The query parameters to bind.
|
||||
* @param bool $failure Value returned on database query failure.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array|bool Queried value or $failure value on failure.
|
||||
*/
|
||||
public function queryValue($queryName, $params = [], $failure = false)
|
||||
{
|
||||
$result = $this->execQuery($queryName, $params);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = $result->fetch(\PDO::FETCH_COLUMN);
|
||||
if ($row === false) {
|
||||
return $failure;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch values from the first column which the given query returns.
|
||||
*
|
||||
* @param string $queryName The query to execute.
|
||||
* @param array $params The query parameters to bind.
|
||||
* @param int $limit Results limit. Defaults to -1 (no limit).
|
||||
* @param int $offset Results offset. Defaults to 0.
|
||||
*
|
||||
* @return array|bool Queried column or FALSE on failure.
|
||||
*/
|
||||
public function queryColumn(
|
||||
$queryName, $params = [], $limit = -1, $offset = 0
|
||||
) {
|
||||
$result = $this->execQuery($queryName, $params, $limit, $offset);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$column = $result->fetchAll(\PDO::FETCH_COLUMN);
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch entity returned by the given query.
|
||||
*
|
||||
* @param string $queryName The query to execute.
|
||||
* @param string $entityClass The entity class name.
|
||||
* @param array $params The query parameters to bind.
|
||||
*
|
||||
* @return mixed|null The queried entity, NULL if it does not exists or
|
||||
* FALSE on failure.
|
||||
*/
|
||||
public function queryEntity($queryName, $entityClass, $params = [])
|
||||
{
|
||||
$result = $this->execQuery($queryName, $params);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result->setFetchMode(\PDO::FETCH_CLASS, $entityClass);
|
||||
$entity = $result->fetch();
|
||||
|
||||
if ($entity === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($entity) === true) {
|
||||
$this->logger->debug(
|
||||
"Empty result for query: " . $queryName,
|
||||
["app" => $this->appName]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch entities returned by the given query.
|
||||
*
|
||||
* @param string $queryName The query to execute.
|
||||
* @param string $entityClass The entity class name.
|
||||
* @param array $params The query parameters to bind.
|
||||
* @param int $limit Results limit. Defaults to -1 (no limit).
|
||||
* @param int $offset Results offset. Defaults to 0.
|
||||
*
|
||||
* @return mixed|null The queried entities or FALSE on failure.
|
||||
*/
|
||||
public function queryEntities(
|
||||
$queryName, $entityClass, $params = [], $limit = -1, $offset = 0
|
||||
) {
|
||||
$result = $this->execQuery($queryName, $params, $limit, $offset);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result->setFetchMode(\PDO::FETCH_CLASS, $entityClass);
|
||||
$entities = $result->fetchAll();
|
||||
|
||||
return $entities;
|
||||
}
|
||||
}
|
||||
195
lib/Query/QueryProvider.php
Normal file
195
lib/Query/QueryProvider.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Query;
|
||||
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Constant\Query;
|
||||
use OCA\UserSQL\Properties;
|
||||
|
||||
/**
|
||||
* Provides queries array.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class QueryProvider implements \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
/**
|
||||
* @var array The queries array.
|
||||
*/
|
||||
private $queries;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param Properties $properties The properties array.
|
||||
*/
|
||||
public function __construct(Properties $properties)
|
||||
{
|
||||
$this->properties = $properties;
|
||||
$this->loadQueries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load queries to the array.
|
||||
*/
|
||||
private function loadQueries()
|
||||
{
|
||||
$group = $this->properties[DB::GROUP_TABLE];
|
||||
$userGroup = $this->properties[DB::USER_GROUP_TABLE];
|
||||
$user = $this->properties[DB::USER_TABLE];
|
||||
|
||||
$gAdmin = $this->properties[DB::GROUP_ADMIN_COLUMN];
|
||||
$gGID = $this->properties[DB::GROUP_GID_COLUMN];
|
||||
$gName = $this->properties[DB::GROUP_NAME_COLUMN];
|
||||
|
||||
$uAvatar = $this->properties[DB::USER_AVATAR_COLUMN];
|
||||
$uEmail = $this->properties[DB::USER_EMAIL_COLUMN];
|
||||
$uHome = $this->properties[DB::USER_HOME_COLUMN];
|
||||
$uName = $this->properties[DB::USER_NAME_COLUMN];
|
||||
$uPassword = $this->properties[DB::USER_PASSWORD_COLUMN];
|
||||
$uUID = $this->properties[DB::USER_UID_COLUMN];
|
||||
|
||||
$ugGID = $this->properties[DB::USER_GROUP_GID_COLUMN];
|
||||
$ugUID = $this->properties[DB::USER_GROUP_UID_COLUMN];
|
||||
|
||||
$gidParam = Query::GID_PARAM;
|
||||
$nameParam = Query::NAME_PARAM;
|
||||
$passwordParam = Query::PASSWORD_PARAM;
|
||||
$searchParam = Query::SEARCH_PARAM;
|
||||
$uidParam = Query::UID_PARAM;
|
||||
|
||||
$groupColumns
|
||||
= "$gGID AS gid, " .
|
||||
"$gName AS name, " .
|
||||
"$gAdmin AS admin";
|
||||
$userColumns
|
||||
= "$uUID AS uid, " .
|
||||
"$uName AS name, " .
|
||||
"$uEmail AS email, " .
|
||||
"$uHome AS home, " .
|
||||
"$uAvatar AS avatar";
|
||||
|
||||
$this->queries = [
|
||||
Query::BELONGS_TO_ADMIN =>
|
||||
"SELECT COUNT($gGID) > 0 AS admin " .
|
||||
"FROM $group, $userGroup " .
|
||||
"WHERE $ugGID = $gGID " .
|
||||
"AND $ugUID = :$uidParam " .
|
||||
"AND $gAdmin",
|
||||
|
||||
Query::COUNT_GROUPS =>
|
||||
"SELECT COUNT($ugGID) " .
|
||||
"FROM $userGroup " .
|
||||
"WHERE $ugGID = :$gidParam " .
|
||||
"AND $ugUID " .
|
||||
"LIKE :$searchParam",
|
||||
|
||||
Query::COUNT_USERS =>
|
||||
"SELECT COUNT($uUID) AS count " .
|
||||
"FROM $user " .
|
||||
"WHERE $uUID LIKE :$searchParam",
|
||||
|
||||
Query::FIND_GROUP =>
|
||||
"SELECT $groupColumns " .
|
||||
"FROM $group " .
|
||||
"WHERE $gGID = :$gidParam",
|
||||
|
||||
Query::FIND_GROUP_USERS =>
|
||||
"SELECT $ugUID AS uid " .
|
||||
"FROM $userGroup " .
|
||||
"WHERE $ugGID = :$gidParam " .
|
||||
"AND $ugUID " .
|
||||
"LIKE :$searchParam " .
|
||||
"ORDER BY $ugUID",
|
||||
|
||||
Query::FIND_GROUPS =>
|
||||
"SELECT $groupColumns " .
|
||||
"FROM $group " .
|
||||
"WHERE $gGID LIKE :$searchParam " .
|
||||
"ORDER BY $gGID",
|
||||
|
||||
Query::FIND_USER =>
|
||||
"SELECT $userColumns, $uPassword AS password " .
|
||||
"FROM $user " .
|
||||
"WHERE $uUID = :$uidParam",
|
||||
|
||||
Query::FIND_USER_GROUPS =>
|
||||
"SELECT $groupColumns " .
|
||||
"FROM $group, $userGroup " .
|
||||
"WHERE $ugGID = $gGID " .
|
||||
"AND $ugUID = :$uidParam " .
|
||||
"ORDER BY $gGID",
|
||||
|
||||
Query::FIND_USERS =>
|
||||
"SELECT $userColumns " .
|
||||
"FROM $user " .
|
||||
"WHERE $uUID LIKE :$searchParam " .
|
||||
"ORDER BY $uUID",
|
||||
|
||||
Query::SAVE_USER =>
|
||||
"UPDATE $user " .
|
||||
"SET $uPassword = :$passwordParam, " .
|
||||
"$uName = :$nameParam " .
|
||||
"WHERE $uUID = :$uidParam",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->queries[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if (isset($this->queries[$offset])) {
|
||||
return $this->queries[$offset];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->queries[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->queries[$offset]);
|
||||
}
|
||||
}
|
||||
150
lib/Repository/GroupRepository.php
Normal file
150
lib/Repository/GroupRepository.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Repository;
|
||||
|
||||
use OCA\UserSQL\Constant\Query;
|
||||
use OCA\UserSQL\Model\Group;
|
||||
use OCA\UserSQL\Query\DataQuery;
|
||||
|
||||
/**
|
||||
* The group repository.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class GroupRepository
|
||||
{
|
||||
/**
|
||||
* @var DataQuery The data query object.
|
||||
*/
|
||||
private $dataQuery;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param DataQuery $dataQuery The data query object.
|
||||
*/
|
||||
public function __construct(DataQuery $dataQuery)
|
||||
{
|
||||
$this->dataQuery = $dataQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a group entity object.
|
||||
*
|
||||
* @param string $gid The group ID.
|
||||
*
|
||||
* @return Group The group entity, NULL if it does not exists or
|
||||
* FALSE on failure.
|
||||
*/
|
||||
public function findByGid($gid)
|
||||
{
|
||||
return $this->dataQuery->queryEntity(
|
||||
Query::FIND_GROUP, Group::class, [Query::GID_PARAM => $gid]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all groups a user belongs to.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
*
|
||||
* @return Group[] Array of group entity objects or FALSE on failure.
|
||||
*/
|
||||
public function findAllByUid($uid)
|
||||
{
|
||||
return $this->dataQuery->queryEntities(
|
||||
Query::FIND_USER_GROUPS, Group::class, [Query::UID_PARAM => $uid]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all user IDs belonging to the group.
|
||||
*
|
||||
* @param string $gid The group ID.
|
||||
* @param string $search The UID search term. Defaults to "" (empty string).
|
||||
* @param int $limit (optional) Results limit.
|
||||
* Defaults to -1 (no limit).
|
||||
* @param int $offset (optional) Results offset. Defaults to 0.
|
||||
*
|
||||
* @return string[] Array of UIDs belonging to the group
|
||||
* or FALSE on failure.
|
||||
*/
|
||||
public function findAllUidsBySearchTerm(
|
||||
$gid, $search = "", $limit = -1, $offset = 0
|
||||
) {
|
||||
return $this->dataQuery->queryColumn(
|
||||
Query::FIND_GROUP_USERS,
|
||||
[Query::GID_PARAM => $gid, Query::SEARCH_PARAM => $search], $limit,
|
||||
$offset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of group entity objects.
|
||||
*
|
||||
* @param string $search The search term. Defaults to "" (empty string).
|
||||
* @param int $limit (optional) Results limit.
|
||||
* Defaults to -1 (no limit).
|
||||
* @param int $offset (optional) Results offset. Defaults to 0.
|
||||
*
|
||||
* @return Group[] Array of group entity objects or FALSE on failure.
|
||||
*/
|
||||
public function findAllBySearchTerm($search = "", $limit = -1, $offset = 0)
|
||||
{
|
||||
return $this->dataQuery->queryEntities(
|
||||
Query::FIND_GROUPS, Group::class, [Query::SEARCH_PARAM => $search],
|
||||
$limit, $offset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of users in given group matching the search term.
|
||||
*
|
||||
* @param string $gid The group ID.
|
||||
* @param string $search The UID search term. Defaults to "" (empty string).
|
||||
*
|
||||
* @return int The number of users in given group matching the search term
|
||||
* or FALSE on failure.
|
||||
*/
|
||||
public function countAll($gid, $search = "")
|
||||
{
|
||||
return $this->dataQuery->queryValue(
|
||||
Query::COUNT_GROUPS,
|
||||
[Query::GID_PARAM => $gid, Query::SEARCH_PARAM => $search]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if the user belongs to any admin group.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
*
|
||||
* @return bool|null TRUE if the user belongs to any admin group,
|
||||
* FALSE if not, NULL on failure.
|
||||
*/
|
||||
public function belongsToAdmin($uid)
|
||||
{
|
||||
return $this->dataQuery->queryValue(
|
||||
Query::BELONGS_TO_ADMIN, [Query::UID_PARAM => $uid], null
|
||||
);
|
||||
}
|
||||
}
|
||||
114
lib/Repository/UserRepository.php
Normal file
114
lib/Repository/UserRepository.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\UserSQL\Repository;
|
||||
|
||||
use OCA\UserSQL\Constant\Query;
|
||||
use OCA\UserSQL\Model\User;
|
||||
use OCA\UserSQL\Query\DataQuery;
|
||||
|
||||
/**
|
||||
* The user repository.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class UserRepository
|
||||
{
|
||||
/**
|
||||
* @var DataQuery The data query object.
|
||||
*/
|
||||
private $dataQuery;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param DataQuery $dataQuery The data query object.
|
||||
*/
|
||||
public function __construct(DataQuery $dataQuery)
|
||||
{
|
||||
$this->dataQuery = $dataQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user entity object.
|
||||
*
|
||||
* @param string $uid The user ID.
|
||||
*
|
||||
* @return User The user entity, NULL if it does not exists or
|
||||
* FALSE on failure.
|
||||
*/
|
||||
public function findByUid($uid)
|
||||
{
|
||||
return $this->dataQuery->queryEntity(
|
||||
Query::FIND_USER, User::class, [Query::UID_PARAM => $uid]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of user entity objects.
|
||||
*
|
||||
* @param string $search The search term. Defaults to "" (empty string).
|
||||
* @param int $limit (optional) Results limit.
|
||||
* Defaults to -1 (no limit).
|
||||
* @param int $offset (optional) Results offset. Defaults to 0.
|
||||
*
|
||||
* @return User[] Array of user entity objects or FALSE on failure.
|
||||
*/
|
||||
public function findAllBySearchTerm($search = "", $limit = -1, $offset = 0)
|
||||
{
|
||||
return $this->dataQuery->queryEntities(
|
||||
Query::FIND_USERS, User::class, [Query::SEARCH_PARAM => $search],
|
||||
$limit, $offset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of users.
|
||||
*
|
||||
* @param string $search The search term. Defaults to "" (empty string).
|
||||
*
|
||||
* @return int The number of users or FALSE on failure.
|
||||
*/
|
||||
public function countAll($search = "")
|
||||
{
|
||||
return $this->dataQuery->queryValue(
|
||||
Query::COUNT_USERS, [Query::SEARCH_PARAM => $search]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an user entity object.
|
||||
*
|
||||
* @param User $user The user entity.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
public function save($user)
|
||||
{
|
||||
return $this->dataQuery->update(
|
||||
Query::SAVE_USER, [
|
||||
Query::NAME_PARAM => $user->name,
|
||||
Query::PASSWORD_PARAM => $user->password,
|
||||
Query::UID_PARAM => $user->uid
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c)
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @author
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,87 +16,64 @@
|
||||
* 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/>.
|
||||
*
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\user_sql\Settings;
|
||||
namespace OCA\UserSQL\Settings;
|
||||
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\Defaults;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\Settings\ISettings;
|
||||
|
||||
/**
|
||||
* The administrator's settings page.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Admin implements ISettings
|
||||
{
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var Defaults */
|
||||
private $defaults;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var Properties The properties array.
|
||||
*/
|
||||
private $properties;
|
||||
|
||||
/**
|
||||
* @param IL10N $l10n
|
||||
* @param Defaults $defaults
|
||||
* @param IConfig $config
|
||||
* The class constructor,
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param Properties $properties The properties array.
|
||||
*/
|
||||
public function __construct(
|
||||
IL10N $l10n,
|
||||
Defaults $defaults,
|
||||
IConfig $config
|
||||
) {
|
||||
$this->l10n = $l10n;
|
||||
$this->defaults = $defaults;
|
||||
$this->config = $config;
|
||||
$this->helper = new \OCA\user_sql\lib\Helper();
|
||||
$this->params = $this->helper->getParameterArray();
|
||||
$this->settings = $this->helper->loadSettingsForDomain('default');
|
||||
public function __construct($AppName, Properties $properties)
|
||||
{
|
||||
$this->appName = $AppName;
|
||||
$this->properties = $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TemplateResponse
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
|
||||
$type = $this->config->getAppValue('user_sql', 'type');
|
||||
$trusted_domains = \OC::$server->getConfig()->getSystemValue('trusted_domains');
|
||||
$inserted = array('default');
|
||||
array_splice($trusted_domains, 0, 0, $inserted);
|
||||
|
||||
$params = [
|
||||
'type' => $type,
|
||||
];
|
||||
$params['allowed_domains'] = array_unique($trusted_domains);
|
||||
|
||||
foreach ($this->params as $key) {
|
||||
$value = $this->settings[$key];
|
||||
$params[$key] = $value;
|
||||
}
|
||||
|
||||
$params["config"] = $this->config;
|
||||
return new TemplateResponse('user_sql', 'admin', $params);
|
||||
return new TemplateResponse($this->appName, "admin", $this->properties->getArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the section ID, e.g. 'sharing'
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSection()
|
||||
{
|
||||
return 'user_sql';
|
||||
return $this->appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* keep the server setting at the top, right after "server settings"
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return 0;
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c)
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @author
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -17,59 +16,79 @@
|
||||
* 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/>.
|
||||
*
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\user_sql\Settings;
|
||||
namespace OCA\UserSQL\Settings;
|
||||
|
||||
use OCP\IL10N;
|
||||
use OCP\Settings\IIconSection;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Settings\IIconSection;
|
||||
|
||||
/**
|
||||
* The section item.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Section implements IIconSection
|
||||
{
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
/**
|
||||
* @var string The application name.
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var IURLGenerator The URL generator.
|
||||
*/
|
||||
private $urlGenerator;
|
||||
/**
|
||||
* @var IL10N The localization service.
|
||||
*/
|
||||
private $localization;
|
||||
|
||||
/**
|
||||
* @param IL10N $l
|
||||
* The class constructor.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param IURLGenerator $urlGenerator The URL generator.
|
||||
* @param IL10N $localization The localization service.
|
||||
*/
|
||||
public function __construct(IURLGenerator $url, IL10N $l)
|
||||
{
|
||||
$this->l = $l;
|
||||
$this->url = $url;
|
||||
public function __construct(
|
||||
$AppName, IURLGenerator $urlGenerator, IL10N $localization
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->localization = $localization;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getID()
|
||||
{
|
||||
return 'user_sql';
|
||||
return $this->appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->l->t('User SQL');
|
||||
return $this->localization->t("SQL Backends");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return 75;
|
||||
return 25;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getIcon()
|
||||
{
|
||||
return $this->url->imagePath('user_sql', 'app-dark.svg');
|
||||
return $this->urlGenerator->imagePath($this->appName, "app-dark.svg");
|
||||
}
|
||||
}
|
||||
|
||||
330
lib/drupal.php
330
lib/drupal.php
@@ -1,330 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Secure password hashing functions for user authentication.
|
||||
* Adopted from Drupal 7.x WD 2018-01-04
|
||||
*
|
||||
* Based on the Portable PHP password hashing framework.
|
||||
* @see http://www.openwall.com/phpass/
|
||||
*
|
||||
* An alternative or custom version of this password hashing API may be
|
||||
* used by setting the variable password_inc to the name of the PHP file
|
||||
* containing replacement user_hash_password(), user_check_password(), and
|
||||
* user_needs_new_hash() functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The standard log2 number of iterations for password stretching. This should
|
||||
* increase by 1 every Drupal version in order to counteract increases in the
|
||||
* speed and power of computers available to crack the hashes.
|
||||
*/
|
||||
define('HASH_COUNT', 15);
|
||||
|
||||
/**
|
||||
* The minimum allowed log2 number of iterations for password stretching.
|
||||
*/
|
||||
define('MIN_HASH_COUNT', 7);
|
||||
|
||||
/**
|
||||
* The maximum allowed log2 number of iterations for password stretching.
|
||||
*/
|
||||
define('MAX_HASH_COUNT', 30);
|
||||
|
||||
/**
|
||||
* The expected (and maximum) number of characters in a hashed password.
|
||||
*/
|
||||
define('HASH_LENGTH', 55);
|
||||
|
||||
/**
|
||||
* Returns a string for mapping an int to the corresponding base 64 character.
|
||||
*/
|
||||
function _password_itoa64()
|
||||
{
|
||||
return './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes bytes into printable base 64 using the *nix standard from crypt().
|
||||
*
|
||||
* @param $input
|
||||
* The string containing bytes to encode.
|
||||
* @param $count
|
||||
* The number of characters (bytes) to encode.
|
||||
*
|
||||
* @return
|
||||
* Encoded string
|
||||
*/
|
||||
function _password_base64_encode($input, $count)
|
||||
{
|
||||
$output = '';
|
||||
$i = 0;
|
||||
$itoa64 = _password_itoa64();
|
||||
do {
|
||||
$value = ord($input[$i++]);
|
||||
$output .= $itoa64[$value & 0x3f];
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 8;
|
||||
}
|
||||
$output .= $itoa64[($value >> 6) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 16;
|
||||
}
|
||||
$output .= $itoa64[($value >> 12) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
$output .= $itoa64[($value >> 18) & 0x3f];
|
||||
} while ($i < $count);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of highly randomized bytes (over the full 8-bit range).
|
||||
*
|
||||
* This function is better than simply calling mt_rand() or any other built-in
|
||||
* PHP function because it can return a long string of bytes (compared to < 4
|
||||
* bytes normally from mt_rand()) and uses the best available pseudo-random
|
||||
* source.
|
||||
*
|
||||
* @param $count
|
||||
* The number of characters (bytes) to return in the string.
|
||||
*/
|
||||
|
||||
function _random_bytes($count)
|
||||
{
|
||||
// $random_state does not use static as it stores random bytes.
|
||||
static $random_state, $bytes, $has_openssl;
|
||||
|
||||
$missing_bytes = $count - strlen($bytes);
|
||||
|
||||
if ($missing_bytes > 0) {
|
||||
// PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
|
||||
// locking on Windows and rendered it unusable.
|
||||
if (!isset($has_openssl)) {
|
||||
$has_openssl = version_compare(PHP_VERSION, '5.3.4',
|
||||
'>=') && function_exists('openssl_random_pseudo_bytes');
|
||||
}
|
||||
|
||||
// openssl_random_pseudo_bytes() will find entropy in a system-dependent
|
||||
// way.
|
||||
if ($has_openssl) {
|
||||
$bytes .= openssl_random_pseudo_bytes($missing_bytes);
|
||||
}
|
||||
|
||||
// Else, read directly from /dev/urandom, which is available on many *nix
|
||||
// systems and is considered cryptographically secure.
|
||||
elseif ($fh = @fopen('/dev/urandom', 'rb')) {
|
||||
// PHP only performs buffered reads, so in reality it will always read
|
||||
// at least 4096 bytes. Thus, it costs nothing extra to read and store
|
||||
// that much so as to speed any additional invocations.
|
||||
$bytes .= fread($fh, max(4096, $missing_bytes));
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
// If we couldn't get enough entropy, this simple hash-based PRNG will
|
||||
// generate a good set of pseudo-random bytes on any system.
|
||||
// Note that it may be important that our $random_state is passed
|
||||
// through hash() prior to being rolled into $output, that the two hash()
|
||||
// invocations are different, and that the extra input into the first one -
|
||||
// the microtime() - is prepended rather than appended. This is to avoid
|
||||
// directly leaking $random_state via the $output stream, which could
|
||||
// allow for trivial prediction of further "random" numbers.
|
||||
if (strlen($bytes) < $count) {
|
||||
// Initialize on the first call. The contents of $_SERVER includes a mix of
|
||||
// user-specific and system information that varies a little with each page.
|
||||
if (!isset($random_state)) {
|
||||
$random_state = print_r($_SERVER, true);
|
||||
if (function_exists('getmypid')) {
|
||||
// Further initialize with the somewhat random PHP process ID.
|
||||
$random_state .= getmypid();
|
||||
}
|
||||
$bytes = '';
|
||||
}
|
||||
|
||||
do {
|
||||
$random_state = hash('sha256', microtime() . mt_rand() . $random_state);
|
||||
$bytes .= hash('sha256', mt_rand() . $random_state, true);
|
||||
} while (strlen($bytes) < $count);
|
||||
}
|
||||
}
|
||||
$output = substr($bytes, 0, $count);
|
||||
$bytes = substr($bytes, $count);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random base 64-encoded salt prefixed with settings for the hash.
|
||||
*
|
||||
* Proper use of salts may defeat a number of attacks, including:
|
||||
* - The ability to try candidate passwords against multiple hashes at once.
|
||||
* - The ability to use pre-hashed lists of candidate passwords.
|
||||
* - The ability to determine whether two users have the same (or different)
|
||||
* password without actually having to guess one of the passwords.
|
||||
*
|
||||
* @param $count_log2
|
||||
* Integer that determines the number of iterations used in the hashing
|
||||
* process. A larger value is more secure, but takes more time to complete.
|
||||
*
|
||||
* @return
|
||||
* A 12 character string containing the iteration count and a random salt.
|
||||
*/
|
||||
function _password_generate_salt($count_log2)
|
||||
{
|
||||
$output = '$S$';
|
||||
// Ensure that $count_log2 is within set bounds.
|
||||
$count_log2 = _password_enforce_log2_boundaries($count_log2);
|
||||
// We encode the final log2 iteration count in base 64.
|
||||
$itoa64 = _password_itoa64();
|
||||
$output .= $itoa64[$count_log2];
|
||||
// 6 bytes is the standard salt for a portable phpass hash.
|
||||
$output .= _password_base64_encode(_random_bytes(6), 6);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that $count_log2 is within set bounds.
|
||||
*
|
||||
* @param $count_log2
|
||||
* Integer that determines the number of iterations used in the hashing
|
||||
* process. A larger value is more secure, but takes more time to complete.
|
||||
*
|
||||
* @return
|
||||
* Integer within set bounds that is closest to $count_log2.
|
||||
*/
|
||||
function _password_enforce_log2_boundaries($count_log2)
|
||||
{
|
||||
if ($count_log2 < MIN_HASH_COUNT) {
|
||||
return MIN_HASH_COUNT;
|
||||
} elseif ($count_log2 > MAX_HASH_COUNT) {
|
||||
return MAX_HASH_COUNT;
|
||||
}
|
||||
|
||||
return (int)$count_log2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using a secure stretched hash.
|
||||
*
|
||||
* By using a salt and repeated hashing the password is "stretched". Its
|
||||
* security is increased because it becomes much more computationally costly
|
||||
* for an attacker to try to break the hash by brute-force computation of the
|
||||
* hashes of a large number of plain-text words or strings to find a match.
|
||||
*
|
||||
* @param $algo
|
||||
* The string name of a hashing algorithm usable by hash(), like 'sha256'.
|
||||
* @param $password
|
||||
* Plain-text password up to 512 bytes (128 to 512 UTF-8 characters) to hash.
|
||||
* @param $setting
|
||||
* An existing hash or the output of _password_generate_salt(). Must be
|
||||
* at least 12 characters (the settings and salt).
|
||||
*
|
||||
* @return
|
||||
* A string containing the hashed password (and salt) or FALSE on failure.
|
||||
* The return string will be truncated at DRUPAL_HASH_LENGTH characters max.
|
||||
*/
|
||||
function _password_crypt($algo, $password, $setting)
|
||||
{
|
||||
// Prevent DoS attacks by refusing to hash large passwords.
|
||||
if (strlen($password) > 512) {
|
||||
return false;
|
||||
}
|
||||
// The first 12 characters of an existing hash are its setting string.
|
||||
$setting = substr($setting, 0, 12);
|
||||
|
||||
if ($setting[0] != '$' || $setting[2] != '$') {
|
||||
return false;
|
||||
}
|
||||
$count_log2 = _password_get_count_log2($setting);
|
||||
// Hashes may be imported from elsewhere, so we allow != DRUPAL_HASH_COUNT
|
||||
if ($count_log2 < MIN_HASH_COUNT || $count_log2 > MAX_HASH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
$salt = substr($setting, 4, 8);
|
||||
// Hashes must have an 8 character salt.
|
||||
if (strlen($salt) != 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the base 2 logarithm into an integer.
|
||||
$count = 1 << $count_log2;
|
||||
|
||||
// We rely on the hash() function being available in PHP 5.2+.
|
||||
$hash = hash($algo, $salt . $password, true);
|
||||
do {
|
||||
$hash = hash($algo, $hash . $password, true);
|
||||
} while (--$count);
|
||||
|
||||
$len = strlen($hash);
|
||||
$output = $setting . _password_base64_encode($hash, $len);
|
||||
// _password_base64_encode() of a 16 byte MD5 will always be 22 characters.
|
||||
// _password_base64_encode() of a 64 byte sha512 will always be 86 characters.
|
||||
$expected = 12 + ceil((8 * $len) / 6);
|
||||
return (strlen($output) == $expected) ? substr($output, 0, HASH_LENGTH) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the log2 iteration count from a stored hash or setting string.
|
||||
*/
|
||||
function _password_get_count_log2($setting)
|
||||
{
|
||||
$itoa64 = _password_itoa64();
|
||||
return strpos($itoa64, $setting[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using a secure hash.
|
||||
*
|
||||
* @param $password
|
||||
* A plain-text password.
|
||||
* @param $count_log2
|
||||
* Optional integer to specify the iteration count. Generally used only during
|
||||
* mass operations where a value less than the default is needed for speed.
|
||||
*
|
||||
* @return
|
||||
* A string containing the hashed password (and a salt), or FALSE on failure.
|
||||
*/
|
||||
function user_hash_password($password, $count_log2 = 0)
|
||||
{
|
||||
if (empty($count_log2)) {
|
||||
// Use the standard iteration count.
|
||||
$count_log2 = variable_get('password_count_log2', DRUPAL_HASH_COUNT);
|
||||
}
|
||||
return _password_crypt('sha512', $password, _password_generate_salt($count_log2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a plain text password matches a stored hashed password.
|
||||
*
|
||||
* @param $password
|
||||
* A plain-text password
|
||||
* @param $hashpass
|
||||
*
|
||||
* @return
|
||||
* TRUE or FALSE.
|
||||
*/
|
||||
function user_check_password($password, $hashpass)
|
||||
{
|
||||
$stored_hash = $hashpass;
|
||||
$type = substr($stored_hash, 0, 3);
|
||||
switch ($type) {
|
||||
case '$S$':
|
||||
// A normal Drupal 7 password using sha512.
|
||||
$hash = _password_crypt('sha512', $password, $stored_hash);
|
||||
break;
|
||||
case '$H$':
|
||||
// phpBB3 uses "$H$" for the same thing as "$P$".
|
||||
case '$P$':
|
||||
// A phpass password generated using md5. This is an
|
||||
// imported password or from an earlier Drupal version.
|
||||
$hash = _password_crypt('md5', $password, $stored_hash);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return ($hash && $stored_hash == $hash);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\user_sql;
|
||||
|
||||
use OCP\Util;
|
||||
|
||||
class OC_GROUP_SQL extends \OC_Group_Backend implements \OCP\GroupInterface
|
||||
{
|
||||
protected $settings;
|
||||
protected $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->helper = new \OCA\user_sql\lib\Helper();
|
||||
$domain = \OC::$server->getRequest()->getServerHost();
|
||||
$this->settings = $this->helper->loadSettingsForDomain($domain);
|
||||
$this->helper->connectToDb($this->settings);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUserGroups($uid)
|
||||
{
|
||||
if (empty($this->settings['sql_group_table'])) {
|
||||
Util::writeLog('OC_USER_SQL', "Group table not configured", Util::DEBUG);
|
||||
return [];
|
||||
}
|
||||
$rows = $this->helper->runQuery('getUserGroups', array('uid' => $uid), false, true);
|
||||
if ($rows === false) {
|
||||
Util::writeLog('OC_USER_SQL', "Found no group", Util::DEBUG);
|
||||
return [];
|
||||
}
|
||||
$groups = array();
|
||||
foreach ($rows as $row) {
|
||||
$groups[] = $row[$this->settings['col_group_name']];
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
|
||||
public function getGroups($search = '', $limit = null, $offset = null)
|
||||
{
|
||||
if (empty($this->settings['sql_group_table'])) {
|
||||
return [];
|
||||
}
|
||||
$search = "%" . $search . "%";
|
||||
$rows = $this->helper->runQuery('getGroups', array('search' => $search), false, true,
|
||||
array('limit' => $limit, 'offset' => $offset));
|
||||
if ($rows === false) {
|
||||
return [];
|
||||
}
|
||||
$groups = array();
|
||||
foreach ($rows as $row) {
|
||||
$groups[] = $row[$this->settings['col_group_name']];
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
|
||||
public function usersInGroup($gid, $search = '', $limit = null, $offset = null)
|
||||
{
|
||||
if (empty($this->settings['sql_group_table'])) {
|
||||
Util::writeLog('OC_USER_SQL', "Group table not configured", Util::DEBUG);
|
||||
return [];
|
||||
}
|
||||
$rows = $this->helper->runQuery('getGroupUsers', array('gid' => $gid), false, true);
|
||||
if ($rows === false) {
|
||||
Util::writeLog('OC_USER_SQL', "Found no users for group", Util::DEBUG);
|
||||
return [];
|
||||
}
|
||||
$users = array();
|
||||
foreach ($rows as $row) {
|
||||
$users[] = $row[$this->settings['col_group_username']];
|
||||
}
|
||||
return $users;
|
||||
}
|
||||
|
||||
public function countUsersInGroup($gid, $search = '')
|
||||
{
|
||||
if (empty($this->settings['sql_group_table'])) {
|
||||
return 0;
|
||||
}
|
||||
$search = "%" . $search . "%";
|
||||
$count = $this->helper->runQuery('countUsersInGroup', array('gid' => $gid, 'search' => $search));
|
||||
if ($count === false) {
|
||||
return 0;
|
||||
} else {
|
||||
return intval(reset($count));
|
||||
}
|
||||
}
|
||||
}
|
||||
427
lib/helper.php
427
lib/helper.php
@@ -1,427 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @author Andreas Böhler and contributors
|
||||
* @copyright 2012-2015 Andreas Böhler <dev (at) aboehler (dot) at>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\user_sql\lib;
|
||||
|
||||
use OCP\IConfig;
|
||||
use OCP\Util;
|
||||
|
||||
class Helper
|
||||
{
|
||||
|
||||
protected $db;
|
||||
protected $db_conn;
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The default constructor initializes some parameters
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->db_conn = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with all supported parameters
|
||||
* @return array Containing strings of the parameters
|
||||
*/
|
||||
public function getParameterArray()
|
||||
{
|
||||
$params = array(
|
||||
'sql_hostname',
|
||||
'sql_username',
|
||||
'sql_password',
|
||||
'sql_database',
|
||||
'sql_table',
|
||||
'sql_driver',
|
||||
'col_username',
|
||||
'col_password',
|
||||
'col_active',
|
||||
'col_displayname',
|
||||
'col_email',
|
||||
'col_gethome',
|
||||
'set_active_invert',
|
||||
'set_allow_pwchange',
|
||||
'set_default_domain',
|
||||
'set_strip_domain',
|
||||
'set_crypt_type',
|
||||
'set_mail_sync_mode',
|
||||
'set_enable_gethome',
|
||||
'set_gethome_mode',
|
||||
'set_gethome',
|
||||
'sql_group_table',
|
||||
'col_group_username',
|
||||
'col_group_name'
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings for a given domain. If the domain is not found,
|
||||
* the settings for 'default' are returned instead.
|
||||
* @param string $domain The domain name
|
||||
* @return array of settings
|
||||
*/
|
||||
public function loadSettingsForDomain($domain)
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL', "Trying to load settings for domain: " . $domain, Util::DEBUG);
|
||||
$settings = array();
|
||||
$sql_host = \OC::$server->getConfig()->getAppValue('user_sql', 'sql_hostname_' . $domain, '');
|
||||
if ($sql_host === '') {
|
||||
$domain = 'default';
|
||||
}
|
||||
$params = $this->getParameterArray();
|
||||
foreach ($params as $param) {
|
||||
$settings[$param] = \OC::$server->getConfig()->getAppValue('user_sql', $param . '_' . $domain, '');
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL', "Loaded settings for domain: " . $domain, Util::DEBUG);
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a given query type and return the results
|
||||
* @param string $type The type of query to run
|
||||
* @param array $params The parameter array of the query (i.e. the values to bind as key-value pairs)
|
||||
* @param bool $execOnly Only execute the query, but don't fetch the results (optional, default = false)
|
||||
* @param bool $fetchArray Fetch an array instead of a single row (optional, default=false)
|
||||
* @param array $limits use the given limits for the query (optional, default = empty)
|
||||
* @return mixed
|
||||
*/
|
||||
public function runQuery($type, $params, $execOnly = false, $fetchArray = false, $limits = array())
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL', "Entering runQuery for type: " . $type, Util::DEBUG);
|
||||
if (!$this->db_conn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'getHome':
|
||||
$query = "SELECT " . $this->settings['col_gethome'] . " FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
break;
|
||||
case 'getMail':
|
||||
$query = "SELECT " . $this->settings['col_email'] . " FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
break;
|
||||
|
||||
case 'setMail':
|
||||
$query = "UPDATE " . $this->settings['sql_table'] . " SET " . $this->settings['col_email'] . " = :currMail WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
break;
|
||||
|
||||
case 'getPass':
|
||||
$query = "SELECT " . $this->settings['col_password'] . " FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
if ($this->settings['col_active'] !== '') {
|
||||
$query .= " AND " . ($this->settings['set_active_invert'] === 'true' ? "NOT " : "") . $this->settings['col_active'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'setPass':
|
||||
$query = "UPDATE " . $this->settings['sql_table'] . " SET " . $this->settings['col_password'] . " = :enc_password WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
break;
|
||||
|
||||
case 'getRedmineSalt':
|
||||
$query = "SELECT salt FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " = :uid;";
|
||||
break;
|
||||
|
||||
case 'countUsers':
|
||||
$query = "SELECT COUNT(*) FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " LIKE :search";
|
||||
if ($this->settings['col_active'] !== '') {
|
||||
$query .= " AND " . ($this->settings['set_active_invert'] === 'true' ? "NOT " : "") . $this->settings['col_active'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'getUsers':
|
||||
$query = "SELECT " . $this->settings['col_username'] . " FROM " . $this->settings['sql_table'];
|
||||
$query .= " WHERE " . $this->settings['col_username'] . " LIKE :search";
|
||||
if ($this->settings['col_active'] !== '') {
|
||||
$query .= " AND " . ($this->settings['set_active_invert'] === 'true' ? "NOT " : "") . $this->settings['col_active'];
|
||||
}
|
||||
$query .= " ORDER BY " . $this->settings['col_username'];
|
||||
break;
|
||||
|
||||
case 'userExists':
|
||||
$query = "SELECT " . $this->settings['col_username'] . " FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
if ($this->settings['col_active'] !== '') {
|
||||
$query .= " AND " . ($this->settings['set_active_invert'] === 'true' ? "NOT " : "") . $this->settings['col_active'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'getDisplayName':
|
||||
$query = "SELECT " . $this->settings['col_displayname'] . " FROM " . $this->settings['sql_table'] . " WHERE " . $this->settings['col_username'] . " = :uid";
|
||||
if ($this->settings['col_active'] !== '') {
|
||||
$query .= " AND " . ($this->settings['set_active_invert'] === 'true' ? "NOT " : "") . $this->settings['col_active'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mysqlEncryptSalt':
|
||||
$query = "SELECT ENCRYPT(:pw, :salt);";
|
||||
break;
|
||||
|
||||
case 'mysqlEncrypt':
|
||||
$query = "SELECT ENCRYPT(:pw);";
|
||||
break;
|
||||
|
||||
case 'mysqlPassword':
|
||||
$query = "SELECT PASSWORD(:pw);";
|
||||
break;
|
||||
|
||||
case 'getUserGroups':
|
||||
$query = "SELECT " . $this->settings['col_group_name'] . " FROM " . $this->settings['sql_group_table'] . " WHERE " . $this->settings['col_group_username'] . " = :uid";
|
||||
break;
|
||||
|
||||
case 'getGroups':
|
||||
$query = "SELECT distinct " . $this->settings['col_group_name'] . " FROM " . $this->settings['sql_group_table'] . " WHERE " . $this->settings['col_group_name'] . " LIKE :search";
|
||||
break;
|
||||
|
||||
case 'getGroupUsers':
|
||||
$query = "SELECT distinct " . $this->settings['col_group_username'] . " FROM " . $this->settings['sql_group_table'] . " WHERE " . $this->settings['col_group_name'] . " = :gid";
|
||||
break;
|
||||
|
||||
case 'countUsersInGroup':
|
||||
$query = "SELECT count(" . $this->settings['col_group_username'] . ") FROM " . $this->settings['sql_group_table'] . " WHERE " . $this->settings['col_group_name'] . " = :gid AND " . $this->settings['col_group_username'] . " LIKE :search";
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($limits['limit']) && $limits['limit'] !== null) {
|
||||
$limit = intval($limits['limit']);
|
||||
$query .= " LIMIT " . $limit;
|
||||
}
|
||||
|
||||
if (isset($limits['offset']) && $limits['offset'] !== null) {
|
||||
$offset = intval($limits['offset']);
|
||||
$query .= " OFFSET " . $offset;
|
||||
}
|
||||
|
||||
Util::writeLog('OC_USER_SQL', "Preparing query: $query", Util::DEBUG);
|
||||
$result = $this->db->prepare($query);
|
||||
foreach ($params as $param => $value) {
|
||||
$result->bindValue(":" . $param, $value);
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL', "Executing query...", Util::DEBUG);
|
||||
if (!$result->execute()) {
|
||||
$err = $result->errorInfo();
|
||||
Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
if ($execOnly === true) {
|
||||
return true;
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL', "Fetching result...", Util::DEBUG);
|
||||
if ($fetchArray === true) {
|
||||
$row = $result->fetchAll();
|
||||
} else {
|
||||
$row = $result->fetch();
|
||||
}
|
||||
|
||||
if (!$row) {
|
||||
return false;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the database using Nextcloud's DBAL
|
||||
* @param array $settings The settings for the connection
|
||||
* @return bool
|
||||
*/
|
||||
public function connectToDb($settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$cm = new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig());
|
||||
$parameters = array(
|
||||
'host' => $this->settings['sql_hostname'],
|
||||
'password' => $this->settings['sql_password'],
|
||||
'user' => $this->settings['sql_username'],
|
||||
'dbname' => $this->settings['sql_database'],
|
||||
'tablePrefix' => ''
|
||||
);
|
||||
try {
|
||||
$this->db = $cm->getConnection($this->settings['sql_driver'], $parameters);
|
||||
$this->db->query("SET NAMES 'UTF8'");
|
||||
$this->db_conn = true;
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Util::writeLog('OC_USER_SQL', 'Failed to connect to the database: ' . $e->getMessage(), Util::ERROR);
|
||||
$this->db_conn = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all of the given columns exist
|
||||
* @param array $parameters The connection parameters
|
||||
* @param string $sql_driver The SQL driver to use
|
||||
* @param string $table The table name to check
|
||||
* @param array $cols The columns to check
|
||||
* @param array True if found, otherwise false
|
||||
* @return bool|string
|
||||
*/
|
||||
public function verifyColumns($parameters, $sql_driver, $table, $cols)
|
||||
{
|
||||
$columns = $this->getColumns($parameters, $sql_driver, $table);
|
||||
$res = true;
|
||||
$err = '';
|
||||
foreach ($cols as $col) {
|
||||
if (!in_array($col, $columns, true)) {
|
||||
$res = false;
|
||||
$err .= $col . ' ';
|
||||
}
|
||||
}
|
||||
if ($res) {
|
||||
return true;
|
||||
} else {
|
||||
return $err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given table exists
|
||||
* @param array $parameters The connection parameters
|
||||
* @param string $sql_driver The SQL driver to use
|
||||
* @param string $table The table name to check
|
||||
* @param array True if found, otherwise false
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyTable($parameters, $sql_driver, $table)
|
||||
{
|
||||
$tablesWithSchema = $this->getTables($parameters, $sql_driver, true);
|
||||
$tablesWithoutSchema = $this->getTables($parameters, $sql_driver, false);
|
||||
return in_array($table, $tablesWithSchema, true) || in_array($table, $tablesWithoutSchema, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of tables for the given connection parameters
|
||||
* @param array $parameters The connection parameters
|
||||
* @param string $sql_driver The SQL driver to use
|
||||
* @param boolean $schema Return table name with schema
|
||||
* @return array The found tables, empty if an error occurred
|
||||
*/
|
||||
public function getTables($parameters, $sql_driver, $schema = true)
|
||||
{
|
||||
$cm = new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig());
|
||||
try {
|
||||
$conn = $cm->getConnection($sql_driver, $parameters);
|
||||
$platform = $conn->getDatabasePlatform();
|
||||
|
||||
$queryTables = $platform->getListTablesSQL();
|
||||
$queryViews = $platform->getListViewsSQL($parameters['dbname']);
|
||||
$ret = array();
|
||||
|
||||
$result = $conn->executeQuery($queryTables);
|
||||
while ($row = $result->fetch()) {
|
||||
$name = $this->getTableNameFromRow($sql_driver, $parameters['dbname'], $row, $schema);
|
||||
$ret[] = $name;
|
||||
}
|
||||
|
||||
$result = $conn->executeQuery($queryViews);
|
||||
while ($row = $result->fetch()) {
|
||||
$name = $this->getViewNameFromRow($sql_driver, $row, $schema);
|
||||
$ret[] = $name;
|
||||
}
|
||||
return $ret;
|
||||
} catch (\Exception $e) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve table name from database list table SQL
|
||||
* @param string $sql_driver The SQL driver to use
|
||||
* @param string $dbname The database name
|
||||
* @param array $row Query result row
|
||||
* @param boolean $schema Return table name with schema
|
||||
* @return string Table name
|
||||
*/
|
||||
public function getTableNameFromRow($sql_driver, $dbname, $row, $schema)
|
||||
{
|
||||
switch ($sql_driver) {
|
||||
case 'mysql':
|
||||
return $row['Tables_in_' . $dbname];
|
||||
case 'pgsql':
|
||||
if ($schema) {
|
||||
return $row['schema_name'] . '.' . $row['table_name'];
|
||||
} else {
|
||||
return $row['table_name'];
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve view name from database list table SQL
|
||||
* @param string $sql_driver The SQL driver to use
|
||||
* @param array $row Query result row
|
||||
* @param boolean $schema Return table name with schema
|
||||
* @return string Table name
|
||||
*/
|
||||
public function getViewNameFromRow($sql_driver, $row, $schema)
|
||||
{
|
||||
switch ($sql_driver) {
|
||||
case 'mysql':
|
||||
return $row['TABLE_NAME'];
|
||||
case 'pgsql':
|
||||
if ($schema) {
|
||||
return $row['schemaname'] . '.' . $row['viewname'];
|
||||
} else {
|
||||
return $row['viewname'];
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of columns for the given connection parameters
|
||||
* @param array $parameters The connection parameters
|
||||
* @param string $sql_driver The SQL driver to use
|
||||
* @param string $table The SQL table to work with
|
||||
* @return array The found column, empty if an error occured
|
||||
*/
|
||||
public function getColumns($parameters, $sql_driver, $table)
|
||||
{
|
||||
$cm = new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig());
|
||||
try {
|
||||
$conn = $cm->getConnection($sql_driver, $parameters);
|
||||
$platform = $conn->getDatabasePlatform();
|
||||
$query = $platform->getListTableColumnsSQL($table);
|
||||
$result = $conn->executeQuery($query);
|
||||
$ret = array();
|
||||
while ($row = $result->fetch()) {
|
||||
switch ($sql_driver) {
|
||||
case 'mysql':
|
||||
$name = $row['Field'];
|
||||
break;
|
||||
case 'pgsql':
|
||||
$name = $row['field'];
|
||||
break;
|
||||
default:
|
||||
return $ret;
|
||||
}
|
||||
$ret[] = $name;
|
||||
}
|
||||
return $ret;
|
||||
} catch (\Exception $e) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
||||
981
lib/user_sql.php
981
lib/user_sql.php
@@ -1,981 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @author Andreas Böhler and contributors
|
||||
* @copyright 2012-2015 Andreas Böhler <dev (at) aboehler (dot) at>
|
||||
*
|
||||
* credits go to Ed W for several SQL injection fixes and caching support
|
||||
* credits go to Frédéric France for providing Joomla support
|
||||
* credits go to Mark Jansenn for providing Joomla 2.5.18+ / 3.2.1+ support
|
||||
* credits go to Dominik Grothaus for providing SSHA256 support and fixing a few bugs
|
||||
* credits go to Sören Eberhardt-Biermann for providing multi-host support
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\user_sql;
|
||||
|
||||
use OC\User\Backend;
|
||||
use OCA\user_sql\HashAlgorithm\HashAlgorithm;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Util;
|
||||
|
||||
abstract class BackendUtility
|
||||
{
|
||||
protected $access;
|
||||
|
||||
/**
|
||||
* constructor, make sure the subclasses call this one!
|
||||
* @param Access $access an instance of Access for LDAP interaction
|
||||
*/
|
||||
public function __construct(Access $access)
|
||||
{
|
||||
$this->access = $access;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class OC_USER_SQL extends BackendUtility implements \OCP\IUserBackend,
|
||||
\OCP\UserInterface
|
||||
{
|
||||
protected $cache;
|
||||
protected $settings;
|
||||
protected $helper;
|
||||
protected $session_cache_name;
|
||||
protected $ocConfig;
|
||||
|
||||
/**
|
||||
* The default constructor. It loads the settings for the given domain
|
||||
* and tries to connect to the database.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$memcache = \OC::$server->getMemCacheFactory();
|
||||
if ($memcache->isAvailable()) {
|
||||
$this->cache = $memcache->create();
|
||||
}
|
||||
$this->helper = new \OCA\user_sql\lib\Helper();
|
||||
$domain = \OC::$server->getRequest()->getServerHost();
|
||||
$this->settings = $this->helper->loadSettingsForDomain($domain);
|
||||
$this->ocConfig = \OC::$server->getConfig();
|
||||
$this->helper->connectToDb($this->settings);
|
||||
$this->session_cache_name = 'USER_SQL_CACHE';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync the user's E-Mail address with the address stored by Nextcloud.
|
||||
* We have three (four) sync modes:
|
||||
* - none: Does nothing
|
||||
* - initial: Do the sync only once from SQL -> Nextcloud
|
||||
* - forcesql: The SQL database always wins and sync to Nextcloud
|
||||
* - forceoc: Nextcloud always wins and syncs to SQL
|
||||
*
|
||||
* @param string $uid The user's ID to sync
|
||||
* @return bool Success or Fail
|
||||
*/
|
||||
private function doEmailSync($uid)
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL', "Entering doEmailSync for UID: $uid",
|
||||
Util::DEBUG);
|
||||
if ($this->settings['col_email'] === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->settings['set_mail_sync_mode'] === 'none') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ocUid = $uid;
|
||||
$uid = $this->doUserDomainMapping($uid);
|
||||
|
||||
$row = $this->helper->runQuery('getMail', array('uid' => $uid));
|
||||
if ($row === false) {
|
||||
return false;
|
||||
}
|
||||
$newMail = $row[$this->settings['col_email']];
|
||||
|
||||
$currMail = $this->ocConfig->getUserValue($ocUid,
|
||||
'settings',
|
||||
'email', '');
|
||||
|
||||
switch ($this->settings['set_mail_sync_mode']) {
|
||||
case 'initial':
|
||||
if ($currMail === '') {
|
||||
$this->ocConfig->setUserValue($ocUid,
|
||||
'settings',
|
||||
'email',
|
||||
$newMail);
|
||||
}
|
||||
break;
|
||||
case 'forcesql':
|
||||
//if($currMail !== $newMail)
|
||||
$this->ocConfig->setUserValue($ocUid,
|
||||
'settings',
|
||||
'email',
|
||||
$newMail);
|
||||
break;
|
||||
case 'forceoc':
|
||||
if (($currMail !== '') && ($currMail !== $newMail)) {
|
||||
$row = $this->helper->runQuery('setMail',
|
||||
array(
|
||||
'uid' => $uid,
|
||||
'currMail' => $currMail
|
||||
)
|
||||
, true);
|
||||
|
||||
if ($row === false) {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Could not update E-Mail address in SQL database!",
|
||||
Util::ERROR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This maps the username to the specified domain name.
|
||||
* It can only append a default domain name.
|
||||
*
|
||||
* @param string $uid The UID to work with
|
||||
* @return string The mapped UID
|
||||
*/
|
||||
private function doUserDomainMapping($uid)
|
||||
{
|
||||
$uid = trim($uid);
|
||||
|
||||
if ($this->settings['set_default_domain'] !== '') {
|
||||
Util::writeLog('OC_USER_SQL', "Append default domain: " .
|
||||
$this->settings['set_default_domain'], Util::DEBUG);
|
||||
if (strpos($uid, '@') === false) {
|
||||
$uid .= "@" . $this->settings['set_default_domain'];
|
||||
}
|
||||
}
|
||||
|
||||
$uid = strtolower($uid);
|
||||
Util::writeLog('OC_USER_SQL', 'Returning mapped UID: ' . $uid,
|
||||
Util::DEBUG);
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actions implemented by this backend
|
||||
* @param $actions
|
||||
* @return bool
|
||||
*/
|
||||
public function implementsActions($actions)
|
||||
{
|
||||
return (bool)((Backend::CHECK_PASSWORD
|
||||
| Backend::GET_DISPLAYNAME
|
||||
| Backend::COUNT_USERS
|
||||
| ($this->settings['set_allow_pwchange'] === 'true' ?
|
||||
Backend::SET_PASSWORD : 0)
|
||||
| ($this->settings['set_enable_gethome'] === 'true' ?
|
||||
Backend::GET_HOME : 0)
|
||||
) & $actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this backend has user listing support
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUserListings()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user's home directory, if enabled
|
||||
* @param string $uid The user's ID to retrieve
|
||||
* @return mixed The user's home directory or false
|
||||
*/
|
||||
public function getHome($uid)
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL', "Entering getHome for UID: $uid",
|
||||
Util::DEBUG);
|
||||
|
||||
if ($this->settings['set_enable_gethome'] !== 'true') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$uidMapped = $this->doUserDomainMapping($uid);
|
||||
$home = false;
|
||||
|
||||
switch ($this->settings['set_gethome_mode']) {
|
||||
case 'query':
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"getHome with Query selected, running Query...",
|
||||
Util::DEBUG);
|
||||
$row = $this->helper->runQuery('getHome',
|
||||
array('uid' => $uidMapped));
|
||||
if ($row === false) {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Got no row, return false",
|
||||
Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$home = $row[$this->settings['col_gethome']];
|
||||
break;
|
||||
|
||||
case 'static':
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"getHome with static selected",
|
||||
Util::DEBUG);
|
||||
$home = $this->settings['set_gethome'];
|
||||
$home = str_replace('%ud', $uidMapped, $home);
|
||||
$home = str_replace('%u', $uid, $home);
|
||||
$home = str_replace('%d',
|
||||
$this->settings['set_default_domain'],
|
||||
$home);
|
||||
break;
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Returning getHome for UID: $uid with Home $home",
|
||||
Util::DEBUG);
|
||||
return $home;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user account using this backend
|
||||
* @return bool always false, as we can't create users
|
||||
*/
|
||||
public function createUser()
|
||||
{
|
||||
// Can't create user
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
'Not possible to create local users from web' .
|
||||
' frontend using SQL user backend', Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user account using this backend
|
||||
* @param string $uid The user's ID to delete
|
||||
* @return bool always false, as we can't delete users
|
||||
*/
|
||||
public function deleteUser($uid)
|
||||
{
|
||||
// Can't delete user
|
||||
Util::writeLog('OC_USER_SQL', 'Not possible to delete local users' .
|
||||
' from web frontend using SQL user backend', Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HashAlgorithm|bool
|
||||
*/
|
||||
private function getHashAlgorithmInstance() {
|
||||
$cryptoType = $this->settings['set_crypt_type'];
|
||||
require_once('HashAlgorithm/'. $cryptoType . '.php');
|
||||
return call_user_func('OCA\\user_sql\\HashAlgorithm\\' . $cryptoType . "::getInstance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set (change) a user password
|
||||
* This can be enabled/disabled in the settings (set_allow_pwchange)
|
||||
*
|
||||
* @param string $uid The user ID
|
||||
* @param string $password The user's new password
|
||||
* @return bool The return status
|
||||
*/
|
||||
public function setPassword($uid, $password)
|
||||
{
|
||||
// Update the user's password - this might affect other services, that
|
||||
// use the same database, as well
|
||||
Util::writeLog('OC_USER_SQL', "Entering setPassword for UID: $uid",
|
||||
Util::DEBUG);
|
||||
|
||||
if ($this->settings['set_allow_pwchange'] !== 'true') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$uid = $this->doUserDomainMapping($uid);
|
||||
|
||||
$row = $this->helper->runQuery('getPass', array('uid' => $uid));
|
||||
if ($row === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hashAlgorithm = $this->getHashAlgorithmInstance();
|
||||
|
||||
if ($hashAlgorithm === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$enc_password = $hashAlgorithm->getPasswordHash($password);
|
||||
|
||||
if ($enc_password === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*$old_password = $row[$this->settings['col_password']];
|
||||
|
||||
// Added and disabled updating passwords for Drupal 7 WD 2018-01-04
|
||||
if ($this->settings['set_crypt_type'] === 'drupal') {
|
||||
return false;
|
||||
} elseif ($this->settings['set_crypt_type'] === 'joomla2') {
|
||||
if (!class_exists('\PasswordHash')) {
|
||||
require_once('PasswordHash.php');
|
||||
}
|
||||
$hasher = new \PasswordHash(10, true);
|
||||
$enc_password = $hasher->HashPassword($password);
|
||||
}
|
||||
// Redmine stores the salt separatedly, this doesn't play nice with
|
||||
// the way we check passwords
|
||||
elseif ($this->settings['set_crypt_type'] === 'redmine') {
|
||||
$salt = $this->helper->runQuery('getRedmineSalt',
|
||||
array('uid' => $uid));
|
||||
if (!$salt) {
|
||||
return false;
|
||||
}
|
||||
$enc_password = sha1($salt['salt'] . sha1($password));
|
||||
|
||||
} elseif ($this->settings['set_crypt_type'] === 'sha1') {
|
||||
$enc_password = sha1($password);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'system') {
|
||||
$prefix = substr($old_password, 0, 2);
|
||||
if ($prefix === "$2") {
|
||||
$enc_password = $this->pw_hash($password);
|
||||
} else {
|
||||
if (($prefix === "$1") or ($prefix[0] != "$")) //old md5 or DES
|
||||
{
|
||||
//Update encryption algorithm
|
||||
$prefix = "$6"; //change to sha512
|
||||
}
|
||||
|
||||
$newsalt = $this->create_systemsalt();
|
||||
$enc_password = crypt($password, $prefix . "$" . $newsalt);
|
||||
}
|
||||
|
||||
} elseif ($this->settings['set_crypt_type'] === 'password_hash') {
|
||||
$enc_password = $this->pw_hash($password);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_md5') {
|
||||
$enc_password = '{MD5}' . OC_USER_SQL::hex_to_base64(md5($password));
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_md5raw') {
|
||||
$enc_password = '{MD5RAW}' . md5($password);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_sha1') {
|
||||
$enc_password = '{SHA}' . OC_USER_SQL::hex_to_base64(sha1($password));
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_sha256') {
|
||||
$enc_password = '{SHA256}' . OC_USER_SQL::hex_to_base64(hash('sha256', $password, false));
|
||||
} else {
|
||||
$enc_password = $this->pacrypt($password, $old_password);
|
||||
}*/
|
||||
|
||||
$res = $this->helper->runQuery('setPass',
|
||||
array('uid' => $uid, 'enc_password' => $enc_password),
|
||||
true);
|
||||
if ($res === false) {
|
||||
Util::writeLog('OC_USER_SQL', "Could not update password!",
|
||||
Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Updated password successfully, return true",
|
||||
Util::DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the password is correct
|
||||
* @param string $uid The username
|
||||
* @param string $password The password
|
||||
* @return bool true/false
|
||||
*
|
||||
* Check if the password is correct without logging in the user
|
||||
*/
|
||||
public function checkPassword($uid, $password)
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Entering checkPassword() for UID: $uid",
|
||||
Util::DEBUG);
|
||||
|
||||
$uid = $this->doUserDomainMapping($uid);
|
||||
|
||||
$row = $this->helper->runQuery('getPass', array('uid' => $uid));
|
||||
if ($row === false) {
|
||||
Util::writeLog('OC_USER_SQL', "Got no row, return false", Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$db_pass = $row[$this->settings['col_password']];
|
||||
|
||||
Util::writeLog('OC_USER_SQL', "Encrypting and checking password",
|
||||
Util::DEBUG);
|
||||
|
||||
$hashAlgorithm = $this->getHashAlgorithmInstance();
|
||||
|
||||
if ($hashAlgorithm === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ret = $hashAlgorithm->checkPassword($password, $db_pass);
|
||||
|
||||
/*// Added handling for Drupal 7 passwords WD 2018-01-04
|
||||
if ($this->settings['set_crypt_type'] === 'drupal') {
|
||||
if (!function_exists('user_check_password')) {
|
||||
require_once('drupal.php');
|
||||
}
|
||||
$ret = user_check_password($password, $db_pass);
|
||||
}
|
||||
// Joomla 2.5.18 switched to phPass, which doesn't play nice with the
|
||||
// way we check passwords
|
||||
elseif ($this->settings['set_crypt_type'] === 'joomla2') {
|
||||
if (!class_exists('\PasswordHash')) {
|
||||
require_once('PasswordHash.php');
|
||||
}
|
||||
$hasher = new \PasswordHash(10, true);
|
||||
$ret = $hasher->CheckPassword($password, $db_pass);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'password_hash') {
|
||||
$ret = password_verify($password, $db_pass);
|
||||
}
|
||||
// Redmine stores the salt separatedly, this doesn't play nice with the
|
||||
// way we check passwords
|
||||
elseif ($this->settings['set_crypt_type'] === 'redmine') {
|
||||
$salt = $this->helper->runQuery('getRedmineSalt',
|
||||
array('uid' => $uid));
|
||||
if (!$salt) {
|
||||
return false;
|
||||
}
|
||||
$ret = sha1($salt['salt'] . sha1($password)) === $db_pass;
|
||||
} elseif ($this->settings['set_crypt_type'] == 'sha1') {
|
||||
$ret = $this->hash_equals(sha1($password), $db_pass);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_md5') {
|
||||
$ret = '{MD5}' . OC_USER_SQL::hex_to_base64(md5($password)) === $db_pass;
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_md5raw') {
|
||||
$ret = '{MD5RAW}' . md5($password) === $db_pass;
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_sha1') {
|
||||
$ret = '{SHA}' . OC_USER_SQL::hex_to_base64(sha1($password)) === $db_pass;
|
||||
} elseif ($this->settings['set_crypt_type'] === 'courier_sha256') {
|
||||
$ret = '{SHA256}' . OC_USER_SQL::hex_to_base64(hash('sha256', $password, false)) === $db_pass;
|
||||
} else {
|
||||
// $ret = $this -> pacrypt($password, $db_pass) === $db_pass;
|
||||
$ret = $this->hash_equals($this->pacrypt($password, $db_pass),
|
||||
$db_pass);
|
||||
}*/
|
||||
|
||||
if ($ret) {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Passwords matching, return true",
|
||||
Util::DEBUG);
|
||||
if ($this->settings['set_strip_domain'] === 'true') {
|
||||
$uid = explode("@", $uid);
|
||||
$uid = $uid[0];
|
||||
}
|
||||
return $uid;
|
||||
} else {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Passwords do not match, return false",
|
||||
Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of users
|
||||
* @return int The user count
|
||||
*/
|
||||
public function countUsers()
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL', "Entering countUsers()",
|
||||
Util::DEBUG);
|
||||
|
||||
$search = "%" . $this->doUserDomainMapping("");
|
||||
$userCount = $this->helper->runQuery('countUsers',
|
||||
array('search' => $search));
|
||||
if ($userCount === false) {
|
||||
$userCount = 0;
|
||||
} else {
|
||||
$userCount = reset($userCount);
|
||||
}
|
||||
|
||||
Util::writeLog('OC_USER_SQL', "Return usercount: " . $userCount,
|
||||
Util::DEBUG);
|
||||
return $userCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all users
|
||||
* @param string $search The search term (can be empty)
|
||||
* @param int $limit The search limit (can be null)
|
||||
* @param int $offset The search offset (can be null)
|
||||
* @return array with all uids
|
||||
*/
|
||||
public function getUsers($search = '', $limit = null, $offset = null)
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Entering getUsers() with Search: $search, " .
|
||||
"Limit: $limit, Offset: $offset", Util::DEBUG);
|
||||
$users = array();
|
||||
|
||||
if ($search !== '') {
|
||||
$search = "%" . $this->doUserDomainMapping($search . "%") . "%";
|
||||
} else {
|
||||
$search = "%" . $this->doUserDomainMapping("") . "%";
|
||||
}
|
||||
|
||||
$rows = $this->helper->runQuery('getUsers',
|
||||
array('search' => $search),
|
||||
false,
|
||||
true,
|
||||
array(
|
||||
'limit' => $limit,
|
||||
'offset' => $offset
|
||||
));
|
||||
if ($rows === false) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$uid = $row[$this->settings['col_username']];
|
||||
if ($this->settings['set_strip_domain'] === 'true') {
|
||||
$uid = explode("@", $uid);
|
||||
$uid = $uid[0];
|
||||
}
|
||||
$users[] = strtolower($uid);
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL', "Return list of results",
|
||||
Util::DEBUG);
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user exists
|
||||
* @param string $uid the username
|
||||
* @return boolean
|
||||
*/
|
||||
public function userExists($uid)
|
||||
{
|
||||
|
||||
$cacheKey = 'sql_user_exists_' . $uid;
|
||||
$cacheVal = $this->getCache($cacheKey);
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"userExists() for UID: $uid cacheVal: $cacheVal",
|
||||
Util::DEBUG);
|
||||
if (!is_null($cacheVal)) {
|
||||
return (bool)$cacheVal;
|
||||
}
|
||||
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Entering userExists() for UID: $uid",
|
||||
Util::DEBUG);
|
||||
|
||||
// Only if the domain is removed for internal user handling,
|
||||
// we should add the domain back when checking existance
|
||||
if ($this->settings['set_strip_domain'] === 'true') {
|
||||
$uid = $this->doUserDomainMapping($uid);
|
||||
}
|
||||
|
||||
$exists = (bool)$this->helper->runQuery('userExists',
|
||||
array('uid' => $uid));;
|
||||
$this->setCache($cacheKey, $exists, 60);
|
||||
|
||||
if (!$exists) {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Empty row, user does not exists, return false",
|
||||
Util::DEBUG);
|
||||
return false;
|
||||
} else {
|
||||
Util::writeLog('OC_USER_SQL', "User exists, return true",
|
||||
Util::DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name of the user
|
||||
* @param string $uid The user ID
|
||||
* @return mixed The user's display name or FALSE
|
||||
*/
|
||||
public function getDisplayName($uid)
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Entering getDisplayName() for UID: $uid",
|
||||
Util::DEBUG);
|
||||
|
||||
$this->doEmailSync($uid);
|
||||
$uid = $this->doUserDomainMapping($uid);
|
||||
|
||||
if (!$this->userExists($uid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = $this->helper->runQuery('getDisplayName',
|
||||
array('uid' => $uid));
|
||||
|
||||
if (!$row) {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"Empty row, user has no display name or " .
|
||||
"does not exist, return false",
|
||||
Util::DEBUG);
|
||||
return false;
|
||||
} else {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"User exists, return true",
|
||||
Util::DEBUG);
|
||||
$displayName = $row[$this->settings['col_displayname']];
|
||||
return $displayName;;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDisplayNames($search = '', $limit = null, $offset = null)
|
||||
{
|
||||
$uids = $this->getUsers($search, $limit, $offset);
|
||||
$displayNames = array();
|
||||
foreach ($uids as $uid) {
|
||||
$displayNames[$uid] = $this->getDisplayName($uid);
|
||||
}
|
||||
return $displayNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the backend name
|
||||
* @return string
|
||||
*/
|
||||
public function getBackendName()
|
||||
{
|
||||
return 'user_sql';
|
||||
}
|
||||
|
||||
/**
|
||||
* The following functions were directly taken from PostfixAdmin and just
|
||||
* slightly modified
|
||||
* to suit our needs.
|
||||
* Encrypt a password,using the apparopriate hashing mechanism as defined in
|
||||
* config.inc.php ($this->crypt_type).
|
||||
* When wanting to compare one pw to another, it's necessary to provide the
|
||||
* salt used - hence
|
||||
* the second parameter ($pw_db), which is the existing hash from the DB.
|
||||
*
|
||||
* @param string $pw cleartext password
|
||||
* @param string $pw_db encrypted password from database
|
||||
* @return string encrypted password.
|
||||
*/
|
||||
/*private function pacrypt($pw, $pw_db = "")
|
||||
{
|
||||
Util::writeLog('OC_USER_SQL', "Entering private pacrypt()",
|
||||
Util::DEBUG);
|
||||
$pw = stripslashes($pw);
|
||||
$password = "";
|
||||
$salt = "";
|
||||
|
||||
if ($this->settings['set_crypt_type'] === 'md5crypt') {
|
||||
$split_salt = preg_split('/\$/', $pw_db);
|
||||
if (isset($split_salt[2])) {
|
||||
$salt = $split_salt[2];
|
||||
}
|
||||
$password = $this->md5crypt($pw, $salt);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'md5') {
|
||||
$password = md5($pw);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'system') {
|
||||
// We never generate salts, as user creation is not allowed here
|
||||
$password = crypt($pw, $pw_db);
|
||||
} elseif ($this->settings['set_crypt_type'] === 'cleartext') {
|
||||
$password = $pw;
|
||||
}
|
||||
|
||||
// See
|
||||
// https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583
|
||||
// this is apparently useful for pam_mysql etc.
|
||||
elseif ($this->settings['set_crypt_type'] === 'mysql_encrypt') {
|
||||
if ($pw_db !== "") {
|
||||
$salt = substr($pw_db, 0, 2);
|
||||
|
||||
$row = $this->helper->runQuery('mysqlEncryptSalt',
|
||||
array('pw' => $pw, 'salt' => $salt));
|
||||
} else {
|
||||
$row = $this->helper->runQuery('mysqlEncrypt',
|
||||
array('pw' => $pw));
|
||||
}
|
||||
|
||||
if ($row === false) {
|
||||
return false;
|
||||
}
|
||||
$password = $row[0];
|
||||
} elseif ($this->settings['set_crypt_type'] === 'mysql_password') {
|
||||
$row = $this->helper->runQuery('mysqlPassword',
|
||||
array('pw' => $pw));
|
||||
|
||||
if ($row === false) {
|
||||
return false;
|
||||
}
|
||||
$password = $row[0];
|
||||
} // The following is by Frédéric France
|
||||
elseif ($this->settings['set_crypt_type'] === 'joomla') {
|
||||
$split_salt = preg_split('/:/', $pw_db);
|
||||
if (isset($split_salt[1])) {
|
||||
$salt = $split_salt[1];
|
||||
}
|
||||
$password = ($salt) ? md5($pw . $salt) : md5($pw);
|
||||
$password .= ':' . $salt;
|
||||
} elseif ($this->settings['set_crypt_type'] === 'ssha256') {
|
||||
$salted_password = base64_decode(
|
||||
preg_replace('/{SSHA256}/i', '', $pw_db));
|
||||
$salt = substr($salted_password, -(strlen($salted_password) - 32));
|
||||
$password = $this->ssha256($pw, $salt);
|
||||
} else {
|
||||
Util::writeLog('OC_USER_SQL',
|
||||
"unknown/invalid crypt_type settings: " .
|
||||
$this->settings['set_crypt_type'],
|
||||
Util::ERROR);
|
||||
die('unknown/invalid Encryption type setting: ' .
|
||||
$this->settings['set_crypt_type']);
|
||||
}
|
||||
Util::writeLog('OC_USER_SQL', "pacrypt() done, return",
|
||||
Util::DEBUG);
|
||||
return $password;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* md5crypt
|
||||
* Creates MD5 encrypted password
|
||||
* @param string $pw The password to encrypt
|
||||
* @param string $salt The salt to use
|
||||
* @param string $magic ?
|
||||
* @return string The encrypted password
|
||||
*/
|
||||
|
||||
/*private function md5crypt($pw, $salt = "", $magic = "")
|
||||
{
|
||||
$MAGIC = "$1$";
|
||||
|
||||
if ($magic === "") {
|
||||
$magic = $MAGIC;
|
||||
}
|
||||
if ($salt === "") {
|
||||
$salt = $this->create_md5salt();
|
||||
}
|
||||
$slist = explode("$", $salt);
|
||||
if ($slist[0] === "1") {
|
||||
$salt = $slist[1];
|
||||
}
|
||||
|
||||
$salt = substr($salt, 0, 8);
|
||||
$ctx = $pw . $magic . $salt;
|
||||
$final = $this->pahex2bin(md5($pw . $salt . $pw));
|
||||
|
||||
for ($i = strlen($pw); $i > 0; $i -= 16) {
|
||||
if ($i > 16) {
|
||||
$ctx .= substr($final, 0, 16);
|
||||
} else {
|
||||
$ctx .= substr($final, 0, $i);
|
||||
}
|
||||
}
|
||||
$i = strlen($pw);
|
||||
|
||||
while ($i > 0) {
|
||||
if ($i & 1) {
|
||||
$ctx .= chr(0);
|
||||
} else {
|
||||
$ctx .= $pw[0];
|
||||
}
|
||||
$i = $i >> 1;
|
||||
}
|
||||
$final = $this->pahex2bin(md5($ctx));
|
||||
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$ctx1 = "";
|
||||
if ($i & 1) {
|
||||
$ctx1 .= $pw;
|
||||
} else {
|
||||
$ctx1 .= substr($final, 0, 16);
|
||||
}
|
||||
if ($i % 3) {
|
||||
$ctx1 .= $salt;
|
||||
}
|
||||
if ($i % 7) {
|
||||
$ctx1 .= $pw;
|
||||
}
|
||||
if ($i & 1) {
|
||||
$ctx1 .= substr($final, 0, 16);
|
||||
} else {
|
||||
$ctx1 .= $pw;
|
||||
}
|
||||
$final = $this->pahex2bin(md5($ctx1));
|
||||
}
|
||||
$passwd = "";
|
||||
$passwd .= $this->to64(((ord($final[0]) << 16) |
|
||||
(ord($final[6]) << 8) | (ord($final[12]))), 4);
|
||||
$passwd .= $this->to64(((ord($final[1]) << 16) |
|
||||
(ord($final[7]) << 8) | (ord($final[13]))), 4);
|
||||
$passwd .= $this->to64(((ord($final[2]) << 16) |
|
||||
(ord($final[8]) << 8) | (ord($final[14]))), 4);
|
||||
$passwd .= $this->to64(((ord($final[3]) << 16) |
|
||||
(ord($final[9]) << 8) | (ord($final[15]))), 4);
|
||||
$passwd .= $this->to64(((ord($final[4]) << 16) |
|
||||
(ord($final[10]) << 8) | (ord($final[5]))), 4);
|
||||
$passwd .= $this->to64(ord($final[11]), 2);
|
||||
return "$magic$salt\$$passwd";
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Create a new salte
|
||||
* @return string The salt
|
||||
*/
|
||||
/*private function create_md5salt()
|
||||
{
|
||||
srand((double)microtime() * 1000000);
|
||||
$salt = substr(md5(rand(0, 9999999)), 0, 8);
|
||||
return $salt;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Encrypt using SSHA256 algorithm
|
||||
* @param string $pw The password
|
||||
* @param string $salt The salt to use
|
||||
* @return string The hashed password, prefixed by {SSHA256}
|
||||
*/
|
||||
/*private function ssha256($pw, $salt)
|
||||
{
|
||||
return '{SSHA256}' . base64_encode(hash('sha256', $pw . $salt, true) . $salt);
|
||||
}*/
|
||||
|
||||
/**
|
||||
* PostfixAdmin's hex2bin function
|
||||
* @param string $str The string to convert
|
||||
* @return string The converted string
|
||||
*/
|
||||
/*private function pahex2bin($str)
|
||||
{
|
||||
if (function_exists('hex2bin')) {
|
||||
return hex2bin($str);
|
||||
} else {
|
||||
$len = strlen($str);
|
||||
$nstr = "";
|
||||
for ($i = 0; $i < $len; $i += 2) {
|
||||
$num = sscanf(substr($str, $i, 2), "%x");
|
||||
$nstr .= chr($num[0]);
|
||||
}
|
||||
return $nstr;
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Convert to 64?
|
||||
*/
|
||||
/*private function to64($v, $n)
|
||||
{
|
||||
$ITOA64 =
|
||||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
$ret = "";
|
||||
while (($n - 1) >= 0) {
|
||||
$n--;
|
||||
$ret .= $ITOA64[$v & 0x3f];
|
||||
$v = $v >> 6;
|
||||
}
|
||||
return $ret;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Store a value in memcache or the session, if no memcache is available
|
||||
* @param string $key The key
|
||||
* @param mixed $value The value to store
|
||||
* @param int $ttl (optional) defaults to 3600 seconds.
|
||||
*/
|
||||
private function setCache($key, $value, $ttl = 3600)
|
||||
{
|
||||
if ($this->cache === null) {
|
||||
$_SESSION[$this->session_cache_name][$key] = array(
|
||||
'value' => $value,
|
||||
'time' => time(),
|
||||
'ttl' => $ttl,
|
||||
);
|
||||
} else {
|
||||
$this->cache->set($key, $value, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a value from memcache or session, if memcache is not available.
|
||||
* Returns NULL if there's no value stored or the value expired.
|
||||
* @param string $key
|
||||
* @return mixed|NULL
|
||||
*/
|
||||
private function getCache($key)
|
||||
{
|
||||
$retVal = null;
|
||||
if ($this->cache === null) {
|
||||
if (isset($_SESSION[$this->session_cache_name],
|
||||
$_SESSION[$this->session_cache_name][$key])) {
|
||||
$value = $_SESSION[$this->session_cache_name][$key];
|
||||
if (time() < $value['time'] + $value['ttl']) {
|
||||
$retVal = $value['value'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$retVal = $this->cache->get($key);
|
||||
}
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
/*private function create_systemsalt($length = 20)
|
||||
{
|
||||
$fp = fopen('/dev/urandom', 'r');
|
||||
$randomString = fread($fp, $length);
|
||||
fclose($fp);
|
||||
$salt = base64_encode($randomString);
|
||||
return $salt;
|
||||
}*/
|
||||
|
||||
/*private function pw_hash($password)
|
||||
{
|
||||
$options = [
|
||||
'cost' => 10,
|
||||
];
|
||||
return password_hash($password, PASSWORD_BCRYPT, $options);
|
||||
|
||||
}*/
|
||||
|
||||
/*function hash_equals($a, $b)
|
||||
{
|
||||
$a_length = strlen($a);
|
||||
|
||||
if ($a_length !== strlen($b)) {
|
||||
return false;
|
||||
}
|
||||
$result = 0;
|
||||
|
||||
// Do not attempt to "optimize" this.
|
||||
for ($i = 0; $i < $a_length; $i++) {
|
||||
$result |= ord($a[$i]) ^ ord($b[$i]);
|
||||
}
|
||||
|
||||
//Hide the length of the string
|
||||
$additional_length = 200 - ($a_length % 200);
|
||||
$tmp = 0;
|
||||
$c = "abCD";
|
||||
for ($i = 0; $i < $additional_length; $i++) {
|
||||
$tmp |= ord($c[0]) ^ ord($c[0]);
|
||||
}
|
||||
|
||||
return $result === 0;
|
||||
}*/
|
||||
|
||||
/*private static function hex_to_base64($hex)
|
||||
{
|
||||
$hex_chr = '';
|
||||
foreach (str_split($hex, 2) as $hexpair) {
|
||||
$hex_chr .= chr(hexdec($hexpair));
|
||||
}
|
||||
return base64_encode($hex_chr);
|
||||
}*/
|
||||
}
|
||||
Reference in New Issue
Block a user