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 e21cbaea4a feat: Support alternate ids for groups and roles (#3738)
e21cbaea4a is described below
commit e21cbaea4a657dd0b760de28819e21a8bdcd5814
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Aug 20 08:16:37 2025 +0200
feat: Support alternate ids for groups and roles (#3738)
* feat: Support alternate ids for groups and roles
* Fix model
---
.../environment/model/OAuthConfiguration.java | 12 +++-
.../parser/OAuthConfigurationParser.java | 19 +++++-
.../streampipes/model/client/user/Group.java | 9 +++
.../apache/streampipes/model/client/user/Role.java | 12 ++++
.../tasks/ApplyDefaultRolesAndPrivilegesTask.java | 16 +++--
.../service/core/oauth2/UserService.java | 71 +++++++++++++++++++---
.../src/lib/model/gen/streampipes-model-client.ts | 10 ++-
ui/src/app/configuration/configuration.module.ts | 2 +
.../alternate-id-configuration.component.html | 48 +++++++++++++++
.../alternate-id-configuration.component.ts} | 39 ++++++------
.../edit-group-dialog.component.html | 7 +++
.../edit-role-dialog.component.html | 20 ++++--
.../edit-role-dialog.component.scss | 1 -
.../edit-role-dialog/edit-role-dialog.component.ts | 19 ++++--
.../role-configuration.component.html | 1 -
15 files changed, 238 insertions(+), 48 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 607bf468b5..65ce2ea3a7 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
@@ -18,6 +18,8 @@
package org.apache.streampipes.commons.environment.model;
+import java.util.Set;
+
public class OAuthConfiguration {
private String authorizationUri;
@@ -35,7 +37,7 @@ public class OAuthConfiguration {
private String emailAttributeName;
private String userIdAttributeName;
private String roleAttributeName;
-
+ private Set<String> defaultRoles;
public String getRegistrationId() {
return registrationId;
@@ -156,4 +158,12 @@ public class OAuthConfiguration {
public void setRegistrationName(String registrationName) {
this.registrationName = registrationName;
}
+
+ public Set<String> getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(Set<String> defaultRoles) {
+ this.defaultRoles = defaultRoles;
+ }
}
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 3650a06104..98d4004ca6 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
@@ -28,6 +28,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* The {@code OAuthConfigurationParser} class is responsible for parsing OAuth
provider configurations
@@ -52,8 +54,16 @@ public class OAuthConfigurationParser {
private static final Logger LOG =
LoggerFactory.getLogger(OAuthConfigurationParser.class);
-
private static final String OAUTH_PREFIX = "SP_OAUTH_PROVIDER";
+ private static final Set<String> defaultRoles = Set.of(
+ "ROLE_PIPELINE_USER",
+ "ROLE_PIPELINE_ADMIN",
+ "ROLE_CONNECT_ADMIN",
+ "ROLE_DASHBOARD_USER",
+ "ROLE_DASHBOARD_ADMIN",
+ "ROLE_DATA_EXPLORER_USER",
+ "ROLE_DATA_EXPLORER_ADMIN"
+ );
public List<OAuthConfiguration> parse(Map<String, String> env) {
Map<String, OAuthConfiguration> oAuthConfigurationsMap = new HashMap<>();
@@ -97,6 +107,12 @@ public class OAuthConfigurationParser {
case "USER_ID_ATTRIBUTE_NAME" ->
oAuthConfiguration.setUserIdAttributeName(value);
case "ROLE_ATTRIBUTE_NAME" ->
oAuthConfiguration.setRoleAttributeName(value);
case "NAME" -> oAuthConfiguration.setRegistrationName(value);
+ case "DEFAULT_ROLES" -> {
+ var defaultRoles = Arrays.stream(value.split(","))
+ .map(String::trim)
+ .collect(Collectors.toSet());
+ oAuthConfiguration.setDefaultRoles(defaultRoles);
+ }
default -> LOG.warn(
"Unknown setting {} for oauth configuration in environment
variable {}",
settingName,
@@ -133,6 +149,7 @@ public class OAuthConfigurationParser {
) {
var oAuthConfiguration =
oAuthConfigurationsMap.computeIfAbsent(registrationId, k -> new
OAuthConfiguration());
oAuthConfiguration.setRegistrationId(registrationId);
+ oAuthConfiguration.setDefaultRoles(defaultRoles);
return oAuthConfiguration;
}
}
diff --git
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Group.java
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Group.java
index 4ce6d272c9..906555ccaf 100644
---
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Group.java
+++
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Group.java
@@ -37,6 +37,7 @@ public class Group implements Storable {
@JsonIgnore
private String type = "group";
+ private Set<String> alternateIds = new HashSet<>();
private String groupName;
private Set<String> roles;
@@ -94,4 +95,12 @@ public class Group implements Storable {
public void setType(String type) {
this.type = type;
}
+
+ public Set<String> getAlternateIds() {
+ return alternateIds;
+ }
+
+ public void setAlternateIds(Set<String> alternateIds) {
+ this.alternateIds = alternateIds;
+ }
}
diff --git
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Role.java
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Role.java
index 84542b9a30..b91bf4a48c 100644
---
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Role.java
+++
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Role.java
@@ -24,7 +24,9 @@ import org.apache.streampipes.model.shared.api.Storable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.gson.annotations.SerializedName;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
@TsModel
public class Role implements Storable {
@@ -35,6 +37,7 @@ public class Role implements Storable {
private String label;
private boolean defaultRole;
private List<String> privilegeIds;
+ private Set<String> alternateIds;
// document type should be persisted to CouchDB with Gson serialization, but
not via Jackson to the UI
@JsonIgnore
@@ -49,6 +52,7 @@ public class Role implements Storable {
role.label = label;
role.defaultRole = true;
role.privilegeIds = privilegeIds;
+ role.alternateIds = new HashSet<>();
return role;
}
@@ -103,4 +107,12 @@ public class Role implements Storable {
public void setPrivilegeIds(List<String> privilegeIds) {
this.privilegeIds = privilegeIds;
}
+
+ public Set<String> getAlternateIds() {
+ return alternateIds;
+ }
+
+ public void setAlternateIds(Set<String> alternateIds) {
+ this.alternateIds = alternateIds;
+ }
}
diff --git
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/ApplyDefaultRolesAndPrivilegesTask.java
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/ApplyDefaultRolesAndPrivilegesTask.java
index da26dc9031..f3d346aa6c 100644
---
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/ApplyDefaultRolesAndPrivilegesTask.java
+++
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/ApplyDefaultRolesAndPrivilegesTask.java
@@ -54,13 +54,17 @@ public class ApplyDefaultRolesAndPrivilegesTask implements
InstallationTask {
private <T extends Storable> void updateDocs(CRUDStorage<T> storage,
List<T> defaultDocs) {
- defaultDocs.forEach(role -> {
- var doc = storage.getElementById(role.getElementId());
- if (doc != null) {
- role.setRev(doc.getRev());
- storage.updateElement(role);
+ defaultDocs.forEach(doc -> {
+ var existingDoc = storage.getElementById(doc.getElementId());
+ if (existingDoc != null) {
+ doc.setRev(existingDoc.getRev());
+ // Preserve alternateIds for Role objects
+ if (doc instanceof Role && existingDoc instanceof Role) {
+ ((Role) doc).setAlternateIds(((Role) existingDoc).getAlternateIds());
+ }
+ storage.updateElement(doc);
} else {
- storage.persist(role);
+ storage.persist(doc);
}
});
}
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 956a99778d..5f766659c8 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
@@ -21,13 +21,17 @@ 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.Group;
+import org.apache.streampipes.model.client.user.Role;
import org.apache.streampipes.model.client.user.UserAccount;
import org.apache.streampipes.resource.management.UserResourceManager;
import
org.apache.streampipes.rest.security.OAuth2AuthenticationProcessingException;
+import org.apache.streampipes.storage.api.CRUDStorage;
import org.apache.streampipes.storage.api.IUserStorage;
import org.apache.streampipes.storage.management.StorageDispatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
@@ -37,15 +41,24 @@ 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 {
+ private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
+
private final IUserStorage userStorage;
+ private final CRUDStorage<Role> roleStorage;
+ private final CRUDStorage<Group> groupStorage;
private final Environment env;
+ private List<Role> allRoles;
+ private List<Group> allGroups;
public UserService() {
this.userStorage =
StorageDispatcher.INSTANCE.getNoSqlStore().getUserStorageAPI();
+ this.roleStorage =
StorageDispatcher.INSTANCE.getNoSqlStore().getRoleStorage();
+ this.groupStorage =
StorageDispatcher.INSTANCE.getNoSqlStore().getUserGroupStorage();
+ this.allGroups = this.groupStorage.findAll();
+ this.allRoles = this.roleStorage.findAll();
this.env = Environments.getEnvironment();
}
@@ -78,11 +91,11 @@ public class UserService {
String.format("Already signed up with another provider %s",
user.getProvider())
);
}
- applyRoles(user, oAuthConfig, attributes);
+ applyRoles(user, oAuthConfig, attributes, false);
userStorage.updateUser(user);
} else {
user = toUserAccount(registrationId, principalId, email, fullName);
- applyRoles(user, oAuthConfig, attributes);
+ applyRoles(user, oAuthConfig, attributes, true);
new UserResourceManager().registerOauthUser(user);
}
@@ -97,7 +110,8 @@ public class UserService {
private void applyRoles(UserAccount user,
OAuthConfiguration oAuthConfig,
- Map<String, Object> attributes) {
+ Map<String, Object> attributes,
+ boolean newUser) {
if (oAuthConfig.getRoleAttributeName() != null) {
Object rolesObject = attributes.get(oAuthConfig.getRoleAttributeName());
@@ -105,20 +119,57 @@ public class UserService {
Set<String> roles = extractRoleOrGroup("ROLE", rolesList);
Set<String> groups = convertGroup(extractRoleOrGroup("GROUP",
rolesList));
+ allRoles.forEach(role -> {
+ if (Objects.nonNull(role.getAlternateIds())) {
+ role.getAlternateIds().forEach(a -> {
+ if (rolesList.contains(a)) {
+ roles.add(role.getElementId());
+ }
+ });
+ }
+ });
+
+ allGroups.forEach(group -> {
+ if (Objects.nonNull(group.getAlternateIds())) {
+ group.getAlternateIds().forEach(a -> {
+ if (rolesList.contains(a)) {
+ groups.add(group.getElementId());
+ }
+ });
+ }
+ });
+
user.setRoles(roles);
user.setGroups(groups);
user.setExternallyManagedRoles(true);
} else {
- applyDefaultRole(user);
+ LOG.warn(
+ "Invalid role attribute: {} of type {}",
+ oAuthConfig.getRoleAttributeName(),
+ Objects.nonNull(rolesObject) ? rolesObject.getClass().getName() :
"null"
+ );
+ applyDefaultRole(user, oAuthConfig.getDefaultRoles(), newUser);
}
} else {
- applyDefaultRole(user);
+ LOG.warn("Applying default roles as no role attribute is configured");
+ applyDefaultRole(user, oAuthConfig.getDefaultRoles(), newUser);
}
}
- private void applyDefaultRole(UserAccount user) {
-
user.setRoles(Stream.of(DefaultRole.ROLE_ADMIN.toString()).collect(Collectors.toSet()));
- user.setExternallyManagedRoles(false);
+ private void applyDefaultRole(UserAccount user,
+ Set<String> defaultRoles,
+ boolean newUser) {
+ if (newUser) {
+ user.setRoles(
+ defaultRoles
+ .stream()
+ .filter(r -> allRoles
+ .stream()
+ .anyMatch(role -> role.getElementId().equals(r)))
+ .collect(Collectors.toSet())
+ );
+ user.setExternallyManagedRoles(false);
+ }
}
private UserAccount toUserAccount(String registrationId,
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 f6d00886bf..09c378e914 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
@@ -20,11 +20,12 @@
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-08-05
18:37:35.
+// Generated using typescript-generator version 3.2.1263 on 2025-08-19
20:00:50.
import { Storable } from './streampipes-model';
export class Group implements Storable {
+ alternateIds: string[];
elementId: string;
groupId: string;
groupName: string;
@@ -36,6 +37,9 @@ export class Group implements Storable {
return data;
}
const instance = target || new Group();
+ instance.alternateIds = __getCopyArrayFn(__identity<string>())(
+ data.alternateIds,
+ );
instance.elementId = data.elementId;
instance.groupId = data.groupId;
instance.groupName = data.groupName;
@@ -189,6 +193,7 @@ export class RawUserApiToken {
}
export class Role implements Storable {
+ alternateIds: string[];
defaultRole: boolean;
elementId: string;
label: string;
@@ -200,6 +205,9 @@ export class Role implements Storable {
return data;
}
const instance = target || new Role();
+ instance.alternateIds = __getCopyArrayFn(__identity<string>())(
+ data.alternateIds,
+ );
instance.defaultRole = data.defaultRole;
instance.elementId = data.elementId;
instance.label = data.label;
diff --git a/ui/src/app/configuration/configuration.module.ts
b/ui/src/app/configuration/configuration.module.ts
index 25fe8959aa..14ad6a1fac 100644
--- a/ui/src/app/configuration/configuration.module.ts
+++ b/ui/src/app/configuration/configuration.module.ts
@@ -102,6 +102,7 @@ import { GenericStorageItemsComponent } from
'./export/export-dialog/generic-sto
import { TranslateModule } from '@ngx-translate/core';
import { CertificateConfigurationComponent } from
'./extensions-service-management/certificate-configuration/certificate-configuration.component';
import { CertificateDetailsDialogComponent } from
'./dialog/certificate-details/certificate-details-dialog.component';
+import { AlternateIdConfigurationComponent } from
'./security-configuration/alternate-id-configuration/alternate-id-configuration.component';
@NgModule({
imports: [
@@ -259,6 +260,7 @@ import { CertificateDetailsDialogComponent } from
'./dialog/certificate-details/
PipelineElementTypeFilter,
CertificateConfigurationComponent,
CertificateDetailsDialogComponent,
+ AlternateIdConfigurationComponent,
],
providers: [
OrderByPipe,
diff --git
a/ui/src/app/configuration/security-configuration/alternate-id-configuration/alternate-id-configuration.component.html
b/ui/src/app/configuration/security-configuration/alternate-id-configuration/alternate-id-configuration.component.html
new file mode 100644
index 0000000000..f02c21b7bb
--- /dev/null
+++
b/ui/src/app/configuration/security-configuration/alternate-id-configuration/alternate-id-configuration.component.html
@@ -0,0 +1,48 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+
+<div fxLayout="column">
+ @for (id of alternateIds; track $index) {
+ <div fxLayout="row" fxLayoutAlign="start center" class="p-5 border">
+ <span fxFlex>{{ id }}</span>
+ <button
+ mat-icon-button
+ color="accent"
+ (click)="removeAlternateId(id)"
+ >
+ <mat-icon>delete</mat-icon>
+ </button>
+ </div>
+ }
+ <div fxLayout="row" fxLayoutAlign="start center" class="alternate-id-row">
+ <mat-form-field color="accent" fxFlex subscriptSizing="dynamic">
+ <mat-label>New Alternate ID</mat-label>
+ <input
+ matInput
+ [(ngModel)]="newAlternateId"
+ (keyup.enter)="addAlternateId()"
+ placeholder="Enter new alternate ID"
+ />
+ </mat-form-field>
+ <div fxLayoutAlign="start center">
+ <button mat-button class="mat-basic" (click)="addAlternateId()">
+ <mat-icon>add</mat-icon><span>Add</span>
+ </button>
+ </div>
+ </div>
+</div>
diff --git
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
b/ui/src/app/configuration/security-configuration/alternate-id-configuration/alternate-id-configuration.component.ts
similarity index 56%
copy from
ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
copy to
ui/src/app/configuration/security-configuration/alternate-id-configuration/alternate-id-configuration.component.ts
index 72634367f4..23838e01b2 100644
---
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
+++
b/ui/src/app/configuration/security-configuration/alternate-id-configuration/alternate-id-configuration.component.ts
@@ -1,4 +1,4 @@
-/*!
+/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
@@ -16,24 +16,27 @@
*
*/
-.lists-container {
- display: flex;
- justify-content: space-between;
-}
+import { Component, Input, OnInit } from '@angular/core';
-.list-section {
- width: 45%;
- font-size: 10pt;
- border: 1px solid var(--color-bg-2);
-}
+@Component({
+ selector: 'sp-alternate-id-configuration',
+ templateUrl: './alternate-id-configuration.component.html',
+ standalone: false,
+})
+export class AlternateIdConfigurationComponent {
+ @Input()
+ alternateIds: string[] = [];
-.button-section {
- display: flex;
- flex-direction: column;
- justify-content: center;
-}
+ newAlternateId: string = '';
+
+ addAlternateId(): void {
+ if (!this.alternateIds) {
+ this.alternateIds = [];
+ }
+ this.alternateIds.push(this.newAlternateId);
+ }
-.privilege-header {
- padding: 10px;
- font-weight: bold;
+ removeAlternateId(id: string): void {
+ this.alternateIds.splice(this.alternateIds.indexOf(id), 1);
+ }
}
diff --git
a/ui/src/app/configuration/security-configuration/edit-group-dialog/edit-group-dialog.component.html
b/ui/src/app/configuration/security-configuration/edit-group-dialog/edit-group-dialog.component.html
index 53eac39a15..1769401974 100644
---
a/ui/src/app/configuration/security-configuration/edit-group-dialog/edit-group-dialog.component.html
+++
b/ui/src/app/configuration/security-configuration/edit-group-dialog/edit-group-dialog.component.html
@@ -44,6 +44,13 @@
{{ role.label }}
</mat-checkbox>
</div>
+ <div fxLayout="column" class="general-options-panel">
+ <span class="general-options-header">Alternate IDs</span>
+ <sp-alternate-id-configuration
+ [alternateIds]="clonedGroup.alternateIds"
+ >
+ </sp-alternate-id-configuration>
+ </div>
</form>
</div>
</div>
diff --git
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.html
b/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.html
index b404b3ff57..be0ed9da1a 100644
---
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.html
+++
b/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.html
@@ -52,7 +52,7 @@
fxLayoutGap="10px"
class="lists-container"
>
- <div class="list-section">
+ <div class="list-section" fxFlex="50">
<div class="privilege-header">
Available Privileges
</div>
@@ -62,7 +62,10 @@
mat-icon-button
color="accent"
(click)="assignPrivilege(privilege)"
- [disabled]="isAssigned(privilege)"
+ [disabled]="
+ isAssigned(privilege) ||
+ clonedRole.defaultRole
+ "
>
<mat-icon>add</mat-icon>
</button>
@@ -70,7 +73,7 @@
</div>
</div>
</div>
- <div class="list-section">
+ <div class="list-section" fxFlex="50">
<div class="privilege-header">
Selected Privileges
</div>
@@ -79,6 +82,7 @@
<button
mat-icon-button
color="accent"
+ [disabled]="clonedRole.defaultRole"
(click)="removePrivilege(privilege)"
>
<mat-icon>remove</mat-icon>
@@ -89,6 +93,14 @@
</div>
</div>
</div>
+
+ <div fxLayout="column" class="general-options-panel">
+ <span class="general-options-header">Alternate IDs</span>
+ <sp-alternate-id-configuration
+ [alternateIds]="clonedRole.alternateIds"
+ >
+ </sp-alternate-id-configuration>
+ </div>
</form>
</div>
</div>
@@ -102,7 +114,7 @@
(click)="save()"
style="margin-right: 10px"
[disabled]="
- !parentForm.valid || selectedPrivileges.length === 0
+ parentForm.invalid || selectedPrivileges.length === 0
"
data-cy="sp-edit-role-save"
>
diff --git
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
b/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
index 72634367f4..ebb3e55286 100644
---
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
+++
b/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.scss
@@ -22,7 +22,6 @@
}
.list-section {
- width: 45%;
font-size: 10pt;
border: 1px solid var(--color-bg-2);
}
diff --git
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.ts
b/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.ts
index 0032d0a3cb..415649811a 100644
---
a/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.ts
+++
b/ui/src/app/configuration/security-configuration/edit-role-dialog/edit-role-dialog.component.ts
@@ -74,14 +74,23 @@ export class EditRoleDialogComponent implements OnInit {
this.parentForm = this.fb.group({});
this.parentForm.addControl(
'label',
- new FormControl(this.clonedRole.label, Validators.required),
+ new FormControl(
+ {
+ value: this.clonedRole.label,
+ disabled: this.clonedRole.defaultRole,
+ },
+ Validators.required,
+ ),
);
this.parentForm.addControl(
'elementId',
- new FormControl(this.clonedRole.elementId, [
- Validators.required,
- Validators.pattern(/^ROLE_[A-Z_]+$/),
- ]),
+ new FormControl(
+ {
+ value: this.clonedRole.elementId,
+ disabled: this.clonedRole.defaultRole,
+ },
+ [Validators.required, Validators.pattern(/^ROLE_[A-Z_]+$/)],
+ ),
);
}
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 b5feadd27e..79c00bdd35 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
@@ -77,7 +77,6 @@
matTooltip="Edit user"
matTooltipPosition="above"
data-cy="role-edit-btn"
- [disabled]="role.defaultRole"
(click)="editRole(role)"
>
<i class="material-icons">edit</i>