Merge pull request #3770 from patriot1burke/master

KEYCLOAK-4077
This commit is contained in:
Bill Burke 2017-01-19 07:35:10 -05:00 committed by GitHub
commit 73d3e8afd9
6 changed files with 355 additions and 0 deletions

View file

@ -17,6 +17,7 @@
package org.keycloak.storage.jpa; package org.keycloak.storage.jpa;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel; import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore; import org.keycloak.credential.UserCredentialStore;
@ -259,6 +260,9 @@ public class JpaUserFederatedStorageProvider implements
consentEntity.setClientId(clientId); consentEntity.setClientId(clientId);
consentEntity.setRealmId(realm.getId()); consentEntity.setRealmId(realm.getId());
consentEntity.setStorageProviderId(new StorageId(userId).getProviderId()); consentEntity.setStorageProviderId(new StorageId(userId).getProviderId());
long currentTime = Time.currentTimeMillis();
consentEntity.setCreatedDate(currentTime);
consentEntity.setLastUpdatedDate(currentTime);
em.persist(consentEntity); em.persist(consentEntity);
em.flush(); em.flush();
@ -335,6 +339,8 @@ public class JpaUserFederatedStorageProvider implements
throw new ModelException("Client with id " + entity.getClientId() + " is not available"); throw new ModelException("Client with id " + entity.getClientId() + " is not available");
} }
UserConsentModel model = new UserConsentModel(client); UserConsentModel model = new UserConsentModel(client);
model.setCreatedDate(entity.getCreatedDate());
model.setLastUpdatedDate(entity.getLastUpdatedDate());
Collection<FederatedUserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles(); Collection<FederatedUserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles();
if (grantedRoleEntities != null) { if (grantedRoleEntities != null) {
@ -404,6 +410,8 @@ public class JpaUserFederatedStorageProvider implements
em.remove(toRemove); em.remove(toRemove);
} }
consentEntity.setLastUpdatedDate(Time.currentTimeMillis());
em.flush(); em.flush();
} }

View file

@ -65,6 +65,14 @@ public class FederatedUserConsentEntity {
@Column(name="CLIENT_ID") @Column(name="CLIENT_ID")
protected String clientId; protected String clientId;
@Column(name = "CREATED_DATE")
private Long createdDate;
@Column(name = "LAST_UPDATED_DATE")
private Long lastUpdatedDate;
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent") @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
Collection<FederatedUserConsentRoleEntity> grantedRoles = new ArrayList<FederatedUserConsentRoleEntity>(); Collection<FederatedUserConsentRoleEntity> grantedRoles = new ArrayList<FederatedUserConsentRoleEntity>();
@ -127,6 +135,22 @@ public class FederatedUserConsentEntity {
this.grantedProtocolMappers = grantedProtocolMappers; this.grantedProtocolMappers = grantedProtocolMappers;
} }
public Long getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Long createdDate) {
this.createdDate = createdDate;
}
public Long getLastUpdatedDate() {
return lastUpdatedDate;
}
public void setLastUpdatedDate(Long lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ 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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="bburke@redhat.com" id="2.5.1">
<addColumn tableName="FED_USER_CONSENT">
<column name="CREATED_DATE" type="BIGINT"/>
<column name="LAST_UPDATED_DATE" type="BIGINT"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View file

@ -45,4 +45,5 @@
<include file="META-INF/jpa-changelog-2.3.0.xml"/> <include file="META-INF/jpa-changelog-2.3.0.xml"/>
<include file="META-INF/jpa-changelog-2.4.0.xml"/> <include file="META-INF/jpa-changelog-2.4.0.xml"/>
<include file="META-INF/jpa-changelog-2.5.0.xml"/> <include file="META-INF/jpa-changelog-2.5.0.xml"/>
<include file="META-INF/jpa-changelog-2.5.1.xml"/>
</databaseChangeLog> </databaseChangeLog>

View file

@ -301,6 +301,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo
return importValidation(realm, user); return importValidation(realm, user);
} }
UserLookupProvider provider = (UserLookupProvider)getStorageProvider(session, realm, storageId.getProviderId()); UserLookupProvider provider = (UserLookupProvider)getStorageProvider(session, realm, storageId.getProviderId());
if (provider == null) return null;
return provider.getUserById(id, realm); return provider.getUserById(id, realm);
} }

View file

@ -0,0 +1,292 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.model;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.federation.storage.UserMapStorageFactory;
import org.keycloak.testsuite.federation.storage.UserPropertyFileStorageFactory;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
@Before
public void setupEnv() {
RealmModel realm = realmManager.createRealm("original");
UserStorageProviderModel model = new UserStorageProviderModel();
model.setName("memory");
model.setPriority(0);
model.setProviderId(UserMapStorageFactory.PROVIDER_ID);
model.setParentId(realm.getId());
realm.addComponentModel(model);
ClientModel fooClient = realm.addClient("foo-client");
ClientModel barClient = realm.addClient("bar-client");
RoleModel realmRole = realm.addRole("realm-role");
RoleModel barClientRole = barClient.addRole("bar-client-role");
ProtocolMapperModel fooMapper = new ProtocolMapperModel();
fooMapper.setName("foo");
fooMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
fooMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
fooMapper = fooClient.addProtocolMapper(fooMapper);
ProtocolMapperModel barMapper = new ProtocolMapperModel();
barMapper.setName("bar");
barMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
barMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
barMapper = barClient.addProtocolMapper(barMapper);
UserModel john = session.users().addUser(realm, "john");
UserModel mary = session.users().addUser(realm, "mary");
UserConsentModel johnFooGrant = new UserConsentModel(fooClient);
johnFooGrant.addGrantedRole(realmRole);
johnFooGrant.addGrantedRole(barClientRole);
johnFooGrant.addGrantedProtocolMapper(fooMapper);
realmManager.getSession().users().addConsent(realm, john.getId(), johnFooGrant);
UserConsentModel johnBarGrant = new UserConsentModel(barClient);
johnBarGrant.addGrantedProtocolMapper(barMapper);
johnBarGrant.addGrantedRole(realmRole);
// Update should fail as grant doesn't yet exists
try {
realmManager.getSession().users().updateConsent(realm, john.getId(), johnBarGrant);
Assert.fail("Not expected to end here");
} catch (ModelException expected) {
}
realmManager.getSession().users().addConsent(realm, john.getId(), johnBarGrant);
UserConsentModel maryFooGrant = new UserConsentModel(fooClient);
maryFooGrant.addGrantedRole(realmRole);
maryFooGrant.addGrantedProtocolMapper(fooMapper);
realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
commit();
}
@Test
public void basicConsentTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
ClientModel barClient = realm.getClientByClientId("bar-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserModel mary = session.users().getUserByUsername("mary", realm);
UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 2);
Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent));
Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate());
UserConsentModel johnBarConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId());
Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1);
Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent));
Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent));
Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate());
UserConsentModel maryConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), fooClient.getId());
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent));
Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary.getId(), barClient.getId()));
}
@Test
public void getAllConsentTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserModel mary = session.users().getUserByUsername("mary", realm);
List<UserConsentModel> johnConsents = realmManager.getSession().users().getConsents(realm, john.getId());
Assert.assertEquals(2, johnConsents.size());
List<UserConsentModel> maryConsents = realmManager.getSession().users().getConsents(realm, mary.getId());
Assert.assertEquals(1, maryConsents.size());
UserConsentModel maryConsent = maryConsents.get(0);
Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
}
@Test
public void updateWithRoleRemovalTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
// Remove foo protocol mapper from johnConsent
ProtocolMapperModel protMapperModel = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
johnConsent.getGrantedProtocolMappers().remove(protMapperModel);
// Remove realm-role and add new-realm-role to johnConsent
RoleModel realmRole = realm.getRole("realm-role");
johnConsent.getGrantedRoles().remove(realmRole);
RoleModel newRealmRole = realm.addRole("new-realm-role");
johnConsent.addGrantedRole(newRealmRole);
realmManager.getSession().users().updateConsent(realm, john.getId(), johnConsent);
commit();
realm = realmManager.getRealm("original");
fooClient = realm.getClientByClientId("foo-client");
john = session.users().getUserByUsername("john", realm);
johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
Assert.assertFalse(isRoleGranted(realm, "realm-role", johnConsent));
Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent));
Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent));
Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
}
@Test
public void revokeTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
UserModel john = session.users().getUserByUsername("john", realm);
realmManager.getSession().users().revokeConsentForClient(realm, john.getId(), fooClient.getId());
commit();
realm = realmManager.getRealm("original");
john = session.users().getUserByUsername("john", realm);
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId()));
}
@Test
public void deleteUserTest() {
// Validate user deleted without any referential constraint errors
RealmModel realm = realmManager.getRealm("original");
UserModel john = session.users().getUserByUsername("john", realm);
session.users().removeUser(realm, john);
}
@Test
public void deleteProtocolMapperTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
ProtocolMapperModel fooMapper = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
fooClient.removeProtocolMapper(fooMapper);
commit();
realm = realmManager.getRealm("original");
fooClient = realm.getClientByClientId("foo-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
Assert.assertFalse(johnConsent.isProtocolMapperGranted(fooMapper));
}
@Test
public void deleteRoleTest() {
RealmModel realm = realmManager.getRealm("original");
RoleModel realmRole = realm.getRole("realm-role");
realm.removeRole(realmRole);
commit();
realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
ClientModel barClient = realm.getClientByClientId("bar-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
Assert.assertEquals(johnConsent.getGrantedRoles().size(), 1);
Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertFalse(johnConsent.isRoleGranted(realmRole));
Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnConsent));
}
@Test
public void deleteClientTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel barClient = realm.getClientByClientId("bar-client");
realm.removeClient(barClient.getId());
commit();
realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
Assert.assertNull(realm.getClientByClientId("bar-client"));
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 1);
Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId()));
}
private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, UserConsentModel consentModel) {
RoleModel role = roleContainer.getRole(roleName);
return consentModel.isRoleGranted(role);
}
private boolean isMapperGranted(ClientModel client, String protocolMapperName, UserConsentModel consentModel) {
ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, protocolMapperName);
return consentModel.isProtocolMapperGranted(protocolMapper);
}
}