Skip to content

Commit 033d09b

Browse files
committed
Add AuthorizeReturnObject Hints
Closes spring-projectsgh-15709
1 parent 4ff76b2 commit 033d09b

File tree

11 files changed

+908
-5
lines changed

11 files changed

+908
-5
lines changed

config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
import org.springframework.context.annotation.Bean;
2727
import org.springframework.context.annotation.Configuration;
2828
import org.springframework.context.annotation.Role;
29+
import org.springframework.security.aot.hint.AuthorizeReturnObjectCoreHintsRegistrar;
30+
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
31+
import org.springframework.security.authorization.AuthorizationProxyFactory;
2932
import org.springframework.security.authorization.method.AuthorizationAdvisor;
3033
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
3134
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
@@ -54,4 +57,10 @@ static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<A
5457
return interceptor;
5558
}
5659

60+
@Bean
61+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
62+
static SecurityHintsRegistrar authorizeReturnObjectHintsRegistrar(AuthorizationProxyFactory proxyFactory) {
63+
return new AuthorizeReturnObjectCoreHintsRegistrar(proxyFactory);
64+
}
65+
5766
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration.aot;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
24+
import org.springframework.aot.generate.GenerationContext;
25+
import org.springframework.aot.hint.RuntimeHints;
26+
import org.springframework.aot.hint.TypeReference;
27+
import org.springframework.aot.test.generate.TestGenerationContext;
28+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.context.aot.ApplicationContextAotGenerator;
32+
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
33+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
34+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
35+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
36+
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
37+
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
38+
import org.springframework.security.config.test.SpringTestContextExtension;
39+
import org.springframework.test.context.junit.jupiter.SpringExtension;
40+
41+
import static org.assertj.core.api.Assertions.assertThat;
42+
43+
/**
44+
* AOT Tests for {@code PrePostMethodSecurityConfiguration}.
45+
*
46+
* @author Evgeniy Cheban
47+
* @author Josh Cummings
48+
*/
49+
@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })
50+
public class EnableMethodSecurityAotTests {
51+
52+
private final ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
53+
54+
private final GenerationContext context = new TestGenerationContext();
55+
56+
@Test
57+
void whenProcessAheadOfTimeThenCreatesAuthorizationProxies() {
58+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
59+
context.register(AppConfig.class);
60+
this.generator.processAheadOfTime(context, this.context);
61+
RuntimeHints hints = this.context.getRuntimeHints();
62+
assertThat(hints.reflection().getTypeHint(TypeReference.of(cglibClassName(Message.class)))).isNotNull();
63+
assertThat(hints.reflection().getTypeHint(TypeReference.of(cglibClassName(User.class)))).isNotNull();
64+
assertThat(hints.proxies()
65+
.jdkProxyHints()
66+
.anyMatch((hint) -> hint.getProxiedInterfaces().contains(TypeReference.of(UserProjection.class)))).isTrue();
67+
}
68+
69+
private static String cglibClassName(Class<?> clazz) {
70+
return clazz.getCanonicalName() + "$$SpringCGLIB$$0";
71+
}
72+
73+
@Configuration
74+
@EnableMethodSecurity
75+
@EnableJpaRepositories
76+
static class AppConfig {
77+
78+
@Bean
79+
DataSource dataSource() {
80+
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
81+
return builder.setType(EmbeddedDatabaseType.HSQL).build();
82+
}
83+
84+
@Bean
85+
LocalContainerEntityManagerFactoryBean entityManagerFactory() {
86+
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
87+
vendorAdapter.setGenerateDdl(true);
88+
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
89+
factory.setJpaVendorAdapter(vendorAdapter);
90+
factory.setPackagesToScan("org.springframework.security.config.annotation.method.configuration.aot");
91+
factory.setDataSource(dataSource());
92+
return factory;
93+
}
94+
95+
}
96+
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration.aot;
18+
19+
import java.time.Instant;
20+
21+
import jakarta.persistence.Entity;
22+
import jakarta.persistence.GeneratedValue;
23+
import jakarta.persistence.GenerationType;
24+
import jakarta.persistence.Id;
25+
import jakarta.persistence.ManyToOne;
26+
27+
import org.springframework.security.access.prepost.PreAuthorize;
28+
import org.springframework.security.authorization.method.AuthorizeReturnObject;
29+
30+
@Entity
31+
public class Message {
32+
33+
@Id
34+
@GeneratedValue(strategy = GenerationType.AUTO)
35+
private Long id;
36+
37+
private String text;
38+
39+
private String summary;
40+
41+
private Instant created = Instant.now();
42+
43+
@ManyToOne
44+
private User to;
45+
46+
@AuthorizeReturnObject
47+
public User getTo() {
48+
return this.to;
49+
}
50+
51+
public void setTo(User to) {
52+
this.to = to;
53+
}
54+
55+
public Long getId() {
56+
return this.id;
57+
}
58+
59+
public void setId(Long id) {
60+
this.id = id;
61+
}
62+
63+
public Instant getCreated() {
64+
return this.created;
65+
}
66+
67+
public void setCreated(Instant created) {
68+
this.created = created;
69+
}
70+
71+
@PreAuthorize("hasAuthority('message:read')")
72+
public String getText() {
73+
return this.text;
74+
}
75+
76+
public void setText(String text) {
77+
this.text = text;
78+
}
79+
80+
@PreAuthorize("hasAuthority('message:read')")
81+
public String getSummary() {
82+
return this.summary;
83+
}
84+
85+
public void setSummary(String summary) {
86+
this.summary = summary;
87+
}
88+
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration.aot;
18+
19+
import org.springframework.data.jpa.repository.Query;
20+
import org.springframework.data.repository.CrudRepository;
21+
import org.springframework.security.authorization.method.AuthorizeReturnObject;
22+
import org.springframework.stereotype.Repository;
23+
24+
/**
25+
* A repository for accessing {@link Message}s.
26+
*
27+
* @author Rob Winch
28+
*/
29+
@Repository
30+
@AuthorizeReturnObject
31+
public interface MessageRepository extends CrudRepository<Message, Long> {
32+
33+
@Query("select m from Message m where m.to.id = ?#{ authentication.name }")
34+
Iterable<Message> findAll();
35+
36+
@Query("from org.springframework.security.config.annotation.method.configuration.aot.User u where u.id = ?#{ authentication.name }")
37+
UserProjection findCurrentUser();
38+
39+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration.aot;
18+
19+
import jakarta.persistence.Entity;
20+
import jakarta.persistence.Id;
21+
22+
import org.springframework.security.access.prepost.PreAuthorize;
23+
24+
/**
25+
* A user.
26+
*
27+
* @author Rob Winch
28+
*/
29+
@Entity(name = "users")
30+
public class User {
31+
32+
@Id
33+
private String id;
34+
35+
private String firstName;
36+
37+
private String lastName;
38+
39+
private String email;
40+
41+
private String password;
42+
43+
public String getId() {
44+
return this.id;
45+
}
46+
47+
public void setId(String id) {
48+
this.id = id;
49+
}
50+
51+
@PreAuthorize("hasAuthority('user:read')")
52+
public String getFirstName() {
53+
return this.firstName;
54+
}
55+
56+
public void setFirstName(String firstName) {
57+
this.firstName = firstName;
58+
}
59+
60+
@PreAuthorize("hasAuthority('user:read')")
61+
public String getLastName() {
62+
return this.lastName;
63+
}
64+
65+
public void setLastName(String lastName) {
66+
this.lastName = lastName;
67+
}
68+
69+
public String getEmail() {
70+
return this.email;
71+
}
72+
73+
public void setEmail(String email) {
74+
this.email = email;
75+
}
76+
77+
public String getPassword() {
78+
return this.password;
79+
}
80+
81+
public void setPassword(String password) {
82+
this.password = password;
83+
}
84+
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration.aot;
18+
19+
public interface UserProjection {
20+
21+
String getFirstName();
22+
23+
String getLastName();
24+
25+
}

0 commit comments

Comments
 (0)