Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2014-11-06 17:27:44 -05:00
commit 666851a44a
15 changed files with 102 additions and 57 deletions

View file

@ -13,14 +13,6 @@
<name>Keycloak Wildfly Adapter Distro</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
<version>${project.version}</version>
<type>zip</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

View file

@ -12,6 +12,13 @@
<listitem>Directory on local filesystem</listitem>
<listitem>Single JSON file on your filesystem</listitem>
</itemizedlist>
When importing using the "dir" or "zip" strategies, note that the files need to follow the naming convention specified below.
If you are importing files which were previously exported, the files already follow this convention.
<itemizedlist>
<listitem>{REALM_NAME}-realm.json, such as "acme-roadrunner-affairs-realm.json" for the realm named "acme-roadrunner-affairs"</listitem>
<listitem>{REALM_NAME}-users-{INDEX}.json, such as "acme-roadrunner-affairs-users-0.json" for the first users file of the realm named "acme-roadrunner-affairs"</listitem>
</itemizedlist>
</para>
<para>
Encrypted ZIP is recommended as export contains many sensitive informations like passwords of your users (even if they are hashed),
@ -111,5 +118,14 @@ bin/standalone.sh -Dkeycloak.migration.action=import
</varlistentry>
</variablelist>
</para>
<para>
When importing realm files that weren't exported before, the option <literal>keycloak.import</literal> can be used. If more than one realm
file needs to be imported, a comma separated list of file names can be specified. This is more appropriate than the cases before, as this
will happen only after the master realm has been initialized. Examples:
<itemizedlist>
<listitem>-Dkeycloak.import=/tmp/realm1.json</listitem>
<listitem>-Dkeycloak.import=/tmp/realm1.json,/tmp/realm2.json</listitem>
</itemizedlist>
</para>
</chapter>

View file

@ -60,6 +60,13 @@
</modules>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -45,7 +45,7 @@ import java.util.Set;
public class ExportUtils {
public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, boolean includeUsers) {
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm);
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, true);
// Audit
rep.setEventsEnabled(realm.isEventsEnabled());

View file

@ -63,35 +63,37 @@
<span tooltip-placement="right" tooltip="Should newly created users be created within LDAP store? Priority effects which provider is chose to sync the new user." class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="vendor">Vendor</label>
<label class="col-sm-2 control-label" for="vendor">Vendor<span class="required">*</span></label>
<div class="col-sm-4">
<div class="select-kc">
<select id="vendor"
ng-model="instance.config.vendor"
ng-options="vendor.id as vendor.name for vendor in ldapVendors">
ng-options="vendor.id as vendor.name for vendor in ldapVendors"
required>
</select>
</div>
</div>
<span tooltip-placement="right" tooltip="LDAP vendor (provider)" class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="usernameLDAPAttribute">Username LDAP attribute </label>
<label class="col-sm-2 control-label" for="usernameLDAPAttribute">Username LDAP attribute<span class="required">*</span></label>
<div class="col-sm-4">
<div class="select-kc">
<select id="usernameLDAPAttribute"
ng-model="instance.config.usernameLDAPAttribute"
ng-options="usernameLDAPAttribute for usernameLDAPAttribute in usernameLDAPAttributes">
ng-options="usernameLDAPAttribute for usernameLDAPAttribute in usernameLDAPAttributes"
required>
</select>
</div>
</div>
<span tooltip-placement="right" tooltip="Name of LDAP attribute, which is mapped as Keycloak username" class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="userObjectClasses">User Object Classes </label>
<label class="col-sm-2 control-label" for="userObjectClasses">User Object Classes<span class="required">*</span></label>
<div class="col-sm-4">
<input class="form-control" id="userObjectClasses" type="text" ng-model="instance.config.userObjectClasses" placeholder="LDAP User Object Classes (div. by comma)">
<input class="form-control" id="userObjectClasses" type="text" ng-model="instance.config.userObjectClasses" placeholder="LDAP User Object Classes (div. by comma)" required>
</div>
<span tooltip-placement="right" tooltip="All values of LDAP objectClass attribute divided by comma, which are used for newly created LDAP users" class="fa fa-info-circle"></span>
<span tooltip-placement="right" tooltip="All values of LDAP objectClass attribute for users in LDAP divided by comma" class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="ldapConnectionUrl">Connection URL<span class="required">*</span></label>

View file

@ -5,9 +5,6 @@
<div id="content">
<h2><span>{{realm.realm}}</span> Realm Public Key <span tooltip-placement="right" tooltip="Realm's public key. This is used to verify any signed tokens or documents created by the realm." class="fa fa-info-circle"></span></h2>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
</div>
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="publicKey">Public key</label>
@ -18,7 +15,7 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="publicKey">Certificate</label>
<label class="col-sm-2 control-label" for="certificate">Certificate</label>
<div class="col-sm-10">
<textarea type="text" id="certificate" name="certificate" class="form-control" rows="5"
@ -26,6 +23,9 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
</div>
</form>
</div>
</div>

View file

@ -132,7 +132,7 @@ public class PasswordPolicy {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + count + " numerical digits" : null;
return count < min ? "Invalid password: must contain at least " + min + " numerical digits" : null;
}
}
@ -152,7 +152,7 @@ public class PasswordPolicy {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + count + " lower case characters": null;
return count < min ? "Invalid password: must contain at least " + min + " lower case characters": null;
}
}
@ -172,7 +172,7 @@ public class PasswordPolicy {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + count + " upper case characters" : null;
return count < min ? "Invalid password: must contain at least " + min + " upper case characters" : null;
}
}
@ -192,7 +192,7 @@ public class PasswordPolicy {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + count + " special characters" : null;
return count < min ? "Invalid password: must contain at least " + min + " special characters" : null;
}
}

View file

@ -75,7 +75,7 @@ public class ModelToRepresentation {
return rep;
}
public static RealmRepresentation toRepresentation(RealmModel realm) {
public static RealmRepresentation toRepresentation(RealmModel realm, boolean internal) {
RealmRepresentation rep = new RealmRepresentation();
rep.setId(realm.getId());
rep.setRealm(realm.getName());
@ -85,13 +85,15 @@ public class ModelToRepresentation {
rep.setUpdateProfileOnInitialSocialLogin(realm.isUpdateProfileOnInitialSocialLogin());
rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
rep.setPublicKey(realm.getPublicKeyPem());
rep.setPrivateKey(realm.getPrivateKeyPem());
String privateKeyPem = realm.getPrivateKeyPem();
if (realm.getCertificatePem() == null && privateKeyPem != null) {
KeycloakModelUtils.generateRealmCertificate(realm);
if (internal) {
rep.setPrivateKey(realm.getPrivateKeyPem());
String privateKeyPem = realm.getPrivateKeyPem();
if (realm.getCertificatePem() == null && privateKeyPem != null) {
KeycloakModelUtils.generateRealmCertificate(realm);
}
rep.setCodeSecret(realm.getCodeSecret());
}
rep.setCertificate(realm.getCertificatePem());
rep.setCodeSecret(realm.getCodeSecret());
rep.setPasswordCredentialGrantAllowed(realm.isPasswordCredentialGrantAllowed());
rep.setRegistrationAllowed(realm.isRegistrationAllowed());
rep.setRememberMe(realm.isRememberMe());

View file

@ -11,55 +11,55 @@ public class PasswordPolicyTest {
@Test
public void testLength() {
PasswordPolicy policy = new PasswordPolicy("length");
Assert.assertNotNull(policy.validate("1234567"));
Assert.assertEquals("Invalid password: minimum length 8", policy.validate("1234567"));
Assert.assertNull(policy.validate("12345678"));
policy = new PasswordPolicy("length(4)");
Assert.assertNotNull(policy.validate("123"));
Assert.assertEquals("Invalid password: minimum length 4", policy.validate("123"));
Assert.assertNull(policy.validate("1234"));
}
@Test
public void testDigits() {
PasswordPolicy policy = new PasswordPolicy("digits");
Assert.assertNotNull(policy.validate("abcd"));
Assert.assertEquals("Invalid password: must contain at least 1 numerical digits", policy.validate("abcd"));
Assert.assertNull(policy.validate("abcd1"));
policy = new PasswordPolicy("digits(2)");
Assert.assertNotNull(policy.validate("abcd1"));
Assert.assertEquals("Invalid password: must contain at least 2 numerical digits", policy.validate("abcd1"));
Assert.assertNull(policy.validate("abcd12"));
}
@Test
public void testLowerCase() {
PasswordPolicy policy = new PasswordPolicy("lowerCase");
Assert.assertNotNull(policy.validate("ABCD1234"));
Assert.assertEquals("Invalid password: must contain at least 1 lower case characters", policy.validate("ABCD1234"));
Assert.assertNull(policy.validate("ABcD1234"));
policy = new PasswordPolicy("lowerCase(2)");
Assert.assertNotNull(policy.validate("ABcD1234"));
Assert.assertEquals("Invalid password: must contain at least 2 lower case characters", policy.validate("ABcD1234"));
Assert.assertNull(policy.validate("aBcD1234"));
}
@Test
public void testUpperCase() {
PasswordPolicy policy = new PasswordPolicy("upperCase");
Assert.assertNotNull(policy.validate("abcd1234"));
Assert.assertEquals("Invalid password: must contain at least 1 upper case characters", policy.validate("abcd1234"));
Assert.assertNull(policy.validate("abCd1234"));
policy = new PasswordPolicy("upperCase(2)");
Assert.assertNotNull(policy.validate("abCd1234"));
Assert.assertEquals("Invalid password: must contain at least 2 upper case characters", policy.validate("abCd1234"));
Assert.assertNull(policy.validate("AbCd1234"));
}
@Test
public void testSpecialChars() {
PasswordPolicy policy = new PasswordPolicy("specialChars");
Assert.assertNotNull(policy.validate("abcd1234"));
Assert.assertEquals("Invalid password: must contain at least 1 special characters", policy.validate("abcd1234"));
Assert.assertNull(policy.validate("ab&d1234"));
policy = new PasswordPolicy("specialChars(2)");
Assert.assertNotNull(policy.validate("ab&d1234"));
Assert.assertEquals("Invalid password: must contain at least 2 special characters", policy.validate("ab&d1234"));
Assert.assertNull(policy.validate("ab&d-234"));
}

View file

@ -194,15 +194,19 @@ public class KeycloakApplication extends Application {
}
public void importRealmFile() {
String file = System.getProperty("keycloak.import");
if (file != null) {
RealmRepresentation rep = null;
try {
rep = loadJson(new FileInputStream(file), RealmRepresentation.class);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
String files = System.getProperty("keycloak.import");
if (files != null) {
StringTokenizer tokenizer = new StringTokenizer(files, ",");
while (tokenizer.hasMoreTokens()) {
String file = tokenizer.nextToken().trim();
RealmRepresentation rep = null;
try {
rep = loadJson(new FileInputStream(file), RealmRepresentation.class);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
importRealm(rep, "file " + file);
}
importRealm(rep, "file " + file);
}
}
@ -223,11 +227,14 @@ public class KeycloakApplication extends Application {
return;
}
RealmModel realm = manager.importRealm(rep);
log.info("Imported realm " + realm.getName() + " from " + from);
session.getTransaction().commit();
try {
RealmModel realm = manager.importRealm(rep);
session.getTransaction().commit();
log.info("Imported realm " + realm.getName() + " from " + from);
} catch (Throwable t) {
session.getTransaction().rollback();
log.warn("Unable to import realm " + rep.getRealm() + " from " + from + ". Cause: " + t.getMessage());
}
} finally {
session.close();
}

View file

@ -158,7 +158,7 @@ public class RealmAdminResource {
@Produces("application/json")
public RealmRepresentation getRealm() {
if (auth.hasView()) {
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm);
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, false);
if (session.realms() instanceof CacheRealmProvider) {
CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
rep.setRealmCacheEnabled(cacheRealmProvider.isEnabled());

View file

@ -100,7 +100,7 @@ public class RealmsAdminResource {
protected void addRealmRep(List<RealmRepresentation> reps, RealmModel realm, ApplicationModel realmManagementApplication) {
if (auth.hasAppRole(realmManagementApplication, AdminRoles.MANAGE_REALM)) {
reps.add(ModelToRepresentation.toRepresentation(realm));
reps.add(ModelToRepresentation.toRepresentation(realm, false));
} else if (auth.hasOneOfAppRole(realmManagementApplication, AdminRoles.ALL_REALM_ROLES)) {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realm.getName());

View file

@ -7,6 +7,7 @@ import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@ -42,6 +43,7 @@ public abstract class AbstractClientTest {
RealmModel testRealm = manager.createRealm(REALM_NAME);
testRealm.setEnabled(true);
KeycloakModelUtils.generateRealmKeys(testRealm);
}
});

View file

@ -6,7 +6,11 @@ import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@ -16,7 +20,15 @@ public class RealmTest extends AbstractClientTest {
@Test
public void getRealms() {
assertNames(keycloak.realms().findAll(), "master", "test", REALM_NAME);
List<RealmRepresentation> realms = keycloak.realms().findAll();
assertNames(realms, "master", "test", REALM_NAME);
for (RealmRepresentation rep : realms) {
assertNull(rep.getPrivateKey());
assertNull(rep.getCodeSecret());
assertNotNull(rep.getPublicKey());
assertNotNull(rep.getCertificate());
}
}
@Test
@ -65,6 +77,11 @@ public class RealmTest extends AbstractClientTest {
RealmRepresentation rep = realm.toRepresentation();
assertEquals(REALM_NAME, rep.getRealm());
assertTrue(rep.isEnabled());
assertNull(rep.getPrivateKey());
assertNull(rep.getCodeSecret());
assertNotNull(rep.getPublicKey());
assertNotNull(rep.getCertificate());
}
}

View file

@ -67,7 +67,7 @@ public class ModelTest extends AbstractModelTest {
}
private RealmModel importExport(RealmModel src, String copyName) {
RealmRepresentation representation = ModelToRepresentation.toRepresentation(src);
RealmRepresentation representation = ModelToRepresentation.toRepresentation(src, true);
representation.setRealm(copyName);
representation.setId(copyName);
RealmModel copy = realmManager.importRealm(representation);