keycloak-uncached
Changes
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java 9(+9 -0)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java 3(+2 -1)
Details
diff --git a/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java b/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java
index 8a4a487..281089a 100644
--- a/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java
+++ b/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java
@@ -37,5 +37,7 @@ public interface AccountProvider extends Provider {
AccountProvider setSessions(List<UserSessionModel> sessions);
+ AccountProvider setPasswordSet(boolean passwordSet);
+
AccountProvider setFeatures(boolean social, boolean events, boolean passwordUpdateSupported);
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
index 552d59a..e7a632b 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
@@ -8,6 +8,7 @@ import org.keycloak.account.freemarker.model.AccountSocialBean;
import org.keycloak.account.freemarker.model.FeaturesBean;
import org.keycloak.account.freemarker.model.LogBean;
import org.keycloak.account.freemarker.model.MessageBean;
+import org.keycloak.account.freemarker.model.PasswordBean;
import org.keycloak.account.freemarker.model.ReferrerBean;
import org.keycloak.account.freemarker.model.SessionsBean;
import org.keycloak.account.freemarker.model.TotpBean;
@@ -50,6 +51,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
private boolean socialEnabled;
private boolean eventsEnabled;
private boolean passwordUpdateSupported;
+ private boolean passwordSet;
private KeycloakSession session;
private FreeMarkerUtil freeMarker;
@@ -133,6 +135,8 @@ public class FreeMarkerAccountProvider implements AccountProvider {
case SESSIONS:
attributes.put("sessions", new SessionsBean(realm, sessions));
break;
+ case PASSWORD:
+ attributes.put("password", new PasswordBean(passwordSet));
}
try {
@@ -146,6 +150,11 @@ public class FreeMarkerAccountProvider implements AccountProvider {
}
}
+ public AccountProvider setPasswordSet(boolean passwordSet) {
+ this.passwordSet = passwordSet;
+ return this;
+ }
+
@Override
public AccountProvider setError(String message) {
this.message = message;
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
index af845d1..e84470f 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
@@ -4,6 +4,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserModel;
+import org.keycloak.services.resources.AccountService;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.social.SocialLoader;
import org.keycloak.social.SocialProvider;
@@ -52,7 +53,7 @@ public class AccountSocialBean {
}
// Removing last social provider is not possible if you don't have other possibility to authenticate
- this.removeLinkPossible = availableLinks > 1 || user.getFederationLink() != null;
+ this.removeLinkPossible = availableLinks > 1 || user.getFederationLink() != null || AccountService.isPasswordSet(user);
}
private SocialLinkModel getSocialLink(Set<SocialLinkModel> userSocialLinks, String socialProviderId) {
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/PasswordBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/PasswordBean.java
new file mode 100644
index 0000000..2e2c3bd
--- /dev/null
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/PasswordBean.java
@@ -0,0 +1,18 @@
+package org.keycloak.account.freemarker.model;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class PasswordBean {
+
+ private boolean passwordSet;
+
+ public PasswordBean(boolean passwordSet) {
+ this.passwordSet = passwordSet;
+ }
+
+ public boolean isPasswordSet() {
+ return passwordSet;
+ }
+
+}
diff --git a/forms/common-themes/src/main/resources/theme/account/base/password.ftl b/forms/common-themes/src/main/resources/theme/account/base/password.ftl
index e5d5531..ccf7d0f 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/password.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/password.ftl
@@ -11,15 +11,17 @@
</div>
<form action="${url.passwordUrl}" class="form-horizontal" method="post">
- <div class="form-group">
- <div class="col-sm-2 col-md-2">
- <label for="password" class="control-label">${rb.password}</label>
- </div>
+ <#if password.passwordSet>
+ <div class="form-group">
+ <div class="col-sm-2 col-md-2">
+ <label for="password" class="control-label">${rb.password}</label>
+ </div>
- <div class="col-sm-10 col-md-10">
- <input type="password" class="form-control" id="password" name="password" autofocus>
+ <div class="col-sm-10 col-md-10">
+ <input type="password" class="form-control" id="password" name="password" autofocus>
+ </div>
</div>
- </div>
+ </#if>
<div class="form-group">
<div class="col-sm-2 col-md-2">
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 0900be8..7e190b4 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -43,6 +43,7 @@ import org.keycloak.models.ModelReadOnlyException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.ModelToRepresentation;
@@ -264,6 +265,10 @@ public class AccountService {
@Path("password")
@GET
public Response passwordPage() {
+ if (auth != null) {
+ account.setPasswordSet(isPasswordSet(auth.getUser()));
+ }
+
return forwardToPage("password", AccountPages.PASSWORD);
}
@@ -491,29 +496,31 @@ public class AccountService {
UserModel user = auth.getUser();
+ boolean requireCurrent = isPasswordSet(user);
+ account.setPasswordSet(requireCurrent);
+
String password = formData.getFirst("password");
String passwordNew = formData.getFirst("password-new");
String passwordConfirm = formData.getFirst("password-confirm");
- if (Validation.isEmpty(passwordNew)) {
- setReferrerOnPage();
- return account.setError(Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
- } else if (!passwordNew.equals(passwordConfirm)) {
- setReferrerOnPage();
- return account.setError(Messages.INVALID_PASSWORD_CONFIRM).createResponse(AccountPages.PASSWORD);
- }
+ if (requireCurrent) {
+ if (Validation.isEmpty(passwordNew)) {
+ setReferrerOnPage();
+ return account.setError(Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
+ }
- UserCredentialModel cred = UserCredentialModel.password(password);
- if (Validation.isEmpty(password)) {
- setReferrerOnPage();
- return account.setError(Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
- } else {
+ UserCredentialModel cred = UserCredentialModel.password(password);
if (!session.users().validCredentials(realm, user, cred)) {
setReferrerOnPage();
return account.setError(Messages.INVALID_PASSWORD_EXISTING).createResponse(AccountPages.PASSWORD);
}
}
+ if (!passwordNew.equals(passwordConfirm)) {
+ setReferrerOnPage();
+ return account.setError(Messages.INVALID_PASSWORD_CONFIRM).createResponse(AccountPages.PASSWORD);
+ }
+
try {
session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
} catch (ModelReadOnlyException mre) {
@@ -528,7 +535,7 @@ public class AccountService {
event.event(EventType.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();
setReferrerOnPage();
- return account.setSuccess("accountPasswordUpdated").createResponse(AccountPages.PASSWORD);
+ return account.setPasswordSet(true).setSuccess("accountPasswordUpdated").createResponse(AccountPages.PASSWORD);
}
@Path("social-update")
@@ -583,7 +590,7 @@ public class AccountService {
if (link != null) {
// Removing last social provider is not possible if you don't have other possibility to authenticate
- if (session.users().getSocialLinks(user, realm).size() > 1 || user.getFederationLink() != null) {
+ if (session.users().getSocialLinks(user, realm).size() > 1 || user.getFederationLink() != null || isPasswordSet(user)) {
session.users().removeSocialLink(realm, user, providerId);
logger.debugv("Social provider {0} removed successfully from user {1}", providerId, user.getUsername());
@@ -681,6 +688,16 @@ public class AccountService {
return oauth.redirect(uriInfo, accountUri.toString());
}
+ public static boolean isPasswordSet(UserModel user) {
+ boolean passwordSet = false;
+ for (UserCredentialValueModel c : user.getCredentialsDirectly()) {
+ if (c.getType().equals(CredentialRepresentation.PASSWORD)) {
+ passwordSet = true;
+ }
+ }
+ return passwordSet;
+ }
+
private String[] getReferrer() {
String referrer = uriInfo.getQueryParameters().getFirst("referrer");
if (referrer == null) {