KEYCLOAK-1758 add-user script

This commit is contained in:
Stian Thorgersen 2015-11-23 14:45:25 +01:00
parent ccd456dba6
commit cfc28b861b
82 changed files with 823 additions and 384 deletions

View file

@ -57,6 +57,8 @@ public class ClientRegistrationCLI {
.create();
aeshConsole.start();
/*
if (args.length > 0) {
CommandContainer command = registry.getCommand(args[0], null);

View file

@ -29,7 +29,7 @@ public class CredentialRepresentation {
private Integer period;
// only used when updating a credential. Might set required action
protected boolean temporary;
protected Boolean temporary;
public String getType() {
return type;
@ -79,11 +79,11 @@ public class CredentialRepresentation {
this.hashIterations = hashIterations;
}
public boolean isTemporary() {
public Boolean isTemporary() {
return temporary;
}
public void setTemporary(boolean temporary) {
public void setTemporary(Boolean temporary) {
this.temporary = temporary;
}

View file

@ -84,7 +84,6 @@ public class RealmRepresentation {
private List<IdentityProviderRepresentation> identityProviders;
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
private List<ProtocolMapperRepresentation> protocolMappers;
private Boolean identityFederationEnabled;
protected Boolean internationalizationEnabled;
protected Set<String> supportedLocales;
protected String defaultLocale;
@ -613,10 +612,6 @@ public class RealmRepresentation {
identityProviders.add(identityProviderRepresentation);
}
public boolean isIdentityFederationEnabled() {
return identityProviders != null && !identityProviders.isEmpty();
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}

View file

@ -17,9 +17,9 @@ public class UserRepresentation {
protected String id;
protected Long createdTimestamp;
protected String username;
protected boolean enabled;
protected boolean totp;
protected boolean emailVerified;
protected Boolean enabled;
protected Boolean totp;
protected Boolean emailVerified;
protected String firstName;
protected String lastName;
protected String email;
@ -98,27 +98,27 @@ public class UserRepresentation {
this.username = username;
}
public boolean isEnabled() {
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public boolean isTotp() {
public Boolean isTotp() {
return totp;
}
public void setTotp(boolean totp) {
public void setTotp(Boolean totp) {
this.totp = totp;
}
public boolean isEmailVerified() {
public Boolean isEmailVerified() {
return emailVerified;
}
public void setEmailVerified(boolean emailVerified) {
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}

View file

@ -3,6 +3,7 @@ package org.keycloak.util;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.TypeReference;
import java.io.IOException;
import java.io.InputStream;
@ -27,7 +28,10 @@ public class JsonSerialization {
public static void writeValueToStream(OutputStream os, Object obj) throws IOException {
mapper.writeValue(os, obj);
}
public static void writeValuePrettyToStream(OutputStream os, Object obj) throws IOException {
prettyMapper.writeValue(os, obj);
}
public static String writeValueAsPrettyString(Object obj) throws IOException {
@ -53,6 +57,10 @@ public class JsonSerialization {
return readValue(bytes, type, false);
}
public static <T> T readValue(InputStream bytes, TypeReference<T> type) throws IOException {
return mapper.readValue(bytes, type);
}
public static <T> T readValue(InputStream bytes, Class<T> type, boolean replaceSystemProperties) throws IOException {
if (replaceSystemProperties) {
return sysPropertiesAwareMapper.readValue(bytes, type);

View file

@ -36,19 +36,27 @@
<artifactId>keycloak-dependencies-server-all</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-adduser</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-extensions</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wf9-server-subsystem</artifactId>
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-feature-pack</artifactId>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.jboss.aesh</groupId>
<artifactId>aesh</artifactId>
</dependency>
</dependencies>
<build>

View file

@ -0,0 +1,73 @@
@echo off
rem -------------------------------------------------------------------------
rem Add User script for Windows
rem -------------------------------------------------------------------------
rem
rem A simple utility for adding new users to the properties file used
rem for domain management authentication out of the box.
rem $Id$
@if not "%ECHO%" == "" echo %ECHO%
@if "%OS%" == "Windows_NT" setlocal
if "%OS%" == "Windows_NT" (
set "DIRNAME=%~dp0%"
) else (
set DIRNAME=.\
)
pushd "%DIRNAME%.."
set "RESOLVED_JBOSS_HOME=%CD%"
popd
if "x%JBOSS_HOME%" == "x" (
set "JBOSS_HOME=%RESOLVED_JBOSS_HOME%"
)
pushd "%JBOSS_HOME%"
set "SANITIZED_JBOSS_HOME=%CD%"
popd
if /i "%RESOLVED_JBOSS_HOME%" NEQ "%SANITIZED_JBOSS_HOME%" (
echo.
echo WARNING: The JBOSS_HOME ^("%SANITIZED_JBOSS_HOME%"^) that this script uses points to a different installation than the one that this script resides in ^("%RESOLVED_JBOSS_HOME%"^). Unpredictable results may occur.
echo.
echo JBOSS_HOME: "%JBOSS_HOME%"
echo.
)
rem Setup JBoss specific properties
if "x%JAVA_HOME%" == "x" (
set JAVA=java
echo JAVA_HOME is not set. Unexpected results may occur.
echo Set JAVA_HOME to the directory of your local JDK to avoid this message.
) else (
set "JAVA=%JAVA_HOME%\bin\java"
)
rem Find jboss-modules.jar, or we can't continue
if exist "%JBOSS_HOME%\jboss-modules.jar" (
set "RUNJAR=%JBOSS_HOME%\jboss-modules.jar"
) else (
echo Could not locate "%JBOSS_HOME%\jboss-modules.jar".
echo Please check that you are in the bin directory when running this script.
goto END
)
rem Set default module root paths
if "x%JBOSS_MODULEPATH%" == "x" (
set "JBOSS_MODULEPATH=%JBOSS_HOME%\modules"
)
rem Uncomment to override standalone and domain user location
rem set "JAVA_OPTS=%JAVA_OPTS% -Djboss.server.config.user.dir=..\standalone\configuration -Djboss.domain.config.user.dir=..\domain\configuration"
"%JAVA%" %JAVA_OPTS% ^
-jar "%JBOSS_HOME%\jboss-modules.jar" ^
-mp "%JBOSS_MODULEPATH%" ^
org.keycloak.keycloak-wildfly-adduser ^
%*
:END
if "x%NOPAUSE%" == "x" pause

View file

@ -0,0 +1,72 @@
#!/bin/sh
# Add User Utility
#
# A simple utility for adding new users to the properties file used
# for domain management authentication out of the box.
#
DIRNAME=`dirname "$0"`
# OS specific support (must be 'true' or 'false').
cygwin=false;
if [ `uname|grep -i CYGWIN` ]; then
cygwin=true;
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JBOSS_HOME" ] &&
JBOSS_HOME=`cygpath --unix "$JBOSS_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$JAVAC_JAR" ] &&
JAVAC_JAR=`cygpath --unix "$JAVAC_JAR"`
fi
# Setup JBOSS_HOME
RESOLVED_JBOSS_HOME=`cd "$DIRNAME/.."; pwd`
if [ "x$JBOSS_HOME" = "x" ]; then
# get the full path (without any relative bits)
JBOSS_HOME=$RESOLVED_JBOSS_HOME
else
SANITIZED_JBOSS_HOME=`cd "$JBOSS_HOME"; pwd`
if [ "$RESOLVED_JBOSS_HOME" != "$SANITIZED_JBOSS_HOME" ]; then
echo "WARNING: The JBOSS_HOME ($SANITIZED_JBOSS_HOME) that this script uses points to a different installation than the one that this script resides in ($RESOLVED_JBOSS_HOME). Unpredictable results may occur."
echo ""
fi
fi
export JBOSS_HOME
# Setup the JVM
if [ "x$JAVA" = "x" ]; then
if [ "x$JAVA_HOME" != "x" ]; then
JAVA="$JAVA_HOME/bin/java"
else
JAVA="java"
fi
fi
if [ "x$JBOSS_MODULEPATH" = "x" ]; then
JBOSS_MODULEPATH="$JBOSS_HOME/modules"
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
JBOSS_HOME=`cygpath --path --windows "$JBOSS_HOME"`
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
JBOSS_MODULEPATH=`cygpath --path --windows "$JBOSS_MODULEPATH"`
fi
# Sample JPDA settings for remote socket debugging
#JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=y"
# Uncomment to override standalone and domain user location
#JAVA_OPTS="$JAVA_OPTS -Djboss.server.config.user.dir=../standalone/configuration -Djboss.domain.config.user.dir=../domain/configuration"
JAVA_OPTS="$JAVA_OPTS"
eval \"$JAVA\" $JAVA_OPTS \
-jar \""$JBOSS_HOME"/jboss-modules.jar\" \
-mp \""${JBOSS_MODULEPATH}"\" \
org.keycloak.keycloak-wildfly-adduser \
'"$@"'

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2010, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.3" name="org.jboss.aesh" slot="0.65">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.jboss.aesh:aesh}"/>
</resources>
<dependencies>
<module name="org.fusesource.jansi" />
</dependencies>
</module>

View file

@ -29,6 +29,6 @@
</resources>
<dependencies>
<module name="org.keycloak.keycloak-wf9-server-subsystem" services="export" export="true"/>
<module name="org.keycloak.keycloak-wildfly-server-subsystem" services="export" export="true"/>
</dependencies>
</module>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wildfly-adduser">
<main-class name="org.keycloak.wildfly.adduser.AddUser"/>
<resources>
<artifact name="${org.keycloak:keycloak-wildfly-adduser}"/>
</resources>
<dependencies>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-model-api"/>
<module name="org.jboss.aesh" slot="0.65"/>
<module name="org.jboss.as.domain-management"/>
<module name="org.codehaus.jackson.jackson-core-asl"/>
</dependencies>
</module>

View file

@ -22,11 +22,11 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wf9-server-subsystem">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wildfly-server-subsystem">
<resources>
<resource-root path="."/>
<artifact name="${org.keycloak:keycloak-wf9-server-subsystem}"/>
<artifact name="${org.keycloak:keycloak-wildfly-server-subsystem}"/>
</resources>
<dependencies>

View file

@ -274,8 +274,8 @@
<!-- subsystems -->
<module-def name="org.keycloak.keycloak-as7-server-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-as7-server-subsystem"/>
<module-def name="org.keycloak.keycloak-eap6-server-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-eap6-server-subsystem"/>
</module-def>
<module-def name="org.keycloak.keycloak-server-subsystem"/>

View file

@ -32,7 +32,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-server-subsystem</artifactId>
<artifactId>keycloak-eap6-server-subsystem</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-as7-server-subsystem">
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-eap6-server-subsystem">
<resources>
<resource-root path="."/>

View file

@ -30,6 +30,6 @@
</resources>
<dependencies>
<module name="org.keycloak.keycloak-as7-server-subsystem" services="export" export="true"/>
<module name="org.keycloak.keycloak-eap6-server-subsystem" services="export" export="true"/>
</dependencies>
</module>

View file

@ -37,7 +37,6 @@
<!ENTITY UserFederation SYSTEM "modules/user-federation.xml">
<!ENTITY Kerberos SYSTEM "modules/kerberos.xml">
<!ENTITY ExportImport SYSTEM "modules/export-import.xml">
<!ENTITY AdminRecovery SYSTEM "modules/admin-recovery.xml">
<!ENTITY ServerCache SYSTEM "modules/cache.xml">
<!ENTITY SecurityVulnerabilities SYSTEM "modules/security-vulnerabilities.xml">
<!ENTITY Clustering SYSTEM "modules/clustering.xml">

View file

@ -1,15 +0,0 @@
<chapter id="admin-recovery">
<title>Recovering the Master Admin User</title>
<para>
It is possible for the "admin" user in the master realm to become inoperable. This may be because it was
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 properties:
<programlisting><![CDATA[
bin/standalone.sh -Dkeycloak.recover-admin=true -Dkeycloak.temp-admin-password=temppassword
]]></programlisting>
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

@ -127,6 +127,25 @@ cd &lt;WILDFLY_HOME&gt;/bin
</listitem>
</itemizedlist>
</para>
<section>
<title>Admin User</title>
<para>
To access the admin console you need an account to login. Currently, there's a default account added
with the username <literal>admin</literal> and password <literal>admin</literal>. You will be required
to change the password on first login. We are planning on removing the built-in account soon and will
instead have an initial step to create the user.
</para>
<para>
You can also create a user with the <literal>add-user</literal> script found in <literal>bin</literal>.
This script will create a temporary file with the details of the user, which are imported at startup.
To add a user with this script run:
<programlisting><![CDATA[
bin/add-user.[sh|bat] -r master -u <username> -p <password>
]]></programlisting>
Then restart the server.
</para>
</section>
<section>
<title>Relational Database Configuration</title>
<para>

View file

@ -17,6 +17,5 @@
<module>as7-adapter-spi</module>
<module>as7-adapter</module>
<module>as7-subsystem</module>
<module>as7-server-subsystem</module>
</modules>
</project>

View file

@ -15,9 +15,7 @@
<modules>
<module>wildfly-adapter</module>
<module>wildfly-extensions</module>
<module>wf8-subsystem</module>
<module>wf9-subsystem</module>
<module>wf9-server-subsystem</module>
</modules>
</project>

View file

@ -996,14 +996,14 @@ public class RepresentationToModel {
// Import users just to user storage. Don't federate
UserModel user = session.userStorage().addUser(newRealm, userRep.getId(), userRep.getUsername(), false, false);
user.setEnabled(userRep.isEnabled());
user.setEnabled(userRep.isEnabled() != null && userRep.isEnabled());
user.setCreatedTimestamp(userRep.getCreatedTimestamp());
user.setEmail(userRep.getEmail());
user.setEmailVerified(userRep.isEmailVerified());
if (userRep.isEmailVerified() != null) user.setEmailVerified(userRep.isEmailVerified());
user.setFirstName(userRep.getFirstName());
user.setLastName(userRep.getLastName());
user.setFederationLink(userRep.getFederationLink());
user.setOtpEnabled(userRep.isTotp());
if (userRep.isTotp() != null) user.setOtpEnabled(userRep.isTotp());
if (userRep.getAttributes() != null) {
for (Map.Entry<String, Object> entry : userRep.getAttributes().entrySet()) {
Object value = entry.getValue();

12
pom.xml
View file

@ -76,7 +76,7 @@
<log4j.version>1.2.17</log4j.version>
<greenmail.version>1.3.1b</greenmail.version>
<xmlsec.version>1.5.1</xmlsec.version>
<aesh.version>0.66</aesh.version>
<aesh.version>0.65.1</aesh.version>
<enforcer.plugin.version>1.4</enforcer.plugin.version>
<jboss.as.plugin.version>7.5.Final</jboss.as.plugin.version>
@ -154,6 +154,7 @@
<module>timer</module>
<module>export-import</module>
<module>util</module>
<module>wildfly</module>
</modules>
<dependencyManagement>
@ -827,7 +828,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-server-subsystem</artifactId>
<artifactId>keycloak-eap6-server-subsystem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@ -842,7 +843,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wf9-server-subsystem</artifactId>
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@ -945,6 +946,11 @@
<artifactId>keycloak-wildfly-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-adduser</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-extensions</artifactId>

View file

@ -60,7 +60,8 @@ public class ExportImportManager {
// Check if master realm was exported. If it's not, then it needs to be created before other realms are imported
if (!importProvider.isMasterRealmExported()) {
new ApplianceBootstrap().bootstrap(sessionFactory, contextPath);
ApplianceBootstrap.setupDefaultRealm(sessionFactory, contextPath);
ApplianceBootstrap.setupDefaultUser(sessionFactory);
}
importProvider.importModel(sessionFactory, strategy);
@ -69,7 +70,8 @@ public class ExportImportManager {
if (!realmName.equals(Config.getAdminRealm())) {
// Check if master realm exists. If it's not, then it needs to be created before other realm is imported
new ApplianceBootstrap().bootstrap(sessionFactory, contextPath);
ApplianceBootstrap.setupDefaultRealm(sessionFactory, contextPath);
ApplianceBootstrap.setupDefaultUser(sessionFactory);
}
importProvider.importRealm(sessionFactory, realmName, strategy);

View file

@ -1,90 +0,0 @@
/*
* 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;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.services.managers.ApplianceBootstrap;
/**
* Static utility class that performs recovery on the master admin account.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
*/
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() {}
public static void recover(KeycloakSessionFactory sessionFactory) {
if (!needRecovery()) return;
KeycloakSession session = sessionFactory.create();
session.getTransaction().begin();
try {
doRecover(session, getTempAdminPassword());
session.getTransaction().commit();
log.info("*******************************");
log.info("Recovered Master Admin account.");
log.info("*******************************");
} finally {
session.close();
System.clearProperty(RECOVER_ADMIN_ACCOUNT);
System.clearProperty(TEMP_ADMIN_PASSWORD);
}
}
private static boolean needRecovery() {
String strNeedRecovery = System.getProperty(RECOVER_ADMIN_ACCOUNT, "false");
return Boolean.parseBoolean(strNeedRecovery);
}
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();
String adminRealmName = Config.getAdminRealm();
RealmModel realm = realmProvider.getRealmByName(adminRealmName);
UserModel adminUser = userProvider.getUserByUsername("admin", realm);
if (adminUser == null) {
adminUser = userProvider.addUser(realm, "admin");
}
ApplianceBootstrap.setupAdminUser(session, realm, adminUser, tempAdminPassword);
}
}

View file

@ -1,32 +0,0 @@
/*
* 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

@ -23,62 +23,71 @@ public class ApplianceBootstrap {
private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class);
public void bootstrap(KeycloakSessionFactory sessionFactory, String contextPath) {
public static boolean setupDefaultRealm(KeycloakSessionFactory sessionFactory, String contextPath) {
KeycloakSession session = sessionFactory.create();
session.getTransaction().begin();
try {
bootstrap(session, contextPath);
String adminRealmName = Config.getAdminRealm();
if (session.realms().getRealm(adminRealmName) != null) {
return false;
}
logger.info("Initializing " + adminRealmName + " realm");
RealmManager manager = new RealmManager(session);
manager.setContextPath(contextPath);
RealmModel realm = manager.createRealm(adminRealmName, adminRealmName);
realm.setName(adminRealmName);
realm.setEnabled(true);
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
realm.setSsoSessionIdleTimeout(1800);
realm.setAccessTokenLifespan(60);
realm.setSsoSessionMaxLifespan(36000);
realm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT);
realm.setAccessCodeLifespan(60);
realm.setAccessCodeLifespanUserAction(300);
realm.setAccessCodeLifespanLogin(1800);
realm.setSslRequired(SslRequired.EXTERNAL);
realm.setRegistrationAllowed(false);
realm.setRegistrationEmailAsUsername(false);
KeycloakModelUtils.generateRealmKeys(realm);
session.getTransaction().commit();
return true;
} finally {
session.close();
}
}
public void bootstrap(KeycloakSession session, String contextPath) {
String adminRealmName = Config.getAdminRealm();
if (session.realms().getRealm(adminRealmName) != null) {
return;
}
public static boolean setupDefaultUser(KeycloakSessionFactory sessionFactory) {
KeycloakSession session = sessionFactory.create();
session.getTransaction().begin();
logger.info("Initializing " + adminRealmName + " realm");
try {
RealmModel realm = session.realms().getRealm(Config.getAdminRealm());
if (session.users().getUserByUsername("admin", realm) == null) {
UserModel adminUser = session.users().addUser(realm, "admin");
RealmManager manager = new RealmManager(session);
manager.setContextPath(contextPath);
RealmModel realm = manager.createRealm(adminRealmName, adminRealmName);
realm.setName(adminRealmName);
realm.setEnabled(true);
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
realm.setSsoSessionIdleTimeout(1800);
realm.setAccessTokenLifespan(60);
realm.setSsoSessionMaxLifespan(36000);
realm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT);
realm.setAccessCodeLifespan(60);
realm.setAccessCodeLifespanUserAction(300);
realm.setAccessCodeLifespanLogin(1800);
realm.setSslRequired(SslRequired.EXTERNAL);
realm.setRegistrationAllowed(false);
realm.setRegistrationEmailAsUsername(false);
KeycloakModelUtils.generateRealmKeys(realm);
adminUser.setEnabled(true);
UserCredentialModel usrCredModel = new UserCredentialModel();
usrCredModel.setType(UserCredentialModel.PASSWORD);
usrCredModel.setValue("admin");
session.users().updateCredential(realm, adminUser, usrCredModel);
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
UserModel adminUser = session.users().addUser(realm, "admin");
setupAdminUser(session, realm, adminUser, "admin");
}
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
adminUser.grantRole(adminRole);
public static void setupAdminUser(KeycloakSession session, RealmModel realm, UserModel adminUser, String password) {
adminUser.setEnabled(true);
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);
adminUser.grantRole(adminRole);
ClientModel accountApp = realm.getClientNameMap().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
for (String r : accountApp.getDefaultRoles()) {
adminUser.grantRole(accountApp.getRole(r));
ClientModel accountApp = realm.getClientNameMap().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
for (String r : accountApp.getDefaultRoles()) {
adminUser.grantRole(accountApp.getRole(r));
}
}
session.getTransaction().commit();
return true;
} finally {
session.close();
}
}

View file

@ -2,6 +2,7 @@ package org.keycloak.services.resources;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.jboss.logging.Logger;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
@ -11,9 +12,11 @@ import org.keycloak.migration.MigrationModelManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.offlineconfig.AdminRecovery;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.DefaultKeycloakSessionFactory;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.BruteForceProtector;
@ -36,10 +39,7 @@ import javax.ws.rs.core.UriInfo;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.*;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -81,7 +81,7 @@ public class KeycloakApplication extends Application {
singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false"))));
setupDefaultRealm(context.getContextPath());
boolean defaultRealmCreated = ApplianceBootstrap.setupDefaultRealm(sessionFactory, context.getContextPath());
migrateModel();
sessionFactory.publish(new PostMigrationEvent());
@ -89,7 +89,11 @@ public class KeycloakApplication extends Application {
new ExportImportManager().checkExportImport(this.sessionFactory, context.getContextPath());
importRealms(context);
AdminRecovery.recover(sessionFactory);
importAddUser();
if (defaultRealmCreated) {
ApplianceBootstrap.setupDefaultUser(sessionFactory);
}
setupScheduledTasks(sessionFactory);
}
@ -153,10 +157,6 @@ public class KeycloakApplication extends Application {
}
}
protected void setupDefaultRealm(String contextPath) {
new ApplianceBootstrap().bootstrap(sessionFactory, contextPath);
}
public static KeycloakSessionFactory createSessionFactory() {
DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
factory.init();
@ -254,6 +254,44 @@ public class KeycloakApplication extends Application {
}
}
public void importAddUser() {
String configDir = System.getProperty("jboss.server.config.dir");
if (configDir != null) {
File addUserFile = new File(configDir + File.separator + "keycloak-add-user.json");
if (addUserFile.isFile()) {
log.info("Importing users from '" + addUserFile + "'");
KeycloakSession session = sessionFactory.create();
try {
session.getTransaction().begin();
List<RealmRepresentation> realms = JsonSerialization.readValue(new FileInputStream(addUserFile), new TypeReference<List<RealmRepresentation>>() {});
for (RealmRepresentation r : realms) {
RealmModel realm = session.realms().getRealmByName(r.getRealm());
if (realm == null) {
throw new Exception("Realm '" + r.getRealm() + "' not found");
}
for (UserRepresentation u : r.getUsers()) {
RepresentationToModel.createUser(session, realm, u, realm.getClientNameMap());
}
}
session.getTransaction().commit();
if (!addUserFile.delete()) {
log.error("Failed to delete '" + addUserFile + "'");
}
} catch (Throwable t) {
session.getTransaction().rollback();
log.error("Failed to import users from '" + addUserFile + "'", t);
} finally {
session.close();
}
}
}
}
private static <T> T loadJson(InputStream is, Class<T> type) {
try {
return JsonSerialization.readValue(is, type);

View file

@ -148,7 +148,7 @@ public class UsersResource {
attrsToRemove = Collections.emptySet();
}
if (rep.isEnabled()) {
if (rep.isEnabled() != null && rep.isEnabled()) {
UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername());
if (failureModel != null) {
failureModel.clearFailures();
@ -219,9 +219,9 @@ public class UsersResource {
user.setFirstName(rep.getFirstName());
user.setLastName(rep.getLastName());
user.setEnabled(rep.isEnabled());
user.setOtpEnabled(rep.isTotp());
user.setEmailVerified(rep.isEmailVerified());
if (rep.isEnabled() != null) user.setEnabled(rep.isEnabled());
if (rep.isTotp() != null) user.setOtpEnabled(rep.isTotp());
if (rep.isEmailVerified() != null) user.setEmailVerified(rep.isEmailVerified());
List<String> reqActions = rep.getRequiredActions();
@ -708,7 +708,7 @@ public class UsersResource {
} catch (ModelReadOnlyException mre) {
throw new BadRequestException("Can't reset password as account is read only");
}
if (pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
if (pass.isTemporary() != null && pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
}

View file

@ -130,8 +130,8 @@ public class UserAttributesForm extends Form {
setEmail(user.getEmail());
setFirstName(user.getFirstName());
setLastName(user.getLastName());
setEnabled(user.isEnabled());
setEmailVerified(user.isEmailVerified());
if (user.isEnabled() != null) setEnabled(user.isEnabled());
if (user.isEmailVerified() != null) setEmailVerified(user.isEmailVerified());
setRequiredActions(user.getRequiredActions());
}

View file

@ -31,6 +31,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-adduser</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>

View file

@ -0,0 +1,64 @@
package org.keycloak.testsuite.adduser;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.KeycloakServer;
import org.keycloak.wildfly.adduser.AddUser;
import java.io.File;
import java.io.IOException;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AddUserTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
private File dir;
@Before
public void before() throws IOException {
dir = folder.newFolder();
System.setProperty("jboss.server.config.user.dir", dir.getAbsolutePath());
System.setProperty("jboss.server.config.dir", dir.getAbsolutePath());
}
@After
public void after() {
System.getProperties().remove("jboss.server.config.user.dir");
System.getProperties().remove("jboss.server.config.dir");
}
@Test
public void addUserTest() throws Throwable {
AddUser.main(new String[]{"-u", "addusertest-admin", "-p", "password"});
Assert.assertEquals(1, dir.listFiles().length);
KeycloakServer server = new KeycloakServer();
try {
server.start();
Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "addusertest-admin", "password", Constants.ADMIN_CONSOLE_CLIENT_ID);
keycloak.realms().findAll();
RealmRepresentation testRealm = new RealmRepresentation();
testRealm.setEnabled(true);
testRealm.setId("test");
testRealm.setRealm("test");
keycloak.realms().create(testRealm);
keycloak.close();
Assert.assertEquals(0, dir.listFiles().length);
} finally {
server.stop();
}
}
}

View file

@ -1,133 +0,0 @@
/*
* 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.testsuite.offlineconfig;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
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.services.managers.RealmManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebRule;
/**
* Test the AdminRecovery class.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
*/
public class AdminRecoveryTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule() {
@Override
protected void after() {
// Need to reset admin user to default password and remove required action to not break next tests
update(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserModel adminUser = session.users().getUserByUsername("admin", adminstrationRealm);
UserCredentialModel password = UserCredentialModel.password("admin");
adminUser.updateCredential(password);
adminUser.removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
}
});
super.after();
}
};
@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();
RealmModel masterRealm = session.realms().getRealmByName("master");
UserModel adminUser = session.users().getUserByUsername("admin", masterRealm);
session.users().removeUser(masterRealm, adminUser);
adminUser = session.users().getUserByUsername("admin", masterRealm);
keycloakRule.stopSession(session, true);
Assert.assertNull(adminUser);
doAdminRecovery(session);
session = keycloakRule.startSession();
adminUser = session.users().getUserByUsername("admin", masterRealm);
Assert.assertNotNull(adminUser);
Assert.assertTrue(adminUser.getRequiredActions().contains(RequiredAction.UPDATE_PASSWORD.toString()));
}
@Test
public void testAdminPasswordRecovery() {
KeycloakSession session = keycloakRule.startSession();
RealmModel masterRealm = session.realms().getRealmByName("master");
UserModel adminUser = session.users().getUserByUsername("admin", masterRealm);
UserCredentialValueModel password = adminUser.getCredentialsDirectly().get(0);
password.setValue("forgotten-password");
adminUser.updateCredentialDirectly(password);
keycloakRule.stopSession(session, true);
Assert.assertEquals("forgotten-password", getAdminPassword());
doAdminRecovery(session);
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());
}
private String getAdminPassword() {
KeycloakSession session = keycloakRule.startSession();
RealmModel masterRealm = session.realms().getRealmByName("master");
UserModel adminUser = session.users().getUserByUsername("admin", masterRealm);
UserCredentialValueModel password = adminUser.getCredentialsDirectly().get(0);
keycloakRule.stopSession(session, true);
return password.getValue();
}
}

View file

@ -22,6 +22,8 @@
package org.keycloak.testsuite.rule;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;

54
wildfly/adduser/pom.xml Executable file
View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013 JBoss Inc
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-parent</artifactId>
<version>1.7.0.Final-SNAPSHOT</version>
</parent>
<artifactId>keycloak-wildfly-adduser</artifactId>
<name>Keycloak WildFly Add User Script</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-common</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.core</groupId>
<artifactId>wildfly-domain-management</artifactId>
<version>${wildfly.core.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.aesh</groupId>
<artifactId>aesh</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,292 @@
package org.keycloak.wildfly.adduser;
import org.codehaus.jackson.type.TypeReference;
import org.jboss.aesh.cl.CommandDefinition;
import org.jboss.aesh.cl.Option;
import org.jboss.aesh.cl.parser.ParserGenerator;
import org.jboss.aesh.console.command.Command;
import org.jboss.aesh.console.command.CommandNotFoundException;
import org.jboss.aesh.console.command.CommandResult;
import org.jboss.aesh.console.command.container.CommandContainer;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.jboss.aesh.console.command.registry.AeshCommandRegistryBuilder;
import org.jboss.aesh.console.command.registry.CommandRegistry;
import org.keycloak.common.util.Base64;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.util.JsonSerialization;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AddUser {
private static final String COMMAND_NAME = "add-user";
private static final int DEFAULT_HASH_ITERATIONS = 100000;
public static void main(String[] args) throws Exception {
AddUserCommand command = new AddUserCommand();
try {
ParserGenerator.parseAndPopulate(command, COMMAND_NAME, args);
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
if (command.isContainer()) {
List<String> l = new LinkedList<>(Arrays.asList(args));
l.remove("--container");
args = l.toArray(new String[l.size()]);
org.jboss.as.domain.management.security.adduser.AddUser.main(args);
} else if (command.isHelp()) {
printHelp(command);
} else {
try {
checkRequired(command, "user");
checkRequired(command, "password");
File addUserFile = getAddUserFile(command);
createUser(addUserFile, command.getRealm(), command.getUser(), command.getPassword(), command.getRoles(), command.getIterations());
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}
private static File getAddUserFile(AddUserCommand command) throws Exception {
File configDir;
if (command.isDomain()) {
if (command.getDc() != null) {
configDir = new File(command.getDc());
} else if (System.getProperty("jboss.domain.config.user.dir") != null) {
configDir = new File(System.getProperty("jboss.domain.config.user.dir"));
} else if (System.getenv("JBOSS_HOME") != null) {
configDir = new File(System.getenv("JBOSS_HOME") + File.separator + "domain" + File.separator + "configuration");
} else {
throw new Exception("Could not find domain configuration directory");
}
} else {
if (command.getSc() != null) {
configDir = new File(command.getSc());
} else if (System.getProperty("jboss.server.config.user.dir") != null) {
configDir = new File(System.getProperty("jboss.server.config.user.dir"));
} else if (System.getenv("JBOSS_HOME") != null) {
configDir = new File(System.getenv("JBOSS_HOME") + File.separator + "standalone" + File.separator + "configuration");
} else {
throw new Exception("Could not find standalone configuration directory");
}
}
if (!configDir.isDirectory()) {
throw new Exception("'" + configDir + "' does not exist or is not a directory");
}
File addUserFile = new File(configDir, "keycloak-add-user.json");
return addUserFile;
}
private static void createUser(File addUserFile, String realmName, String userName, String password, String rolesString, int iterations) throws Exception {
List<RealmRepresentation> realms;
if (addUserFile.isFile()) {
realms = JsonSerialization.readValue(new FileInputStream(addUserFile), new TypeReference<List<RealmRepresentation>>() {});
} else {
realms = new LinkedList<>();
}
if (realmName == null) {
realmName = "master";
}
RealmRepresentation realm = null;
for (RealmRepresentation r : realms) {
if (r.getRealm().equals(realmName)) {
realm = r;
}
}
if (realm == null) {
realm = new RealmRepresentation();
realm.setRealm(realmName);
realms.add(realm);
realm.setUsers(new LinkedList<UserRepresentation>());
}
for (UserRepresentation u : realm.getUsers()) {
if (u.getUsername().equals(userName)) {
throw new Exception("User with username '" + userName + "' already added to '" + addUserFile + "'");
}
}
UserRepresentation user = new UserRepresentation();
user.setEnabled(true);
user.setUsername(userName);
user.setCredentials(new LinkedList<CredentialRepresentation>());
byte[] salt = Pbkdf2PasswordEncoder.getSalt();
iterations = iterations > 0 ? iterations : DEFAULT_HASH_ITERATIONS;
CredentialRepresentation credentials = new CredentialRepresentation();
credentials.setType(CredentialRepresentation.PASSWORD);
credentials.setHashIterations(iterations);
credentials.setSalt(Base64.encodeBytes(salt));
credentials.setHashedSaltedValue(new Pbkdf2PasswordEncoder(salt).encode(password, iterations));
user.getCredentials().add(credentials);
String[] roles;
if (rolesString != null) {
roles = rolesString.split(",");
} else {
if (realmName.equals("master")) {
roles = new String[] { "admin" };
} else {
roles = new String[] { "realm-management/realm-admin" };
}
}
for (String r : roles) {
if (r.indexOf('/') != -1) {
String[] cr = r.split("/");
String client = cr[0];
String clientRole = cr[1];
if (user.getClientRoles() == null) {
user.setClientRoles(new HashMap<String, List<String>>());
}
if (user.getClientRoles().get(client) == null) {
user.getClientRoles().put(client, new LinkedList<String>());
}
user.getClientRoles().get(client).add(clientRole);
} else {
if (user.getRealmRoles() == null) {
user.setRealmRoles(new LinkedList<String>());
}
user.getRealmRoles().add(r);
}
}
realm.getUsers().add(user);
JsonSerialization.writeValuePrettyToStream(new FileOutputStream(addUserFile), realms);
System.out.println("Added '" + userName + "' to '" + addUserFile + "', restart server to load user");
}
private static void checkRequired(Command command, String field) throws Exception {
Method m = command.getClass().getMethod("get" + Character.toUpperCase(field.charAt(0)) + field.substring(1));
if (m.invoke(command) == null) {
Option option = command.getClass().getDeclaredField(field).getAnnotation(Option.class);
String optionName;
if (option != null && option.shortName() != '\u0000') {
optionName = "-" + option.shortName() + ", --" + field;
} else {
optionName = "--" + field;
}
throw new Exception("Option: " + optionName + " is required");
}
}
private static void printHelp(Command command) throws CommandNotFoundException {
CommandRegistry registry = new AeshCommandRegistryBuilder().command(command).create();
CommandContainer commandContainer = registry.getCommand(command.getClass().getAnnotation(CommandDefinition.class).name(), null);
String help = commandContainer.printHelp(null);
System.out.println(help);
}
@CommandDefinition(name= COMMAND_NAME, description = "[options...]")
public static class AddUserCommand implements Command {
@Option(shortName = 'r', hasValue = true, description = "Name of realm to add user to")
private String realm;
@Option(shortName = 'u', hasValue = true, description = "Name of the user")
private String user;
@Option(shortName = 'p', hasValue = true, description = "Password of the user")
private String password;
@Option(hasValue = true, description = "Roles to add to the user")
private String roles;
@Option(hasValue = true, description = "Hash iterations")
private int iterations;
@Option(hasValue = false, description = "Enable domain mode")
private boolean domain;
@Option(hasValue = false, description = "Add user to underlying container. For usage use '--container --help'")
private boolean container;
@Option(hasValue = true, description = "Define the location of the server config directory")
private String sc;
@Option(hasValue = true, description = "Define the location of the domain config directory")
private String dc;
@Option(shortName = 'h', hasValue = false, description = "Display this help and exit")
private boolean help;
@Override
public CommandResult execute(CommandInvocation commandInvocation) throws IOException, InterruptedException {
return CommandResult.SUCCESS;
}
public String getRealm() {
return realm;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public String getRoles() {
return roles;
}
public int getIterations() {
return iterations;
}
public boolean isDomain() {
return domain;
}
public boolean isContainer() {
return container;
}
public String getSc() {
return sc;
}
public String getDc() {
return dc;
}
public boolean isHelp() {
return help;
}
}
}

View file

@ -19,9 +19,8 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<artifactId>keycloak-wildfly-parent</artifactId>
<version>1.7.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-wildfly-extensions</artifactId>

22
wildfly/pom.xml Executable file
View file

@ -0,0 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.7.0.Final-SNAPSHOT</version>
</parent>
<name>Keycloak WildFly Integration</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-wildfly-parent</artifactId>
<packaging>pom</packaging>
<modules>
<module>adduser</module>
<module>extensions</module>
<module>server-subsystem</module>
<module>server-eap6-subsystem</module>
</modules>
</project>

View file

@ -19,13 +19,12 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<artifactId>keycloak-wildfly-parent</artifactId>
<version>1.7.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-as7-server-subsystem</artifactId>
<name>Keycloak AS7 / EAP 6 Server Subsystem</name>
<artifactId>keycloak-eap6-server-subsystem</artifactId>
<name>Keycloak EAP 6 Server Subsystem</name>
<description/>
<packaging>jar</packaging>

View file

@ -19,13 +19,12 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<artifactId>keycloak-wildfly-parent</artifactId>
<version>1.7.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-wf9-server-subsystem</artifactId>
<name>Keycloak Wildfly 9 Server Subsystem</name>
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
<name>Keycloak WildFly Server Subsystem</name>
<description/>
<packaging>jar</packaging>
@ -96,11 +95,5 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-adapter</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>