fix: adds a check for duplicate users/clients to simplify cmd errors (#31583)

also changes temp-admin-service to temp-admin

closes: #31160

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-08-08 08:20:33 -04:00 committed by GitHub
parent e090b0d260
commit 7ce6f12fe3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 73 additions and 47 deletions

View file

@ -17,27 +17,22 @@
package org.keycloak.quarkus.runtime.integration.jaxrs; package org.keycloak.quarkus.runtime.integration.jaxrs;
import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.common.annotation.Blocking;
import org.keycloak.Config;
import org.keycloak.config.BootstrapAdminOptions; import org.keycloak.config.BootstrapAdminOptions;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.platform.Platform; import org.keycloak.platform.Platform;
import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.cli.command.AbstractNonServerCommand; import org.keycloak.quarkus.runtime.cli.command.AbstractNonServerCommand;
import org.keycloak.quarkus.runtime.configuration.Configuration; import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
import org.keycloak.quarkus.runtime.integration.QuarkusPlatform; import org.keycloak.quarkus.runtime.integration.QuarkusPlatform;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.utils.StringUtil; import org.keycloak.utils.StringUtil;
import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.common.annotation.Blocking;
import jakarta.enterprise.event.Observes; import jakarta.enterprise.event.Observes;
import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.ApplicationPath;
@ -85,23 +80,23 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
try { try {
//Integer expiration = Configuration.getOptionalKcValue(BootstrapAdminOptions.EXPIRATION.getKey()).map(Integer::valueOf).orElse(null); //Integer expiration = Configuration.getOptionalKcValue(BootstrapAdminOptions.EXPIRATION.getKey()).map(Integer::valueOf).orElse(null);
if (StringUtil.isNotBlank(adminPassword)) { if (StringUtil.isNotBlank(adminPassword) && !createTemporaryMasterRealmAdminUser(adminUsername, adminPassword, /*expiration,*/ session)) {
createTemporaryMasterRealmAdminUser(adminUsername, adminPassword, /*expiration,*/ session); throw new RuntimeException("Aborting startup and the creation of the master realm, because the temporary admin user account could not be created.");
} }
if (StringUtil.isNotBlank(clientSecret)) { if (StringUtil.isNotBlank(clientSecret) && !createTemporaryMasterRealmAdminService(clientId, clientSecret, /*expiration,*/ session)) {
createTemporaryMasterRealmAdminService(clientId, clientSecret, /*expiration,*/ session); throw new RuntimeException("Aborting startup and the creation of the master realm, because the temporary admin service account could not be created.");
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new RuntimeException("Invalid admin expiration value provided. An integer is expected.", e); throw new RuntimeException("Invalid admin expiration value provided. An integer is expected.", e);
} }
} }
public void createTemporaryMasterRealmAdminUser(String adminUserName, String adminPassword, /*Integer adminExpiration,*/ KeycloakSession session) { public boolean createTemporaryMasterRealmAdminUser(String adminUserName, String adminPassword, /*Integer adminExpiration,*/ KeycloakSession session) {
new ApplianceBootstrap(session).createTemporaryMasterRealmAdminUser(adminUserName, adminPassword /*, adminExpiration*/, false); return new ApplianceBootstrap(session).createTemporaryMasterRealmAdminUser(adminUserName, adminPassword /*, adminExpiration*/, false);
} }
public void createTemporaryMasterRealmAdminService(String clientId, String clientSecret, /*Integer adminExpiration,*/ KeycloakSession session) { public boolean createTemporaryMasterRealmAdminService(String clientId, String clientSecret, /*Integer adminExpiration,*/ KeycloakSession session) {
new ApplianceBootstrap(session).createTemporaryMasterRealmAdminService(clientId, clientSecret /*, adminExpiration*/); return new ApplianceBootstrap(session).createTemporaryMasterRealmAdminService(clientId, clientSecret /*, adminExpiration*/);
} }
} }

View file

@ -88,7 +88,7 @@ public interface ServicesLogger extends BasicLogger {
@LogMessage(level = ERROR) @LogMessage(level = ERROR)
@Message(id=10, value="Failed to add user '%s' to realm '%s': user with username exists") @Message(id=10, value="Failed to add user '%s' to realm '%s': user with username exists")
void addUserFailedUserExists(String user, String realm); void addUserFailedUserExists(String user, String realm);
@LogMessage(level = ERROR) @LogMessage(level = ERROR)
@Message(id=11, value="Failed to add user '%s' to realm '%s'") @Message(id=11, value="Failed to add user '%s' to realm '%s'")
void addUserFailed(@Cause Throwable t, String user, String realm); void addUserFailed(@Cause Throwable t, String user, String realm);
@ -465,10 +465,15 @@ public interface ServicesLogger extends BasicLogger {
void scriptEngineCreated(String engineName, String engineVersion, String mimeType); void scriptEngineCreated(String engineName, String engineVersion, String mimeType);
@LogMessage(level = DEBUG) @LogMessage(level = DEBUG)
@Message(id=107, value="Skipping create admin user. Admin already exists in realm '%s'.") @Message(id=107, value="Skipping create admin user. User(s) already exist in realm '%s'.")
void addAdminUserFailedAdminExists(String realm); void addAdminUserFailedUsersExist(String realm);
@LogMessage(level = WARN) @LogMessage(level = WARN)
@Message(id=108, value="URI '%s' doesn't match any trustedHost or trustedDomain") @Message(id=108, value="URI '%s' doesn't match any trustedHost or trustedDomain")
void uriDoesntMatch(String uri); void uriDoesntMatch(String uri);
@LogMessage(level = ERROR)
@Message(id=109, value="Failed to add client '%s' to realm '%s': client with client ID exists")
void addClientFailedClientExists(String clientId, String realm);
} }

View file

@ -23,6 +23,7 @@ import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
@ -113,7 +114,14 @@ public class ApplianceBootstrap {
return true; return true;
} }
public void createTemporaryMasterRealmAdminUser(String username, String password, /*Integer expriationMinutes,*/ boolean initialUser) { /**
* Create a temporary admin user
* @param username
* @param password
* @param initialUser if true only create the user if no other users exist
* @return false if the user could not be created
*/
public boolean createTemporaryMasterRealmAdminUser(String username, String password, /*Integer expriationMinutes,*/ boolean initialUser) {
RealmModel realm = session.realms().getRealmByName(Config.getAdminRealm()); RealmModel realm = session.realms().getRealmByName(Config.getAdminRealm());
session.getContext().setRealm(realm); session.getContext().setRealm(realm);
@ -121,26 +129,38 @@ public class ApplianceBootstrap {
//expriationMinutes = expriationMinutes == null ? DEFAULT_TEMP_ADMIN_EXPIRATION : expriationMinutes; //expriationMinutes = expriationMinutes == null ? DEFAULT_TEMP_ADMIN_EXPIRATION : expriationMinutes;
if (initialUser && session.users().getUsersCount(realm) > 0) { if (initialUser && session.users().getUsersCount(realm) > 0) {
ServicesLogger.LOGGER.addAdminUserFailedAdminExists(Config.getAdminRealm()); ServicesLogger.LOGGER.addAdminUserFailedUsersExist(Config.getAdminRealm());
return; return false;
} }
UserModel adminUser = session.users().addUser(realm, username); try {
adminUser.setEnabled(true); UserModel adminUser = session.users().addUser(realm, username);
// TODO: is this appropriate, does it need to be managed? adminUser.setEnabled(true);
// adminUser.setSingleAttribute("temporary_admin", Boolean.TRUE.toString()); // TODO: is this appropriate, does it need to be managed?
// also set the expiration - could be relative to a creation timestamp, or computed // adminUser.setSingleAttribute("temporary_admin", Boolean.TRUE.toString());
// also set the expiration - could be relative to a creation timestamp, or computed
UserCredentialModel usrCredModel = UserCredentialModel.password(password);
adminUser.credentialManager().updateCredential(usrCredModel); UserCredentialModel usrCredModel = UserCredentialModel.password(password);
adminUser.credentialManager().updateCredential(usrCredModel);
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
adminUser.grantRole(adminRole); RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
adminUser.grantRole(adminRole);
ServicesLogger.LOGGER.createdTemporaryAdminUser(username);
ServicesLogger.LOGGER.createdTemporaryAdminUser(username);
} catch (ModelDuplicateException e) {
ServicesLogger.LOGGER.addUserFailedUserExists(username, Config.getAdminRealm());
return false;
}
return true;
} }
public void createTemporaryMasterRealmAdminService(String clientId, String clientSecret /*, Integer expriationMinutes*/) { /**
* Create a temporary admin service account
* @param clientId the client ID
* @param clientSecret the client secret
* @return false if the service account could not be created
*/
public boolean createTemporaryMasterRealmAdminService(String clientId, String clientSecret /*, Integer expriationMinutes*/) {
RealmModel realm = session.realms().getRealmByName(Config.getAdminRealm()); RealmModel realm = session.realms().getRealmByName(Config.getAdminRealm());
session.getContext().setRealm(realm); session.getContext().setRealm(realm);
@ -154,17 +174,23 @@ public class ApplianceBootstrap {
adminClient.setPublicClient(false); adminClient.setPublicClient(false);
adminClient.setSecret(clientSecret); adminClient.setSecret(clientSecret);
ClientModel adminClientModel = ClientManager.createClient(session, realm, adminClient); try {
ClientModel adminClientModel = ClientManager.createClient(session, realm, adminClient);
new ClientManager(new RealmManager(session)).enableServiceAccount(adminClientModel);
UserModel serviceAccount = session.users().getServiceAccount(adminClientModel); new ClientManager(new RealmManager(session)).enableServiceAccount(adminClientModel);
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN); UserModel serviceAccount = session.users().getServiceAccount(adminClientModel);
serviceAccount.grantRole(adminRole); RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
serviceAccount.grantRole(adminRole);
// TODO: set temporary
// also set the expiration - could be relative to a creation timestamp, or computed // TODO: set temporary
// also set the expiration - could be relative to a creation timestamp, or computed
ServicesLogger.LOGGER.createdTemporaryAdminService(clientId);
ServicesLogger.LOGGER.createdTemporaryAdminService(clientId);
} catch (ModelDuplicateException e) {
ServicesLogger.LOGGER.addClientFailedClientExists(clientId, Config.getAdminRealm());
return false;
}
return true;
} }
public void createMasterRealmUser(String username, String password) { public void createMasterRealmUser(String username, String password) {