Compare commits

...

5 Commits

Author SHA1 Message Date
Côme Chilliet 95d655b43a feat(users): Add user creation date support (WIP)
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2024-11-21 17:13:49 +01:00
Côme Chilliet fd366a6d8f feat(users): Use -1 for unknown firstLogin instead of setting it to current date
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2024-11-21 16:06:15 +01:00
Côme Chilliet 7e44535206 fix(tests): Adapt tests to firstLogin new feature
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2024-11-21 16:06:14 +01:00
Côme Chilliet 5c4803040f chore: Update openapi specs
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2024-11-21 16:06:14 +01:00
Côme Chilliet f43ebddace feat: Add first login timestamp of each user to oc_preferences and user:info output
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2024-11-21 16:06:14 +01:00
12 changed files with 106 additions and 17 deletions
@@ -116,6 +116,7 @@ abstract class AUserData extends OCSController {
// Find the data
$data['id'] = $targetUserObject->getUID();
$data['firstLogin'] = $targetUserObject->getFirstLogin() * 1000;
$data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
$data['backend'] = $targetUserObject->getBackendClassName();
$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
@@ -46,6 +46,7 @@ namespace OCA\Provisioning_API;
* headlineScope?: Provisioning_APIUserDetailsScope,
* id: string,
* language: string,
* firstLogin: int,
* lastLogin: int,
* locale: string,
* manager: string,
@@ -92,6 +92,7 @@
"headline",
"id",
"language",
"firstLogin",
"lastLogin",
"locale",
"manager",
@@ -195,6 +196,10 @@
"language": {
"type": "string"
},
"firstLogin": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
+5
View File
@@ -139,6 +139,7 @@
"headline",
"id",
"language",
"firstLogin",
"lastLogin",
"locale",
"manager",
@@ -242,6 +243,10 @@
"language": {
"type": "string"
},
"firstLogin": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
+5
View File
@@ -139,6 +139,7 @@
"headline",
"id",
"language",
"firstLogin",
"lastLogin",
"locale",
"manager",
@@ -242,6 +243,10 @@
"language": {
"type": "string"
},
"firstLogin": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
@@ -1147,6 +1147,10 @@ class UsersControllerTest extends TestCase {
->expects($this->once())
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
@@ -1169,6 +1173,7 @@ class UsersControllerTest extends TestCase {
'id' => 'UID',
'enabled' => true,
'storageLocation' => '/var/www/newtcloud/data/UID',
'firstLogin' => 1511191471000,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => ['group3'],
@@ -1273,6 +1278,10 @@ class UsersControllerTest extends TestCase {
->expects($this->once())
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
@@ -1308,6 +1317,7 @@ class UsersControllerTest extends TestCase {
$expected = [
'id' => 'UID',
'enabled' => true,
'firstLogin' => 1511191471000,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => [],
@@ -1455,6 +1465,10 @@ class UsersControllerTest extends TestCase {
->expects($this->once())
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
@@ -1485,6 +1499,7 @@ class UsersControllerTest extends TestCase {
$expected = [
'id' => 'UID',
'firstLogin' => 1511191471000,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => [],
+3
View File
@@ -634,4 +634,7 @@ class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, I
public function getDisabledUserList(?int $limit = null, int $offset = 0, string $search = ''): array {
throw new \Exception('This is implemented directly in User_Proxy');
}
public function getUserCreationDate(string $uid): ?\DateTime {
}
}
+6 -1
View File
@@ -14,11 +14,12 @@ use OCP\IUserBackend;
use OCP\Notification\IManager as INotificationManager;
use OCP\User\Backend\ICountMappedUsersBackend;
use OCP\User\Backend\ICountUsersBackend;
use OCP\User\Backend\IGetUserCreationDateBackend;
use OCP\User\Backend\IProvideEnabledStateBackend;
use OCP\UserInterface;
use Psr\Log\LoggerInterface;
class User_Proxy extends Proxy implements IUserBackend, UserInterface, IUserLDAP, ICountUsersBackend, ICountMappedUsersBackend, IProvideEnabledStateBackend {
class User_Proxy extends Proxy implements IUserBackend, UserInterface, IUserLDAP, ICountUsersBackend, ICountMappedUsersBackend, IProvideEnabledStateBackend, IGetUserCreationDateBackend {
/** @var User_LDAP[] */
private array $backends = [];
private ?User_LDAP $refBackend = null;
@@ -443,4 +444,8 @@ class User_Proxy extends Proxy implements IUserBackend, UserInterface, IUserLDAP
)
);
}
public function getUserCreationDate(string $uid): ?\DateTime {
return $this->handleRequest($uid, 'getUserCreationDate', [$uid]) ?: null;
}
}
+18 -1
View File
@@ -47,6 +47,22 @@ class Info extends Base {
return 1;
}
$groups = $this->groupManager->getUserGroupIds($user);
$firstLogin = $user->getFirstLogin();
$lastLogin = $user->getLastLogin();
if ($firstLogin < 0) {
$firstSeen = 'unknown';
} elseif ($firstLogin === 0) {
$firstSeen = 'never';
} else {
$firstSeen = date(\DateTimeInterface::ATOM, $firstLogin); // ISO-8601
}
if ($lastLogin < 0) {
$lastSeen = 'unknown';
} elseif ($lastLogin === 0) {
$lastSeen = 'never';
} else {
$lastSeen = date(\DateTimeInterface::ATOM, $lastLogin); // ISO-8601
}
$data = [
'user_id' => $user->getUID(),
'display_name' => $user->getDisplayName(),
@@ -56,7 +72,8 @@ class Info extends Base {
'groups' => $groups,
'quota' => $user->getQuota(),
'storage' => $this->getStorageInfo($user),
'last_seen' => date(\DateTimeInterface::ATOM, $user->getLastLogin()), // ISO-8601
'first_seen' => $firstSeen,
'last_seen' => $lastSeen,
'user_directory' => $user->getHome(),
'backend' => $user->getBackendClassName()
];
+6 -2
View File
@@ -60,11 +60,15 @@ class LazyUser implements IUser {
return $this->getUser()->setDisplayName($displayName);
}
public function getLastLogin() {
public function getLastLogin(): int {
return $this->getUser()->getLastLogin();
}
public function updateLastLoginTimestamp() {
public function getFirstLogin(): int {
return $this->getUser()->getFirstLogin();
}
public function updateLastLoginTimestamp(): bool {
return $this->getUser()->updateLastLoginTimestamp();
}
+29 -10
View File
@@ -65,8 +65,8 @@ class User implements IUser {
/** @var string */
private $home;
/** @var int|null */
private $lastLogin;
private ?int $lastLogin = null;
private ?int $firstLogin = null;
/** @var IAvatarManager */
private $avatarManager;
@@ -202,28 +202,47 @@ class User implements IUser {
/**
* returns the timestamp of the user's last login or 0 if the user did never
* login
*
* @return int
*/
public function getLastLogin() {
public function getLastLogin(): int {
if ($this->lastLogin === null) {
$this->lastLogin = (int)$this->config->getUserValue($this->uid, 'login', 'lastLogin', 0);
}
return (int)$this->lastLogin;
return $this->lastLogin;
}
/**
* returns the timestamp of the user's last login or 0 if the user did never
* login
*/
public function getFirstLogin(): int {
if ($this->firstLogin === null) {
$this->firstLogin = (int)$this->config->getUserValue($this->uid, 'login', 'firstLogin', 0);
}
return $this->firstLogin;
}
/**
* updates the timestamp of the most recent login of this user
*/
public function updateLastLoginTimestamp() {
public function updateLastLoginTimestamp(): bool {
$previousLogin = $this->getLastLogin();
$firstLogin = $this->getFirstLogin();
$now = time();
$firstTimeLogin = $previousLogin === 0;
if ($now - $previousLogin > 60) {
$this->lastLogin = time();
$this->config->setUserValue(
$this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
$this->lastLogin = $now;
$this->config->setUserValue($this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
}
if ($firstLogin === 0) {
if ($firstTimeLogin) {
$this->firstLogin = $now;
} else {
/* Unknown first login, most likely was before upgrade to Nextcloud 31 */
$this->firstLogin = -1;
}
$this->config->setUserValue($this->uid, 'login', 'firstLogin', (string)$this->firstLogin);
}
return $firstTimeLogin;
+12 -3
View File
@@ -50,13 +50,22 @@ interface IUser {
* @return int
* @since 8.0.0
*/
public function getLastLogin();
public function getLastLogin(): int;
/**
* updates the timestamp of the most recent login of this user
* Returns the timestamp of the user's first login, 0 if the user did never login, or -1 if the data is unknown (first login was on an older version)
*
* @since 31.0.0
*/
public function getFirstLogin(): int;
/**
* Updates the timestamp of the most recent login of this user (and first login if needed)
*
* @return bool whether this is the first login
* @since 8.0.0
*/
public function updateLastLoginTimestamp();
public function updateLastLoginTimestamp(): bool;
/**
* Delete the user