keycloak-aplcache
Changes
model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java 10(+10 -0)
model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java 2(+2 -0)
Details
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
index 2a21202..6d1ffa1 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
@@ -47,6 +47,14 @@
<input ng-model="application.enabled" name="enabled" id="enabled" onoffswitch />
</div>
<div class="form-group">
+ <label for="baseUrl" class="control-label">Base URL</label>
+
+ <div class="controls">
+ <input class="input-small" type="text" name="baseUrl" id="baseUrl"
+ data-ng-model="application.baseUrl">
+ </div>
+ </div>
+ <div class="form-group">
<label for="adminUrl" class="control-label">Admin URL</label>
<div class="controls">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html
index d472c61..16a1697 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html
@@ -36,11 +36,12 @@
<tr data-ng-show="applications.length > 0">
<th>Application Name</th>
<th>Enabled</th>
+ <th>Base URL</th>
</tr>
</thead>
<tfoot data-ng-show="applications && applications.length > 5"> <!-- todo -->
<tr>
- <td colspan="2">
+ <td colspan="3">
<div class="table-nav">
<a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
@@ -54,6 +55,7 @@
<tr ng-repeat="app in applications">
<td><a href="#/realms/{{realm.id}}/applications/{{app.id}}">{{app.name}}</a></td>
<td>{{app.enabled}}</td>
+ <td ng-class="{'text-muted': !app.baseUrl}">{{app.baseUrl || "Not defined"}}</td>
</tr>
</tbody>
</table>
diff --git a/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
index 8f7d2ab..559ebc2 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
@@ -12,6 +12,7 @@ public class ApplicationRepresentation {
protected String id;
protected String name;
protected String adminUrl;
+ protected String baseUrl;
protected boolean surrogateAuthRequired;
protected boolean enabled;
protected List<CredentialRepresentation> credentials;
@@ -89,6 +90,14 @@ public class ApplicationRepresentation {
this.adminUrl = adminUrl;
}
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
public List<CredentialRepresentation> getCredentials() {
return credentials;
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
index cf46b1e..7a4cc69 100755
--- a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
@@ -10,6 +10,7 @@ import java.util.List;
public class OAuthClientRepresentation {
protected String id;
protected String name;
+ protected String baseUrl;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected boolean enabled;
@@ -39,6 +40,14 @@ public class OAuthClientRepresentation {
this.enabled = enabled;
}
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
public List<String> getRedirectUris() {
return redirectUris;
}
diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java
index 49635a0..579659d 100755
--- a/forms/src/main/java/org/keycloak/forms/UrlBean.java
+++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java
@@ -36,6 +36,8 @@ public class UrlBean {
private boolean socialRegistration;
+ private String referrerURI;
+
public boolean isSocialRegistration() {
return socialRegistration;
}
@@ -44,9 +46,10 @@ public class UrlBean {
this.socialRegistration = socialRegistration;
}
- public UrlBean(RealmBean realm, URI baseURI){
+ public UrlBean(RealmBean realm, URI baseURI, String referrerURI){
this.realm = realm;
this.baseURI = baseURI;
+ this.referrerURI = referrerURI;
}
public RealmBean getRealm() {
@@ -136,4 +139,8 @@ public class UrlBean {
return Urls.loginActionEmailVerification(baseURI, realm.getId()).toString();
}
+ public String getReferrerURI() {
+ return referrerURI;
+ }
+
}
diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
index 28ed554..329dbf2 100755
--- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
+++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java
@@ -35,6 +35,7 @@ import org.keycloak.forms.TemplateBean;
import org.keycloak.forms.TotpBean;
import org.keycloak.forms.UrlBean;
import org.keycloak.forms.UserBean;
+import org.keycloak.models.ApplicationModel;
import org.keycloak.services.FormService;
import org.keycloak.services.resources.flows.Pages;
@@ -54,22 +55,22 @@ public class FormServiceImpl implements FormService {
private static final String ID = "FormServiceId";
private static final String BUNDLE = "org.keycloak.forms.messages";
- private final Map<String, Command> commandMap = new HashMap<String,Command>();
+ private final Map<String, CommandCommon> commandMap = new HashMap<String, CommandCommon>();
public FormServiceImpl(){
commandMap.put(Pages.LOGIN, new CommandLogin());
commandMap.put(Pages.REGISTER, new CommandRegister());
- commandMap.put(Pages.ACCOUNT, new CommandAccount());
- commandMap.put(Pages.LOGIN_UPDATE_PROFILE, new CommandPassword());
- commandMap.put(Pages.PASSWORD, new CommandPassword());
- commandMap.put(Pages.LOGIN_RESET_PASSWORD, new CommandPassword());
- commandMap.put(Pages.LOGIN_UPDATE_PASSWORD, new CommandPassword());
- commandMap.put(Pages.ACCESS, new CommandAccess());
- commandMap.put(Pages.SOCIAL, new CommandSocial());
+ commandMap.put(Pages.ACCOUNT, new CommandCommon());
+ commandMap.put(Pages.LOGIN_UPDATE_PROFILE, new CommandCommon());
+ commandMap.put(Pages.PASSWORD, new CommandCommon());
+ commandMap.put(Pages.LOGIN_RESET_PASSWORD, new CommandCommon());
+ commandMap.put(Pages.LOGIN_UPDATE_PASSWORD, new CommandCommon());
+ commandMap.put(Pages.ACCESS, new CommandCommon());
+ commandMap.put(Pages.SOCIAL, new CommandCommon());
commandMap.put(Pages.TOTP, new CommandTotp());
commandMap.put(Pages.LOGIN_CONFIG_TOTP, new CommandTotp());
commandMap.put(Pages.LOGIN_TOTP, new CommandLoginTotp());
- commandMap.put(Pages.LOGIN_VERIFY_EMAIL, new CommandVerifyEmail());
+ commandMap.put(Pages.LOGIN_VERIFY_EMAIL, new CommandCommon());
commandMap.put(Pages.OAUTH_GRANT, new CommandOAuthGrant());
}
@@ -117,121 +118,69 @@ public class FormServiceImpl implements FormService {
return out.toString();
}
- private class CommandTotp implements Command {
+ private class CommandCommon {
+ protected RealmBean realm;
+ protected UrlBean url;
+ protected UserBean user;
+ protected LoginBean login;
+
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
- RealmBean realm = new RealmBean(dataBean.getRealm());
+ realm = new RealmBean(dataBean.getRealm());
+
+ String referrer = dataBean.getQueryParam("referrer");
+ String referrerUri = null;
+ if (referrer != null) {
+ for (ApplicationModel a : dataBean.getRealm().getApplications()) {
+ if (a.getName().equals(referrer)) {
+ referrerUri = a.getBaseUrl();
+ break;
+ }
+ }
+ }
+
+ url = new UrlBean(realm, dataBean.getBaseURI(), referrerUri);
+ url.setSocialRegistration(dataBean.getSocialRegistration());
+ user = new UserBean(dataBean.getUserModel());
+ login = new LoginBean(realm, dataBean.getFormData());
attributes.put("realm", realm);
- attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
-
- UserBean user = new UserBean(dataBean.getUserModel());
+ attributes.put("url", url);
attributes.put("user", user);
-
- TotpBean totp = new TotpBean(user, dataBean.getContextPath());
- attributes.put("totp", totp);
-
- attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
- }
- }
-
- private class CommandSocial implements Command {
- public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
- RealmBean realm = new RealmBean(dataBean.getRealm());
- attributes.put("user", new UserBean(dataBean.getUserModel()));
- attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
- }
- }
-
- private class CommandEmail implements Command {
- public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+ attributes.put("login", login);
}
}
- private class CommandPassword implements Command {
+ private class CommandTotp extends CommandCommon {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
- RealmBean realm = new RealmBean(dataBean.getRealm());
+ super.exec(attributes, dataBean);
- attributes.put("realm", realm);
- attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
- attributes.put("user", new UserBean(dataBean.getUserModel()));
- attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+ attributes.put("totp", new TotpBean(user, dataBean.getContextPath()));
}
}
- private class CommandLoginTotp implements Command {
+ private class CommandLoginTotp extends CommandCommon {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
-
- RealmBean realm = new RealmBean(dataBean.getRealm());
-
- attributes.put("realm", realm);
-
- UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
- url.setSocialRegistration(dataBean.getSocialRegistration());
-
- attributes.put("url", url);
- attributes.put("user", new UserBean(dataBean.getUserModel()));
- attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+ super.exec(attributes, dataBean);
RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
-
SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url);
attributes.put("social", social);
}
}
- private class CommandAccess implements Command {
- public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
- RealmBean realm = new RealmBean(dataBean.getRealm());
-
- attributes.put("realm", realm);
- attributes.put("user", new UserBean(dataBean.getUserModel()));
- attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
- }
- }
-
- private class CommandAccount implements Command {
- public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
- RealmBean realm = new RealmBean(dataBean.getRealm());
-
- attributes.put("realm", realm);
- attributes.put("url", new UrlBean(realm, dataBean.getBaseURI()));
- attributes.put("user", new UserBean(dataBean.getUserModel()));
- attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
- }
- }
-
- private class CommandLogin implements Command {
+ private class CommandLogin extends CommandCommon {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
- RealmBean realm = new RealmBean(dataBean.getRealm());
-
- attributes.put("realm", realm);
-
- UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
- url.setSocialRegistration(dataBean.getSocialRegistration());
-
- attributes.put("url", url);
- attributes.put("user", new UserBean(dataBean.getUserModel()));
- attributes.put("login", new LoginBean(realm, dataBean.getFormData()));
+ super.exec(attributes, dataBean);
RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
-
SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url);
attributes.put("social", social);
}
}
- private class CommandRegister implements Command {
+ private class CommandRegister extends CommandCommon {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
-
- RealmBean realm = new RealmBean(dataBean.getRealm());
-
- attributes.put("realm", realm);
-
- UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
- url.setSocialRegistration(dataBean.getSocialRegistration());
-
- attributes.put("url", url);
- attributes.put("user", new UserBean(dataBean.getUserModel()));
+ super.exec(attributes, dataBean);
RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
attributes.put("register", register);
@@ -241,8 +190,9 @@ public class FormServiceImpl implements FormService {
}
}
- private class CommandOAuthGrant implements Command {
+ private class CommandOAuthGrant extends CommandCommon {
public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
+ super.exec(attributes, dataBean);
OAuthGrantBean oauth = new OAuthGrantBean();
oauth.setAction(dataBean.getOAuthAction());
@@ -255,22 +205,4 @@ public class FormServiceImpl implements FormService {
}
}
- private class CommandVerifyEmail implements Command {
- public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean) {
-
- RealmBean realm = new RealmBean(dataBean.getRealm());
-
- attributes.put("realm", realm);
-
- UrlBean url = new UrlBean(realm, dataBean.getBaseURI());
- url.setSocialRegistration(dataBean.getSocialRegistration());
-
- attributes.put("url", url);
- }
- }
-
- private interface Command {
- public void exec(Map<String, Object> attributes, FormServiceDataBean dataBean);
- }
-
}
\ No newline at end of file
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl
index 4e7caac..cdd5fef 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/account.ftl
@@ -27,7 +27,7 @@
</div>
</fieldset>
<div class="form-actions">
- <#--a href="#">« Back to my application</a-->
+ <#if url.referrerURI??><a href="${url.referrerURI}">Back to application</a></#if>
<button type="submit" class="primary">Save</button>
<button type="submit">Cancel</button>
</div>
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/password.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/password.ftl
index 5c48264..b92f3e6 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/password.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/password.ftl
@@ -23,7 +23,7 @@
</div>
</fieldset>
<div class="form-actions">
- <#--a href="#">« Back to my application</a-->
+ <#if url.referrerURI??><a href="${url.referrerURI}">Back to application</a></#if>
<button type="submit" class="primary">Save</button>
<button type="submit">Cancel</button>
</div>
diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
index 5f4d6dc..cad065e 100755
--- a/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
+++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/totp.ftl
@@ -54,6 +54,7 @@
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
</div>
<div class="form-actions">
+ <#if url.referrerURI??><a href="${url.referrerURI}">Back to application</a></#if>
<button type="submit" class="primary">Submit</button>
</div>
</form>
diff --git a/model/api/src/main/java/org/keycloak/models/ApplicationModel.java b/model/api/src/main/java/org/keycloak/models/ApplicationModel.java
index 9039f4f..e5374b8 100755
--- a/model/api/src/main/java/org/keycloak/models/ApplicationModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ApplicationModel.java
@@ -29,6 +29,10 @@ public interface ApplicationModel extends RoleContainerModel, RoleMapperModel, S
void setManagementUrl(String url);
+ String getBaseUrl();
+
+ void setBaseUrl(String url);
+
List<String> getDefaultRoles();
void addDefaultRole(String name);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
index ad5f9e1..c196edd 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
@@ -83,6 +83,16 @@ public class ApplicationAdapter implements ApplicationModel {
}
@Override
+ public String getBaseUrl() {
+ return application.getBaseUrl();
+ }
+
+ @Override
+ public void setBaseUrl(String url) {
+ application.setBaseUrl(url);
+ }
+
+ @Override
public RoleModel getRole(String name) {
Collection<RoleEntity> roles = application.getRoles();
if (roles == null) return null;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
index c8a7400..c0e2144 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
@@ -25,6 +25,7 @@ public class ApplicationEntity {
private String name;
private boolean enabled;
private boolean surrogateAuthRequired;
+ private String baseUrl;
private String managementUrl;
@OneToOne(fetch = FetchType.EAGER)
@@ -58,6 +59,14 @@ public class ApplicationEntity {
this.surrogateAuthRequired = surrogateAuthRequired;
}
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
public String getManagementUrl() {
return managementUrl;
}
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
index e6fe85b..98a510a 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
@@ -106,6 +106,17 @@ public class ApplicationAdapter implements ApplicationModel {
}
@Override
+ public String getBaseUrl() {
+ return applicationData.getBaseUrl();
+ }
+
+ @Override
+ public void setBaseUrl(String url) {
+ applicationData.setBaseUrl(url);
+ updateApplication();
+ }
+
+ @Override
public RoleAdapter getRole(String name) {
Role role = SampleModel.getRole(getIdm(), name);
if (role == null) return null;
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java
index 3adb8f3..ec4b4e7 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java
@@ -13,6 +13,7 @@ public class ApplicationData extends AbstractPartition {
private boolean enabled;
private boolean surrogateAuthRequired;
private String managementUrl;
+ private String baseUrl;
private User resourceUser;
private String[] defaultRoles;
@@ -59,6 +60,15 @@ public class ApplicationData extends AbstractPartition {
}
@AttributeProperty
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ @AttributeProperty
public String getManagementUrl() {
return managementUrl;
}
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java
index 88af1a6..8e450a7 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java
@@ -31,6 +31,8 @@ public class ApplicationEntity implements Serializable {
private boolean surrogateAuthRequired;
@AttributeValue
private String managementUrl;
+ @AttributeValue
+ private String baseUrl;
@AttributeValue
private String[] defaultRoles;
diff --git a/services/src/main/java/org/keycloak/services/FormService.java b/services/src/main/java/org/keycloak/services/FormService.java
index 9a357a3..716e76b 100755
--- a/services/src/main/java/org/keycloak/services/FormService.java
+++ b/services/src/main/java/org/keycloak/services/FormService.java
@@ -53,6 +53,7 @@ public interface FormService {
private FormFlows.MessageType messageType;
private MultivaluedMap<String, String> formData;
+ private Map<String, String> queryParams;
private URI baseURI;
private List<SocialProvider> socialProviders;
@@ -87,10 +88,11 @@ public interface FormService {
private String contextPath;
- public FormServiceDataBean(RealmModel realm, UserModel userModel, MultivaluedMap<String, String> formData, String message) {
+ public FormServiceDataBean(RealmModel realm, UserModel userModel, MultivaluedMap<String, String> formData, Map<String, String> queryParams, String message) {
this.realm = realm;
this.userModel = userModel;
this.formData = formData;
+ this.queryParams = queryParams;
this.message = message;
socialProviders = new LinkedList<SocialProvider>();
@@ -125,6 +127,16 @@ public interface FormService {
return formData;
}
+ public Map<String, String> getQueryParams() {
+ return queryParams;
+ }
+
+
+ public String getQueryParam(String key) {
+ return queryParams != null ? queryParams.get(key) : null;
+ }
+
+
public void setFormData(MultivaluedMap<String, String> formData) {
this.formData = formData;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
index e3c3322..1e5a24f 100755
--- a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
@@ -50,6 +50,7 @@ public class ApplicationManager {
applicationModel.setEnabled(resourceRep.isEnabled());
applicationModel.setManagementUrl(resourceRep.getAdminUrl());
applicationModel.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
+ applicationModel.setBaseUrl(resourceRep.getBaseUrl());
applicationModel.updateApplication();
UserModel resourceUser = applicationModel.getApplicationUser();
@@ -128,6 +129,7 @@ public class ApplicationManager {
resource.setName(rep.getName());
resource.setEnabled(rep.isEnabled());
resource.setManagementUrl(rep.getAdminUrl());
+ resource.setBaseUrl(rep.getBaseUrl());
resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
resource.updateApplication();
@@ -153,6 +155,7 @@ public class ApplicationManager {
rep.setEnabled(applicationModel.isEnabled());
rep.setAdminUrl(applicationModel.getManagementUrl());
rep.setSurrogateAuthRequired(applicationModel.isSurrogateAuthRequired());
+ rep.setBaseUrl(applicationModel.getBaseUrl());
Set<String> redirectUris = applicationModel.getApplicationUser().getRedirectUris();
if (redirectUris != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 584ef1c..22e7269 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -46,6 +46,7 @@ import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.Providers;
import java.net.URI;
+import java.net.URISyntaxException;
import java.util.List;
/**
@@ -89,7 +90,15 @@ public class AccountService {
if (!hasAccess(auth)) {
return noAccess();
}
- return Flows.forms(realm, request, uriInfo).setUser(auth.getUser()).forwardToForm(template);
+
+ FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(auth.getUser());
+
+ String referrer = getReferrer();
+ if (referrer != null) {
+ forms.setQueryParam("referrer", referrer);
+ }
+
+ return forms.forwardToForm(template);
} else {
return login(path);
}
@@ -321,11 +330,8 @@ public class AccountService {
throw new BadRequestException();
}
- UriBuilder redirectBuilder = Urls.accountBase(uriInfo.getBaseUri());
- if (path != null) {
- redirectBuilder.path(path);
- }
- URI redirectUri = redirectBuilder.build(realm.getId());
+ URI accountUri = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getId());
+ URI redirectUri = path != null ? accountUri.resolve(path) : accountUri;
NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), client, Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId()));
return Response.status(302).cookie(cookie).location(redirectUri).build();
@@ -353,6 +359,11 @@ public class AccountService {
URI accountUri = Urls.accountPageBuilder(uriInfo.getBaseUri()).path(AccountService.class, "loginRedirect").build(realm.getId());
+ String referrer = getReferrer();
+ if (referrer != null) {
+ path = (path != null ? path : "") + "?referrer=" + referrer;
+ }
+
oauth.setStateCookiePath(accountUri.getPath());
return oauth.redirect(uriInfo, accountUri.toString(), path);
}
@@ -393,4 +404,23 @@ public class AccountService {
return application.hasRole(user, role);
}
+ private String getReferrer() {
+ String referrer = uriInfo.getQueryParameters().getFirst("referrer");
+ if (referrer != null) {
+ return referrer;
+ }
+
+ String referrerUrl = headers.getHeaderString("Referer");
+ if (referrerUrl != null) {
+ for (ApplicationModel a : realm.getApplications()) {
+ if (a.getBaseUrl() != null && referrerUrl.startsWith(a.getBaseUrl())) {
+ return a.getName();
+ }
+ }
+ return null;
+ }
+
+ return null;
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
index 3dcd6b5..e95c3aa 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
@@ -39,18 +39,16 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class FormFlows {
- public static final String DATA = "KEYCLOAK_FORMS_DATA";
- public static final String ERROR_MESSAGE = "KEYCLOAK_FORMS_ERROR_MESSAGE";
- public static final String USER = UserModel.class.getName();
- public static final String SOCIAL_REGISTRATION = "socialRegistration";
public static final String CODE = "code";
// TODO refactor/rename "error" to "message" everywhere where it makes sense
@@ -61,6 +59,8 @@ public class FormFlows {
private MultivaluedMap<String, String> formData;
+ private Map<String, String> queryParams;
+
private RealmModel realm;
private HttpRequest request;
@@ -118,6 +118,12 @@ public class FormFlows {
uriBuilder.queryParam(CODE, accessCode.getCode());
}
+ if (queryParams != null) {
+ for (Map.Entry<String, String> q : queryParams.entrySet()) {
+ uriBuilder.replaceQueryParam(q.getKey(), q.getValue());
+ }
+ }
+
URI baseURI = uriBuilder.build();
formDataBean.setBaseURI(baseURI);
@@ -140,7 +146,7 @@ public class FormFlows {
public Response forwardToForm(String template) {
- FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error);
+ FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, queryParams, error);
formDataBean.setMessageType(messageType);
return forwardToForm(template, formDataBean);
@@ -192,7 +198,7 @@ public class FormFlows {
public Response forwardToOAuthGrant(){
- FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error);
+ FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, queryParams, error);
formDataBean.setOAuthRealmRolesRequested((List<RoleModel>) request.getAttribute("realmRolesRequested"));
formDataBean.setOAuthResourceRolesRequested((MultivaluedMap<String, RoleModel>) request.getAttribute("resourceRolesRequested"));
@@ -208,6 +214,14 @@ public class FormFlows {
return this;
}
+ public FormFlows setQueryParam(String key, String value) {
+ if (queryParams == null) {
+ queryParams = new HashMap<String, String>();
+ }
+ queryParams.put(key, value);
+ return this;
+ }
+
public FormFlows setError(String error) {
this.error = error;
return this;
diff --git a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
index f96e089..e322481 100755
--- a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
+++ b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
@@ -40,6 +40,7 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
realm = manager.createRealm("original");
application = realm.addApplication("application");
+ application.setBaseUrl("http://base");
application.setManagementUrl("http://management");
application.setName("app-name");
application.addRole("role-1");
@@ -82,6 +83,7 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
public static void assertEquals(ApplicationModel expected, ApplicationModel actual) {
Assert.assertEquals(expected.getName(), actual.getName());
+ Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl());
Assert.assertEquals(expected.getManagementUrl(), actual.getManagementUrl());
Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java
index 9aa9655..996085e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/ApplicationServlet.java
@@ -39,23 +39,11 @@ import java.util.List;
*/
public class ApplicationServlet extends HttpServlet {
+ private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String title = "";
- String body = "";
-
- StringBuffer sb = req.getRequestURL();
- sb.append("?");
- sb.append(req.getQueryString());
-
- List<NameValuePair> query = null;
-
- try {
- query = URLEncodedUtils.parse(new URI(sb.toString()), "UTF-8");
- } catch (URISyntaxException e) {
- throw new ServletException(e);
- }
-
if (req.getRequestURI().endsWith("auth")) {
title = "AUTH_RESPONSE";
} else if (req.getRequestURI().endsWith("logout")) {
@@ -65,7 +53,11 @@ public class ApplicationServlet extends HttpServlet {
}
PrintWriter pw = resp.getWriter();
- pw.printf("<html><head><title>%s</title></head><body>%s</body>", title, body);
+ pw.printf("<html><head><title>%s</title></head><body>", title);
+
+ pw.printf(LINK, "http://localhost:8081/auth-server/rest/realms/test/account", "account", "account");
+
+ pw.print("</body></html>");
pw.flush();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java
index 4d0d3ca..17d6348 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AccountTest.java
@@ -112,6 +112,28 @@ public class AccountTest {
}
@Test
+ public void returnToAppFromHeader() {
+ appPage.open();
+ appPage.openAccount();
+ loginPage.login("test-user@localhost", "password");
+
+ Assert.assertTrue(profilePage.isCurrent());
+ profilePage.backToApplication();
+
+ Assert.assertTrue(appPage.isCurrent());
+ }
+
+ @Test
+ public void returnToAppFromQueryParam() {
+ driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
+ loginPage.login("test-user@localhost", "password");
+ Assert.assertTrue(profilePage.isCurrent());
+ profilePage.backToApplication();
+
+ Assert.assertTrue(appPage.isCurrent());
+ }
+
+ @Test
public void changePassword() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
index a9f6495..d19ca29 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
@@ -30,7 +30,7 @@ import org.openqa.selenium.support.FindBy;
*/
public class AccountUpdateProfilePage extends AbstractAccountPage {
- private static String PATH = Constants.AUTH_SERVER_ROOT + "/rest/realms/test/account";
+ public static String PATH = Constants.AUTH_SERVER_ROOT + "/rest/realms/test/account";
@FindBy(id = "firstName")
private WebElement firstNameInput;
@@ -41,6 +41,10 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
@FindBy(id = "email")
private WebElement emailInput;
+
+ @FindBy(linkText = "Back to application")
+ private WebElement backToApplicationLink;
+
@FindBy(css = "button[type=\"submit\"]")
private WebElement submitButton;
@@ -78,6 +82,10 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
driver.navigate().to(PATH);
}
+ public void backToApplication() {
+ backToApplicationLink.click();
+ }
+
public boolean isSuccess(){
return feedbackMessage != null && "Success!".equals(feedbackMessage.getText());
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java
index 3faa197..c0ce139 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AppPage.java
@@ -22,6 +22,9 @@
package org.keycloak.testsuite.pages;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@@ -29,6 +32,9 @@ public class AppPage extends AbstractPage {
private String baseUrl = "http://localhost:8081/app";
+ @FindBy(id = "account")
+ private WebElement accountLink;
+
@Override
public void open() {
driver.navigate().to(baseUrl);
@@ -43,6 +49,10 @@ public class AppPage extends AbstractPage {
return RequestType.valueOf(driver.getTitle());
}
+ public void openAccount() {
+ accountLink.click();
+ }
+
public enum RequestType {
AUTH_RESPONSE, LOGOUT_REQUEST, APP_REQUEST
}
diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json
index 4122174..cf8d8ae 100755
--- a/testsuite/integration/src/test/resources/testrealm.json
+++ b/testsuite/integration/src/test/resources/testrealm.json
@@ -72,6 +72,7 @@
{
"name": "test-app",
"enabled": true,
+ "baseUrl": "http://localhost:8081/app",
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{