parent
1f197aa96b
commit
07b0df8f62
21 changed files with 292 additions and 7 deletions
|
@ -0,0 +1,41 @@
|
|||
package org.keycloak.migration.migrators;
|
||||
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.AccountRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
public class MigrateTo20_0_0 implements Migration {
|
||||
|
||||
public static final ModelVersion VERSION = new ModelVersion("20.0.0");
|
||||
|
||||
@Override
|
||||
public void migrate(KeycloakSession session) {
|
||||
|
||||
session.realms().getRealmsStream().forEach(this::addViewGroupsRole);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
|
||||
addViewGroupsRole(realm);
|
||||
}
|
||||
|
||||
private void addViewGroupsRole(RealmModel realm) {
|
||||
ClientModel accountClient = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
if (accountClient != null && accountClient.getRole(AccountRoles.VIEW_GROUPS) == null) {
|
||||
RoleModel viewGroupsRole = accountClient.addRole(AccountRoles.VIEW_GROUPS);
|
||||
viewGroupsRole.setDescription("${role_" + AccountRoles.VIEW_GROUPS + "}");
|
||||
ClientModel accountConsoleClient = realm.getClientByClientId(Constants.ACCOUNT_CONSOLE_CLIENT_ID);
|
||||
accountConsoleClient.addScopeMapping(viewGroupsRole);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelVersion getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.keycloak.migration.ModelVersion;
|
|||
import org.keycloak.migration.migrators.MigrateTo12_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo14_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo18_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo20_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_2_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_3_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_4_0;
|
||||
|
@ -104,7 +105,8 @@ public class LegacyMigrationManager implements MigrationManager {
|
|||
new MigrateTo9_0_4(),
|
||||
new MigrateTo12_0_0(),
|
||||
new MigrateTo14_0_0(),
|
||||
new MigrateTo18_0_0()
|
||||
new MigrateTo18_0_0(),
|
||||
new MigrateTo20_0_0()
|
||||
};
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
|
|
@ -29,6 +29,7 @@ public interface AccountRoles {
|
|||
String VIEW_CONSENT = "view-consent";
|
||||
String MANAGE_CONSENT = "manage-consent";
|
||||
String DELETE_ACCOUNT = "delete-account";
|
||||
String VIEW_GROUPS = "view-groups";
|
||||
|
||||
String[] DEFAULT = {VIEW_PROFILE, MANAGE_ACCOUNT};
|
||||
|
||||
|
|
|
@ -438,6 +438,8 @@ public class RealmManager {
|
|||
RoleModel manageConsentRole = accountClient.addRole(AccountRoles.MANAGE_CONSENT);
|
||||
manageConsentRole.setDescription("${role_" + AccountRoles.MANAGE_CONSENT + "}");
|
||||
manageConsentRole.addCompositeRole(viewConsentRole);
|
||||
RoleModel viewGroups = accountClient.addRole(AccountRoles.VIEW_GROUPS);
|
||||
viewGroups.setDescription("${role_" + AccountRoles.VIEW_GROUPS + "}");
|
||||
|
||||
KeycloakModelUtils.setupDeleteAccount(accountClient);
|
||||
|
||||
|
@ -458,6 +460,7 @@ public class RealmManager {
|
|||
accountConsoleClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
|
||||
accountConsoleClient.addScopeMapping(accountClient.getRole(AccountRoles.MANAGE_ACCOUNT));
|
||||
accountConsoleClient.addScopeMapping(accountClient.getRole(AccountRoles.VIEW_GROUPS));
|
||||
|
||||
ProtocolMapperModel audienceMapper = new ProtocolMapperModel();
|
||||
audienceMapper.setName(OIDCLoginProtocolFactory.AUDIENCE_RESOLVE);
|
||||
|
|
|
@ -138,15 +138,21 @@ public class AccountConsole {
|
|||
|
||||
boolean isTotpConfigured = false;
|
||||
boolean deleteAccountAllowed = false;
|
||||
boolean isViewGroupsEnabled= false;
|
||||
if (user != null) {
|
||||
isTotpConfigured = user.credentialManager().isConfiguredFor(realm.getOTPPolicy().getType());
|
||||
RoleModel deleteAccountRole = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getRole(AccountRoles.DELETE_ACCOUNT);
|
||||
deleteAccountAllowed = deleteAccountRole != null && user.hasRole(deleteAccountRole) && realm.getRequiredActionProviderByAlias(DeleteAccount.PROVIDER_ID).isEnabled();
|
||||
RoleModel viewGrouRole = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getRole(AccountRoles.VIEW_GROUPS);
|
||||
isViewGroupsEnabled = viewGrouRole != null && user.hasRole(viewGrouRole);
|
||||
}
|
||||
|
||||
map.put("isTotpConfigured", isTotpConfigured);
|
||||
|
||||
map.put("deleteAccountAllowed", deleteAccountAllowed);
|
||||
|
||||
map.put("isViewGroupsEnabled", isViewGroupsEnabled);
|
||||
|
||||
map.put("updateEmailFeatureEnabled", Profile.isFeatureEnabled(Profile.Feature.UPDATE_EMAIL));
|
||||
RequiredActionProviderModel updateEmailActionProvider = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_EMAIL.name());
|
||||
map.put("updateEmailActionEnabled", updateEmailActionProvider != null && updateEmailActionProvider.isEnabled());
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.stream.Stream;
|
|||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.POST;
|
||||
|
@ -66,6 +67,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.representations.account.ClientRepresentation;
|
||||
import org.keycloak.representations.account.ConsentRepresentation;
|
||||
|
@ -74,6 +76,7 @@ import org.keycloak.representations.account.UserProfileAttributeMetadata;
|
|||
import org.keycloak.representations.account.UserProfileMetadata;
|
||||
import org.keycloak.representations.account.UserRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.services.managers.UserConsentManager;
|
||||
|
@ -484,6 +487,15 @@ public class AccountRestService {
|
|||
return new LinkedAccountsResource(session, request, client, auth, event, user);
|
||||
}
|
||||
|
||||
@Path("/groups")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Stream<GroupRepresentation> groupMemberships(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
||||
auth.require(AccountRoles.VIEW_GROUPS);
|
||||
return ModelToRepresentation.toGroupHierarchy(user, !briefRepresentation);
|
||||
}
|
||||
|
||||
@Path("/applications")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
|
|
@ -627,7 +627,7 @@ public class ClientTest extends AbstractAdminTest {
|
|||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll(), AccountRoles.VIEW_PROFILE);
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective(), AccountRoles.VIEW_PROFILE);
|
||||
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS, AccountRoles.VIEW_APPLICATIONS, AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT, AccountRoles.DELETE_ACCOUNT);
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS, AccountRoles.VIEW_APPLICATIONS, AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT, AccountRoles.DELETE_ACCOUNT, AccountRoles.VIEW_GROUPS);
|
||||
|
||||
Assert.assertNames(scopesResource.getAll().getRealmMappings(), "realm-composite");
|
||||
Assert.assertNames(scopesResource.getAll().getClientMappings().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), AccountRoles.VIEW_PROFILE);
|
||||
|
@ -643,7 +643,7 @@ public class ClientTest extends AbstractAdminTest {
|
|||
Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "realm-composite", "realm-child", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME);
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll());
|
||||
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.VIEW_PROFILE, AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS, AccountRoles.VIEW_APPLICATIONS, AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT, AccountRoles.DELETE_ACCOUNT);
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.VIEW_PROFILE, AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS, AccountRoles.VIEW_APPLICATIONS, AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT, AccountRoles.DELETE_ACCOUNT, AccountRoles.VIEW_GROUPS);
|
||||
|
||||
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective());
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ import static org.junit.Assert.assertTrue;
|
|||
import static org.junit.Assert.fail;
|
||||
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
|
||||
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
|
||||
import static org.keycloak.models.AccountRoles.VIEW_GROUPS;
|
||||
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
|
||||
import static org.keycloak.testsuite.Assert.assertNames;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
||||
|
@ -319,6 +320,12 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testPostLogoutRedirectUrisSet(migrationRealm);
|
||||
}
|
||||
|
||||
protected void testMigrationTo20_0_0() {
|
||||
testViewGroups(masterRealm);
|
||||
testViewGroups(migrationRealm);
|
||||
}
|
||||
|
||||
|
||||
protected void testDeleteAccount(RealmResource realm) {
|
||||
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
||||
ClientResource accountResource = realm.clients().get(accountClient.getId());
|
||||
|
@ -387,9 +394,8 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
MappingsRepresentation scopes = clientResource.getScopeMappings().getAll();
|
||||
assertNull(scopes.getRealmMappings());
|
||||
assertEquals(1, scopes.getClientMappings().size());
|
||||
assertEquals(1, scopes.getClientMappings().get(ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings().size());
|
||||
assertEquals(MANAGE_ACCOUNT, scopes.getClientMappings().get(ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings().get(0).getName());
|
||||
|
||||
assertEquals(2, scopes.getClientMappings().get(ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings().size());
|
||||
Assert.assertNames(scopes.getClientMappings().get(ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), MANAGE_ACCOUNT, VIEW_GROUPS);
|
||||
List<ProtocolMapperRepresentation> mappers = clientResource.getProtocolMappers().getMappers();
|
||||
assertEquals(1, mappers.size());
|
||||
assertEquals("oidc-audience-resolve-mapper", mappers.get(0).getProtocolMapper());
|
||||
|
@ -491,6 +497,14 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected void testViewGroups(RealmResource realm) {
|
||||
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
||||
|
||||
ClientResource accountResource = realm.clients().get(accountClient.getId());
|
||||
RoleRepresentation viewAppRole = accountResource.roles().get(VIEW_GROUPS).toRepresentation();
|
||||
assertNotNull(viewAppRole);
|
||||
}
|
||||
|
||||
protected void testRoleManageAccountLinks(RealmResource... realms) {
|
||||
log.info("testing role manage account links");
|
||||
for (RealmResource realm : realms) {
|
||||
|
@ -954,6 +968,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testMigrationTo19_0_0();
|
||||
}
|
||||
|
||||
protected void testMigrationTo20_x() {
|
||||
testMigrationTo20_0_0();
|
||||
}
|
||||
|
||||
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
|
||||
if (supportedAuthzServices) {
|
||||
testDecisionStrategySetOnResourceServer();
|
||||
|
|
|
@ -66,5 +66,6 @@ public class JsonFileImport1301MigrationClientPoliciesTest extends AbstractJsonF
|
|||
Assert.assertTrue(clientProfiles.getProfiles().isEmpty());
|
||||
ClientPoliciesRepresentation clientPolicies = adminClient.realms().realm("test").clientPoliciesPoliciesResource().getPolicies();
|
||||
Assert.assertTrue(clientPolicies.getPolicies().isEmpty());
|
||||
testViewGroups(masterRealm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo20_x();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -72,6 +72,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo20_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo20_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo20_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ public class JsonFileImport903MigrationTest extends AbstractJsonFileImportMigrat
|
|||
checkRealmsImported();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo20_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,6 +68,8 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
testExtremelyLongClientAttribute(migrationRealm);
|
||||
|
||||
testMigrationTo20_x();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -78,6 +80,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo19_x();
|
||||
testMigrationTo20_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -97,6 +100,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo19_x();
|
||||
testMigrationTo20_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -117,6 +121,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo19_x();
|
||||
testMigrationTo20_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -145,6 +150,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo19_x();
|
||||
testMigrationTo20_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -166,6 +172,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
testMigrationTo19_x();
|
||||
testMigrationTo20_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
|
|
@ -80,6 +80,7 @@ role_create-realm=Create realm
|
|||
role_view-realm=View realm
|
||||
role_view-users=View users
|
||||
role_view-applications=View applications
|
||||
role_view-groups=View groups
|
||||
role_view-clients=View clients
|
||||
role_view-events=View events
|
||||
role_view-identity-providers=View identity providers
|
||||
|
|
|
@ -48,7 +48,8 @@
|
|||
isTotpConfigured : ${isTotpConfigured?c},
|
||||
deleteAccountAllowed : ${deleteAccountAllowed?c},
|
||||
updateEmailFeatureEnabled: ${updateEmailFeatureEnabled?c},
|
||||
updateEmailActionEnabled: ${updateEmailActionEnabled?c}
|
||||
updateEmailActionEnabled: ${updateEmailActionEnabled?c},
|
||||
isViewGroupsEnabled : ${isViewGroupsEnabled?c}
|
||||
}
|
||||
|
||||
var availableLocales = [];
|
||||
|
|
|
@ -166,3 +166,11 @@ error-username-invalid-character=''{0}'' contains invalid character.
|
|||
error-person-name-invalid-character='{0}' contains invalid character.
|
||||
|
||||
updateEmail=Update email
|
||||
|
||||
#groups
|
||||
groupLabel=Groups
|
||||
groupDescriptionLabel=View groups that you are associated with
|
||||
path=Path
|
||||
directMembership=Direct membership
|
||||
noGroups=No groups
|
||||
noGroupsText=You are not joined in any group
|
||||
|
|
|
@ -47,6 +47,16 @@
|
|||
"modulePath": "/content/applications-page/ApplicationsPage.js",
|
||||
"componentName": "ApplicationsPage"
|
||||
},
|
||||
{
|
||||
"id": "groups",
|
||||
"path": "groups",
|
||||
"icon": "pf-icon-server-group",
|
||||
"label": "groupLabel",
|
||||
"descriptionLabel": "groupDescriptionLabel",
|
||||
"modulePath": "/content/group-page/GroupsPage.js",
|
||||
"componentName": "GroupsPage",
|
||||
"hidden": "!features.isViewGroupsEnabled"
|
||||
},
|
||||
{
|
||||
"id": "resources",
|
||||
"icon": "pf-icon-repository",
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
DataList,
|
||||
DataListItem,
|
||||
DataListItemRow,
|
||||
DataListCell,
|
||||
DataListItemCells,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { ContentPage } from '../ContentPage';
|
||||
import { HttpResponse } from '../../account-service/account.service';
|
||||
import { AccountServiceContext } from '../../account-service/AccountServiceContext';
|
||||
import { Msg } from '../../widgets/Msg';
|
||||
|
||||
export interface GroupsPageProps {
|
||||
}
|
||||
|
||||
export interface GroupsPageState {
|
||||
groups: Group[];
|
||||
directGroups: Group[];
|
||||
isDirectMembership: boolean;
|
||||
}
|
||||
|
||||
interface Group {
|
||||
id?: string;
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export class GroupsPage extends React.Component<GroupsPageProps, GroupsPageState> {
|
||||
static contextType = AccountServiceContext;
|
||||
context: React.ContextType<typeof AccountServiceContext>;
|
||||
|
||||
public constructor(props: GroupsPageProps, context: React.ContextType<typeof AccountServiceContext>) {
|
||||
super(props);
|
||||
this.context = context;
|
||||
this.state = {
|
||||
groups: [],
|
||||
directGroups: [],
|
||||
isDirectMembership: false
|
||||
};
|
||||
|
||||
this.fetchGroups();
|
||||
}
|
||||
|
||||
private fetchGroups(): void {
|
||||
this.context!.doGet<Group[]>("/groups")
|
||||
.then((response: HttpResponse<Group[]>) => {
|
||||
const directGroups = response.data || [];
|
||||
const groups = [...directGroups];
|
||||
const groupsPaths = directGroups.map(s => s.path);
|
||||
directGroups.forEach((el) => this.getParents(el, groups, groupsPaths))
|
||||
this.setState({
|
||||
groups: groups,
|
||||
directGroups: directGroups
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getParents(el: Group, groups: Group[], groupsPaths: string[]): void {
|
||||
const parentPath = el.path.slice(0, el.path.lastIndexOf('/'));
|
||||
if (parentPath && (groupsPaths.indexOf(parentPath) === -1)) {
|
||||
|
||||
el = {
|
||||
name: parentPath.slice(parentPath.lastIndexOf('/')+1),
|
||||
path: parentPath
|
||||
};
|
||||
groups.push(el);
|
||||
groupsPaths.push(parentPath);
|
||||
|
||||
this.getParents(el, groups, groupsPaths);
|
||||
}
|
||||
}
|
||||
|
||||
private changeDirectMembership = (checked: boolean,event: React.FormEvent<HTMLInputElement> )=> {
|
||||
this.setState({
|
||||
isDirectMembership: checked
|
||||
});
|
||||
}
|
||||
|
||||
private emptyGroup(): React.ReactNode {
|
||||
|
||||
return (
|
||||
<DataListItem key='emptyItem' aria-labelledby="empty-item">
|
||||
<DataListItemRow key='emptyRow'>
|
||||
<DataListItemCells dataListCells={[
|
||||
<DataListCell key='empty'><strong><Msg msgKey='noGroupsText' /></strong></DataListCell>
|
||||
]} />
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
)
|
||||
}
|
||||
|
||||
private renderGroupList(group: Group, appIndex: number): React.ReactNode {
|
||||
|
||||
return (
|
||||
<DataListItem id={`${appIndex}-group`} key={'group-' + appIndex} aria-labelledby="groups-list" >
|
||||
<DataListItemRow>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell id={`${appIndex}-group-name`} width={2} key={'name-' + appIndex}>
|
||||
{group.name}
|
||||
</DataListCell>,
|
||||
<DataListCell id={`${appIndex}-group-path`} width={2} key={'path-' + appIndex}>
|
||||
{group.path}
|
||||
</DataListCell>,
|
||||
<DataListCell id={`${appIndex}-group-directMembership`} width={2} key={'directMembership-' + appIndex}>
|
||||
<Checkbox id={`${appIndex}-checkbox-directMembership`} isChecked={group.id != null} isDisabled={true} />
|
||||
</DataListCell>
|
||||
]}
|
||||
/>
|
||||
</DataListItemRow>
|
||||
|
||||
</DataListItem>
|
||||
)
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
return (
|
||||
<ContentPage title={Msg.localize('groupLabel')}>
|
||||
<DataList id="groups-list" aria-label={Msg.localize('groupLabel')} isCompact>
|
||||
<DataListItem id="groups-list-header" aria-labelledby="Columns names">
|
||||
<DataListItemRow >
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell key='directMembership-header' >
|
||||
<Checkbox
|
||||
label={Msg.localize('directMembership')}
|
||||
id="directMembership-checkbox"
|
||||
isChecked={this.state.isDirectMembership}
|
||||
onChange={this.changeDirectMembership}
|
||||
/>
|
||||
|
||||
</DataListCell>
|
||||
]}
|
||||
/>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
<DataListItem id="groups-list-header" aria-labelledby="Columns names">
|
||||
<DataListItemRow >
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell key='group-name-header' width={2}>
|
||||
<strong><Msg msgKey='Name' /></strong>
|
||||
</DataListCell>,
|
||||
<DataListCell key='group-path-header' width={2}>
|
||||
<strong><Msg msgKey='path' /></strong>
|
||||
</DataListCell>,
|
||||
<DataListCell key='group-direct-membership-header' width={2}>
|
||||
<strong><Msg msgKey='directMembership' /></strong>
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
{this.state.groups.length === 0
|
||||
? this.emptyGroup()
|
||||
: (this.state.isDirectMembership ? this.state.directGroups.map((group: Group, appIndex: number) =>
|
||||
this.renderGroupList(group, appIndex)
|
||||
) : this.state.groups.map((group: Group, appIndex: number) =>
|
||||
this.renderGroupList(group, appIndex)))}
|
||||
</DataList>
|
||||
</ContentPage>
|
||||
);
|
||||
}
|
||||
};
|
|
@ -26,6 +26,7 @@
|
|||
deleteAccountAllowed: boolean;
|
||||
updateEmailFeatureEnabled: boolean;
|
||||
updateEmailActionEnabled: boolean;
|
||||
isViewGroupsEnabled: boolean;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue