Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: added support for user API keys (DB/frontend part) #2372

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ version 28-git (????-??-??):
specific sms-like section configuration variable names [chilan]
- improvement: send document confirm notifications if document is created with 'closed'
status [chilan]
- enhancement: added support for user API keys [interduo]

version 27.0 (2021-08-20):

Expand Down
4 changes: 3 additions & 1 deletion doc/lms.mysql
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ CREATE TABLE users (
persistentsettings mediumtext NOT NULL DEFAULT '',
twofactorauth smallint NOT NULL DEFAULT 0,
twofactorauthsecretkey varchar(255) DEFAULT NULL,
api smallint DEFAULT 0 NOT NULL,
apikey text DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY login (login)
) ENGINE=InnoDB;
Expand Down Expand Up @@ -4332,4 +4334,4 @@ INSERT INTO netdevicemodels (name, alternative_name, netdeviceproducerid) VALUES
('XR7', 'XR7 MINI PCI PCBA', 2),
('XR9', 'MINI PCI 600MW 900MHZ', 2);

INSERT INTO dbinfo (keytype, keyvalue) VALUES ('dbversion', '2023053000');
INSERT INTO dbinfo (keytype, keyvalue) VALUES ('dbversion', '2023060100');
4 changes: 3 additions & 1 deletion doc/lms.pgsql
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ CREATE TABLE users (
persistentsettings text NOT NULL DEFAULT '',
twofactorauth smallint NOT NULL DEFAULT 0,
twofactorauthsecretkey varchar(255) DEFAULT NULL,
api smallint DEFAULT 0 NOT NULL,
apikey text DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE (login)
);
Expand Down Expand Up @@ -4377,6 +4379,6 @@ INSERT INTO netdevicemodels (name, alternative_name, netdeviceproducerid) VALUES
('XR7', 'XR7 MINI PCI PCBA', 2),
('XR9', 'MINI PCI 600MW 900MHZ', 2);

INSERT INTO dbinfo (keytype, keyvalue) VALUES ('dbversion', '2023053000');
INSERT INTO dbinfo (keytype, keyvalue) VALUES ('dbversion', '2023060100');

COMMIT;
6 changes: 6 additions & 0 deletions lib/LMS.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,12 @@ public function isUserNetworkPasswordSet($id)
return $manager->isUserNetworkPasswordSet($id);
}

public function hasUserApiKeySet($id)
{
$manager = $this->getUserManager();
return $manager->hasUserApiKeySet($id);
}

/*
* Customers functions
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/LMSDB_common.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*/

// here should be always the newest version of database!
define('DBVERSION', '2023053000');
define('DBVERSION', '2023060100');

/**
*
Expand Down
90 changes: 61 additions & 29 deletions lib/LMSManagers/LMSUserManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,41 @@ class LMSUserManager extends LMSManager implements LMSUserManagerInterface
*/
public function setUserPassword($id, $passwd, $net = false)
{
if ($net) {
$args = array(
'netpasswd' => empty($passwd) ? null : $passwd,
SYSLOG::RES_USER => $id
);
$result = $this->db->Execute(
'UPDATE users SET netpasswd = ?
WHERE id = ?',
array_values($args)
);
} else {
$args = array(
'passwd' => password_hash($passwd, PASSWORD_DEFAULT),
'passwdforcechange' => 0,
SYSLOG::RES_USER => $id
);
$result = $this->db->Execute(
'UPDATE users SET passwd = ?, passwdlastchange = ?NOW?, passwdforcechange = ?
WHERE id = ?',
array_values($args)
);
$this->db->Execute('INSERT INTO passwdhistory (userid, hash) VALUES (?, ?)', array($id, password_hash($passwd, PASSWORD_DEFAULT)));
switch ($net) {
case 2:
$args = array(
'passwd' => empty($passwd) ? null : password_hash($passwd, PASSWORD_DEFAULT),
SYSLOG::RES_USER => $id
);
$result = $this->db->Execute(
'UPDATE users SET apikey = ?
WHERE id = ?',
array_values($args)
);
break;
case 1:
$args = array(
'netpasswd' => empty($passwd) ? null : $passwd,
SYSLOG::RES_USER => $id
);
$result = $this->db->Execute(
'UPDATE users SET netpasswd = ?
WHERE id = ?',
array_values($args)
);
break;
default:
$args = array(
'passwd' => password_hash($passwd, PASSWORD_DEFAULT),
'passwdforcechange' => 0,
SYSLOG::RES_USER => $id
);
$result = $this->db->Execute(
'UPDATE users SET passwd = ?, passwdlastchange = ?NOW?, passwdforcechange = ?
WHERE id = ?',
array_values($args)
);
$this->db->Execute('INSERT INTO passwdhistory (userid, hash) VALUES (?, ?)', array($id, password_hash($passwd, PASSWORD_DEFAULT)));
}
if ($result && $this->syslog) {
unset($args['passwd']);
Expand Down Expand Up @@ -206,7 +219,7 @@ public function getUserList($params = array())

$userlist = $this->db->GetAllByKey(
'SELECT id, login, name, phone, lastlogindate, lastloginip, passwdexpiration, passwdlastchange, access,
accessfrom, accessto, rname, twofactorauth'
accessfrom, accessto, rname, twofactorauth, api'
. ' FROM ' . (isset($superuser) ? 'vallusers' : 'vusers')
. ' WHERE deleted = 0'
. (isset($userAccess) ? ' AND access = 1 AND accessfrom <= ?NOW? AND (accessto >=?NOW? OR accessto = 0)' : '' )
Expand Down Expand Up @@ -274,6 +287,8 @@ public function userAdd($user)
'position' => $user['position'],
'ntype' => !empty($user['ntype']) ? $user['ntype'] : null,
'phone' => !empty($user['phone']) ? $user['phone'] : null,
'api' => empty($user['api']) ? 0 : 1,
'apikey' => empty($user['apikey']) ? null : password_hash($user['apikey'], PASSWORD_DEFAULT),
'passwdforcechange' => isset($user['passwdforcechange']) ? 1 : 0,
'passwdexpiration' => !empty($user['passwdexpiration']) ? $user['passwdexpiration'] : 0,
'access' => !empty($user['access']) ? 1 : 0,
Expand All @@ -284,9 +299,9 @@ public function userAdd($user)
);
$user_inserted = $this->db->Execute(
'INSERT INTO users (login, firstname, lastname, issuer, email, passwd, netpasswd, rights,
hosts, trustedhosts, position, ntype, phone,
hosts, trustedhosts, position, ntype, phone, api, apikey,
passwdforcechange, passwdexpiration, access, accessfrom, accessto, twofactorauth, twofactorauthsecretkey)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
array_values($args)
);
if ($user_inserted) {
Expand Down Expand Up @@ -507,6 +522,7 @@ public function userUpdate($user)
'position' => $user['position'],
'ntype' => !empty($user['ntype']) ? $user['ntype'] : null,
'phone' => !empty($user['phone']) ? $user['phone'] : null,
'api' => empty($user['api']) ? 0 : 1,
'passwdforcechange' => isset($user['passwdforcechange']) ? 1 : 0,
'passwdexpiration' => !empty($user['passwdexpiration']) ? $user['passwdexpiration'] : 0,
'access' => !empty($user['access']) ? 1 : 0,
Expand All @@ -518,7 +534,7 @@ public function userUpdate($user)
);
$res = $this->db->Execute(
'UPDATE users SET login = ?, firstname = ?, lastname = ?, issuer = ?, email = ?, rights = ?,
hosts = ?, trustedhosts = ?, position = ?, ntype = ?, phone = ?, passwdforcechange = ?, passwdexpiration = ?,
hosts = ?, trustedhosts = ?, position = ?, ntype = ?, phone = ?, api = ?, passwdforcechange = ?, passwdexpiration = ?,
access = ?, accessfrom = ?, accessto = ?, twofactorauth = ?, twofactorauthsecretkey = ?
WHERE id = ?',
array_values($args)
Expand Down Expand Up @@ -663,15 +679,20 @@ public function PasswdExistsInHistory($id, $passwd)

public function checkPassword($password, $net = false)
{
if ($net) {
if ($net == 1) {
return $this->db->GetOne(
'SELECT netpasswd FROM users WHERE id = ?',
array(Auth::GetCurrentUser())
) == $password;
} else {
$dbpasswd = $this->db->GetOne(
'SELECT passwd FROM users WHERE id = ?',
array(Auth::GetCurrentUser())
'SELECT ? FROM users
WHERE
id = ?',
array(
($net == 2) ? 'apikey' : 'passwd',
Auth::GetCurrentUser(),
)
);
return password_verify($password, $dbpasswd);
}
Expand All @@ -687,4 +708,15 @@ public function isUserNetworkPasswordSet($id)
)
) == 1;
}

public function hasUserApiKeySet($id)
{
return $this->db->GetOne(
'SELECT 1 FROM users
WHERE
id = ?
AND apikey IS NOT NULL',
array($id)
) == 1;
}
}
2 changes: 2 additions & 0 deletions lib/LMSManagers/LMSUserManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ public function PasswdExistsInHistory($id, $passwd);
public function checkPassword($password, $net = false);

public function isUserNetworkPasswordSet($id);

public function hasUserApiKeySet($id);
}
8 changes: 5 additions & 3 deletions lib/common.php
Original file line number Diff line number Diff line change
Expand Up @@ -1100,10 +1100,12 @@ function is_natural($var)
return preg_match('/^[1-9][0-9]*$/', $var);
}

function check_password_strength($password)
function check_password_strength($password, $passlength = 8)
{
return (preg_match('/[a-z]/', $password) && preg_match('/[A-Z]/', $password)
&& preg_match('/[0-9]/', $password) && mb_strlen($password) >= 8);
return preg_match('/[a-z]/', $password)
&& preg_match('/[A-Z]/', $password)
&& preg_match('/[0-9]/', $password)
&& mb_strlen($password) > $passlength;
}

function access_denied()
Expand Down
10 changes: 9 additions & 1 deletion lib/locale/pl_PL/strings.php
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,14 @@
$_LANG['Sale Registry'] = 'Rejestr sprzedaży';
$_LANG['Sale Registry for period $a - $b'] = 'Rejestr sprzedaży za okres $a - $b';
$_LANG['Save'] = 'Zapisz';
$_LANG['Please input API Key'] = 'Podaj klucz API';
$_LANG['API access'] = 'Dostęp API';
$_LANG['API access could not be turned on when API key is empty!'] = 'Dostęp API nie może być włączony gdy klucz API jest pusty!';
$_LANG['Enter new API key'] = 'Wprowadź nowy klucz API';
$_LANG['Change API key'] = 'Zmień klucz API';
$_LANG['Repeat new API key'] = 'Powtórz nowy klucz API';
$_LANG['API keys do not match!'] = 'Klucze API nie są takie same!';
$_LANG['API key should contain at least one capital letter, one lower case letter, one digit and should consist of at least 16 and maximum 8192 characters!'] = 'Klucz API powinien zawierać co najmniej jedną dużą, małą literę, cyfrę, co najmniej 16 i maksymalnie 8192 znaki!';
$_LANG['Save & Print'] = 'Zapisz i drukuj';
$_LANG['Scan'] = 'Skanuj';
$_LANG['Search'] = 'Szukaj';
Expand Down Expand Up @@ -5673,7 +5681,7 @@
$_LANG['Network password'] = 'Hasło sieciowe';
$_LANG['Network Password'] = 'Hasło sieciowe';
$_LANG['Change your network password'] = 'Zmiana Twojego hasła sieciowego';
$_LANG['Repeat network password'] = 'Powtórz hasło sieciowe';
$_LANG['Repeat new network password'] = 'Powtórz nowe hasło sieciowe';
$_LANG['Network password can be used to authenticate users via Radius server.'] = 'Hasło sieciowe może być używane do uwierzytelniania użytkowników w oparciu o serwer Radius.';
$_LANG['Change network password'] = 'Zmiana hasła sieciowego';

Expand Down
53 changes: 53 additions & 0 deletions lib/upgradedb/mysql.2023060100.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* LMS version 1.11-git
*
* (C) Copyright 2001-2023 LMS Developers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
*/

$this->BeginTrans();

$this->Execute("ALTER TABLE users ADD COLUMN api smallint DEFAULT 0 NOT NULL");
$this->Execute("ALTER TABLE users ADD COLUMN apikey text DEFAULT NULL");

$this->Execute("DROP VIEW vallusers");
$this->Execute("DROP VIEW vusers");

$this->Execute("
CREATE VIEW vusers AS
SELECT u.*, CONCAT(u.firstname, ' ', u.lastname) AS name, CONCAT(u.lastname, ' ', u.firstname) AS rname
FROM users u
LEFT JOIN userdivisions ud ON u.id = ud.userid
WHERE lms_current_user() = 0 OR ud.divisionid IN (
SELECT ud2.divisionid
FROM userdivisions ud2
WHERE ud2.userid = lms_current_user()
)
GROUP BY u.id
");

$this->Execute("
CREATE VIEW vallusers AS
SELECT *, CONCAT(firstname, ' ', lastname) AS name, CONCAT(lastname, ' ', firstname) AS rname
FROM users
");

$this->Execute("UPDATE dbinfo SET keyvalue = ? WHERE keytype = ?", array('2023060100', 'dbversion'));

$this->CommitTrans();
53 changes: 53 additions & 0 deletions lib/upgradedb/postgres.2023060100.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* LMS version 1.11-git
*
* (C) Copyright 2001-2023 LMS Developers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
*/

$this->BeginTrans();

$this->Execute("ALTER TABLE users ADD COLUMN api smallint DEFAULT 0 NOT NULL");
$this->Execute("ALTER TABLE users ADD COLUMN apikey text DEFAULT NULL");

$this->Execute("DROP VIEW vallusers");
$this->Execute("DROP VIEW vusers");

$this->Execute("
CREATE VIEW vusers AS
SELECT u.*, (u.firstname || ' ' || u.lastname) AS name, (u.lastname || ' ' || u.firstname) AS rname
FROM users u
LEFT JOIN userdivisions ud ON u.id = ud.userid
WHERE lms_current_user() = 0 OR ud.divisionid IN (
SELECT ud2.divisionid
FROM userdivisions ud2
WHERE ud2.userid = lms_current_user()
)
GROUP BY u.id
");

$this->Execute("
CREATE VIEW vallusers AS
SELECT *, (firstname || ' ' || lastname) AS name, (lastname || ' ' || firstname) AS rname
FROM users
");

$this->Execute("UPDATE dbinfo SET keyvalue = ? WHERE keytype = ?", array('2023060100', 'dbversion'));

$this->CommitTrans();
8 changes: 8 additions & 0 deletions modules/useradd.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
}
}

if (!empty($useradd['apikey']) && !check_password_strength($useradd['apikey'])) {
$error['apikey'] = trans('The password should contain at least one capital letter, one lower case letter, one digit and should consist of at least 16 characters!');
}

if (!empty($useradd['api']) && empty($useradd['apikey'])) {
$error['apikey'] = $error['api'] = trans('API access could not be turned on when API key is empty!');
}

if (!empty($useradd['accessfrom'])) {
$accessfrom = date_to_timestamp($useradd['accessfrom']);
if (empty($accessfrom)) {
Expand Down
Loading