This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new bc56f6a466 feat(#3717): Support external role assignments for OAuth
providers (#3718)
bc56f6a466 is described below
commit bc56f6a466ff21236654737082a4ea24bdadbcc2
Author: Dominik Riemer <[email protected]>
AuthorDate: Thu Aug 7 08:23:21 2025 +0200
feat(#3717): Support external role assignments for OAuth providers (#3718)
---
.../environment/model/OAuthConfiguration.java | 9 +
.../parser/OAuthConfigurationParser.java | 3 +-
.../streampipes/model/client/user/UserAccount.java | 9 +
.../service/core/oauth2/UserService.java | 59 +++-
.../src/lib/model/gen/streampipes-model-client.ts | 5 +-
.../edit-user-dialog.component.html | 45 +--
.../edit-user-dialog/edit-user-dialog.component.ts | 313 +++++++++++----------
.../user-group-configuration.component.html | 9 +
.../user-group-configuration.component.ts | 2 +-
9 files changed, 276 insertions(+), 178 deletions(-)
diff --git
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/model/OAuthConfiguration.java
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/model/OAuthConfiguration.java
index 7ab566f460..607bf468b5 100644
---
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/model/OAuthConfiguration.java
+++
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/model/OAuthConfiguration.java
@@ -34,6 +34,7 @@ public class OAuthConfiguration {
private String userInfoUri;
private String emailAttributeName;
private String userIdAttributeName;
+ private String roleAttributeName;
public String getRegistrationId() {
@@ -136,6 +137,14 @@ public class OAuthConfiguration {
return userIdAttributeName;
}
+ public String getRoleAttributeName() {
+ return roleAttributeName;
+ }
+
+ public void setRoleAttributeName(String roleAttributeName) {
+ this.roleAttributeName = roleAttributeName;
+ }
+
public void setUserIdAttributeName(String userIdAttributeName) {
this.userIdAttributeName = userIdAttributeName;
}
diff --git
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/parser/OAuthConfigurationParser.java
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/parser/OAuthConfigurationParser.java
index 48168566d2..3650a06104 100644
---
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/parser/OAuthConfigurationParser.java
+++
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/environment/parser/OAuthConfigurationParser.java
@@ -75,7 +75,7 @@ public class OAuthConfigurationParser {
) {
var parts = getParts(key);
if (parts.length >= 5) {
- // containst the identifier of the provider (e.g. azure, github, ...)
+ // contains the identifier of the provider (e.g. azure, github, ...)
var registrationId = getRegistrationId(parts);
var settingName = getSettingName(parts);
@@ -95,6 +95,7 @@ public class OAuthConfigurationParser {
case "USER_INFO_URI" -> oAuthConfiguration.setUserInfoUri(value);
case "EMAIL_ATTRIBUTE_NAME" ->
oAuthConfiguration.setEmailAttributeName(value);
case "USER_ID_ATTRIBUTE_NAME" ->
oAuthConfiguration.setUserIdAttributeName(value);
+ case "ROLE_ATTRIBUTE_NAME" ->
oAuthConfiguration.setRoleAttributeName(value);
case "NAME" -> oAuthConfiguration.setRegistrationName(value);
default -> LOG.warn(
"Unknown setting {} for oauth configuration in environment
variable {}",
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 98bcfbca5c..6df8e62555 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
@@ -45,6 +45,7 @@ public class UserAccount extends Principal {
* The authentication provider (LOCAL or one of the configured OAuth
providers
*/
protected String provider;
+ protected boolean externallyManagedRoles = false;
public UserAccount() {
super(PrincipalType.USER_ACCOUNT);
@@ -172,4 +173,12 @@ public class UserAccount extends Principal {
public void setProvider(String provider) {
this.provider = provider;
}
+
+ public boolean isExternallyManagedRoles() {
+ return externallyManagedRoles;
+ }
+
+ public void setExternallyManagedRoles(boolean externallyManagedRoles) {
+ this.externallyManagedRoles = externallyManagedRoles;
+ }
}
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 c861c278a6..956a99778d 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
@@ -20,6 +20,7 @@ package org.apache.streampipes.service.core.oauth2;
import org.apache.streampipes.commons.environment.Environment;
import org.apache.streampipes.commons.environment.Environments;
+import org.apache.streampipes.commons.environment.model.OAuthConfiguration;
import org.apache.streampipes.model.client.user.DefaultRole;
import org.apache.streampipes.model.client.user.UserAccount;
import org.apache.streampipes.resource.management.UserResourceManager;
@@ -31,8 +32,11 @@ import
org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
public class UserService {
@@ -74,10 +78,15 @@ public class UserService {
String.format("Already signed up with another provider %s",
user.getProvider())
);
}
+ applyRoles(user, oAuthConfig, attributes);
+ userStorage.updateUser(user);
} else {
- new
UserResourceManager().registerOauthUser(toUserAccount(registrationId,
principalId, email, fullName));
- user = (UserAccount) userStorage.getUserById(principalId);
+ user = toUserAccount(registrationId, principalId, email, fullName);
+ applyRoles(user, oAuthConfig, attributes);
+ new UserResourceManager().registerOauthUser(user);
}
+
+ user = (UserAccount) userStorage.getUserById(principalId);
return OidcUserAccountDetails.create(user, attributes, idToken,
userInfo);
} else {
throw new OAuth2AuthenticationProcessingException(
@@ -86,12 +95,37 @@ public class UserService {
}
}
+ private void applyRoles(UserAccount user,
+ OAuthConfiguration oAuthConfig,
+ Map<String, Object> attributes) {
+ if (oAuthConfig.getRoleAttributeName() != null) {
+ Object rolesObject = attributes.get(oAuthConfig.getRoleAttributeName());
+
+ if (rolesObject instanceof List<?> rolesList) {
+ Set<String> roles = extractRoleOrGroup("ROLE", rolesList);
+ Set<String> groups = convertGroup(extractRoleOrGroup("GROUP",
rolesList));
+
+ user.setRoles(roles);
+ user.setGroups(groups);
+ user.setExternallyManagedRoles(true);
+ } else {
+ applyDefaultRole(user);
+ }
+ } else {
+ applyDefaultRole(user);
+ }
+ }
+
+ private void applyDefaultRole(UserAccount user) {
+
user.setRoles(Stream.of(DefaultRole.ROLE_ADMIN.toString()).collect(Collectors.toSet()));
+ user.setExternallyManagedRoles(false);
+ }
+
private UserAccount toUserAccount(String registrationId,
String principalId,
String email,
Object fullName) {
- var roles = Stream.of(DefaultRole.ROLE_ADMIN.toString()).toList();
- var user = UserAccount.from(email, null, new HashSet<>(roles));
+ var user = UserAccount.from(email, null, new HashSet<>());
user.setPrincipalId(principalId);
if (Objects.nonNull(fullName)) {
user.setFullName(fullName.toString());
@@ -100,4 +134,21 @@ public class UserService {
user.setProvider(registrationId);
return user;
}
+
+ private Set<String> extractRoleOrGroup(String type,
+ List<?> roles) {
+ return roles.stream()
+ .filter(role -> role instanceof String)
+ .filter(role -> ((String) role).startsWith(type))
+ .map(role -> (String) role)
+ .collect(Collectors.toSet());
+ }
+
+ private Set<String> convertGroup(Set<String> groups) {
+ return groups.stream()
+ .map(group -> group.split("_"))
+ .filter(parts -> parts.length == 2)
+ .map(parts -> parts[1])
+ .collect(Collectors.toSet());
+ }
}
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 a865b8ea10..f6d00886bf 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,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-02-24
11:25:02.
+// Generated using typescript-generator version 3.2.1263 on 2025-08-05
18:37:35.
import { Storable } from './streampipes-model';
@@ -231,6 +232,7 @@ export class ServiceAccount extends Principal {
export class UserAccount extends Principal {
darkMode: boolean;
+ externallyManagedRoles: boolean;
fullName: string;
hideTutorial: boolean;
password: string;
@@ -247,6 +249,7 @@ export class UserAccount extends Principal {
const instance = target || new UserAccount();
super.fromData(data, instance);
instance.darkMode = data.darkMode;
+ instance.externallyManagedRoles = data.externallyManagedRoles;
instance.fullName = data.fullName;
instance.hideTutorial = data.hideTutorial;
instance.password = data.password;
diff --git
a/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
b/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
index 72687566c2..d46527b426 100644
---
a/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
+++
b/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
@@ -20,7 +20,7 @@
<div class="sp-dialog-content">
<div fxFlex="100" fxLayout="column" class="p-15">
<sp-warning-box *ngIf="isUserAccount && isExternalProvider">
- Some settings of externally-managed users cannot be changed.
+ Settings of externally-managed users cannot be changed.
</sp-warning-box>
<form [formGroup]="parentForm" fxFlex="100" fxLayout="column">
<div class="general-options-panel" fxLayout="column">
@@ -123,6 +123,9 @@
<span class="general-options-header">Groups</span>
<mat-checkbox
*ngFor="let group of availableGroups"
+ [disabled]="
+ isExternalProvider && isExternallyManagedRoles
+ "
[value]="group.groupId"
[checked]="user.groups.indexOf(group.groupId) > -1"
(change)="changeGroupAssignment($event)"
@@ -135,6 +138,9 @@
<mat-checkbox
*ngFor="let role of availableRoles$ | async"
[value]="role.elementId"
+ [disabled]="
+ isExternalProvider && isExternallyManagedRoles
+ "
[checked]="user.roles.indexOf(role.elementId) > -1"
(change)="changeRoleAssignment($event)"
[attr.data-cy]="'role-' + role.elementId"
@@ -142,21 +148,23 @@
{{ role.label }}
</mat-checkbox>
</div>
- <div fxLayout="column" class="general-options-panel">
- <span class="general-options-header">Account</span>
- <mat-checkbox
- formControlName="accountEnabled"
- data-cy="new-user-enabled"
- >
- Enabled
- </mat-checkbox>
- <mat-checkbox
- formControlName="accountLocked"
- data-cy="new-user-locked"
- >
- Locked
- </mat-checkbox>
- </div>
+ @if (!isExternalProvider) {
+ <div fxLayout="column" class="general-options-panel">
+ <span class="general-options-header">Account</span>
+ <mat-checkbox
+ formControlName="accountEnabled"
+ data-cy="new-user-enabled"
+ >
+ Enabled
+ </mat-checkbox>
+ <mat-checkbox
+ formControlName="accountLocked"
+ data-cy="new-user-locked"
+ >
+ Locked
+ </mat-checkbox>
+ </div>
+ }
</form>
</div>
</div>
@@ -170,7 +178,10 @@
color="accent"
(click)="save()"
style="margin-right: 10px"
- [disabled]="!parentForm.valid"
+ [disabled]="
+ parentForm.invalid ||
+ (isExternalProvider && isExternallyManagedRoles)
+ "
data-cy="sp-element-edit-user-save"
>
<i class="material-icons">save</i><span> Save</span>
diff --git
a/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.ts
b/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.ts
index 885a89b8d0..a6ff998ed0 100644
---
a/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.ts
+++
b/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.ts
@@ -59,7 +59,8 @@ export class EditUserDialogComponent implements OnInit {
editMode: boolean;
isUserAccount: boolean;
- isExternalProvider: boolean = false;
+ isExternalProvider = false;
+ isExternallyManagedRoles = false;
parentForm: UntypedFormGroup;
clonedUser: UserAccount | ServiceAccount;
@@ -85,138 +86,163 @@ export class EditUserDialogComponent implements OnInit {
) {}
ngOnInit(): void {
- const filterObject =
+ this.initRoleFilter();
+ this.loadInitialData();
+ this.cloneUser();
+ this.initForm();
+ this.handleFormChanges();
+ }
+
+ save() {
+ this.registrationError = undefined;
+
+ const saveCallback = () => this.close(true);
+ const errorCallback = (error: any) => {
+ this.registrationError = error.error.notifications
+ ? error.error.notifications[0].title
+ : 'Unknown error';
+ };
+
+ if (this.editMode) {
+ const update$ = this.isUserAccount
+ ? this.userService.updateUser(this.clonedUser as UserAccount)
+ : this.userService.updateService(
+ this.clonedUser as ServiceAccount,
+ );
+
+ update$.subscribe(() => {
+ if (this.emailChanged) {
+ this.authService.logout();
+ this.close(false);
+ this.router.navigate(['login']);
+ } else {
+ saveCallback();
+ }
+ });
+ } else {
+ const create$ = this.isUserAccount
+ ? this.userService.createUser(this.clonedUser as UserAccount)
+ : this.userService.createServiceAccount(
+ this.clonedUser as ServiceAccount,
+ );
+
+ create$.subscribe(saveCallback, errorCallback);
+ }
+ }
+
+ private initRoleFilter(): void {
+ const filterRole =
this.user instanceof UserAccount
? UserRole.ROLE_SERVICE_ADMIN
: UserRole.ROLE_ADMIN;
+
this.availableRoles$ = this.availableRolesService
.getAvailableRoles()
.pipe(
map(roles =>
roles
- .filter(role => role.elementId !== filterObject)
+ .filter(role => role.elementId !== filterRole)
.sort((a, b) => a.label.localeCompare(b.label)),
),
);
- this.mailConfigService
- .getMailConfig()
- .subscribe(
- config => (this.emailConfigured = config.emailConfigured),
- );
- this.userGroupService.getAllUserGroups().subscribe(response => {
- this.availableGroups = response;
+ }
+
+ private loadInitialData(): void {
+ this.mailConfigService.getMailConfig().subscribe(config => {
+ this.emailConfigured = config.emailConfigured;
});
- this.clonedUser =
- this.user instanceof UserAccount
- ? UserAccount.fromData(this.user, new UserAccount())
- : ServiceAccount.fromData(this.user, new ServiceAccount());
- this.isUserAccount = this.user instanceof UserAccount;
+
+ this.userGroupService.getAllUserGroups().subscribe(groups => {
+ this.availableGroups = groups;
+ });
+ }
+
+ private cloneUser(): void {
+ const isUserAccount = this.user instanceof UserAccount;
+
+ this.isUserAccount = isUserAccount;
this.isExternalProvider =
- this.user instanceof UserAccount && this.user.provider !== 'local';
- this.parentForm = this.fb.group({});
- let usernameValidators = [];
- if (this.isUserAccount) {
- if ((this.clonedUser as UserAccount).provider === 'local') {
- usernameValidators = [Validators.required, Validators.email];
- } else {
- usernameValidators = [Validators.email];
- }
- } else {
- usernameValidators = [Validators.required];
+ isUserAccount && this.user.provider !== 'local';
+ this.isExternallyManagedRoles =
+ isUserAccount && this.user.externallyManagedRoles;
+
+ this.clonedUser = isUserAccount
+ ? UserAccount.fromData(this.user, new UserAccount())
+ : ServiceAccount.fromData(this.user, new ServiceAccount());
+ }
+
+ private initForm(): void {
+ const form: Record<string, any> = {};
+
+ form['username'] = [
+ this.clonedUser.username,
+ this.getUsernameValidators(),
+ ];
+
+ if (!this.isExternalProvider) {
+ form['accountEnabled'] = [this.clonedUser.accountEnabled];
+ form['accountLocked'] = [this.clonedUser.accountLocked];
}
- this.parentForm.addControl(
- 'username',
- new UntypedFormControl(this.clonedUser.username),
- );
- this.parentForm.controls['username'].setValidators(usernameValidators);
- this.parentForm.addControl(
- 'accountEnabled',
- new UntypedFormControl(this.clonedUser.accountEnabled),
- );
- this.parentForm.addControl(
- 'accountLocked',
- new UntypedFormControl(this.clonedUser.accountLocked),
- );
+
if (this.clonedUser instanceof UserAccount) {
- this.parentForm.addControl(
- 'fullName',
- new UntypedFormControl(this.clonedUser.fullName),
- );
+ form['fullName'] = [this.clonedUser.fullName];
} else {
- this.parentForm.addControl(
- 'clientSecret',
- new UntypedFormControl(this.clonedUser.clientSecret, [
- Validators.required,
- Validators.minLength(35),
- ]),
- );
+ form['clientSecret'] = [
+ this.clonedUser.clientSecret,
+ [Validators.required, Validators.minLength(35)],
+ ];
}
if (!this.editMode && this.clonedUser instanceof UserAccount) {
- this.parentForm.addControl(
- 'password',
- new UntypedFormControl(
- this.clonedUser.password,
- Validators.required,
- ),
- );
- this.parentForm.addControl(
- 'repeatPassword',
- new UntypedFormControl(),
- );
- this.parentForm.addControl(
- 'sendPasswordToUser',
- new UntypedFormControl(this.sendPasswordToUser),
- );
- this.parentForm.setValidators(this.checkPasswords);
+ form['password'] = [this.clonedUser.password, Validators.required];
+ form['repeatPassword'] = [''];
+ form['sendPasswordToUser'] = [this.sendPasswordToUser];
}
+ this.parentForm = this.fb.group(form, {
+ validators:
+ this.editMode || !this.isUserAccount
+ ? null
+ : this.checkPasswords,
+ });
+
if (this.isExternalProvider) {
- this.parentForm.controls['username'].disable();
- this.parentForm.controls['fullName'].disable();
+ this.parentForm.get('username')?.disable();
+ this.parentForm.get('fullName')?.disable();
}
+ }
+ private handleFormChanges(): void {
this.parentForm.valueChanges.subscribe(v => {
- this.clonedUser.username = v.username;
- this.clonedUser.accountLocked = v.accountLocked;
- this.clonedUser.accountEnabled = v.accountEnabled;
+ const raw = this.parentForm.getRawValue();
+ if (!this.isUserAccount || !this.isExternalProvider) {
+ this.clonedUser.username = v.username;
+ }
+
+ if (!this.isExternalProvider) {
+ this.clonedUser.accountLocked = raw.accountLocked;
+ this.clonedUser.accountEnabled = raw.accountEnabled;
+ }
+
if (this.clonedUser instanceof UserAccount) {
this.emailChanged =
this.clonedUser.username !== this.user.username &&
this.user.username ===
this.currentUserService.getCurrentUser().username &&
this.editMode;
- this.clonedUser.fullName = v.fullName;
+
+ if (!this.isExternalProvider) {
+ this.clonedUser.fullName = v.fullName;
+ }
+
if (!this.editMode) {
this.sendPasswordToUser = v.sendPasswordToUser;
+
if (this.sendPasswordToUser) {
- if (this.parentForm.controls['password']) {
- this.parentForm.removeControl('password');
- this.parentForm.removeControl('repeatPassword');
- this.parentForm.removeValidators(
- this.checkPasswords,
- );
- this.clonedUser.password = undefined;
- }
+ this.removePasswordControls();
} else {
- if (!this.parentForm.controls['password']) {
- this.parentForm.addControl(
- 'password',
- new UntypedFormControl(
- this.clonedUser.password,
- Validators.required,
- ),
- );
- this.parentForm.addControl(
- 'repeatPassword',
- new UntypedFormControl(),
- );
- this.parentForm.setValidators(this.checkPasswords);
- }
+ this.addPasswordControlsIfMissing();
this.clonedUser.password = v.password;
- this.parentForm.controls.password.setValidators(
- Validators.required,
- );
}
}
} else {
@@ -228,6 +254,42 @@ export class EditUserDialogComponent implements OnInit {
});
}
+ private removePasswordControls(): void {
+ this.parentForm.removeControl('password');
+ this.parentForm.removeControl('repeatPassword');
+ this.parentForm.clearValidators();
+ if (this.clonedUser instanceof UserAccount) {
+ this.clonedUser.password = undefined;
+ }
+ }
+
+ private addPasswordControlsIfMissing(): void {
+ if (!this.parentForm.get('password')) {
+ this.parentForm.addControl(
+ 'password',
+ new UntypedFormControl('', Validators.required),
+ );
+ this.parentForm.addControl(
+ 'repeatPassword',
+ new UntypedFormControl(),
+ );
+ this.parentForm.setValidators(this.checkPasswords);
+ }
+ }
+
+ private getUsernameValidators(): ValidatorFn[] {
+ if (this.isUserAccount) {
+ return this.user.provider === 'local'
+ ? [Validators.required, Validators.email]
+ : [Validators.email];
+ }
+ return [Validators.required];
+ }
+
+ close(refresh: boolean) {
+ this.dialogRef.close(refresh);
+ }
+
checkPasswords: ValidatorFn = (
group: AbstractControl,
): ValidationErrors | null => {
@@ -240,63 +302,6 @@ export class EditUserDialogComponent implements OnInit {
return pass.value === confirmPass.value ? null : { notMatching: true };
};
- save() {
- this.registrationError = undefined;
- if (this.editMode) {
- if (this.isUserAccount) {
- this.userService
- .updateUser(this.clonedUser as UserAccount)
- .subscribe(() => {
- if (this.emailChanged) {
- this.authService.logout();
- this.close(false);
- this.router.navigate(['login']);
- } else {
- this.close(true);
- }
- });
- } else {
- this.userService
- .updateService(this.clonedUser as ServiceAccount)
- .subscribe(() => {
- this.close(true);
- });
- }
- } else {
- if (this.isUserAccount) {
- this.userService
- .createUser(this.clonedUser as UserAccount)
- .subscribe(
- () => {
- this.close(true);
- },
- error => {
- this.registrationError = error.error.notifications
- ? error.error.notifications[0].title
- : 'Unknown error';
- },
- );
- } else {
- this.userService
- .createServiceAccount(this.clonedUser as ServiceAccount)
- .subscribe(
- () => {
- this.close(true);
- },
- error => {
- this.registrationError = error.error.notifications
- ? error.error.notifications[0].title
- : 'Unknown error';
- },
- );
- }
- }
- }
-
- close(refresh: boolean) {
- this.dialogRef.close(refresh);
- }
-
changeGroupAssignment(event: MatCheckboxChange) {
if (this.clonedUser.groups.indexOf(event.source.value) > -1) {
this.clonedUser.groups.splice(
diff --git
a/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.html
b/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.html
index d3d3f60b7e..97f186e049 100644
---
a/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.html
+++
b/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.html
@@ -45,6 +45,15 @@
</td>
</ng-container>
+ <ng-container matColumnDef="groupId">
+ <th mat-header-cell mat-sort-header *matHeaderCellDef>
+ Group ID
+ </th>
+ <td mat-cell *matCellDef="let group">
+ {{ group.elementId }}
+ </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/user-group-configuration/user-group-configuration.component.ts
b/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.ts
index e8bc605135..b00799cc6f 100644
---
a/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.ts
+++
b/ui/src/app/configuration/security-configuration/user-group-configuration/user-group-configuration.component.ts
@@ -36,7 +36,7 @@ import { MatDialog } from '@angular/material/dialog';
export class SecurityUserGroupConfigComponent implements OnInit {
dataSource: MatTableDataSource<Group>;
- displayedColumns: string[] = ['groupName', 'edit'];
+ displayedColumns: string[] = ['groupName', 'groupId', 'edit'];
constructor(
private userGroupService: UserGroupService,