Merge pull request #1859 from stianst/reset-pass
KEYCLOAK-1758 add-user script
This commit is contained in:
commit
3685a185d4
82 changed files with 823 additions and 384 deletions
|
@ -57,6 +57,8 @@ public class ClientRegistrationCLI {
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
aeshConsole.start();
|
aeshConsole.start();
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
CommandContainer command = registry.getCommand(args[0], null);
|
CommandContainer command = registry.getCommand(args[0], null);
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class CredentialRepresentation {
|
||||||
private Integer period;
|
private Integer period;
|
||||||
|
|
||||||
// only used when updating a credential. Might set required action
|
// only used when updating a credential. Might set required action
|
||||||
protected boolean temporary;
|
protected Boolean temporary;
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return type;
|
return type;
|
||||||
|
@ -79,11 +79,11 @@ public class CredentialRepresentation {
|
||||||
this.hashIterations = hashIterations;
|
this.hashIterations = hashIterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTemporary() {
|
public Boolean isTemporary() {
|
||||||
return temporary;
|
return temporary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTemporary(boolean temporary) {
|
public void setTemporary(Boolean temporary) {
|
||||||
this.temporary = temporary;
|
this.temporary = temporary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,6 @@ public class RealmRepresentation {
|
||||||
private List<IdentityProviderRepresentation> identityProviders;
|
private List<IdentityProviderRepresentation> identityProviders;
|
||||||
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
|
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
|
||||||
private List<ProtocolMapperRepresentation> protocolMappers;
|
private List<ProtocolMapperRepresentation> protocolMappers;
|
||||||
private Boolean identityFederationEnabled;
|
|
||||||
protected Boolean internationalizationEnabled;
|
protected Boolean internationalizationEnabled;
|
||||||
protected Set<String> supportedLocales;
|
protected Set<String> supportedLocales;
|
||||||
protected String defaultLocale;
|
protected String defaultLocale;
|
||||||
|
@ -613,10 +612,6 @@ public class RealmRepresentation {
|
||||||
identityProviders.add(identityProviderRepresentation);
|
identityProviders.add(identityProviderRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isIdentityFederationEnabled() {
|
|
||||||
return identityProviders != null && !identityProviders.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ProtocolMapperRepresentation> getProtocolMappers() {
|
public List<ProtocolMapperRepresentation> getProtocolMappers() {
|
||||||
return protocolMappers;
|
return protocolMappers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ public class UserRepresentation {
|
||||||
protected String id;
|
protected String id;
|
||||||
protected Long createdTimestamp;
|
protected Long createdTimestamp;
|
||||||
protected String username;
|
protected String username;
|
||||||
protected boolean enabled;
|
protected Boolean enabled;
|
||||||
protected boolean totp;
|
protected Boolean totp;
|
||||||
protected boolean emailVerified;
|
protected Boolean emailVerified;
|
||||||
protected String firstName;
|
protected String firstName;
|
||||||
protected String lastName;
|
protected String lastName;
|
||||||
protected String email;
|
protected String email;
|
||||||
|
@ -98,27 +98,27 @@ public class UserRepresentation {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public Boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(Boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTotp() {
|
public Boolean isTotp() {
|
||||||
return totp;
|
return totp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTotp(boolean totp) {
|
public void setTotp(Boolean totp) {
|
||||||
this.totp = totp;
|
this.totp = totp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmailVerified() {
|
public Boolean isEmailVerified() {
|
||||||
return emailVerified;
|
return emailVerified;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmailVerified(boolean emailVerified) {
|
public void setEmailVerified(Boolean emailVerified) {
|
||||||
this.emailVerified = emailVerified;
|
this.emailVerified = emailVerified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.util;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
import org.codehaus.jackson.map.SerializationConfig;
|
import org.codehaus.jackson.map.SerializationConfig;
|
||||||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||||
|
import org.codehaus.jackson.type.TypeReference;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -27,7 +28,10 @@ public class JsonSerialization {
|
||||||
|
|
||||||
public static void writeValueToStream(OutputStream os, Object obj) throws IOException {
|
public static void writeValueToStream(OutputStream os, Object obj) throws IOException {
|
||||||
mapper.writeValue(os, obj);
|
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 {
|
public static String writeValueAsPrettyString(Object obj) throws IOException {
|
||||||
|
@ -53,6 +57,10 @@ public class JsonSerialization {
|
||||||
return readValue(bytes, type, false);
|
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 {
|
public static <T> T readValue(InputStream bytes, Class<T> type, boolean replaceSystemProperties) throws IOException {
|
||||||
if (replaceSystemProperties) {
|
if (replaceSystemProperties) {
|
||||||
return sysPropertiesAwareMapper.readValue(bytes, type);
|
return sysPropertiesAwareMapper.readValue(bytes, type);
|
||||||
|
|
|
@ -36,19 +36,27 @@
|
||||||
<artifactId>keycloak-dependencies-server-all</artifactId>
|
<artifactId>keycloak-dependencies-server-all</artifactId>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-wildfly-adduser</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-wildfly-extensions</artifactId>
|
<artifactId>keycloak-wildfly-extensions</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-wf9-server-subsystem</artifactId>
|
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.wildfly</groupId>
|
<groupId>org.wildfly</groupId>
|
||||||
<artifactId>wildfly-feature-pack</artifactId>
|
<artifactId>wildfly-feature-pack</artifactId>
|
||||||
<type>zip</type>
|
<type>zip</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.aesh</groupId>
|
||||||
|
<artifactId>aesh</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -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
|
|
@ -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 \
|
||||||
|
'"$@"'
|
|
@ -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>
|
|
@ -29,6 +29,6 @@
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<dependencies>
|
<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>
|
</dependencies>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -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>
|
|
@ -22,11 +22,11 @@
|
||||||
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
~ 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>
|
<resources>
|
||||||
<resource-root path="."/>
|
<resource-root path="."/>
|
||||||
<artifact name="${org.keycloak:keycloak-wf9-server-subsystem}"/>
|
<artifact name="${org.keycloak:keycloak-wildfly-server-subsystem}"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
|
@ -274,8 +274,8 @@
|
||||||
|
|
||||||
<!-- subsystems -->
|
<!-- subsystems -->
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-as7-server-subsystem">
|
<module-def name="org.keycloak.keycloak-eap6-server-subsystem">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-as7-server-subsystem"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-eap6-server-subsystem"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-server-subsystem"/>
|
<module-def name="org.keycloak.keycloak-server-subsystem"/>
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-as7-server-subsystem</artifactId>
|
<artifactId>keycloak-eap6-server-subsystem</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
~ 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>
|
<resources>
|
||||||
<resource-root path="."/>
|
<resource-root path="."/>
|
|
@ -30,6 +30,6 @@
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<dependencies>
|
<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>
|
</dependencies>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
<!ENTITY UserFederation SYSTEM "modules/user-federation.xml">
|
<!ENTITY UserFederation SYSTEM "modules/user-federation.xml">
|
||||||
<!ENTITY Kerberos SYSTEM "modules/kerberos.xml">
|
<!ENTITY Kerberos SYSTEM "modules/kerberos.xml">
|
||||||
<!ENTITY ExportImport SYSTEM "modules/export-import.xml">
|
<!ENTITY ExportImport SYSTEM "modules/export-import.xml">
|
||||||
<!ENTITY AdminRecovery SYSTEM "modules/admin-recovery.xml">
|
|
||||||
<!ENTITY ServerCache SYSTEM "modules/cache.xml">
|
<!ENTITY ServerCache SYSTEM "modules/cache.xml">
|
||||||
<!ENTITY SecurityVulnerabilities SYSTEM "modules/security-vulnerabilities.xml">
|
<!ENTITY SecurityVulnerabilities SYSTEM "modules/security-vulnerabilities.xml">
|
||||||
<!ENTITY Clustering SYSTEM "modules/clustering.xml">
|
<!ENTITY Clustering SYSTEM "modules/clustering.xml">
|
||||||
|
|
|
@ -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>
|
|
|
@ -127,6 +127,25 @@ cd <WILDFLY_HOME>/bin
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</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>
|
<section>
|
||||||
<title>Relational Database Configuration</title>
|
<title>Relational Database Configuration</title>
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -17,6 +17,5 @@
|
||||||
<module>as7-adapter-spi</module>
|
<module>as7-adapter-spi</module>
|
||||||
<module>as7-adapter</module>
|
<module>as7-adapter</module>
|
||||||
<module>as7-subsystem</module>
|
<module>as7-subsystem</module>
|
||||||
<module>as7-server-subsystem</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
|
@ -15,9 +15,7 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>wildfly-adapter</module>
|
<module>wildfly-adapter</module>
|
||||||
<module>wildfly-extensions</module>
|
|
||||||
<module>wf8-subsystem</module>
|
<module>wf8-subsystem</module>
|
||||||
<module>wf9-subsystem</module>
|
<module>wf9-subsystem</module>
|
||||||
<module>wf9-server-subsystem</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
|
@ -996,14 +996,14 @@ public class RepresentationToModel {
|
||||||
|
|
||||||
// Import users just to user storage. Don't federate
|
// Import users just to user storage. Don't federate
|
||||||
UserModel user = session.userStorage().addUser(newRealm, userRep.getId(), userRep.getUsername(), false, false);
|
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.setCreatedTimestamp(userRep.getCreatedTimestamp());
|
||||||
user.setEmail(userRep.getEmail());
|
user.setEmail(userRep.getEmail());
|
||||||
user.setEmailVerified(userRep.isEmailVerified());
|
if (userRep.isEmailVerified() != null) user.setEmailVerified(userRep.isEmailVerified());
|
||||||
user.setFirstName(userRep.getFirstName());
|
user.setFirstName(userRep.getFirstName());
|
||||||
user.setLastName(userRep.getLastName());
|
user.setLastName(userRep.getLastName());
|
||||||
user.setFederationLink(userRep.getFederationLink());
|
user.setFederationLink(userRep.getFederationLink());
|
||||||
user.setOtpEnabled(userRep.isTotp());
|
if (userRep.isTotp() != null) user.setOtpEnabled(userRep.isTotp());
|
||||||
if (userRep.getAttributes() != null) {
|
if (userRep.getAttributes() != null) {
|
||||||
for (Map.Entry<String, Object> entry : userRep.getAttributes().entrySet()) {
|
for (Map.Entry<String, Object> entry : userRep.getAttributes().entrySet()) {
|
||||||
Object value = entry.getValue();
|
Object value = entry.getValue();
|
||||||
|
|
12
pom.xml
12
pom.xml
|
@ -76,7 +76,7 @@
|
||||||
<log4j.version>1.2.17</log4j.version>
|
<log4j.version>1.2.17</log4j.version>
|
||||||
<greenmail.version>1.3.1b</greenmail.version>
|
<greenmail.version>1.3.1b</greenmail.version>
|
||||||
<xmlsec.version>1.5.1</xmlsec.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>
|
<enforcer.plugin.version>1.4</enforcer.plugin.version>
|
||||||
<jboss.as.plugin.version>7.5.Final</jboss.as.plugin.version>
|
<jboss.as.plugin.version>7.5.Final</jboss.as.plugin.version>
|
||||||
|
@ -154,6 +154,7 @@
|
||||||
<module>timer</module>
|
<module>timer</module>
|
||||||
<module>export-import</module>
|
<module>export-import</module>
|
||||||
<module>util</module>
|
<module>util</module>
|
||||||
|
<module>wildfly</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -827,7 +828,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-as7-server-subsystem</artifactId>
|
<artifactId>keycloak-eap6-server-subsystem</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -842,7 +843,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-wf9-server-subsystem</artifactId>
|
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -945,6 +946,11 @@
|
||||||
<artifactId>keycloak-wildfly-adapter</artifactId>
|
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-wildfly-adduser</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-wildfly-extensions</artifactId>
|
<artifactId>keycloak-wildfly-extensions</artifactId>
|
||||||
|
|
|
@ -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
|
// Check if master realm was exported. If it's not, then it needs to be created before other realms are imported
|
||||||
if (!importProvider.isMasterRealmExported()) {
|
if (!importProvider.isMasterRealmExported()) {
|
||||||
new ApplianceBootstrap().bootstrap(sessionFactory, contextPath);
|
ApplianceBootstrap.setupDefaultRealm(sessionFactory, contextPath);
|
||||||
|
ApplianceBootstrap.setupDefaultUser(sessionFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
importProvider.importModel(sessionFactory, strategy);
|
importProvider.importModel(sessionFactory, strategy);
|
||||||
|
@ -69,7 +70,8 @@ public class ExportImportManager {
|
||||||
|
|
||||||
if (!realmName.equals(Config.getAdminRealm())) {
|
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
|
// 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);
|
importProvider.importRealm(sessionFactory, realmName, strategy);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,22 +23,14 @@ public class ApplianceBootstrap {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class);
|
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();
|
KeycloakSession session = sessionFactory.create();
|
||||||
session.getTransaction().begin();
|
session.getTransaction().begin();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bootstrap(session, contextPath);
|
|
||||||
session.getTransaction().commit();
|
|
||||||
} finally {
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bootstrap(KeycloakSession session, String contextPath) {
|
|
||||||
String adminRealmName = Config.getAdminRealm();
|
String adminRealmName = Config.getAdminRealm();
|
||||||
if (session.realms().getRealm(adminRealmName) != null) {
|
if (session.realms().getRealm(adminRealmName) != null) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Initializing " + adminRealmName + " realm");
|
logger.info("Initializing " + adminRealmName + " realm");
|
||||||
|
@ -61,15 +53,26 @@ public class ApplianceBootstrap {
|
||||||
realm.setRegistrationEmailAsUsername(false);
|
realm.setRegistrationEmailAsUsername(false);
|
||||||
KeycloakModelUtils.generateRealmKeys(realm);
|
KeycloakModelUtils.generateRealmKeys(realm);
|
||||||
|
|
||||||
UserModel adminUser = session.users().addUser(realm, "admin");
|
session.getTransaction().commit();
|
||||||
setupAdminUser(session, realm, adminUser, "admin");
|
return true;
|
||||||
|
} finally {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setupAdminUser(KeycloakSession session, RealmModel realm, UserModel adminUser, String password) {
|
public static boolean setupDefaultUser(KeycloakSessionFactory sessionFactory) {
|
||||||
|
KeycloakSession session = sessionFactory.create();
|
||||||
|
session.getTransaction().begin();
|
||||||
|
|
||||||
|
try {
|
||||||
|
RealmModel realm = session.realms().getRealm(Config.getAdminRealm());
|
||||||
|
if (session.users().getUserByUsername("admin", realm) == null) {
|
||||||
|
UserModel adminUser = session.users().addUser(realm, "admin");
|
||||||
|
|
||||||
adminUser.setEnabled(true);
|
adminUser.setEnabled(true);
|
||||||
UserCredentialModel usrCredModel = new UserCredentialModel();
|
UserCredentialModel usrCredModel = new UserCredentialModel();
|
||||||
usrCredModel.setType(UserCredentialModel.PASSWORD);
|
usrCredModel.setType(UserCredentialModel.PASSWORD);
|
||||||
usrCredModel.setValue(password);
|
usrCredModel.setValue("admin");
|
||||||
session.users().updateCredential(realm, adminUser, usrCredModel);
|
session.users().updateCredential(realm, adminUser, usrCredModel);
|
||||||
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||||
|
|
||||||
|
@ -81,5 +84,11 @@ public class ApplianceBootstrap {
|
||||||
adminUser.grantRole(accountApp.getRole(r));
|
adminUser.grantRole(accountApp.getRole(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
session.getTransaction().commit();
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.services.resources;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import org.codehaus.jackson.JsonNode;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.codehaus.jackson.type.TypeReference;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.core.Dispatcher;
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
|
@ -11,9 +12,11 @@ import org.keycloak.migration.MigrationModelManager;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.PostMigrationEvent;
|
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.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
||||||
import org.keycloak.services.managers.ApplianceBootstrap;
|
import org.keycloak.services.managers.ApplianceBootstrap;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
|
@ -36,10 +39,7 @@ import javax.ws.rs.core.UriInfo;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @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"))));
|
singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false"))));
|
||||||
|
|
||||||
setupDefaultRealm(context.getContextPath());
|
boolean defaultRealmCreated = ApplianceBootstrap.setupDefaultRealm(sessionFactory, context.getContextPath());
|
||||||
|
|
||||||
migrateModel();
|
migrateModel();
|
||||||
sessionFactory.publish(new PostMigrationEvent());
|
sessionFactory.publish(new PostMigrationEvent());
|
||||||
|
@ -89,7 +89,11 @@ public class KeycloakApplication extends Application {
|
||||||
new ExportImportManager().checkExportImport(this.sessionFactory, context.getContextPath());
|
new ExportImportManager().checkExportImport(this.sessionFactory, context.getContextPath());
|
||||||
importRealms(context);
|
importRealms(context);
|
||||||
|
|
||||||
AdminRecovery.recover(sessionFactory);
|
importAddUser();
|
||||||
|
|
||||||
|
if (defaultRealmCreated) {
|
||||||
|
ApplianceBootstrap.setupDefaultUser(sessionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
setupScheduledTasks(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() {
|
public static KeycloakSessionFactory createSessionFactory() {
|
||||||
DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
|
DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
|
||||||
factory.init();
|
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) {
|
private static <T> T loadJson(InputStream is, Class<T> type) {
|
||||||
try {
|
try {
|
||||||
return JsonSerialization.readValue(is, type);
|
return JsonSerialization.readValue(is, type);
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class UsersResource {
|
||||||
attrsToRemove = Collections.emptySet();
|
attrsToRemove = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep.isEnabled()) {
|
if (rep.isEnabled() != null && rep.isEnabled()) {
|
||||||
UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername());
|
UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername());
|
||||||
if (failureModel != null) {
|
if (failureModel != null) {
|
||||||
failureModel.clearFailures();
|
failureModel.clearFailures();
|
||||||
|
@ -219,9 +219,9 @@ public class UsersResource {
|
||||||
user.setFirstName(rep.getFirstName());
|
user.setFirstName(rep.getFirstName());
|
||||||
user.setLastName(rep.getLastName());
|
user.setLastName(rep.getLastName());
|
||||||
|
|
||||||
user.setEnabled(rep.isEnabled());
|
if (rep.isEnabled() != null) user.setEnabled(rep.isEnabled());
|
||||||
user.setOtpEnabled(rep.isTotp());
|
if (rep.isTotp() != null) user.setOtpEnabled(rep.isTotp());
|
||||||
user.setEmailVerified(rep.isEmailVerified());
|
if (rep.isEmailVerified() != null) user.setEmailVerified(rep.isEmailVerified());
|
||||||
|
|
||||||
List<String> reqActions = rep.getRequiredActions();
|
List<String> reqActions = rep.getRequiredActions();
|
||||||
|
|
||||||
|
@ -708,7 +708,7 @@ public class UsersResource {
|
||||||
} catch (ModelReadOnlyException mre) {
|
} catch (ModelReadOnlyException mre) {
|
||||||
throw new BadRequestException("Can't reset password as account is read only");
|
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();
|
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,8 +130,8 @@ public class UserAttributesForm extends Form {
|
||||||
setEmail(user.getEmail());
|
setEmail(user.getEmail());
|
||||||
setFirstName(user.getFirstName());
|
setFirstName(user.getFirstName());
|
||||||
setLastName(user.getLastName());
|
setLastName(user.getLastName());
|
||||||
setEnabled(user.isEnabled());
|
if (user.isEnabled() != null) setEnabled(user.isEnabled());
|
||||||
setEmailVerified(user.isEmailVerified());
|
if (user.isEmailVerified() != null) setEmailVerified(user.isEmailVerified());
|
||||||
setRequiredActions(user.getRequiredActions());
|
setRequiredActions(user.getRequiredActions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-admin-client</artifactId>
|
<artifactId>keycloak-admin-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-wildfly-adduser</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>log4j</groupId>
|
<groupId>log4j</groupId>
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j</artifactId>
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,6 +22,8 @@
|
||||||
package org.keycloak.testsuite.rule;
|
package org.keycloak.testsuite.rule;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
|
54
wildfly/adduser/pom.xml
Executable file
54
wildfly/adduser/pom.xml
Executable 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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,9 +19,8 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-wildfly-parent</artifactId>
|
||||||
<version>1.7.0.Final-SNAPSHOT</version>
|
<version>1.7.0.Final-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>keycloak-wildfly-extensions</artifactId>
|
<artifactId>keycloak-wildfly-extensions</artifactId>
|
22
wildfly/pom.xml
Executable file
22
wildfly/pom.xml
Executable 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>
|
|
@ -19,13 +19,12 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-wildfly-parent</artifactId>
|
||||||
<version>1.7.0.Final-SNAPSHOT</version>
|
<version>1.7.0.Final-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>keycloak-as7-server-subsystem</artifactId>
|
<artifactId>keycloak-eap6-server-subsystem</artifactId>
|
||||||
<name>Keycloak AS7 / EAP 6 Server Subsystem</name>
|
<name>Keycloak EAP 6 Server Subsystem</name>
|
||||||
<description/>
|
<description/>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
|
@ -19,13 +19,12 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-wildfly-parent</artifactId>
|
||||||
<version>1.7.0.Final-SNAPSHOT</version>
|
<version>1.7.0.Final-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>keycloak-wf9-server-subsystem</artifactId>
|
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
|
||||||
<name>Keycloak Wildfly 9 Server Subsystem</name>
|
<name>Keycloak WildFly Server Subsystem</name>
|
||||||
<description/>
|
<description/>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
@ -96,11 +95,5 @@
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-wildfly-adapter</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
Loading…
Reference in a new issue