This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch
3798-add-creation-and-last-login-date-to-user-object
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to
refs/heads/3798-add-creation-and-last-login-date-to-user-object by this push:
new d353cc47ea feat(#3798): Show creation and last login date in user
configuration view
d353cc47ea is described below
commit d353cc47eaec682ef8eacbbc9271631e2239803c
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Oct 1 09:54:01 2025 +0200
feat(#3798): Show creation and last login date in user configuration view
---
.../streampipes/model/client/user/UserAccount.java | 20 +++++++++++++++++
.../resource/management/UserResourceManager.java | 6 ++++-
.../streampipes/rest/impl/Authentication.java | 2 ++
.../service/core/oauth2/UserService.java | 4 +++-
.../src/lib/model/gen/streampipes-model-client.ts | 9 ++++++--
.../src/lib/model/gen/streampipes-model.ts | 3 ++-
.../components/sp-label/sp-label.component.scss | 2 +-
.../lib/components/sp-label/sp-label.component.ts | 6 ++---
.../split-section/split-section.component.html | 2 +-
ui/src/app/configuration/configuration.module.ts | 3 ++-
.../abstract-security-principal-config.ts | 17 +++++++-------
.../role-configuration.component.html | 1 +
.../security-user-config.component.html | 26 ++++++++++++++++++++++
.../security-user-config.component.ts | 18 +++++++++++++--
.../existing-adapters.component.html | 13 +++++------
.../existing-adapters.component.scss | 9 --------
16 files changed, 103 insertions(+), 38 deletions(-)
diff --git
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
index 8fc20b0f0f..9b1dbcdf44 100644
---
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
+++
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
@@ -42,6 +42,9 @@ public class UserAccount extends Principal {
protected boolean darkMode = false;
protected boolean hasAcknowledged = false;
+ protected long createdAtMillis;
+ protected long lastLoginAtMillis;
+
/**
* The authentication provider (LOCAL or one of the configured OAuth
providers
*/
@@ -55,6 +58,7 @@ public class UserAccount extends Principal {
this.preferredDataProcessors = new ArrayList<>();
this.preferredDataSinks = new ArrayList<>();
this.preferredDataStreams = new ArrayList<>();
+ this.createdAtMillis = System.currentTimeMillis();
this.provider = UserAccount.LOCAL;
}
@@ -190,4 +194,20 @@ public class UserAccount extends Principal {
public void setHasAcknowledged(boolean hasAcknowledged) {
this.hasAcknowledged = hasAcknowledged;
}
+
+ public long getCreatedAtMillis() {
+ return createdAtMillis;
+ }
+
+ public void setCreatedAtMillis(long createdAtMillis) {
+ this.createdAtMillis = createdAtMillis;
+ }
+
+ public long getLastLoginAtMillis() {
+ return lastLoginAtMillis;
+ }
+
+ public void setLastLoginAtMillis(long lastLoginAtMillis) {
+ this.lastLoginAtMillis = lastLoginAtMillis;
+ }
}
diff --git
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
index 57cbd98437..343ecf0b3c 100644
---
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
+++
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
@@ -134,6 +134,10 @@ public class UserResourceManager extends
AbstractResourceManager<IUserStorage> {
}
}
+ public void updateUser(Principal principal) {
+ db.updateUser(principal);
+ }
+
private void createTokenAndSendActivationMail(String username) throws
IOException {
String activationCode = TokenUtil.generateToken(RECOVERY_TOKEN_LENGTH);
storeActivationCode(username, activationCode);
@@ -194,7 +198,7 @@ public class UserResourceManager extends
AbstractResourceManager<IUserStorage> {
}
- public void registerOauthUser(UserAccount userAccount) {
+ public void storeUser(UserAccount userAccount) {
db.storeUser(userAccount);
}
}
diff --git
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
index dc2d6d252a..ac61cd1939 100644
---
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
+++
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
@@ -157,6 +157,8 @@ public class Authentication extends AbstractRestResource {
Principal principal = ((PrincipalUserDetails<?>)
auth.getPrincipal()).getDetails();
if (principal instanceof UserAccount) {
JwtAuthenticationResponse tokenResp = makeJwtResponse(auth);
+ ((UserAccount)
principal).setLastLoginAtMillis(System.currentTimeMillis());
+ getSpResourceManager().manageUsers().updateUser(principal);
return ok(tokenResp);
} else {
throw new BadCredentialsException("Could not create auth token");
diff --git
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
index 5f766659c8..a692e3a503 100755
---
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
+++
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
@@ -92,11 +92,13 @@ public class UserService {
);
}
applyRoles(user, oAuthConfig, attributes, false);
+ user.setLastLoginAtMillis(System.currentTimeMillis());
userStorage.updateUser(user);
} else {
user = toUserAccount(registrationId, principalId, email, fullName);
+ user.setLastLoginAtMillis(System.currentTimeMillis());
applyRoles(user, oAuthConfig, attributes, true);
- new UserResourceManager().registerOauthUser(user);
+ new UserResourceManager().storeUser(user);
}
user = (UserAccount) userStorage.getUserById(principalId);
diff --git
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
index f6532c9dbd..47ae72ca3e 100644
---
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
+++
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
@@ -16,12 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
+
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-09-09
16:10:12.
+// Generated using typescript-generator version 3.2.1263 on 2025-10-01
08:36:56.
-import { Storable } from '.streampipes-model';
+import { Storable } from './streampipes-model';
export class Group implements Storable {
alternateIds: string[];
@@ -240,11 +241,13 @@ export class ServiceAccount extends Principal {
}
export class UserAccount extends Principal {
+ createdAtMillis: number;
darkMode: boolean;
externallyManagedRoles: boolean;
fullName: string;
hasAcknowledged: boolean;
hideTutorial: boolean;
+ lastLoginAtMillis: number;
password: string;
preferredDataProcessors: string[];
preferredDataSinks: string[];
@@ -258,11 +261,13 @@ export class UserAccount extends Principal {
}
const instance = target || new UserAccount();
super.fromData(data, instance);
+ instance.createdAtMillis = data.createdAtMillis;
instance.darkMode = data.darkMode;
instance.externallyManagedRoles = data.externallyManagedRoles;
instance.fullName = data.fullName;
instance.hasAcknowledged = data.hasAcknowledged;
instance.hideTutorial = data.hideTutorial;
+ instance.lastLoginAtMillis = data.lastLoginAtMillis;
instance.password = data.password;
instance.preferredDataProcessors = __getCopyArrayFn(
__identity<string>(),
diff --git
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index f2df1c882b..6823c795ec 100644
---
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -16,10 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
+
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-09-09
16:10:11.
+// Generated using typescript-generator version 3.2.1263 on 2025-10-01
08:36:50.
export class NamedStreamPipesEntity implements Storable {
'@class':
diff --git
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
index a115d10892..432d26687c 100644
---
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
+++
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
@@ -20,7 +20,7 @@
border-radius: 15px;
min-width: 50px;
padding: 5px 7px;
- border: 1px solid;
+ //border: 1px solid;
display: inline-block;
text-align: center;
}
diff --git
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
index 398b0f7982..8f52fb8d05 100644
---
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
+++
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
@@ -27,7 +27,7 @@ import { SpColorizationService } from
'../../services/colorization.service';
})
export class SpLabelComponent implements OnInit {
@Input()
- labelText: string;
+ labelText: string | number;
@Input()
small = false;
@@ -35,9 +35,9 @@ export class SpLabelComponent implements OnInit {
@Input()
size: 'small' | 'medium' | 'large' = 'large';
- _labelBackground: string;
+ _labelBackground: string = 'var(--color-bg-2)';
- labelTextColor = '';
+ labelTextColor = 'var(--color-default-text)';
cssClass = '';
constructor(private colorizationService: SpColorizationService) {}
diff --git
a/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
b/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
index 54ec3df0de..be2e8d315c 100644
---
a/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
+++
b/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
@@ -18,7 +18,7 @@
<div fxFlex="100" fxLayout="column">
<div fxLayout="row">
- <div fxFlex="300px" fxLayout="column" class="split-section">
+ <div fxFlex="250px" fxLayout="column" class="split-section">
<div class="split-section-title">{{ title }}</div>
<div class="split-section-description">{{ subtitle }}</div>
</div>
diff --git a/ui/src/app/configuration/configuration.module.ts
b/ui/src/app/configuration/configuration.module.ts
index af9f1d23e6..51f8856b7d 100644
--- a/ui/src/app/configuration/configuration.module.ts
+++ b/ui/src/app/configuration/configuration.module.ts
@@ -82,7 +82,7 @@ import { SpConfigurationLinkSettingsComponent } from
'./general-configuration/li
import { SitesConfigurationComponent } from
'./sites-configuration/sites-configuration.component';
import { LocationFeaturesConfigurationComponent } from
'./sites-configuration/location-features-configuration/location-features-configuration.component';
import { SiteAreaConfigurationComponent } from
'./sites-configuration/site-area-configuration/site-area-configuration.component';
-import { MatSort } from '@angular/material/sort';
+import { MatSort, MatSortModule } from '@angular/material/sort';
import { ManageSiteDialogComponent } from
'./dialog/manage-site/manage-site-dialog.component';
import { EditAssetLocationComponent } from
'./dialog/manage-site/edit-location/edit-location.component';
import { EditAssetLocationAreaComponent } from
'./dialog/manage-site/edit-location/edit-location-area/edit-location-area.component';
@@ -129,6 +129,7 @@ import { SelectDataExportComponent } from
'./dialog/data-retention-dialog/compon
MatPaginatorModule,
MatRadioModule,
MatSelectModule,
+ MatSortModule,
FormsModule,
DragDropModule,
CoreUiModule,
diff --git
a/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
b/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
index b3f639b301..4cc91b1515 100644
---
a/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
+++
b/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
@@ -16,7 +16,7 @@
*
*/
-import { Directive, OnInit, ViewChild } from '@angular/core';
+import { Directive, inject, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
@@ -42,18 +42,14 @@ export abstract class AbstractSecurityPrincipalConfig<
{
users: T[] = [];
- @ViewChild(MatPaginator) paginator: MatPaginator;
- pageSize = 1;
@ViewChild(MatSort) sort: MatSort;
dataSource: MatTableDataSource<T>;
- constructor(
- protected userService: UserService,
- protected userAdminService: UserAdminService,
- protected dialogService: DialogService,
- private dialog: MatDialog,
- ) {}
+ protected userService = inject(UserService);
+ protected userAdminService = inject(UserAdminService);
+ protected dialogService = inject(DialogService);
+ private dialog = inject(MatDialog);
ngOnInit(): void {
this.load();
@@ -88,6 +84,9 @@ export abstract class AbstractSecurityPrincipalConfig<
this.getObservable().subscribe(response => {
this.users = response;
this.dataSource = new MatTableDataSource(this.users);
+ setTimeout(() => {
+ this.dataSource.sort = this.sort;
+ });
});
}
diff --git
a/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
b/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
index 3d143fb285..b315eb152b 100644
---
a/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
+++
b/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
@@ -32,6 +32,7 @@
*ngIf="dataSource"
[dataSource]="dataSource"
[columns]="displayedColumns"
+ matSort
data-cy="security-role-config"
>
<ng-container matColumnDef="roleName">
diff --git
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
index 91f1d4aee4..b8b2f26925 100644
---
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
+++
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
@@ -63,6 +63,32 @@
</td>
</ng-container>
+ <ng-container matColumnDef="createdAtMillis">
+ <th mat-header-cell mat-sort-header *matHeaderCellDef>
+ {{ 'Created' | translate }}
+ </th>
+ <td mat-cell *matCellDef="let account">
+ {{
+ this.dateFormatService.formatDate(
+ account.createdAtMillis
+ )
+ }}
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="lastLoginAtMillis">
+ <th mat-header-cell mat-sort-header *matHeaderCellDef>
+ {{ 'Last Login' | translate }}
+ </th>
+ <td mat-cell *matCellDef="let account">
+ {{
+ this.dateFormatService.formatDate(
+ account.lastLoginAtMillis
+ )
+ }}
+ </td>
+ </ng-container>
+
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef class="text-right">
Actions
diff --git
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
index fd4d232781..3326cf6d39 100644
---
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
+++
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
@@ -16,10 +16,11 @@
*
*/
-import { Component } from '@angular/core';
+import { Component, inject } from '@angular/core';
import { UserAccount } from '@streampipes/platform-services';
import { AbstractSecurityPrincipalConfig } from
'../abstract-security-principal-config';
import { Observable } from 'rxjs';
+import { DateFormatService } from '@streampipes/shared-ui';
@Component({
selector: 'sp-security-user-config',
@@ -28,7 +29,16 @@ import { Observable } from 'rxjs';
standalone: false,
})
export class SecurityUserConfigComponent extends
AbstractSecurityPrincipalConfig<UserAccount> {
- displayedColumns: string[] = ['username', 'provider', 'fullName', 'edit'];
+ displayedColumns: string[] = [
+ 'username',
+ 'provider',
+ 'fullName',
+ 'createdAtMillis',
+ 'lastLoginAtMillis',
+ 'edit',
+ ];
+
+ public dateFormatService = inject(DateFormatService);
getObservable(): Observable<UserAccount[]> {
return this.userAdminService.getAllUserAccounts();
@@ -43,4 +53,8 @@ export class SecurityUserConfigComponent extends
AbstractSecurityPrincipalConfig
user.provider = 'local';
return user;
}
+
+ formatDate(timestamp?: number): string {
+ return this.dateFormatService.formatDate(timestamp);
+ }
}
diff --git
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
index 73f8efa9cd..3f441fe4ef 100644
---
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
+++
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
@@ -210,14 +210,13 @@
#Messages
</th>
<td mat-cell *matCellDef="let adapter">
- <h5 class="monitoring-info">
- {{
+ <sp-label
+ size="small"
+ [labelText]="
adapterMetrics[adapter.elementId]
- ? adapterMetrics[adapter.elementId]
- .messagesOut.counter
- : 0
- }}
- </h5>
+ ?.messagesOut.counter || 0
+ "
+ ></sp-label>
</td>
</ng-container>
diff --git
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
index a619e1a47a..52a48bdf92 100644
---
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
+++
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
@@ -36,15 +36,6 @@
padding-left: 10px;
}
-.monitoring-info {
- color: var(--color-default-text);
- border: 1px solid var(--color-default-text);
- background: var(--color-bg-0);
- border-radius: 5px;
- padding: 5px;
- text-align: center;
-}
-
.icon {
max-width: 60px;
}