Merge pull request #350 from stianst/constraints

Enforce that realm name is unique in model
This commit is contained in:
Stian Thorgersen 2014-04-29 10:44:42 +01:00
commit 1fa019e9dd
21 changed files with 193 additions and 67 deletions

View file

@ -7,6 +7,8 @@ public interface AuditProvider extends AuditListener {
public EventQuery createQuery();
public void clear();
public void clear(String realmId);
public void clear(String realmId, long olderThan);

View file

@ -28,6 +28,12 @@ public class JpaAuditProvider implements AuditProvider {
return new JpaEventQuery(em);
}
@Override
public void clear() {
beginTx();
em.createQuery("delete from EventEntity").executeUpdate();
}
@Override
public void clear(String realmId) {
beginTx();

View file

@ -1,6 +1,5 @@
package org.keycloak.audit.mongo;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
@ -27,6 +26,11 @@ public class MongoAuditProvider implements AuditProvider {
return new MongoEventQuery(audit);
}
@Override
public void clear() {
audit.remove(new BasicDBObject());
}
@Override
public void clear(String realmId) {
audit.remove(new BasicDBObject("realmId", realmId));

View file

@ -34,8 +34,7 @@ public abstract class AbstractAuditProviderTest {
@After
public void after() {
provider.clear("realmId");
provider.clear("realmId2");
provider.clear();
provider.close();
factory.close();
}

View file

@ -39,6 +39,4 @@ public interface ApplicationModel extends RoleContainerModel, ClientModel {
boolean isBearerOnly();
void setBearerOnly(boolean only);
void addScope(RoleModel role);
}

View file

@ -245,11 +245,6 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
em.flush();
}
@Override
public void addScope(RoleModel role) {
realm.addScopeMapping(this, role);
}
public boolean equals(Object o) {
if (o == null) return false;
if (o == this) return true;

View file

@ -4,6 +4,7 @@ import org.hibernate.exception.ConstraintViolationException;
import org.keycloak.models.ModelException;
import org.keycloak.models.ModelDuplicateException;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
@ -33,6 +34,8 @@ public class PersistenceExceptionConverter implements InvocationHandler {
Throwable c = e.getCause();
if (c.getCause() != null && c.getCause() instanceof ConstraintViolationException) {
throw new ModelDuplicateException(c);
} if (c instanceof EntityExistsException) {
throw new ModelDuplicateException(c);
} else {
throw new ModelException(c);
}

View file

@ -35,7 +35,7 @@ public class RealmEntity {
@Id
protected String id;
//@Column(unique = true)
@Column(unique = true)
protected String name;
protected boolean enabled;

View file

@ -179,17 +179,21 @@ public class MongoStoreImpl implements MongoStore {
try {
dbCollection.insert(dbObject);
} catch (MongoException e) {
if (e instanceof MongoException.DuplicateKey) {
throw new ModelDuplicateException(e);
} else {
throw new ModelException(e);
}
throw convertException(e);
}
// Treat object as created in this transaction (It is already submited to transaction)
context.addCreatedEntity(entity);
}
public static ModelException convertException(MongoException e) {
if (e instanceof MongoException.DuplicateKey) {
return new ModelDuplicateException(e);
} else {
return new ModelException(e);
}
}
@Override
public void updateEntity(final MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
MongoTask fullUpdateTask = new MongoTask() {

View file

@ -123,6 +123,7 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
roleEntity.setApplicationId(getId());
getMongoStore().insertEntity(roleEntity, invocationContext);
return new RoleAdapter(getRealm(), roleEntity, this, invocationContext);
}
@ -159,11 +160,6 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
return result;
}
@Override
public void addScope(RoleModel role) {
getMongoStore().pushItemToList(getMongoEntity(), "scopeIds", role.getId(), true, invocationContext);
}
@Override
public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
Set<RoleModel> result = new HashSet<RoleModel>();

View file

@ -46,10 +46,6 @@ public class MongoKeycloakSession implements KeycloakSession {
@Override
public RealmModel createRealm(String id, String name) {
if (getRealm(id) != null) {
throw new IllegalStateException("Realm with id '" + id + "' already exists");
}
RealmEntity newRealm = new RealmEntity();
newRealm.setId(id);
newRealm.setName(name);

View file

@ -1,7 +1,9 @@
package org.keycloak.models.mongo.keycloak.adapters;
import com.mongodb.MongoException;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.impl.MongoStoreImpl;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -35,7 +37,11 @@ public class MongoKeycloakTransaction implements KeycloakTransaction {
throw new IllegalStateException("Can't commit as transaction marked for rollback");
}
try {
invocationContext.commit();
} catch (MongoException e) {
throw MongoStoreImpl.convertException(e);
}
}
@Override

View file

@ -541,6 +541,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
roleEntity.setRealmId(getId());
getMongoStore().insertEntity(roleEntity, invocationContext);
return new RoleAdapter(this, roleEntity, this, invocationContext);
}

View file

@ -23,7 +23,7 @@ import java.util.Set;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@MongoCollection(collectionName = "realms")
//@MongoIndex(fields = { "name" }, unique = true)
@MongoIndex(fields = { "name" }, unique = true)
public class RealmEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
private String name;
@ -422,5 +422,8 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
// Remove all applications of this realm
context.getMongoStore().removeEntities(ApplicationEntity.class, query, context);
// Remove all clients of this realm
context.getMongoStore().removeEntities(OAuthClientEntity.class, query, context);
}
}

View file

@ -6,11 +6,15 @@ import java.io.InputStream;
import java.util.Set;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.Config;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
@ -24,23 +28,41 @@ import org.keycloak.util.JsonSerialization;
*/
public class AbstractModelTest {
protected KeycloakSessionFactory factory;
protected static KeycloakSessionFactory factory;
protected static ProviderSessionFactory providerSessionFactory;
protected KeycloakSession identitySession;
protected RealmManager realmManager;
protected ProviderSessionFactory providerSessionFactory;
protected ProviderSession providerSession;
@BeforeClass
public static void beforeClass() {
factory = KeycloakApplication.createSessionFactory();
providerSessionFactory = KeycloakApplication.createProviderSessionFactory();
KeycloakSession identitySession = factory.createSession();
try {
identitySession.getTransaction().begin();
new ApplianceBootstrap().bootstrap(identitySession, "/auth");
identitySession.getTransaction().commit();
} finally {
identitySession.close();
}
}
@AfterClass
public static void afterClass() {
providerSessionFactory.close();
factory.close();
}
@Before
public void before() throws Exception {
factory = KeycloakApplication.createSessionFactory();
identitySession = factory.createSession();
identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession);
providerSessionFactory = KeycloakApplication.createProviderSessionFactory();
providerSession = providerSessionFactory.createSession();
new ApplianceBootstrap().bootstrap(identitySession, "/auth");
}
@After
@ -48,12 +70,35 @@ public class AbstractModelTest {
identitySession.getTransaction().commit();
providerSession.close();
identitySession.close();
providerSessionFactory.close();
factory.close();
identitySession = factory.createSession();
try {
identitySession.getTransaction().begin();
RealmManager rm = new RealmManager(identitySession);
for (RealmModel realm : identitySession.getRealms()) {
if (!realm.getName().equals(Config.getAdminRealm())) {
rm.removeRealm(realm);
}
}
identitySession.getTransaction().commit();
} finally {
identitySession.close();
}
}
protected void commit() {
commit(false);
}
protected void commit(boolean rollback) {
if (rollback) {
identitySession.getTransaction().rollback();
} else {
identitySession.getTransaction().commit();
}
identitySession.close();
identitySession = factory.createSession();
identitySession.getTransaction().begin();
@ -68,8 +113,6 @@ public class AbstractModelTest {
os.write(c);
}
byte[] bytes = os.toByteArray();
System.out.println(new String(bytes));
return JsonSerialization.readValue(bytes, RealmRepresentation.class);
}

View file

@ -6,6 +6,7 @@ import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
@ -44,7 +45,6 @@ public class AdapterTest extends AbstractModelTest {
realmModel.setUpdateProfileOnInitialSocialLogin(true);
realmModel.addDefaultRole("foo");
System.out.println(realmModel.getId());
realmModel = realmManager.getRealm(realmModel.getId());
Assert.assertNotNull(realmModel);
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
@ -302,7 +302,7 @@ public class AdapterTest extends AbstractModelTest {
}
String[] usernames = users.toArray(new String[users.size()]);
Arrays.sort(usernames);
Assert.assertArrayEquals(new String[] { "doublefirst", "doublelast"}, usernames);
Assert.assertArrayEquals(new String[]{"doublefirst", "doublelast"}, usernames);
}
{
@ -472,5 +472,67 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertFalse(realmModel.hasRole(user, appBarRole));
}
// TODO: test scopes
@Test
public void testScopes() throws Exception {
test1CreateRealm();
RoleModel realmRole = realmModel.addRole("realm");
ApplicationModel app1 = realmModel.addApplication("app1");
RoleModel appRole = app1.addRole("app");
ApplicationModel app2 = realmModel.addApplication("app2");
realmModel.addScopeMapping(app2, realmRole);
realmModel.addScopeMapping(app2, appRole);
OAuthClientModel client = realmModel.addOAuthClient("client");
realmModel.addScopeMapping(client, realmRole);
realmModel.addScopeMapping(client, appRole);
commit();
realmModel = identitySession.getRealmByName("JUGGLER");
app1 = realmModel.getApplicationByName("app1");
app2 = realmModel.getApplicationByName("app2");
client = realmModel.getOAuthClient("client");
Set<RoleModel> scopeMappings = realmModel.getScopeMappings(app2);
Assert.assertEquals(2, scopeMappings.size());
Assert.assertTrue(scopeMappings.contains(realmModel.getRole("realm")));
Assert.assertTrue(scopeMappings.contains(app1.getRole("app")));
scopeMappings = realmModel.getScopeMappings(client);
Assert.assertEquals(2, scopeMappings.size());
Assert.assertTrue(scopeMappings.contains(realmModel.getRole("realm")));
Assert.assertTrue(scopeMappings.contains(app1.getRole("app")));
}
@Test
public void testRealmNameCollisions() throws Exception {
test1CreateRealm();
commit();
// Try to create realm with duplicate name
try {
test1CreateRealm();
commit();
Assert.fail("Expected exception");
} catch (ModelDuplicateException e) {
}
commit(true);
// Ty to rename realm to duplicate name
realmModel = realmManager.createRealm("JUGGLER2");
commit();
realmModel = realmManager.getRealmByName("JUGGLER2");
try {
realmModel.setName("JUGGLER");
commit();
Assert.fail("Expected exception");
} catch (ModelDuplicateException e) {
}
commit(true);
}
}

View file

@ -7,8 +7,10 @@ import javax.ws.rs.core.MultivaluedMap;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@ -29,22 +31,38 @@ import org.keycloak.authentication.AuthenticationProviderManager;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class AuthProvidersLDAPTest extends AbstractModelTest {
private static LDAPEmbeddedServer embeddedServer;
private RealmModel realm;
private AuthenticationManager am;
private LDAPEmbeddedServer embeddedServer;
@Before
@Override
public void before() throws Exception {
super.before();
@BeforeClass
public static void beforeClass() {
AbstractModelTest.beforeClass();
try {
this.embeddedServer = new LDAPEmbeddedServer();
this.embeddedServer.setup();
this.embeddedServer.importLDIF("ldap/users.ldif");
embeddedServer = new LDAPEmbeddedServer();
embeddedServer.setup();
embeddedServer.importLDIF("ldap/users.ldif");
} catch (Exception e) {
throw new RuntimeException("Error starting Embedded LDAP server.", e);
}
}
@AfterClass
public static void afterClass() {
AbstractModelTest.afterClass();
try {
embeddedServer.tearDown();
} catch (Exception e) {
throw new RuntimeException("Error starting Embedded LDAP server.", e);
}
}
@Before
public void before() throws Exception {
super.before();
// Create realm and configure ldap
realm = realmManager.createRealm("realm");
@ -55,17 +73,6 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
am = new AuthenticationManager(providerSession);
}
@After
@Override
public void after() throws Exception {
super.after();
try {
this.embeddedServer.tearDown();
} catch (Exception e) {
throw new RuntimeException("Error starting Embedded LDAP server.", e);
}
}
@Test
public void testLdapAuthentication() {
MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password");

View file

@ -70,6 +70,7 @@ public class ModelTest extends AbstractModelTest {
private RealmModel importExport(RealmModel src, String copyName) {
RealmRepresentation representation = ModelToRepresentation.toRepresentation(src);
representation.setRealm(copyName);
RealmModel copy = realmManager.createRealm(copyName);
realmManager.importRealm(representation, copy);
return realmManager.getRealm(copy.getId());

View file

@ -22,8 +22,8 @@ public class MultipleRealmsTest extends AbstractModelTest {
@Override
public void before() throws Exception {
super.before();
realm1 = identitySession.createRealm("id1", "realm1");
realm2 = identitySession.createRealm("id2", "realm2");
realm1 = realmManager.createRealm("id1", "realm1");
realm2 = realmManager.createRealm("id2", "realm2");
createObjects(realm1);
createObjects(realm2);
@ -93,7 +93,7 @@ public class MultipleRealmsTest extends AbstractModelTest {
realm.addRole("role2");
app1.addRole("app1Role1");
app1.addScope(realm.getRole("role1"));
realm.addScopeMapping(app1, realm.getRole("role1"));
realm.addOAuthClient("cl1");
}

View file

@ -71,7 +71,7 @@ public class ApplianceBootstrap {
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
adminConsole.addScope(adminRole);
realm.addScopeMapping(adminConsole, adminRole);
UserModel adminUser = realm.addUser("admin");
adminUser.setEnabled(true);

View file

@ -87,14 +87,14 @@ public class CompositeRoleTest {
final ApplicationModel realmComposite1Application = new ApplicationManager(manager).createApplication(realm, "REALM_COMPOSITE_1_APPLICATION");
realmComposite1Application.setEnabled(true);
realmComposite1Application.addScope(realmComposite1);
realm.addScopeMapping(realmComposite1Application, realmComposite1);
realmComposite1Application.setBaseUrl("http://localhost:8081/app");
realmComposite1Application.setManagementUrl("http://localhost:8081/app/logout");
realmComposite1Application.setSecret("password");
final ApplicationModel realmRole1Application = new ApplicationManager(manager).createApplication(realm, "REALM_ROLE_1_APPLICATION");
realmRole1Application.setEnabled(true);
realmRole1Application.addScope(realmRole1);
realm.addScopeMapping(realmRole1Application, realmRole1);
realmRole1Application.setBaseUrl("http://localhost:8081/app");
realmRole1Application.setManagementUrl("http://localhost:8081/app/logout");
realmRole1Application.setSecret("password");
@ -127,7 +127,7 @@ public class CompositeRoleTest {
appCompositeApplication.setManagementUrl("http://localhost:8081/app/logout");
appCompositeApplication.setSecret("password");
final RoleModel appCompositeRole = appCompositeApplication.addRole("APP_COMPOSITE_ROLE");
appCompositeApplication.addScope(appRole2);
realm.addScopeMapping(appCompositeApplication, appRole2);
appCompositeRole.addCompositeRole(realmRole1);
appCompositeRole.addCompositeRole(realmRole2);
appCompositeRole.addCompositeRole(realmRole3);