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

feat: Add first login timestamp of each user to oc_preferences and user:info output #49377

Open
wants to merge 4 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 apps/provisioning_api/lib/Controller/AUserData.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ protected function getUserData(string $userId, bool $includeScopes = false): ?ar

// 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());
Expand Down
1 change: 1 addition & 0 deletions apps/provisioning_api/lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
* headlineScope?: Provisioning_APIUserDetailsScope,
* id: string,
* language: string,
* firstLogin: int,
* lastLogin: int,
* locale: string,
* manager: string,
Expand Down
5 changes: 5 additions & 0 deletions apps/provisioning_api/openapi-administration.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"headline",
"id",
"language",
"firstLogin",
"lastLogin",
"locale",
"manager",
Expand Down Expand Up @@ -195,6 +196,10 @@
"language": {
"type": "string"
},
"firstLogin": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
Expand Down
5 changes: 5 additions & 0 deletions apps/provisioning_api/openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"headline",
"id",
"language",
"firstLogin",
"lastLogin",
"locale",
"manager",
Expand Down Expand Up @@ -242,6 +243,10 @@
"language": {
"type": "string"
},
"firstLogin": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
Expand Down
5 changes: 5 additions & 0 deletions apps/provisioning_api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"headline",
"id",
"language",
"firstLogin",
"lastLogin",
"locale",
"manager",
Expand Down Expand Up @@ -242,6 +243,10 @@
"language": {
"type": "string"
},
"firstLogin": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
Expand Down
15 changes: 15 additions & 0 deletions apps/provisioning_api/tests/Controller/UsersControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,10 @@ public function testGetUserDataAsAdmin(): void {
->expects($this->once())
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
Expand All @@ -1169,6 +1173,7 @@ public function testGetUserDataAsAdmin(): void {
'id' => 'UID',
'enabled' => true,
'storageLocation' => '/var/www/newtcloud/data/UID',
'firstLogin' => 1511191471000,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => ['group3'],
Expand Down Expand Up @@ -1273,6 +1278,10 @@ public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
->expects($this->once())
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
Expand Down Expand Up @@ -1308,6 +1317,7 @@ public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
$expected = [
'id' => 'UID',
'enabled' => true,
'firstLogin' => 1511191471000,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => [],
Expand Down Expand Up @@ -1455,6 +1465,10 @@ public function testGetUserDataAsSubAdminSelfLookup(): void {
->expects($this->once())
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
Expand Down Expand Up @@ -1485,6 +1499,7 @@ public function testGetUserDataAsSubAdminSelfLookup(): void {

$expected = [
'id' => 'UID',
'firstLogin' => 1511191471000,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => [],
Expand Down
19 changes: 18 additions & 1 deletion core/Command/User/Info.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int
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(),
Expand All @@ -56,7 +72,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'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()
];
Expand Down
8 changes: 6 additions & 2 deletions lib/private/User/LazyUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ public function setDisplayName($displayName) {
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();
}

Expand Down
39 changes: 29 additions & 10 deletions lib/private/User/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -202,28 +202,47 @@ private function ensureAccountManager() {
/**
* 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;
Expand Down
15 changes: 12 additions & 3 deletions lib/public/IUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,22 @@ public function setDisplayName($displayName);
* @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
Expand Down
Loading