SEC-2676: Add SpEL Spring Security Integration

This commit is contained in:
Rob Winch
2014-07-29 20:04:37 -05:00
parent 4a633a938a
commit 1f861f512a
15 changed files with 600 additions and 2 deletions

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2002-2014 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. 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.springframework.security.data.repository.query;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
import org.springframework.data.repository.query.spi.Function;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Map;
/**
* <p>
* By defining this object as a Bean, Spring Security is exposed as SpEL expressions for creating Spring Data
* queries.
* </p>
*
* <p>With Java based configuration, we can define the bean using the following:</p>
*
* <p>For example, if you return a UserDetails that extends the following User object:</p>
*
* <pre>
* @Entity
* public class User {
* @GeneratedValue(strategy = GenerationType.AUTO)
* @Id
* private Long id;
*
* ...
* </pre>
*
* <p>And you have a Message object that looks like the following:</p>
*
* <pre>
* @Entity
* public class Message {
* @Id
* @GeneratedValue(strategy = GenerationType.AUTO)
* private Long id;
*
* @OneToOne
* private User to;
*
* ...
* </pre>
*
* You can use the following {@code Query} annotation to search for only messages that are to the current user:
*
* <pre>
* @Repository
* public interface SecurityMessageRepository extends MessageRepository {
*
* @Query("select m from Message m where m.to.id = ?#{ principal?.id }")
* List<Message> findAll();
* }
* </pre>
*
* This works because the principal in this instance is a User which has an id field on it.
*
* @since 4.0
* @author Rob Winch
*/
public class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {
private Authentication authentication;
/**
* Creates a new instance that uses the current {@link Authentication} found on the
* {@link org.springframework.security.core.context.SecurityContextHolder}.
*/
public SecurityEvaluationContextExtension() {
}
/**
* Creates a new instance that always uses the same {@link Authentication} object.
*
* @param authentication the {@link Authentication} to use
*/
public SecurityEvaluationContextExtension(Authentication authentication) {
this.authentication = authentication;
}
@Override
public String getExtensionId() {
return "security";
}
@Override
public Object getRootObject() {
Authentication authentication = getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
private Authentication getAuthentication() {
if(authentication != null) {
return authentication;
}
SecurityContext context = SecurityContextHolder.getContext();
return context.getAuthentication();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2002-2014 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. 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.springframework.security.data.repository.query;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.fest.assertions.Assertions.assertThat;
public class SecurityEvaluationContextExtensionTests {
SecurityEvaluationContextExtension securityExtension;
@Before
public void setup() {
securityExtension = new SecurityEvaluationContextExtension();
}
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
@Test(expected = IllegalArgumentException.class)
public void getRootObjectSecurityContextHolderAuthenticationNull() {
getRoot().getAuthentication();
}
@Test
public void getRootObjectSecurityContextHolderAuthentication() {
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
SecurityContextHolder.getContext().setAuthentication(authentication);
assertThat(getRoot().getAuthentication()).isSameAs(authentication);
}
@Test
public void getRootObjectExplicitAuthenticationOverridesSecurityContextHolder() {
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
securityExtension = new SecurityEvaluationContextExtension(explicit);
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
SecurityContextHolder.getContext().setAuthentication(authentication);
assertThat(getRoot().getAuthentication()).isSameAs(explicit);
}
@Test
public void getRootObjectExplicitAuthentication() {
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
securityExtension = new SecurityEvaluationContextExtension(explicit);
assertThat(getRoot().getAuthentication()).isSameAs(explicit);
}
private SecurityExpressionRoot getRoot() {
return (SecurityExpressionRoot) securityExtension.getRootObject();
}
}