#119 - Polishing.

Changed the update clause to set the the entire principal the reflect a domain object design that's closer to what might be set up if Spring Data auditing is used, too.

Made use of Lombok in the domain objects where possible. Reduced visibility of repository interfaces to package scope. Minor test case cleanups.
This commit is contained in:
Oliver Gierke
2015-08-01 21:08:38 +02:00
parent 50faabb2af
commit 9bb284fc84
5 changed files with 61 additions and 141 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,9 @@
*/
package example.springdata.jpa.security;
import lombok.Data;
import lombok.Getter;
import java.util.Date;
import javax.persistence.Entity;
@@ -24,93 +27,28 @@ import javax.persistence.ManyToOne;
/**
* @author Thomas Darimont
* @author Oliver Gierke
*/
@Entity
@Data
public class BusinessObject {
@Id @GeneratedValue Long id;
String data;
private @Id @GeneratedValue @Getter Long id;
private @ManyToOne User lastModifiedBy;
private Date lastModifiedDate;
@ManyToOne User owner;
String lastModifiedByUsername;
Date lastModifiedDate;
public BusinessObject() {}
private final String data;
private final @ManyToOne User owner;
public BusinessObject(String data, User owner) {
this.data = data;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
public String getLastModifiedByUsername() {
return lastModifiedByUsername;
}
public void setLastModifiedByUsername(String lastModifiedByUsername) {
this.lastModifiedByUsername = lastModifiedByUsername;
}
public Date getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(Date lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BusinessObject that = (BusinessObject) o;
if (data != null ? !data.equals(that.data) : that.data != null)
return false;
if (id != null ? !id.equals(that.id) : that.id != null)
return false;
if (owner != null ? !owner.equals(that.owner) : that.owner != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (data != null ? data.hashCode() : 0);
result = 31 * result + (owner != null ? owner.hashCode() : 0);
return result;
@SuppressWarnings("unused")
private BusinessObject() {
this.data = null;
this.owner = null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@ import org.springframework.data.repository.CrudRepository;
* Repository to manage {@link BusinessObject} instances.
*
* @author Thomas Darimont
* @author Oliver Gierke
*/
public interface BusinessObjectRepository extends CrudRepository<BusinessObject, Long> {
}
interface BusinessObjectRepository extends CrudRepository<BusinessObject, Long> {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,9 @@ import org.springframework.data.repository.Repository;
/**
* @author Thomas Darimont
* @auhtor Oliver Gierke
*/
public interface SecureBusinessObjectRepository extends Repository<BusinessObject,Long>{
interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {
/**
* Here we demonstrate the usage of SpEL expression within a custom query.
@@ -44,7 +45,7 @@ public interface SecureBusinessObjectRepository extends Repository<BusinessObjec
*/
@Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
/**
* Here we apply a dynamic filter condition in there query depending of the role of the current principal.
*
@@ -52,11 +53,11 @@ public interface SecureBusinessObjectRepository extends Repository<BusinessObjec
*/
@Query("select o from BusinessObject o where o.owner.id = ?#{principal.id} or 1=?#{hasRole('ROLE_ADMIN') ? 1 : 0}")
List<BusinessObject> findBusinessObjectsForCurrentUserById();
/**
* Here we demonstrate the use of SecurityContext information in dynamic SPEL parameters in a JPAQL update statement.
* Here we demonstrate the use of SecurityContext information in dynamic SpEL parameters in a JPQL update statement.
*/
@Modifying
@Query("update BusinessObject b set b.data = upper(b.data), b.lastModifiedByUsername = :#{#security.principal.firstname}, b.lastModifiedDate = :#{new java.util.Date()}")
@Query("update BusinessObject b set b.data = upper(b.data), b.lastModifiedBy = :#{#security.principal}, b.lastModifiedDate = :#{new java.util.Date()}")
void modifiyDataWithRecordingSecurityContext();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,10 @@
*/
package example.springdata.jpa.security;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@@ -23,33 +27,18 @@ import javax.persistence.Id;
* @author Thomas Darimont
*/
@Entity
@Data
@RequiredArgsConstructor
public class User {
@Id @GeneratedValue Long id;
private @Id @GeneratedValue @Getter Long id;
private final String firstname, lastname, emailAddress;
String firstname;
String lastname;
String emailAddress;
@SuppressWarnings("unused")
private User() {
public User(String firstname, String lastname, String emailAddress) {
this.firstname = firstname;
this.lastname = lastname;
this.emailAddress = emailAddress;
}
public Long getId() {
return id;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String getEmailAddress() {
return emailAddress;
this.firstname = null;
this.lastname = null;
this.emailAddress = null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,32 +50,25 @@ public class SecurityIntegrationTests {
@Autowired SecureBusinessObjectRepository secureBusinessObjectRepository;
@Autowired EntityManager em;
User tom;
User olli;
User admin;
UsernamePasswordAuthenticationToken olliAuth;
UsernamePasswordAuthenticationToken tomAuth;
UsernamePasswordAuthenticationToken adminAuth;
BusinessObject object1;
BusinessObject object2;
BusinessObject object3;
User tom, ollie, admin;
UsernamePasswordAuthenticationToken olliAuth, tomAuth, adminAuth;
BusinessObject object1, object2, object3;
@Before
public void setup() {
tom = userRepository.save(new User("thomas", "darimont", "tdarimont@example.org"));
olli = userRepository.save(new User("oliver", "gierke", "ogierke@example.org"));
ollie = userRepository.save(new User("oliver", "gierke", "ogierke@example.org"));
admin = userRepository.save(new User("admin", "admin", "admin@example.org"));
object1 = businessObjectRepository.save(new BusinessObject("object1", olli));
object2 = businessObjectRepository.save(new BusinessObject("object2", olli));
object1 = businessObjectRepository.save(new BusinessObject("object1", ollie));
object2 = businessObjectRepository.save(new BusinessObject("object2", ollie));
object3 = businessObjectRepository.save(new BusinessObject("object3", tom));
olliAuth = new UsernamePasswordAuthenticationToken(olli, "x");
olliAuth = new UsernamePasswordAuthenticationToken(ollie, "x");
tomAuth = new UsernamePasswordAuthenticationToken(tom, "x");
adminAuth = new UsernamePasswordAuthenticationToken(admin, "x", singleton(new SimpleGrantedAuthority("ROLE_ADMIN")));
adminAuth = new UsernamePasswordAuthenticationToken(admin, "x",
singleton(new SimpleGrantedAuthority("ROLE_ADMIN")));
}
@Test
@@ -88,7 +81,7 @@ public class SecurityIntegrationTests {
assertThat(businessObjects, hasSize(1));
assertThat(businessObjects, contains(object3));
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(olli, "x"));
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(ollie, "x"));
businessObjects = secureBusinessObjectRepository.findBusinessObjectsForCurrentUser();
@@ -135,23 +128,23 @@ public class SecurityIntegrationTests {
assertThat(businessObjects, hasSize(3));
assertThat(businessObjects, contains(object1, object2, object3));
}
@Test
public void customUpdateStatementShouldAllowToUseSecurityContextInformationViaSpelParameters() {
SecurityContextHolder.getContext().setAuthentication(adminAuth);
//Detaching items to get them out of the query cache in order to see the updated values.
em.detach(object1);
// Detaching items to get them out of the query cache in order to see the updated values.
em.detach(object1);
em.detach(object2);
em.detach(object3);
secureBusinessObjectRepository.modifiyDataWithRecordingSecurityContext();
for(BusinessObject bo : businessObjectRepository.findAll()) {
for (BusinessObject bo : businessObjectRepository.findAll()) {
assertThat(bo.getLastModifiedDate(), is(notNullValue()));
assertThat(bo.getLastModifiedByUsername(), is("admin"));
assertThat(bo.getLastModifiedBy().getFirstname(), is("admin"));
}
}
}