Merge pull request #959 from mposolda/master
KEYCLOAK-1035 Brokered identity linked by account management may not be recognized during login
This commit is contained in:
commit
0b67437fa7
8 changed files with 157 additions and 33 deletions
|
@ -49,7 +49,7 @@ public class AccountFederatedIdentityBean {
|
||||||
.queryParam("stateChecker", stateChecker)
|
.queryParam("stateChecker", stateChecker)
|
||||||
.build().toString();
|
.build().toString();
|
||||||
|
|
||||||
FederatedIdentityEntry entry = new FederatedIdentityEntry(identity, provider.getName(), actionUrl);
|
FederatedIdentityEntry entry = new FederatedIdentityEntry(identity, provider.getId(), provider.getName(), actionUrl);
|
||||||
this.identities.add(entry);
|
this.identities.add(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,17 +78,19 @@ public class AccountFederatedIdentityBean {
|
||||||
public class FederatedIdentityEntry {
|
public class FederatedIdentityEntry {
|
||||||
|
|
||||||
private FederatedIdentityModel federatedIdentityModel;
|
private FederatedIdentityModel federatedIdentityModel;
|
||||||
|
private final String providerId;
|
||||||
private final String providerName;
|
private final String providerName;
|
||||||
private final String actionUrl;
|
private final String actionUrl;
|
||||||
|
|
||||||
public FederatedIdentityEntry(FederatedIdentityModel federatedIdentityModel, String providerName, String actionUrl) {
|
public FederatedIdentityEntry(FederatedIdentityModel federatedIdentityModel, String providerId, String providerName, String actionUrl) {
|
||||||
this.federatedIdentityModel = federatedIdentityModel;
|
this.federatedIdentityModel = federatedIdentityModel;
|
||||||
|
this.providerId = providerId;
|
||||||
this.providerName = providerName;
|
this.providerName = providerName;
|
||||||
this.actionUrl = actionUrl;
|
this.actionUrl = actionUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProviderId() {
|
public String getProviderId() {
|
||||||
return federatedIdentityModel != null ? federatedIdentityModel.getIdentityProvider() : null;
|
return providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProviderName() {
|
public String getProviderName() {
|
||||||
|
|
|
@ -19,10 +19,10 @@
|
||||||
<div class="col-sm-5 col-md-5">
|
<div class="col-sm-5 col-md-5">
|
||||||
<#if identity.connected>
|
<#if identity.connected>
|
||||||
<#if federatedIdentity.removeLinkPossible>
|
<#if federatedIdentity.removeLinkPossible>
|
||||||
<a href="${identity.actionUrl}" type="submit" class="btn btn-primary btn-lg">Remove ${identity.providerName!}</a>
|
<a href="${identity.actionUrl}" type="submit" id="remove-${identity.providerId!}" class="btn btn-primary btn-lg">Remove ${identity.providerName!}</a>
|
||||||
</#if>
|
</#if>
|
||||||
<#else>
|
<#else>
|
||||||
<a href="${identity.actionUrl}" type="submit" class="btn btn-primary btn-lg">Add ${identity.providerName!}</a>
|
<a href="${identity.actionUrl}" type="submit" id="add-${identity.providerId!}" class="btn btn-primary btn-lg">Add ${identity.providerName!}</a>
|
||||||
</#if>
|
</#if>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources;
|
package org.keycloak.services.resources;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
|
@ -77,6 +78,8 @@ import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
|
||||||
@Path("/broker")
|
@Path("/broker")
|
||||||
public class AuthenticationBrokerResource {
|
public class AuthenticationBrokerResource {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(AuthenticationBrokerResource.class);
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
private UriInfo uriInfo;
|
private UriInfo uriInfo;
|
||||||
|
|
||||||
|
@ -132,6 +135,7 @@ public class AuthenticationBrokerResource {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
logger.error("Could not send authentication request to identity provider " + providerId, e);
|
||||||
String message = "Could not send authentication request to identity provider";
|
String message = "Could not send authentication request to identity provider";
|
||||||
event.error(message);
|
event.error(message);
|
||||||
return redirectToErrorPage(realm, message);
|
return redirectToErrorPage(realm, message);
|
||||||
|
@ -278,6 +282,7 @@ public class AuthenticationBrokerResource {
|
||||||
|
|
||||||
return performLocalAuthentication(realm, providerId, identity, clientCode);
|
return performLocalAuthentication(realm, providerId, identity, clientCode);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
logger.error("Could not authenticate user against provider " + providerId, e);
|
||||||
if (session.getTransaction().isActive()) {
|
if (session.getTransaction().isActive()) {
|
||||||
session.getTransaction().rollback();
|
session.getTransaction().rollback();
|
||||||
}
|
}
|
||||||
|
@ -333,34 +338,36 @@ public class AuthenticationBrokerResource {
|
||||||
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
|
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
UserModel user = session.users().getUserByEmail(updatedIdentity.getEmail(), realm);
|
if (federatedUser == null) {
|
||||||
String errorMessage = "federatedIdentityEmailExists";
|
|
||||||
|
|
||||||
if (user == null) {
|
UserModel existingUser = session.users().getUserByEmail(updatedIdentity.getEmail(), realm);
|
||||||
user = session.users().getUserByUsername(updatedIdentity.getUsername(), realm);
|
String errorMessage = "federatedIdentityEmailExists";
|
||||||
errorMessage = "federatedIdentityUsernameExists";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user == null) {
|
if (existingUser == null) {
|
||||||
federatedUser = session.users().addUser(realm, updatedIdentity.getUsername());
|
existingUser = session.users().getUserByUsername(updatedIdentity.getUsername(), realm);
|
||||||
federatedUser.setEnabled(true);
|
errorMessage = "federatedIdentityUsernameExists";
|
||||||
federatedUser.setFirstName(updatedIdentity.getFirstName());
|
|
||||||
federatedUser.setLastName(updatedIdentity.getLastName());
|
|
||||||
federatedUser.setEmail(updatedIdentity.getEmail());
|
|
||||||
|
|
||||||
session.users().addFederatedIdentity(realm, federatedUser, federatedIdentityModel);
|
|
||||||
|
|
||||||
event.clone().user(federatedUser).event(EventType.REGISTER)
|
|
||||||
.detail(Details.REGISTER_METHOD, authMethod)
|
|
||||||
.detail(Details.EMAIL, federatedUser.getEmail())
|
|
||||||
.removeDetail("auth_method")
|
|
||||||
.success();
|
|
||||||
|
|
||||||
if (identityProviderConfig.isUpdateProfileFirstLogin()) {
|
|
||||||
federatedUser.addRequiredAction(UPDATE_PROFILE);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (federatedUser == null) {
|
if (existingUser == null) {
|
||||||
|
logger.debug("Creating user " + updatedIdentity.getUsername() + " and linking to federation provider " + providerId);
|
||||||
|
federatedUser = session.users().addUser(realm, updatedIdentity.getUsername());
|
||||||
|
federatedUser.setEnabled(true);
|
||||||
|
federatedUser.setFirstName(updatedIdentity.getFirstName());
|
||||||
|
federatedUser.setLastName(updatedIdentity.getLastName());
|
||||||
|
federatedUser.setEmail(updatedIdentity.getEmail());
|
||||||
|
|
||||||
|
session.users().addFederatedIdentity(realm, federatedUser, federatedIdentityModel);
|
||||||
|
|
||||||
|
event.clone().user(federatedUser).event(EventType.REGISTER)
|
||||||
|
.detail(Details.REGISTER_METHOD, authMethod)
|
||||||
|
.detail(Details.EMAIL, federatedUser.getEmail())
|
||||||
|
.removeDetail("auth_method")
|
||||||
|
.success();
|
||||||
|
|
||||||
|
if (identityProviderConfig.isUpdateProfileFirstLogin()) {
|
||||||
|
federatedUser.addRequiredAction(UPDATE_PROFILE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
|
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
|
||||||
.setClientSessionCode(clientCode.getCode())
|
.setClientSessionCode(clientCode.getCode())
|
||||||
.setError(errorMessage)
|
.setError(errorMessage)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.keycloak.testsuite.broker;
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.keycloak.broker.kerberos.KerberosIdentityProviderFactory;
|
||||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||||
import org.keycloak.social.facebook.FacebookIdentityProviderFactory;
|
import org.keycloak.social.facebook.FacebookIdentityProviderFactory;
|
||||||
|
@ -47,6 +48,7 @@ public abstract class AbstractIdentityProviderModelTest extends AbstractModelTes
|
||||||
this.expectedProviders.add(FacebookIdentityProviderFactory.PROVIDER_ID);
|
this.expectedProviders.add(FacebookIdentityProviderFactory.PROVIDER_ID);
|
||||||
this.expectedProviders.add(GitHubIdentityProviderFactory.PROVIDER_ID);
|
this.expectedProviders.add(GitHubIdentityProviderFactory.PROVIDER_ID);
|
||||||
this.expectedProviders.add(TwitterIdentityProviderFactory.PROVIDER_ID);
|
this.expectedProviders.add(TwitterIdentityProviderFactory.PROVIDER_ID);
|
||||||
|
this.expectedProviders.add(KerberosIdentityProviderFactory.PROVIDER_ID);
|
||||||
|
|
||||||
this.expectedProviders = Collections.unmodifiableSet(this.expectedProviders);
|
this.expectedProviders = Collections.unmodifiableSet(this.expectedProviders);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.representations.IDToken;
|
||||||
import org.keycloak.testsuite.OAuthClient;
|
import org.keycloak.testsuite.OAuthClient;
|
||||||
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
||||||
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
|
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
|
||||||
|
import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
|
||||||
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
||||||
|
@ -92,6 +93,9 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
@WebResource
|
@WebResource
|
||||||
protected AccountPasswordPage changePasswordPage;
|
protected AccountPasswordPage changePasswordPage;
|
||||||
|
|
||||||
|
@WebResource
|
||||||
|
protected AccountFederatedIdentityPage accountFederatedIdentityPage;
|
||||||
|
|
||||||
private KeycloakSession session;
|
private KeycloakSession session;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -118,6 +122,7 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSuccessfulAuthenticationWithoutUpdateProfile() {
|
public void testSuccessfulAuthenticationWithoutUpdateProfile() {
|
||||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||||
|
identityProviderModel.setUpdateProfileFirstLogin(false);
|
||||||
|
|
||||||
assertSuccessfulAuthentication(identityProviderModel);
|
assertSuccessfulAuthentication(identityProviderModel);
|
||||||
}
|
}
|
||||||
|
@ -202,6 +207,38 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
assertEquals("User with email already exists. Please login to account management to link the account.", element.getText());
|
assertEquals("User with email already exists. Please login to account management to link the account.", element.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountManagementLinkIdentity() {
|
||||||
|
// Login as pedroigor to account management
|
||||||
|
accountFederatedIdentityPage.realm("realm-with-broker");
|
||||||
|
accountFederatedIdentityPage.open();
|
||||||
|
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
||||||
|
loginPage.login("pedroigor", "password");
|
||||||
|
assertTrue(accountFederatedIdentityPage.isCurrent());
|
||||||
|
|
||||||
|
// Link my "pedroigor" identity with "test-user" from brokered Keycloak
|
||||||
|
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||||
|
accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getId());
|
||||||
|
|
||||||
|
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
||||||
|
this.loginPage.login("test-user", "password");
|
||||||
|
doAfterProviderAuthentication();
|
||||||
|
|
||||||
|
// Assert identity linked in account management
|
||||||
|
assertTrue(accountFederatedIdentityPage.isCurrent());
|
||||||
|
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getId() + "\""));
|
||||||
|
|
||||||
|
// Logout from account management
|
||||||
|
accountFederatedIdentityPage.logout();
|
||||||
|
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
||||||
|
|
||||||
|
// Assert I am logged immediately to account management
|
||||||
|
loginPage.clickSocial(identityProviderModel.getId());
|
||||||
|
doAfterProviderAuthentication();
|
||||||
|
assertTrue(accountFederatedIdentityPage.isCurrent());
|
||||||
|
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getId() + "\""));
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = NoSuchElementException.class)
|
@Test(expected = NoSuchElementException.class)
|
||||||
public void testIdentityProviderNotAllowed() {
|
public void testIdentityProviderNotAllowed() {
|
||||||
this.driver.navigate().to("http://localhost:8081/test-app/");
|
this.driver.navigate().to("http://localhost:8081/test-app/");
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
package org.keycloak.testsuite.broker;
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.broker.kerberos.KerberosIdentityProvider;
|
||||||
|
import org.keycloak.broker.kerberos.KerberosIdentityProviderConfig;
|
||||||
|
import org.keycloak.broker.kerberos.KerberosIdentityProviderFactory;
|
||||||
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
||||||
import org.keycloak.broker.oidc.OIDCIdentityProvider;
|
import org.keycloak.broker.oidc.OIDCIdentityProvider;
|
||||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||||
|
@ -150,6 +153,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
assertGitHubIdentityProviderConfig(identityProvider);
|
assertGitHubIdentityProviderConfig(identityProvider);
|
||||||
} else if (TwitterIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
} else if (TwitterIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
||||||
assertTwitterIdentityProviderConfig(identityProvider);
|
assertTwitterIdentityProviderConfig(identityProvider);
|
||||||
|
} else if (KerberosIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
||||||
|
assertKerberosIdentityProviderConfig(identityProvider);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -243,8 +248,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||||
TwitterIdentityProvider gitHubIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
|
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
|
||||||
OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig();
|
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-twitter", config.getId());
|
assertEquals("model-twitter", config.getId());
|
||||||
assertEquals(TwitterIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(TwitterIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
|
@ -255,6 +260,20 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
assertEquals("clientSecret", config.getClientSecret());
|
assertEquals("clientSecret", config.getClientSecret());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertKerberosIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||||
|
KerberosIdentityProvider kerberosIdentityProvider = new KerberosIdentityProviderFactory().create(identityProvider);
|
||||||
|
KerberosIdentityProviderConfig config = kerberosIdentityProvider.getConfig();
|
||||||
|
|
||||||
|
assertEquals("model-kerberos", config.getId());
|
||||||
|
assertEquals(KerberosIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
|
assertEquals("Kerberos", config.getName());
|
||||||
|
assertEquals(true, config.isEnabled());
|
||||||
|
assertEquals(true, config.isUpdateProfileFirstLogin());
|
||||||
|
assertEquals("HTTP/server.domain.org@DOMAIN.ORG", config.getServerPrincipal());
|
||||||
|
assertEquals("/etc/http.keytab", config.getKeyTab());
|
||||||
|
assertTrue(config.getDebug());
|
||||||
|
}
|
||||||
|
|
||||||
private RealmModel installTestRealm() throws IOException {
|
private RealmModel installTestRealm() throws IOException {
|
||||||
RealmRepresentation realmRepresentation = loadJson("broker-test/test-realm-with-broker.json");
|
RealmRepresentation realmRepresentation = loadJson("broker-test/test-realm-with-broker.json");
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.keycloak.testsuite.pages;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
|
import org.keycloak.services.resources.flows.Urls;
|
||||||
|
import org.keycloak.testsuite.Constants;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class AccountFederatedIdentityPage extends AbstractAccountPage {
|
||||||
|
|
||||||
|
public AccountFederatedIdentityPage() {};
|
||||||
|
|
||||||
|
private String realmName = "test";
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
driver.navigate().to(getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void realm(String realmName) {
|
||||||
|
this.realmName = realmName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return Urls.accountFederatedIdentityPage(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).build(), realmName).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCurrent() {
|
||||||
|
return driver.getTitle().contains("Account Management") && driver.getPageSource().contains("Federated Identities");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clickAddProvider(String providerId) {
|
||||||
|
driver.findElement(By.id("add-" + providerId)).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clickRemoveProvider(String providerId) {
|
||||||
|
driver.findElement(By.id("remove-" + providerId)).click();
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,6 +140,18 @@
|
||||||
"userInfoUrl": "http://localhost:8082/auth/realms/realm-with-oidc-identity-provider/protocol/openid-connect/userinfo",
|
"userInfoUrl": "http://localhost:8082/auth/realms/realm-with-oidc-identity-provider/protocol/openid-connect/userinfo",
|
||||||
"defaultScope": "email profile"
|
"defaultScope": "email profile"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : "model-kerberos",
|
||||||
|
"providerId" : "kerberos",
|
||||||
|
"name" : "Kerberos",
|
||||||
|
"enabled": true,
|
||||||
|
"updateProfileFirstLogin" : "true",
|
||||||
|
"config": {
|
||||||
|
"serverPrincipal": "HTTP/server.domain.org@DOMAIN.ORG",
|
||||||
|
"keyTab": "/etc/http.keytab",
|
||||||
|
"debug": "true"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"users": [
|
"users": [
|
||||||
|
@ -151,7 +163,10 @@
|
||||||
{ "type" : "password",
|
{ "type" : "password",
|
||||||
"value" : "password" }
|
"value" : "password" }
|
||||||
],
|
],
|
||||||
"realmRoles": ["manager"]
|
"realmRoles": ["manager"],
|
||||||
|
"applicationRoles": {
|
||||||
|
"account": [ "manage-account" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"applications": [
|
"applications": [
|
||||||
|
|
Loading…
Reference in a new issue