KEYCLOAK-18590 Save Button Enabled For Empty Attributes

This commit is contained in:
Martin Bartoš 2021-08-19 15:00:29 +02:00 committed by Marek Posolda
parent fd2787ae7d
commit 7c243c8427
5 changed files with 51 additions and 12 deletions

View file

@ -474,7 +474,8 @@ public class ModelToRepresentation {
session.clientPolicy().updateRealmRepresentationFromModel(realm, rep); session.clientPolicy().updateRealmRepresentationFromModel(realm, rep);
rep.setAttributes(stripRealmAttributesIncludedAsFields(realm.getAttributes())); // Append realm attributes to representation
rep.getAttributes().putAll(stripRealmAttributesIncludedAsFields(realm.getAttributes()));
if (!internal) { if (!internal) {
rep = StripSecretsUtils.strip(rep); rep = StripSecretsUtils.strip(rep);

View file

@ -73,6 +73,12 @@ public class TokenSettings extends RealmSettings {
@FindBy(id = "actionTokenAttributeTime") @FindBy(id = "actionTokenAttributeTime")
private WebElement actionTokenAttributeTime; private WebElement actionTokenAttributeTime;
@FindBy(name = "requestUriLifespanUnit")
private Select requestUriLifespanUnit;
@FindBy(id = "requestUriLifespan")
private WebElement requestUriLifespanTimeout;
@FindBy(xpath = "//button[@data-ng-click='resetToDefaultToken(actionTokenId)']") @FindBy(xpath = "//button[@data-ng-click='resetToDefaultToken(actionTokenId)']")
private WebElement resetButton; private WebElement resetButton;
@ -84,13 +90,17 @@ public class TokenSettings extends RealmSettings {
setTimeout(sessionLifespanTimeoutUnit, sessionLifespanTimeout, time, unit); setTimeout(sessionLifespanTimeoutUnit, sessionLifespanTimeout, time, unit);
} }
public void setRequestUriLifespanTimeout(int time, TimeUnit unit) {
setTimeout(requestUriLifespanUnit, requestUriLifespanTimeout, time, unit);
}
public void setOperation(String tokenType, int time, TimeUnit unit) { public void setOperation(String tokenType, int time, TimeUnit unit) {
selectOperation(tokenType); selectOperation(tokenType);
setTimeout(actionTokenAttributeUnit, actionTokenAttributeTime, time, unit); setTimeout(actionTokenAttributeUnit, actionTokenAttributeTime, time, unit);
} }
private void setTimeout(Select timeoutElement, WebElement unitElement, private void setTimeout(Select timeoutElement, WebElement unitElement,
int timeout, TimeUnit unit) { int timeout, TimeUnit unit) {
timeoutElement.selectByValue(capitalize(unit.name().toLowerCase())); timeoutElement.selectByValue(capitalize(unit.name().toLowerCase()));
UIUtils.setTextInputValue(unitElement, valueOf(timeout)); UIUtils.setTextInputValue(unitElement, valueOf(timeout));
} }
@ -112,5 +122,13 @@ public class TokenSettings extends RealmSettings {
actionTokenAttributeSelect.selectByValue(tokenType.toLowerCase()); actionTokenAttributeSelect.selectByValue(tokenType.toLowerCase());
pause(500); // wait for the form to be updated; there isn't currently a better way pause(500); // wait for the form to be updated; there isn't currently a better way
} }
public int getRequestUriLifespanTimeout() {
return Integer.parseInt(requestUriLifespanTimeout.getAttribute("value"));
}
public TimeUnit getRequestUriLifespanUnits() {
return TimeUnit.valueOf(requestUriLifespanUnit.getFirstSelectedOption().getText().toUpperCase());
}
} }
} }

View file

@ -23,6 +23,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionToken; import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionToken;
import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken; import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
import org.keycloak.models.ParConfig;
import org.keycloak.models.jpa.entities.RealmAttributes; import org.keycloak.models.jpa.entities.RealmAttributes;
import org.keycloak.testsuite.auth.page.account.Account; import org.keycloak.testsuite.auth.page.account.Account;
import org.keycloak.testsuite.console.page.realm.TokenSettings; import org.keycloak.testsuite.console.page.realm.TokenSettings;
@ -34,10 +35,11 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad; import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
@ -188,6 +190,28 @@ public class TokensTest extends AbstractRealmTest {
} }
@Test
public void testParRequestUriLifespan() {
int defaultMinutes = (int) TimeUnit.SECONDS.toMinutes(ParConfig.DEFAULT_PAR_REQUEST_URI_LIFESPAN);
assertThat(tokenSettingsPage.form().getRequestUriLifespanTimeout(), is(defaultMinutes));
tokenSettingsPage.form().setRequestUriLifespanTimeout(30, TimeUnit.MINUTES);
tokenSettingsPage.form().save();
assertAlertSuccess();
assertThat(tokenSettingsPage.form().getRequestUriLifespanTimeout(), is(30));
assertThat(tokenSettingsPage.form().getRequestUriLifespanUnits(), is(TimeUnit.MINUTES));
tokenSettingsPage.form().setRequestUriLifespanTimeout(20,TimeUnit.HOURS);
tokenSettingsPage.form().save();
assertAlertSuccess();
assertThat(tokenSettingsPage.form().getRequestUriLifespanTimeout(), is(20));
assertThat(tokenSettingsPage.form().getRequestUriLifespanUnits(), is(TimeUnit.HOURS));
}
private Map<String, Integer> getUserActionTokens() { private Map<String, Integer> getUserActionTokens() {
Map<String, Integer> userActionTokens = new HashMap<>(); Map<String, Integer> userActionTokens = new HashMap<>();
adminClient.realm(testRealmPage.getAuthRealm()).toRepresentation().getAttributes().entrySet().stream() adminClient.realm(testRealmPage.getAuthRealm()).toRepresentation().getAttributes().entrySet().stream()

View file

@ -1325,8 +1325,8 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
$scope.realm.actionTokenGeneratedByAdminLifespan = TimeUnit2.asUnit(realm.actionTokenGeneratedByAdminLifespan); $scope.realm.actionTokenGeneratedByAdminLifespan = TimeUnit2.asUnit(realm.actionTokenGeneratedByAdminLifespan);
$scope.realm.actionTokenGeneratedByUserLifespan = TimeUnit2.asUnit(realm.actionTokenGeneratedByUserLifespan); $scope.realm.actionTokenGeneratedByUserLifespan = TimeUnit2.asUnit(realm.actionTokenGeneratedByUserLifespan);
$scope.realm.oauth2DeviceCodeLifespan = TimeUnit2.asUnit(realm.oauth2DeviceCodeLifespan); $scope.realm.oauth2DeviceCodeLifespan = TimeUnit2.asUnit(realm.oauth2DeviceCodeLifespan);
$scope.requestUriLifespan = TimeUnit2.asUnit(realm.attributes.parRequestUriLifespan); $scope.realm.attributes.parRequestUriLifespan = TimeUnit2.asUnit(realm.attributes.parRequestUriLifespan);
$scope.realm.attributes = realm.attributes $scope.realm.attributes = realm.attributes;
var oldCopy = angular.copy($scope.realm); var oldCopy = angular.copy($scope.realm);
$scope.changed = false; $scope.changed = false;
@ -1337,10 +1337,6 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
} }
}, true); }, true);
$scope.$watch('requestUriLifespan', function () {
$scope.changed = true;
}, true);
$scope.$watch('actionLifespanId', function () { $scope.$watch('actionLifespanId', function () {
// changedActionLifespanId signals other watchers that we were merely // changedActionLifespanId signals other watchers that we were merely
// changing the dropdown and we should not enable 'save' button // changing the dropdown and we should not enable 'save' button
@ -1390,7 +1386,7 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
$scope.realm.actionTokenGeneratedByAdminLifespan = $scope.realm.actionTokenGeneratedByAdminLifespan.toSeconds(); $scope.realm.actionTokenGeneratedByAdminLifespan = $scope.realm.actionTokenGeneratedByAdminLifespan.toSeconds();
$scope.realm.actionTokenGeneratedByUserLifespan = $scope.realm.actionTokenGeneratedByUserLifespan.toSeconds(); $scope.realm.actionTokenGeneratedByUserLifespan = $scope.realm.actionTokenGeneratedByUserLifespan.toSeconds();
$scope.realm.oauth2DeviceCodeLifespan = $scope.realm.oauth2DeviceCodeLifespan.toSeconds(); $scope.realm.oauth2DeviceCodeLifespan = $scope.realm.oauth2DeviceCodeLifespan.toSeconds();
$scope.realm.attributes.parRequestUriLifespan = $scope.requestUriLifespan.toSeconds().toString(); $scope.realm.attributes.parRequestUriLifespan = $scope.realm.attributes.parRequestUriLifespan.toSeconds();
Realm.update($scope.realm, function () { Realm.update($scope.realm, function () {
$route.reload(); $route.reload();

View file

@ -324,9 +324,9 @@
<label class="col-md-2 control-label" for="requestUriLifespan" class="two-lines">{{:: 'request-uri-lifespan' | translate}}</label> <label class="col-md-2 control-label" for="requestUriLifespan" class="two-lines">{{:: 'request-uri-lifespan' | translate}}</label>
<div class="col-md-6 time-selector"> <div class="col-md-6 time-selector">
<input class="form-control" type="number" required min="1" max="31536000" string-to-number data-ng-model="requestUriLifespan.time" <input class="form-control" type="number" required min="1" max="31536000" string-to-number data-ng-model="realm.attributes.parRequestUriLifespan.time"
id="requestUriLifespan" name="requestUriLifespan"> id="requestUriLifespan" name="requestUriLifespan">
<select class="form-control" name="requestUriLifespan" data-ng-model="requestUriLifespan.unit"> <select class="form-control" name="requestUriLifespanUnit" data-ng-model="realm.attributes.parRequestUriLifespan.unit">
<option value="Minutes">{{:: 'minutes' | translate}}</option> <option value="Minutes">{{:: 'minutes' | translate}}</option>
<option value="Hours">{{:: 'hours' | translate}}</option> <option value="Hours">{{:: 'hours' | translate}}</option>
</select> </select>