Require user to specify a temporary admin password to do admin recovery.
This commit is contained in:
parent
e48aafd588
commit
e977a363ef
5 changed files with 72 additions and 13 deletions
|
@ -5,11 +5,11 @@
|
||||||
accidentally deleted, its role mappings were removed, or the password was simply forgotten.
|
accidentally deleted, its role mappings were removed, or the password was simply forgotten.
|
||||||
</para>
|
</para>
|
||||||
<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[
|
<programlisting><![CDATA[
|
||||||
bin/standalone.sh -Dkeycloak.recover-admin=true
|
bin/standalone.sh -Dkeycloak.recover-admin=true -Dkeycloak.temp-admin-password=temppassword
|
||||||
]]></programlisting>
|
]]></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.
|
prompted to immediately change this password.
|
||||||
</para>
|
</para>
|
||||||
</chapter>
|
</chapter>
|
|
@ -36,6 +36,7 @@ public class AdminRecovery {
|
||||||
private static final Logger log = Logger.getLogger(AdminRecovery.class);
|
private static final Logger log = Logger.getLogger(AdminRecovery.class);
|
||||||
|
|
||||||
public static final String RECOVER_ADMIN_ACCOUNT = "keycloak.recover-admin";
|
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
|
// Don't allow instances
|
||||||
private AdminRecovery() {}
|
private AdminRecovery() {}
|
||||||
|
@ -47,14 +48,15 @@ public class AdminRecovery {
|
||||||
|
|
||||||
session.getTransaction().begin();
|
session.getTransaction().begin();
|
||||||
try {
|
try {
|
||||||
doRecover(session);
|
doRecover(session, getTempAdminPassword());
|
||||||
session.getTransaction().commit();
|
session.getTransaction().commit();
|
||||||
log.info("*******************************");
|
log.info("*******************************");
|
||||||
log.info("Recovered Master Admin account.");
|
log.info("Recovered Master Admin account.");
|
||||||
log.info("*******************************");
|
log.info("*******************************");
|
||||||
} finally {
|
} finally {
|
||||||
session.close();
|
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);
|
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();
|
RealmProvider realmProvider = session.realms();
|
||||||
UserProvider userProvider = session.users();
|
UserProvider userProvider = session.users();
|
||||||
|
|
||||||
|
@ -75,6 +85,6 @@ public class AdminRecovery {
|
||||||
adminUser = userProvider.addUser(realm, "admin");
|
adminUser = userProvider.addUser(realm, "admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplianceBootstrap.setupAdminUser(session, realm, adminUser);
|
ApplianceBootstrap.setupAdminUser(session, realm, adminUser, tempAdminPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,15 +61,15 @@ public class ApplianceBootstrap {
|
||||||
KeycloakModelUtils.generateRealmKeys(realm);
|
KeycloakModelUtils.generateRealmKeys(realm);
|
||||||
|
|
||||||
UserModel adminUser = session.users().addUser(realm, "admin");
|
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);
|
adminUser.setEnabled(true);
|
||||||
UserCredentialModel password = new UserCredentialModel();
|
UserCredentialModel usrCredModel = new UserCredentialModel();
|
||||||
password.setType(UserCredentialModel.PASSWORD);
|
usrCredModel.setType(UserCredentialModel.PASSWORD);
|
||||||
password.setValue("admin");
|
usrCredModel.setValue(password);
|
||||||
session.users().updateCredential(realm, adminUser, password);
|
session.users().updateCredential(realm, adminUser, usrCredModel);
|
||||||
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||||
|
|
||||||
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
|
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.offlineconfig;
|
package org.keycloak.testsuite.offlineconfig;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -27,6 +28,7 @@ import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
import org.keycloak.offlineconfig.AdminRecovery;
|
import org.keycloak.offlineconfig.AdminRecovery;
|
||||||
|
import org.keycloak.offlineconfig.OfflineConfigException;
|
||||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||||
import org.keycloak.testsuite.rule.WebRule;
|
import org.keycloak.testsuite.rule.WebRule;
|
||||||
|
|
||||||
|
@ -42,6 +44,13 @@ public class AdminRecoveryTest {
|
||||||
@Rule
|
@Rule
|
||||||
public WebRule webRule = new WebRule(this);
|
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
|
@Test
|
||||||
public void testAdminDeletedRecovery() {
|
public void testAdminDeletedRecovery() {
|
||||||
KeycloakSession session = keycloakRule.startSession();
|
KeycloakSession session = keycloakRule.startSession();
|
||||||
|
@ -78,8 +87,16 @@ public class AdminRecoveryTest {
|
||||||
Assert.assertNotEquals("forgotten-password", getAdminPassword());
|
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) {
|
private void doAdminRecovery(KeycloakSession session) {
|
||||||
System.setProperty(AdminRecovery.RECOVER_ADMIN_ACCOUNT, "true");
|
System.setProperty(AdminRecovery.RECOVER_ADMIN_ACCOUNT, "true");
|
||||||
|
System.setProperty(AdminRecovery.TEMP_ADMIN_PASSWORD, "foo");
|
||||||
AdminRecovery.recover(session.getKeycloakSessionFactory());
|
AdminRecovery.recover(session.getKeycloakSessionFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue