[KEYCLOAK-14346] Base URL for applications is broken
This commit is contained in:
parent
76717134ba
commit
1434f14663
5 changed files with 56 additions and 10 deletions
|
@ -10,7 +10,9 @@ public class ClientRepresentation {
|
||||||
private boolean userConsentRequired;
|
private boolean userConsentRequired;
|
||||||
private boolean inUse;
|
private boolean inUse;
|
||||||
private boolean offlineAccess;
|
private boolean offlineAccess;
|
||||||
|
private String rootUrl;
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
|
private String effectiveUrl;
|
||||||
private ConsentRepresentation consent;
|
private ConsentRepresentation consent;
|
||||||
|
|
||||||
public String getClientId() {
|
public String getClientId() {
|
||||||
|
@ -61,6 +63,14 @@ public class ClientRepresentation {
|
||||||
this.offlineAccess = offlineAccess;
|
this.offlineAccess = offlineAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRootUrl() {
|
||||||
|
return rootUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootUrl(String rootUrl) {
|
||||||
|
this.rootUrl = rootUrl;
|
||||||
|
}
|
||||||
|
|
||||||
public String getBaseUrl() {
|
public String getBaseUrl() {
|
||||||
return baseUrl;
|
return baseUrl;
|
||||||
}
|
}
|
||||||
|
@ -69,6 +79,14 @@ public class ClientRepresentation {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEffectiveUrl() {
|
||||||
|
return effectiveUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEffectiveUrl(String effectiveUrl) {
|
||||||
|
this.effectiveUrl = effectiveUrl;
|
||||||
|
}
|
||||||
|
|
||||||
public ConsentRepresentation getConsent() {
|
public ConsentRepresentation getConsent() {
|
||||||
return consent;
|
return consent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.keycloak.services.managers.Auth;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.services.resources.Cors;
|
import org.keycloak.services.resources.Cors;
|
||||||
import org.keycloak.services.resources.account.resources.ResourcesService;
|
import org.keycloak.services.resources.account.resources.ResourcesService;
|
||||||
|
import org.keycloak.services.util.ResolveRelative;
|
||||||
import org.keycloak.storage.ReadOnlyException;
|
import org.keycloak.storage.ReadOnlyException;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
@ -291,7 +292,9 @@ public class AccountRestService {
|
||||||
representation.setUserConsentRequired(model.isConsentRequired());
|
representation.setUserConsentRequired(model.isConsentRequired());
|
||||||
representation.setInUse(inUseClients.contains(model.getClientId()));
|
representation.setInUse(inUseClients.contains(model.getClientId()));
|
||||||
representation.setOfflineAccess(offlineClients.contains(model.getClientId()));
|
representation.setOfflineAccess(offlineClients.contains(model.getClientId()));
|
||||||
|
representation.setRootUrl(model.getRootUrl());
|
||||||
representation.setBaseUrl(model.getBaseUrl());
|
representation.setBaseUrl(model.getBaseUrl());
|
||||||
|
representation.setEffectiveUrl(ResolveRelative.resolveRelativeUri(session, model.getRootUrl(), model.getBaseUrl()));
|
||||||
UserConsentModel consentModel = consents.get(model.getClientId());
|
UserConsentModel consentModel = consents.get(model.getClientId());
|
||||||
if(consentModel != null) {
|
if(consentModel != null) {
|
||||||
representation.setConsent(modelToRepresentation(consentModel));
|
representation.setConsent(modelToRepresentation(consentModel));
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentati
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||||
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
|
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
|
||||||
|
import org.keycloak.services.util.ResolveRelative;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
|
@ -643,8 +644,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
|
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
|
||||||
Assert.assertThat(apps.keySet(), containsInAnyOrder("in-use-client", "always-display-client"));
|
Assert.assertThat(apps.keySet(), containsInAnyOrder("in-use-client", "always-display-client"));
|
||||||
|
|
||||||
assertClientRep(apps.get("in-use-client"), "In Use Client", null, false, true, false, inUseClientAppUri);
|
assertClientRep(apps.get("in-use-client"), "In Use Client", null, false, true, false, null, inUseClientAppUri);
|
||||||
assertClientRep(apps.get("always-display-client"), "Always Display Client", null, false, false, false, alwaysDisplayClientAppUri);
|
assertClientRep(apps.get("always-display-client"), "Always Display Client", null, false, false, false, null, alwaysDisplayClientAppUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -666,7 +667,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
|
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
|
||||||
Assert.assertThat(apps.keySet(), containsInAnyOrder("offline-client", "always-display-client"));
|
Assert.assertThat(apps.keySet(), containsInAnyOrder("offline-client", "always-display-client"));
|
||||||
|
|
||||||
assertClientRep(apps.get("offline-client"), "Offline Client", null, false, true, true, offlineClientAppUri);
|
assertClientRep(apps.get("offline-client"), "Offline Client", null, false, true, true, null, offlineClientAppUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -705,21 +706,44 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
Assert.assertThat(apps.keySet(), containsInAnyOrder(appId, "always-display-client"));
|
Assert.assertThat(apps.keySet(), containsInAnyOrder(appId, "always-display-client"));
|
||||||
|
|
||||||
ClientRepresentation app = apps.get(appId);
|
ClientRepresentation app = apps.get(appId);
|
||||||
assertClientRep(app, null, "A third party application", true, false, false, "http://localhost:8180/auth/realms/master/app/auth");
|
assertClientRep(app, null, "A third party application", true, false, false, null, "http://localhost:8180/auth/realms/master/app/auth");
|
||||||
assertFalse(app.getConsent().getGrantedScopes().isEmpty());
|
assertFalse(app.getConsent().getGrantedScopes().isEmpty());
|
||||||
ConsentScopeRepresentation grantedScope = app.getConsent().getGrantedScopes().get(0);
|
ConsentScopeRepresentation grantedScope = app.getConsent().getGrantedScopes().get(0);
|
||||||
assertEquals(clientScopeRepresentation.getId(), grantedScope.getId());
|
assertEquals(clientScopeRepresentation.getId(), grantedScope.getId());
|
||||||
assertEquals(clientScopeRepresentation.getName(), grantedScope.getName());
|
assertEquals(clientScopeRepresentation.getName(), grantedScope.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertClientRep(ClientRepresentation clientRep, String name, String description, boolean userConsentRequired, boolean inUse, boolean offlineAccess, String baseUrl) {
|
@Test
|
||||||
|
public void listApplicationsWithRootUrl() throws Exception {
|
||||||
|
oauth.clientId("root-url-client");
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "view-applications-access", "password");
|
||||||
|
Assert.assertNull(tokenResponse.getErrorDescription());
|
||||||
|
|
||||||
|
TokenUtil token = new TokenUtil("view-applications-access", "password");
|
||||||
|
List<ClientRepresentation> applications = SimpleHttp
|
||||||
|
.doGet(getAccountUrl("applications"), httpClient)
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.auth(token.getToken())
|
||||||
|
.asJson(new TypeReference<List<ClientRepresentation>>() {
|
||||||
|
});
|
||||||
|
assertFalse(applications.isEmpty());
|
||||||
|
|
||||||
|
Map<String, ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
|
||||||
|
Assert.assertThat(apps.keySet(), containsInAnyOrder("root-url-client", "always-display-client"));
|
||||||
|
|
||||||
|
assertClientRep(apps.get("root-url-client"), null, null, false, true, false, "http://localhost:8180/foo/bar", "/baz");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertClientRep(ClientRepresentation clientRep, String name, String description, boolean userConsentRequired, boolean inUse, boolean offlineAccess, String rootUrl, String baseUrl) {
|
||||||
assertNotNull(clientRep);
|
assertNotNull(clientRep);
|
||||||
assertEquals(name, clientRep.getClientName());
|
assertEquals(name, clientRep.getClientName());
|
||||||
assertEquals(description, clientRep.getDescription());
|
assertEquals(description, clientRep.getDescription());
|
||||||
assertEquals(userConsentRequired, clientRep.isUserConsentRequired());
|
assertEquals(userConsentRequired, clientRep.isUserConsentRequired());
|
||||||
assertEquals(inUse, clientRep.isInUse());
|
assertEquals(inUse, clientRep.isInUse());
|
||||||
assertEquals(offlineAccess, clientRep.isOfflineAccess());
|
assertEquals(offlineAccess, clientRep.isOfflineAccess());
|
||||||
|
assertEquals(rootUrl, clientRep.getRootUrl());
|
||||||
assertEquals(baseUrl, clientRep.getBaseUrl());
|
assertEquals(baseUrl, clientRep.getBaseUrl());
|
||||||
|
assertEquals(ResolveRelative.resolveRelativeUri(null, null, rootUrl, baseUrl), clientRep.getEffectiveUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -200,6 +200,7 @@
|
||||||
"http://localhost:8180/foo/bar/*",
|
"http://localhost:8180/foo/bar/*",
|
||||||
"https://localhost:8543/foo/bar/*"
|
"https://localhost:8543/foo/bar/*"
|
||||||
],
|
],
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,7 +58,7 @@ export interface Consent {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Application {
|
interface Application {
|
||||||
baseUrl: string;
|
effectiveUrl: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
consent: Consent;
|
consent: Consent;
|
||||||
|
@ -117,7 +117,7 @@ export class ApplicationsPage extends React.Component<ApplicationsPageProps, App
|
||||||
<ContentPage title={Msg.localize('applicationsPageTitle')}>
|
<ContentPage title={Msg.localize('applicationsPageTitle')}>
|
||||||
<DataList id="applications-list" aria-label={Msg.localize('applicationsPageTitle')}>
|
<DataList id="applications-list" aria-label={Msg.localize('applicationsPageTitle')}>
|
||||||
{this.state.applications.map((application: Application, appIndex: number) => {
|
{this.state.applications.map((application: Application, appIndex: number) => {
|
||||||
const appUrl: string = application.userConsentRequired ? application.baseUrl : '/auth' + application.baseUrl;
|
const appUrl: string = application.effectiveUrl;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataListItem id={this.elementId("client-id", application)} key={'application-' + appIndex} aria-labelledby="applications-list" isExpanded={this.state.isRowOpen[appIndex]}>
|
<DataListItem id={this.elementId("client-id", application)} key={'application-' + appIndex} aria-labelledby="applications-list" isExpanded={this.state.isRowOpen[appIndex]}>
|
||||||
|
@ -140,11 +140,11 @@ export class ApplicationsPage extends React.Component<ApplicationsPageProps, App
|
||||||
<DataListCell id={this.elementId('status', application)} width={2} key={'status-' + appIndex}>
|
<DataListCell id={this.elementId('status', application)} width={2} key={'status-' + appIndex}>
|
||||||
{application.inUse ? Msg.localize('inUse') : Msg.localize('notInUse')}
|
{application.inUse ? Msg.localize('inUse') : Msg.localize('notInUse')}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell id={this.elementId('baseurl', application)} width={4} key={'baseUrl-' + appIndex}>
|
<DataListCell id={this.elementId('effectiveurl', application)} width={4} key={'effectiveUrl-' + appIndex}>
|
||||||
<button className="pf-c-button pf-m-link" type="button" onClick={() => window.open(appUrl)}>
|
<button className="pf-c-button pf-m-link" type="button" onClick={() => window.open(appUrl)}>
|
||||||
<span className="pf-c-button__icon">
|
<span className="pf-c-button__icon">
|
||||||
<i className="fas fa-link" aria-hidden="true"></i>
|
<i className="fas fa-link" aria-hidden="true"></i>
|
||||||
</span>{application.baseUrl}</button>
|
</span>{application.effectiveUrl}</button>
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -161,7 +161,7 @@ export class ApplicationsPage extends React.Component<ApplicationsPageProps, App
|
||||||
{application.description &&
|
{application.description &&
|
||||||
<GridItem><strong>{Msg.localize('description') + ': '}</strong> {application.description}</GridItem>
|
<GridItem><strong>{Msg.localize('description') + ': '}</strong> {application.description}</GridItem>
|
||||||
}
|
}
|
||||||
<GridItem><strong>{Msg.localize('baseUrl') + ': '}</strong> {application.baseUrl}</GridItem>
|
<GridItem><strong>{Msg.localize('effectiveUrl') + ': '}</strong> {application.effectiveUrl}</GridItem>
|
||||||
{application.consent &&
|
{application.consent &&
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<GridItem span={12}>
|
<GridItem span={12}>
|
||||||
|
|
Loading…
Reference in a new issue