Require user to specify a temporary admin password to do admin recovery.

This commit is contained in:
Stan Silvert 2015-06-08 13:04:52 -04:00
parent e48aafd588
commit e977a363ef
5 changed files with 72 additions and 13 deletions

View file

@ -5,11 +5,11 @@
accidentally deleted, its role mappings were removed, or the password was simply forgotten.
</para>
<para>
To recover the master admin user, just start the server with the following system property:
To recover the master admin user, just start the server with the following system properties:
<programlisting><![CDATA[
bin/standalone.sh -Dkeycloak.recover-admin=true
bin/standalone.sh -Dkeycloak.recover-admin=true -Dkeycloak.temp-admin-password=temppassword
]]></programlisting>
Then you can log in to the master admin account with the default password "admin". You will then be
Then you can log in to the master admin account with your temporary password. You will then be
prompted to immediately change this password.
</para>
</chapter>

View file

@ -36,6 +36,7 @@ public class AdminRecovery {
private static final Logger log = Logger.getLogger(AdminRecovery.class);
public static final String RECOVER_ADMIN_ACCOUNT = "keycloak.recover-admin";
public static final String TEMP_ADMIN_PASSWORD = "keycloak.temp-admin-password";
// Don't allow instances
private AdminRecovery() {}
@ -47,14 +48,15 @@ public class AdminRecovery {
session.getTransaction().begin();
try {
doRecover(session);
doRecover(session, getTempAdminPassword());
session.getTransaction().commit();
log.info("*******************************");
log.info("Recovered Master Admin account.");
log.info("*******************************");
} finally {
session.close();
System.setProperty(AdminRecovery.RECOVER_ADMIN_ACCOUNT, "false");
System.clearProperty(RECOVER_ADMIN_ACCOUNT);
System.clearProperty(TEMP_ADMIN_PASSWORD);
}
}
@ -63,7 +65,15 @@ public class AdminRecovery {
return Boolean.parseBoolean(strNeedRecovery);
}
private static void doRecover(KeycloakSession session) {
private static String getTempAdminPassword() {
String tempAdminPassword = System.getProperty(TEMP_ADMIN_PASSWORD);
if ((tempAdminPassword == null) || tempAdminPassword.isEmpty()) {
throw new OfflineConfigException("Must provide temporary admin password to recover admin account.");
}
return tempAdminPassword;
}
private static void doRecover(KeycloakSession session, String tempAdminPassword) {
RealmProvider realmProvider = session.realms();
UserProvider userProvider = session.users();
@ -75,6 +85,6 @@ public class AdminRecovery {
adminUser = userProvider.addUser(realm, "admin");
}
ApplianceBootstrap.setupAdminUser(session, realm, adminUser);
ApplianceBootstrap.setupAdminUser(session, realm, adminUser, tempAdminPassword);
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.offlineconfig;
/**
* Runtime exception thrown when an offline configuration fails. Offline
* configuration is defined as any configuration done before the Keycloak Server
* starts accepting requests.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
*/
public class OfflineConfigException extends IllegalStateException {
public OfflineConfigException(String msg) {
super(msg);
}
}

View file

@ -61,15 +61,15 @@ public class ApplianceBootstrap {
KeycloakModelUtils.generateRealmKeys(realm);
UserModel adminUser = session.users().addUser(realm, "admin");
setupAdminUser(session, realm, adminUser);
setupAdminUser(session, realm, adminUser, "admin");
}
public static void setupAdminUser(KeycloakSession session, RealmModel realm, UserModel adminUser) {
public static void setupAdminUser(KeycloakSession session, RealmModel realm, UserModel adminUser, String password) {
adminUser.setEnabled(true);
UserCredentialModel password = new UserCredentialModel();
password.setType(UserCredentialModel.PASSWORD);
password.setValue("admin");
session.users().updateCredential(realm, adminUser, password);
UserCredentialModel usrCredModel = new UserCredentialModel();
usrCredModel.setType(UserCredentialModel.PASSWORD);
usrCredModel.setValue(password);
session.users().updateCredential(realm, adminUser, usrCredModel);
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.offlineconfig;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
@ -27,6 +28,7 @@ import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.offlineconfig.AdminRecovery;
import org.keycloak.offlineconfig.OfflineConfigException;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebRule;
@ -42,6 +44,13 @@ public class AdminRecoveryTest {
@Rule
public WebRule webRule = new WebRule(this);
// Verifies that system properties were cleared at the end of recovery
@After
public void verifySysPropsCleared() {
Assert.assertNull(System.getProperty(AdminRecovery.RECOVER_ADMIN_ACCOUNT));
Assert.assertNull(System.getProperty(AdminRecovery.TEMP_ADMIN_PASSWORD));
}
@Test
public void testAdminDeletedRecovery() {
KeycloakSession session = keycloakRule.startSession();
@ -78,8 +87,16 @@ public class AdminRecoveryTest {
Assert.assertNotEquals("forgotten-password", getAdminPassword());
}
@Test(expected = OfflineConfigException.class)
public void testAdminRecoveryWithoutPassword() {
KeycloakSession session = keycloakRule.startSession();
System.setProperty(AdminRecovery.RECOVER_ADMIN_ACCOUNT, "true");
AdminRecovery.recover(session.getKeycloakSessionFactory());
}
private void doAdminRecovery(KeycloakSession session) {
System.setProperty(AdminRecovery.RECOVER_ADMIN_ACCOUNT, "true");
System.setProperty(AdminRecovery.TEMP_ADMIN_PASSWORD, "foo");
AdminRecovery.recover(session.getKeycloakSessionFactory());
}